Merge "Improve shortcut backup & restore."
diff --git a/Android.mk b/Android.mk
index 98e4299..6186c55 100644
--- a/Android.mk
+++ b/Android.mk
@@ -548,9 +548,10 @@
telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl \
wifi/java/android/net/wifi/IWifiManager.aidl \
- wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl \
- wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl \
wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl \
+ wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl \
+ wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl \
+ wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl \
wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
wifi/java/android/net/wifi/rtt/IRttCallback.aidl \
wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl \
@@ -1030,6 +1031,7 @@
-since $(SRC_API_DIR)/24.txt 24 \
-since $(SRC_API_DIR)/25.txt 25 \
-since $(SRC_API_DIR)/26.txt 26 \
+ -since $(SRC_API_DIR)/27.txt 27 \
-werror -lerror -hide 111 -hide 113 -hide 121 -hide 125 -hide 126 -hide 127 -hide 128 \
-overview $(LOCAL_PATH)/core/java/overview.html \
diff --git a/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
new file mode 100644
index 0000000..586c385
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/BoringLayoutCreateDrawPerfTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.text;
+
+import static android.text.Layout.Alignment.ALIGN_NORMAL;
+
+import android.graphics.Canvas;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.text.NonEditableTextGenerator.TextType;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Performance test for {@link BoringLayout} create and draw.
+ */
+@LargeTest
+@RunWith(Parameterized.class)
+public class BoringLayoutCreateDrawPerfTest {
+
+ private static final boolean[] BOOLEANS = new boolean[]{false, true};
+ private static final float SPACING_ADD = 10f;
+ private static final float SPACING_MULT = 1.5f;
+
+ @Parameterized.Parameters(name = "cached={3},{1}chars,{0}")
+ public static Collection cases() {
+ final List<Object[]> params = new ArrayList<>();
+ for (int length : new int[]{128}) {
+ for (boolean cached : BOOLEANS) {
+ for (TextType textType : new TextType[]{TextType.STRING,
+ TextType.SPANNABLE_BUILDER}) {
+ params.add(new Object[]{textType.name(), length, textType, cached});
+ }
+ }
+ }
+ return params;
+ }
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private final int mLength;
+ private final TextType mTextType;
+ private final boolean mCached;
+ private final TextPaint mTextPaint;
+
+ public BoringLayoutCreateDrawPerfTest(String label, int length, TextType textType,
+ boolean cached) {
+ mLength = length;
+ mCached = cached;
+ mTextType = textType;
+ mTextPaint = new TextPaint();
+ mTextPaint.setTextSize(10);
+ }
+
+ /**
+ * Measures the creation time for {@link BoringLayout}.
+ */
+ @Test
+ public void timeCreate() throws Exception {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ final CharSequence text = createRandomText();
+ // isBoring result is calculated in another test, we want to measure only the
+ // create time for Boring without isBoring check. Therefore it is calculated here.
+ final BoringLayout.Metrics metrics = BoringLayout.isBoring(text, mTextPaint);
+ if (mCached) createLayout(text, metrics);
+ state.resumeTiming();
+
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ if (!mCached) Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+
+ createLayout(text, metrics);
+ }
+ }
+
+ /**
+ * Measures the draw time for {@link BoringLayout} or {@link StaticLayout}.
+ */
+ @Test
+ public void timeDraw() throws Throwable {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ final RenderNode node = RenderNode.create("benchmark", null);
+ final CharSequence text = createRandomText();
+ final BoringLayout.Metrics metrics = BoringLayout.isBoring(text, mTextPaint);
+ final Layout layout = createLayout(text, metrics);
+ state.resumeTiming();
+
+ while (state.keepRunning()) {
+
+ state.pauseTiming();
+ final DisplayListCanvas canvas = node.start(1200, 200);
+ final int save = canvas.save();
+ if (!mCached) Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+
+ layout.draw(canvas);
+
+ state.pauseTiming();
+ canvas.restoreToCount(save);
+ node.end(canvas);
+ state.resumeTiming();
+ }
+ }
+
+ private CharSequence createRandomText() {
+ return new NonEditableTextGenerator(new Random(0))
+ .setSequenceLength(mLength)
+ .setCreateBoring(true)
+ .setTextType(mTextType)
+ .build();
+ }
+
+ private Layout createLayout(CharSequence text,
+ BoringLayout.Metrics metrics) {
+ return BoringLayout.make(text, mTextPaint, Integer.MAX_VALUE /*width*/,
+ ALIGN_NORMAL, SPACING_MULT, SPACING_ADD, metrics, true /*includePad*/);
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java b/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java
new file mode 100644
index 0000000..9d11f29
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/BoringLayoutIsBoringPerfTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.text;
+
+import android.graphics.Canvas;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.text.NonEditableTextGenerator.TextType;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Performance test for {@link BoringLayout#isBoring(CharSequence, TextPaint)}.
+ */
+@LargeTest
+@RunWith(Parameterized.class)
+public class BoringLayoutIsBoringPerfTest {
+
+ private static final boolean[] BOOLEANS = new boolean[]{false, true};
+
+ @Parameterized.Parameters(name = "cached={4},{1}chars,{0}")
+ public static Collection cases() {
+ final List<Object[]> params = new ArrayList<>();
+ for (int length : new int[]{128}) {
+ for (boolean boring : BOOLEANS) {
+ for (boolean cached : BOOLEANS) {
+ for (TextType textType : new TextType[]{TextType.STRING,
+ TextType.SPANNABLE_BUILDER}) {
+ params.add(new Object[]{
+ (boring ? "Boring" : "NotBoring") + "," + textType.name(),
+ length, boring, textType, cached});
+ }
+ }
+ }
+ }
+ return params;
+ }
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private final int mLength;
+ private final TextType mTextType;
+ private final boolean mCreateBoring;
+ private final boolean mCached;
+ private final TextPaint mTextPaint;
+
+ public BoringLayoutIsBoringPerfTest(String label, int length, boolean boring, TextType textType,
+ boolean cached) {
+ mLength = length;
+ mCreateBoring = boring;
+ mCached = cached;
+ mTextType = textType;
+ mTextPaint = new TextPaint();
+ mTextPaint.setTextSize(10);
+ }
+
+ /**
+ * Measure the time for the {@link BoringLayout#isBoring(CharSequence, TextPaint)}.
+ */
+ @Test
+ public void timeIsBoring() throws Exception {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ final CharSequence text = createRandomText();
+ if (mCached) BoringLayout.isBoring(text, mTextPaint);
+ state.resumeTiming();
+
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ if (!mCached) Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+
+ BoringLayout.isBoring(text, mTextPaint);
+ }
+ }
+
+ private CharSequence createRandomText() {
+ return new NonEditableTextGenerator(new Random(0))
+ .setSequenceLength(mLength)
+ .setCreateBoring(mCreateBoring)
+ .setTextType(mTextType)
+ .build();
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/text/NonEditableTextGenerator.java b/apct-tests/perftests/core/src/android/text/NonEditableTextGenerator.java
new file mode 100644
index 0000000..7c0cf0e
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/NonEditableTextGenerator.java
@@ -0,0 +1,138 @@
+package android.text;
+
+import static android.text.Spanned.SPAN_INCLUSIVE_INCLUSIVE;
+
+import android.text.style.BulletSpan;
+
+import java.util.Random;
+
+/**
+ *
+ */
+public class NonEditableTextGenerator {
+
+ enum TextType {
+ STRING,
+ SPANNED,
+ SPANNABLE_BUILDER
+ }
+
+ private boolean mCreateBoring;
+ private TextType mTextType;
+ private int mSequenceLength;
+ private final Random mRandom;
+
+ public NonEditableTextGenerator(Random random) {
+ mRandom = random;
+ }
+
+ public NonEditableTextGenerator setCreateBoring(boolean createBoring) {
+ mCreateBoring = createBoring;
+ return this;
+ }
+
+ public NonEditableTextGenerator setTextType(TextType textType) {
+ mTextType = textType;
+ return this;
+ }
+
+ public NonEditableTextGenerator setSequenceLength(int sequenceLength) {
+ mSequenceLength = sequenceLength;
+ return this;
+ }
+
+ /**
+ * Sample charSequence generated:
+ * NRjPzjvUadHmH ExoEoTqfx pCLw qtndsqfpk AqajVCbgjGZ igIeC dfnXRgA
+ */
+ public CharSequence build() {
+ final RandomCharSequenceGenerator sequenceGenerator = new RandomCharSequenceGenerator(
+ mRandom);
+ if (mSequenceLength > 0) {
+ sequenceGenerator.setSequenceLength(mSequenceLength);
+ }
+
+ final CharSequence charSequence = sequenceGenerator.buildLatinSequence();
+
+ switch (mTextType) {
+ case SPANNED:
+ case SPANNABLE_BUILDER:
+ return createSpannable(charSequence);
+ case STRING:
+ default:
+ return createString(charSequence);
+ }
+ }
+
+ private Spannable createSpannable(CharSequence charSequence) {
+ final Spannable spannable = (mTextType == TextType.SPANNABLE_BUILDER) ?
+ new SpannableStringBuilder(charSequence) : new SpannableString(charSequence);
+
+ if (!mCreateBoring) {
+ // add a paragraph style to make it non boring
+ spannable.setSpan(new BulletSpan(), 0, spannable.length(), SPAN_INCLUSIVE_INCLUSIVE);
+ }
+
+ spannable.setSpan(new Object(), 0, spannable.length(), SPAN_INCLUSIVE_INCLUSIVE);
+ spannable.setSpan(new Object(), 0, 1, SPAN_INCLUSIVE_INCLUSIVE);
+
+ return spannable;
+ }
+
+ private String createString(CharSequence charSequence) {
+ if (mCreateBoring) {
+ return charSequence.toString();
+ } else {
+ // BoringLayout checks to see if there is a surrogate pair and if so tells that
+ // the charSequence is not suitable for boring. Add an emoji to make it non boring.
+ // Emoji is added instead of RTL, since emoji stays in the same run and is a more
+ // common case.
+ return charSequence.toString() + "\uD83D\uDC68\uD83C\uDFFF";
+ }
+ }
+
+ public static class RandomCharSequenceGenerator {
+
+ private static final int DEFAULT_MIN_WORD_LENGTH = 3;
+ private static final int DEFAULT_MAX_WORD_LENGTH = 15;
+ private static final int DEFAULT_SEQUENCE_LENGTH = 256;
+
+ private int mMinWordLength = DEFAULT_MIN_WORD_LENGTH;
+ private int mMaxWordLength = DEFAULT_MAX_WORD_LENGTH;
+ private int mSequenceLength = DEFAULT_SEQUENCE_LENGTH;
+ private final Random mRandom;
+
+ public RandomCharSequenceGenerator(Random random) {
+ mRandom = random;
+ }
+
+ public RandomCharSequenceGenerator setSequenceLength(int sequenceLength) {
+ mSequenceLength = sequenceLength;
+ return this;
+ }
+
+ public CharSequence buildLatinSequence() {
+ final StringBuilder result = new StringBuilder();
+ while (result.length() < mSequenceLength) {
+ // add random word
+ result.append(buildLatinWord());
+ result.append(' ');
+ }
+ return result.substring(0, mSequenceLength);
+ }
+
+ public CharSequence buildLatinWord() {
+ final StringBuilder result = new StringBuilder();
+ // create a random length that is (mMinWordLength + random amount of chars) where
+ // total size is less than mMaxWordLength
+ final int length = mRandom.nextInt(mMaxWordLength - mMinWordLength) + mMinWordLength;
+ while (result.length() < length) {
+ // add random letter
+ int base = mRandom.nextInt(2) == 0 ? 'A' : 'a';
+ result.append(Character.toChars(mRandom.nextInt(26) + base));
+ }
+ return result.toString();
+ }
+ }
+
+}
diff --git a/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
new file mode 100644
index 0000000..6768798
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/PaintMeasureDrawPerfTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.text;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Performance test for single line measure and draw using {@link Paint} and {@link Canvas}.
+ */
+@LargeTest
+@RunWith(Parameterized.class)
+public class PaintMeasureDrawPerfTest {
+
+ private static final boolean[] BOOLEANS = new boolean[]{false, true};
+
+ @Parameterized.Parameters(name = "cached={1},{0}chars")
+ public static Collection cases() {
+ final List<Object[]> params = new ArrayList<>();
+ for (int length : new int[]{128}) {
+ for (boolean cached : BOOLEANS) {
+ params.add(new Object[]{length, cached});
+ }
+ }
+ return params;
+ }
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private final int mLength;
+ private final boolean mCached;
+ private final TextPaint mTextPaint;
+
+
+ public PaintMeasureDrawPerfTest(int length, boolean cached) {
+ mLength = length;
+ mCached = cached;
+ mTextPaint = new TextPaint();
+ mTextPaint.setTextSize(10);
+ }
+
+ /**
+ * Measure the time for {@link Paint#measureText(String)}
+ */
+ @Test
+ public void timeMeasure() throws Exception {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ final String text = createRandomText();
+ if (mCached) mTextPaint.measureText(text);
+ state.resumeTiming();
+
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ if (!mCached) Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+
+ mTextPaint.measureText(text);
+ }
+ }
+
+ /**
+ * Measures the time for {@link Canvas#drawText(String, float, float, Paint)}
+ */
+ @Test
+ public void timeDraw() throws Throwable {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ final RenderNode node = RenderNode.create("benchmark", null);
+ final String text = createRandomText();
+ if (mCached) mTextPaint.measureText(text);
+ state.resumeTiming();
+
+ while (state.keepRunning()) {
+
+ state.pauseTiming();
+ final DisplayListCanvas canvas = node.start(1200, 200);
+ final int save = canvas.save();
+ if (!mCached) Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+
+ canvas.drawText(text, 0 /*x*/, 100 /*y*/, mTextPaint);
+
+ state.pauseTiming();
+ canvas.restoreToCount(save);
+ node.end(canvas);
+ state.resumeTiming();
+ }
+ }
+
+ private String createRandomText() {
+ return (String) new NonEditableTextGenerator(new Random(0))
+ .setSequenceLength(mLength)
+ .setCreateBoring(true)
+ .setTextType(NonEditableTextGenerator.TextType.STRING)
+ .build();
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
new file mode 100644
index 0000000..bfdb758
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/StaticLayoutCreateDrawPerfTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.text;
+
+import static android.text.Layout.Alignment.ALIGN_NORMAL;
+
+import android.graphics.Canvas;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.filters.LargeTest;
+import android.text.NonEditableTextGenerator.TextType;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Performance test for multi line, single style {@link StaticLayout} creation/draw.
+ */
+@LargeTest
+@RunWith(Parameterized.class)
+public class StaticLayoutCreateDrawPerfTest {
+
+ private static final boolean[] BOOLEANS = new boolean[]{false, true};
+
+ private static final float SPACING_ADD = 10f;
+ private static final float SPACING_MULT = 1.5f;
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Parameterized.Parameters(name = "cached={3},{1}chars,{0}")
+ public static Collection cases() {
+ final List<Object[]> params = new ArrayList<>();
+ for (int length : new int[]{128}) {
+ for (boolean cached : BOOLEANS) {
+ for (TextType textType : new TextType[]{TextType.STRING,
+ TextType.SPANNABLE_BUILDER}) {
+ params.add(new Object[]{textType.name(), length, textType, cached});
+ }
+ }
+ }
+ return params;
+ }
+
+ private final int mLineWidth;
+ private final int mLength;
+ private final TextType mTextType;
+ private final boolean mCached;
+ private final TextPaint mTextPaint;
+
+ public StaticLayoutCreateDrawPerfTest(String label, int length, TextType textType,
+ boolean cached) {
+ mLength = length;
+ mTextType = textType;
+ mCached = cached;
+ mTextPaint = new TextPaint();
+ mTextPaint.setTextSize(10);
+ mLineWidth = Integer.MAX_VALUE;
+ }
+
+ /**
+ * Measures the creation time for a multi line {@link StaticLayout}.
+ */
+ @Test
+ public void timeCreate() throws Exception {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ final CharSequence text = createRandomText(mLength);
+ createLayout(text);
+ state.resumeTiming();
+
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ if (!mCached) Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+
+ createLayout(text);
+ }
+ }
+
+ /**
+ * Measures the draw time for a multi line {@link StaticLayout}.
+ */
+ @Test
+ public void timeDraw() throws Exception {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ final RenderNode node = RenderNode.create("benchmark", null);
+ final CharSequence text = createRandomText(mLength);
+ final Layout layout = createLayout(text);
+ state.resumeTiming();
+
+ while (state.keepRunning()) {
+
+ state.pauseTiming();
+ final DisplayListCanvas canvas = node.start(1200, 200);
+ int save = canvas.save();
+ if (!mCached) Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+
+ layout.draw(canvas);
+
+ state.pauseTiming();
+ canvas.restoreToCount(save);
+ node.end(canvas);
+ state.resumeTiming();
+ }
+ }
+
+ private Layout createLayout(CharSequence text) {
+ return StaticLayout.Builder.obtain(text, 0 /*start*/, text.length() /*end*/, mTextPaint,
+ mLineWidth)
+ .setAlignment(ALIGN_NORMAL)
+ .setIncludePad(true)
+ .setLineSpacing(SPACING_ADD, SPACING_MULT)
+ .build();
+ }
+
+ private CharSequence createRandomText(int length) {
+ return new NonEditableTextGenerator(new Random(0))
+ .setSequenceLength(length)
+ .setTextType(mTextType)
+ .build();
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
new file mode 100644
index 0000000..ff2d57e
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/text/TextViewSetTextMeasurePerfTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.text;
+
+import static android.view.View.MeasureSpec.AT_MOST;
+import static android.view.View.MeasureSpec.UNSPECIFIED;
+
+import android.graphics.Canvas;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.text.NonEditableTextGenerator.TextType;
+import android.view.DisplayListCanvas;
+import android.view.RenderNode;
+import android.widget.TextView;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.Random;
+
+/**
+ * Performance test for {@link TextView} measure/draw.
+ */
+@LargeTest
+@RunWith(Parameterized.class)
+public class TextViewSetTextMeasurePerfTest {
+
+ private static final boolean[] BOOLEANS = new boolean[]{false, true};
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Parameterized.Parameters(name = "cached={3},{1}chars,{0}")
+ public static Collection cases() {
+ final List<Object[]> params = new ArrayList<>();
+ for (int length : new int[]{128}) {
+ for (boolean cached : BOOLEANS) {
+ for (TextType textType : new TextType[]{TextType.STRING,
+ TextType.SPANNABLE_BUILDER}) {
+ params.add(new Object[]{textType.name(), length, textType, cached});
+ }
+ }
+ }
+ return params;
+ }
+
+ private final int mLineWidth;
+ private final int mLength;
+ private final TextType mTextType;
+ private final boolean mCached;
+ private final TextPaint mTextPaint;
+
+ public TextViewSetTextMeasurePerfTest(String label, int length, TextType textType,
+ boolean cached) {
+ mLength = length;
+ mTextType = textType;
+ mCached = cached;
+ mTextPaint = new TextPaint();
+ mTextPaint.setTextSize(10);
+ mLineWidth = Integer.MAX_VALUE;
+ }
+
+ /**
+ * Measures the time to setText and measure for a {@link TextView}.
+ */
+ @Test
+ public void timeCreate() throws Exception {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ final CharSequence text = createRandomText(mLength);
+ final TextView textView = new TextView(InstrumentationRegistry.getTargetContext());
+ textView.setText(text);
+ state.resumeTiming();
+
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ textView.setTextLocale(Locale.UK);
+ textView.setTextLocale(Locale.US);
+ if (!mCached) Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+
+ textView.setText(text);
+ textView.measure(AT_MOST | mLineWidth, UNSPECIFIED);
+ }
+ }
+
+ /**
+ * Measures the time to draw for a {@link TextView}.
+ */
+ @Test
+ public void timeDraw() throws Exception {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+ state.pauseTiming();
+ Canvas.freeTextLayoutCaches();
+ final RenderNode node = RenderNode.create("benchmark", null);
+ final CharSequence text = createRandomText(mLength);
+ final TextView textView = new TextView(InstrumentationRegistry.getTargetContext());
+ textView.setText(text);
+ state.resumeTiming();
+
+ while (state.keepRunning()) {
+
+ state.pauseTiming();
+ final DisplayListCanvas canvas = node.start(1200, 200);
+ int save = canvas.save();
+ textView.setTextLocale(Locale.UK);
+ textView.setTextLocale(Locale.US);
+ if (!mCached) Canvas.freeTextLayoutCaches();
+ state.resumeTiming();
+
+ textView.draw(canvas);
+
+ state.pauseTiming();
+ canvas.restoreToCount(save);
+ node.end(canvas);
+ state.resumeTiming();
+ }
+ }
+
+ private CharSequence createRandomText(int length) {
+ return new NonEditableTextGenerator(new Random(0))
+ .setSequenceLength(length)
+ .setCreateBoring(false)
+ .setTextType(mTextType)
+ .build();
+ }
+}
diff --git a/apct-tests/perftests/multiuser/Android.mk b/apct-tests/perftests/multiuser/Android.mk
index e3f7775..2db0dd6 100644
--- a/apct-tests/perftests/multiuser/Android.mk
+++ b/apct-tests/perftests/multiuser/Android.mk
@@ -20,7 +20,8 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test
+ android-support-test \
+ ub-uiautomator
LOCAL_PACKAGE_NAME := MultiUserPerfTests
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
index c7bebf3..629e6f4 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
@@ -17,13 +17,16 @@
import android.os.Bundle;
import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.UiDevice;
+import java.io.IOException;
import java.util.ArrayList;
// Based on //platform/frameworks/base/apct-tests/perftests/utils/BenchmarkState.java
public class BenchmarkRunner {
- private static long COOL_OFF_PERIOD_MS = 2000;
+ private static final long COOL_OFF_PERIOD_MS = 1000;
private static final int NUM_ITERATIONS = 4;
@@ -70,9 +73,13 @@
}
private void prepareForNextRun() {
- // TODO: Once http://b/63115387 is fixed, look into using "am wait-for-broadcast-idle"
- // command instead of waiting for a fixed amount of time.
SystemClock.sleep(COOL_OFF_PERIOD_MS);
+ try {
+ UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+ .executeShellCommand("am wait-for-broadcast-idle");
+ } catch (IOException e) {
+ throw new IllegalStateException("Cannot execute shell command", e);
+ }
mStartTimeNs = System.nanoTime();
mPausedDurationNs = 0;
}
diff --git a/api/current.txt b/api/current.txt
index e2ed40e..a2e242d 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -362,6 +362,7 @@
field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
field public static final int canRetrieveWindowContent = 16843653; // 0x1010385
field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230
+ field public static final int cantSaveState = 16844142; // 0x101056e
field public static final deprecated int capitalize = 16843113; // 0x1010169
field public static final int category = 16843752; // 0x10103e8
field public static final int centerBright = 16842956; // 0x10100cc
@@ -31842,6 +31843,7 @@
field public static final java.lang.String DISALLOW_SET_WALLPAPER = "no_set_wallpaper";
field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location";
field public static final java.lang.String DISALLOW_SMS = "no_sms";
+ field public static final java.lang.String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
@@ -37214,8 +37216,10 @@
}
public static class ImageTransformation.Builder {
- ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
- method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+ ctor public deprecated ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
+ ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int, java.lang.CharSequence);
+ method public deprecated android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+ method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int, java.lang.CharSequence);
method public android.service.autofill.ImageTransformation build();
}
@@ -37233,6 +37237,9 @@
field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR;
}
+ public abstract interface Sanitizer {
+ }
+
public final class SaveCallback {
method public void onFailure(java.lang.CharSequence);
method public void onSuccess();
@@ -37256,6 +37263,7 @@
public static final class SaveInfo.Builder {
ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
ctor public SaveInfo.Builder(int);
+ method public android.service.autofill.SaveInfo.Builder addSanitizer(android.service.autofill.Sanitizer, android.view.autofill.AutofillId...);
method public android.service.autofill.SaveInfo build();
method public android.service.autofill.SaveInfo.Builder setCustomDescription(android.service.autofill.CustomDescription);
method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
@@ -37274,6 +37282,13 @@
field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
}
+ public final class TextValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
+ ctor public TextValueSanitizer(java.util.regex.Pattern, java.lang.String);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.TextValueSanitizer> CREATOR;
+ }
+
public abstract interface Transformation {
}
@@ -37613,8 +37628,12 @@
method public java.lang.String getOverrideGroupKey();
method public int getRank();
method public int getSuppressedVisualEffects();
+ method public int getUserSentiment();
method public boolean isAmbient();
method public boolean matchesInterruptionFilter();
+ field public static final int USER_SENTIMENT_NEGATIVE = -1; // 0xffffffff
+ field public static final int USER_SENTIMENT_NEUTRAL = 0; // 0x0
+ field public static final int USER_SENTIMENT_POSITIVE = 1; // 0x1
}
public static class NotificationListenerService.RankingMap implements android.os.Parcelable {
@@ -40024,8 +40043,12 @@
field public static final java.lang.String EXTRA_MBMS_DOWNLOAD_RESULT = "android.telephony.extra.MBMS_DOWNLOAD_RESULT";
field public static final java.lang.String EXTRA_MBMS_FILE_INFO = "android.telephony.extra.MBMS_FILE_INFO";
field public static final int RESULT_CANCELLED = 2; // 0x2
+ field public static final int RESULT_DOWNLOAD_FAILURE = 6; // 0x6
field public static final int RESULT_EXPIRED = 3; // 0x3
+ field public static final int RESULT_FILE_ROOT_UNREACHABLE = 8; // 0x8
field public static final int RESULT_IO_ERROR = 4; // 0x4
+ field public static final int RESULT_OUT_OF_STORAGE = 7; // 0x7
+ field public static final int RESULT_SERVICE_ID_NOT_DEFINED = 5; // 0x5
field public static final int RESULT_SUCCESSFUL = 1; // 0x1
field public static final int STATUS_ACTIVELY_DOWNLOADING = 1; // 0x1
field public static final int STATUS_PENDING_DOWNLOAD = 2; // 0x2
@@ -40692,11 +40715,10 @@
}
public static class DownloadRequest.Builder {
- ctor public DownloadRequest.Builder();
+ ctor public DownloadRequest.Builder(android.net.Uri);
method public android.telephony.mbms.DownloadRequest build();
method public android.telephony.mbms.DownloadRequest.Builder setAppIntent(android.content.Intent);
method public android.telephony.mbms.DownloadRequest.Builder setServiceInfo(android.telephony.mbms.FileServiceInfo);
- method public android.telephony.mbms.DownloadRequest.Builder setSource(android.net.Uri);
method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int);
}
@@ -45736,8 +45758,8 @@
method protected boolean awakenScrollBars(int);
method protected boolean awakenScrollBars(int, boolean);
method public void bringToFront();
- method public void buildDrawingCache();
- method public void buildDrawingCache(boolean);
+ method public deprecated void buildDrawingCache();
+ method public deprecated void buildDrawingCache(boolean);
method public void buildLayer();
method public boolean callOnClick();
method public boolean canResolveLayoutDirection();
@@ -45762,7 +45784,7 @@
method protected int computeVerticalScrollRange();
method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
method public void createContextMenu(android.view.ContextMenu);
- method public void destroyDrawingCache();
+ method public deprecated void destroyDrawingCache();
method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
method public boolean dispatchCapturedPointerEvent(android.view.MotionEvent);
method public void dispatchConfigurationChanged(android.content.res.Configuration);
@@ -45843,10 +45865,10 @@
method public static int getDefaultSize(int, int);
method public android.view.Display getDisplay();
method public final int[] getDrawableState();
- method public android.graphics.Bitmap getDrawingCache();
- method public android.graphics.Bitmap getDrawingCache(boolean);
- method public int getDrawingCacheBackgroundColor();
- method public int getDrawingCacheQuality();
+ method public deprecated android.graphics.Bitmap getDrawingCache();
+ method public deprecated android.graphics.Bitmap getDrawingCache(boolean);
+ method public deprecated int getDrawingCacheBackgroundColor();
+ method public deprecated int getDrawingCacheQuality();
method public void getDrawingRect(android.graphics.Rect);
method public long getDrawingTime();
method public float getElevation();
@@ -45974,8 +45996,8 @@
method public boolean hasTransientState();
method public boolean hasWindowFocus();
method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup);
- method public void invalidate(android.graphics.Rect);
- method public void invalidate(int, int, int, int);
+ method public deprecated void invalidate(android.graphics.Rect);
+ method public deprecated void invalidate(int, int, int, int);
method public void invalidate();
method public void invalidateDrawable(android.graphics.drawable.Drawable);
method public void invalidateOutline();
@@ -45985,7 +46007,7 @@
method public boolean isClickable();
method public boolean isContextClickable();
method public boolean isDirty();
- method public boolean isDrawingCacheEnabled();
+ method public deprecated boolean isDrawingCacheEnabled();
method public boolean isDuplicateParentStateEnabled();
method public boolean isEnabled();
method public final boolean isFocusable();
@@ -46156,9 +46178,9 @@
method public void setContentDescription(java.lang.CharSequence);
method public void setContextClickable(boolean);
method public void setDefaultFocusHighlightEnabled(boolean);
- method public void setDrawingCacheBackgroundColor(int);
- method public void setDrawingCacheEnabled(boolean);
- method public void setDrawingCacheQuality(int);
+ method public deprecated void setDrawingCacheBackgroundColor(int);
+ method public deprecated void setDrawingCacheEnabled(boolean);
+ method public deprecated void setDrawingCacheQuality(int);
method public void setDuplicateParentStateEnabled(boolean);
method public void setElevation(float);
method public void setEnabled(boolean);
@@ -46309,9 +46331,9 @@
field public static final int DRAG_FLAG_GLOBAL_URI_READ = 1; // 0x1
field public static final int DRAG_FLAG_GLOBAL_URI_WRITE = 2; // 0x2
field public static final int DRAG_FLAG_OPAQUE = 512; // 0x200
- field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
- field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
- field public static final int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
+ field public static final deprecated int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
+ field public static final deprecated int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
+ field public static final deprecated int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
field protected static final int[] EMPTY_STATE_SET;
field protected static final int[] ENABLED_FOCUSED_SELECTED_STATE_SET;
field protected static final int[] ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
@@ -46703,7 +46725,7 @@
method public android.animation.LayoutTransition getLayoutTransition();
method public int getNestedScrollAxes();
method public android.view.ViewGroupOverlay getOverlay();
- method public int getPersistentDrawingCache();
+ method public deprecated int getPersistentDrawingCache();
method public boolean getTouchscreenBlocksFocus();
method public int indexOfChild(android.view.View);
method public final deprecated void invalidateChild(android.view.View, android.graphics.Rect);
@@ -46756,7 +46778,7 @@
method public void setAddStatesFromChildren(boolean);
method public deprecated void setAlwaysDrawnWithCacheEnabled(boolean);
method public deprecated void setAnimationCacheEnabled(boolean);
- method protected void setChildrenDrawingCacheEnabled(boolean);
+ method protected deprecated void setChildrenDrawingCacheEnabled(boolean);
method protected void setChildrenDrawingOrderEnabled(boolean);
method protected deprecated void setChildrenDrawnWithCacheEnabled(boolean);
method public void setClipChildren(boolean);
@@ -46768,7 +46790,7 @@
method public void setLayoutTransition(android.animation.LayoutTransition);
method public void setMotionEventSplittingEnabled(boolean);
method public void setOnHierarchyChangeListener(android.view.ViewGroup.OnHierarchyChangeListener);
- method public void setPersistentDrawingCache(int);
+ method public deprecated void setPersistentDrawingCache(int);
method protected void setStaticTransformationsEnabled(boolean);
method public void setTouchscreenBlocksFocus(boolean);
method public void setTransitionGroup(boolean);
@@ -46786,10 +46808,10 @@
field public static final int FOCUS_BLOCK_DESCENDANTS = 393216; // 0x60000
field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0
field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1
- field public static final int PERSISTENT_ALL_CACHES = 3; // 0x3
- field public static final int PERSISTENT_ANIMATION_CACHE = 1; // 0x1
- field public static final int PERSISTENT_NO_CACHE = 0; // 0x0
- field public static final int PERSISTENT_SCROLLING_CACHE = 2; // 0x2
+ field public static final deprecated int PERSISTENT_ALL_CACHES = 3; // 0x3
+ field public static final deprecated int PERSISTENT_ANIMATION_CACHE = 1; // 0x1
+ field public static final deprecated int PERSISTENT_NO_CACHE = 0; // 0x0
+ field public static final deprecated int PERSISTENT_SCROLLING_CACHE = 2; // 0x2
}
public static class ViewGroup.LayoutParams {
diff --git a/api/system-current.txt b/api/system-current.txt
index a072422..1e2ce4fa 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -494,6 +494,7 @@
field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
field public static final int canRetrieveWindowContent = 16843653; // 0x1010385
field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230
+ field public static final int cantSaveState = 16844142; // 0x101056e
field public static final deprecated int capitalize = 16843113; // 0x1010169
field public static final int category = 16843752; // 0x10103e8
field public static final int centerBright = 16842956; // 0x10100cc
@@ -27225,7 +27226,6 @@
}
public static final class TvInputManager.Hardware {
- method public boolean dispatchKeyEventToHdmi(android.view.KeyEvent);
method public void overrideAudioSink(int, java.lang.String, int, int, int);
method public void setStreamVolume(float);
method public boolean setSurface(android.view.Surface, android.media.tv.TvStreamConfig);
@@ -34686,6 +34686,7 @@
field public static final java.lang.String DISALLOW_SET_WALLPAPER = "no_set_wallpaper";
field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location";
field public static final java.lang.String DISALLOW_SMS = "no_sms";
+ field public static final java.lang.String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
@@ -40310,8 +40311,10 @@
}
public static class ImageTransformation.Builder {
- ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
- method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+ ctor public deprecated ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
+ ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int, java.lang.CharSequence);
+ method public deprecated android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+ method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int, java.lang.CharSequence);
method public android.service.autofill.ImageTransformation build();
}
@@ -40329,6 +40332,9 @@
field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR;
}
+ public abstract interface Sanitizer {
+ }
+
public final class SaveCallback {
method public void onFailure(java.lang.CharSequence);
method public void onSuccess();
@@ -40352,6 +40358,7 @@
public static final class SaveInfo.Builder {
ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
ctor public SaveInfo.Builder(int);
+ method public android.service.autofill.SaveInfo.Builder addSanitizer(android.service.autofill.Sanitizer, android.view.autofill.AutofillId...);
method public android.service.autofill.SaveInfo build();
method public android.service.autofill.SaveInfo.Builder setCustomDescription(android.service.autofill.CustomDescription);
method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
@@ -40370,6 +40377,13 @@
field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
}
+ public final class TextValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
+ ctor public TextValueSanitizer(java.util.regex.Pattern, java.lang.String);
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.TextValueSanitizer> CREATOR;
+ }
+
public abstract interface Transformation {
}
@@ -40597,6 +40611,7 @@
field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
field public static final java.lang.String KEY_PEOPLE = "key_people";
field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
+ field public static final java.lang.String KEY_USER_SENTIMENT = "key_user_sentiment";
}
public final class Condition implements android.os.Parcelable {
@@ -40651,6 +40666,7 @@
method public final void adjustNotifications(java.util.List<android.service.notification.Adjustment>);
method public final android.os.IBinder onBind(android.content.Intent);
method public abstract android.service.notification.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification);
+ method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int);
method public abstract void onNotificationSnoozedUntilContext(android.service.notification.StatusBarNotification, java.lang.String);
method public final void unsnoozeNotification(java.lang.String);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
@@ -40735,8 +40751,12 @@
method public java.lang.String getOverrideGroupKey();
method public int getRank();
method public int getSuppressedVisualEffects();
+ method public int getUserSentiment();
method public boolean isAmbient();
method public boolean matchesInterruptionFilter();
+ field public static final int USER_SENTIMENT_NEGATIVE = -1; // 0xffffffff
+ field public static final int USER_SENTIMENT_NEUTRAL = 0; // 0x0
+ field public static final int USER_SENTIMENT_POSITIVE = 1; // 0x1
}
public static class NotificationListenerService.RankingMap implements android.os.Parcelable {
@@ -40747,6 +40767,32 @@
field public static final android.os.Parcelable.Creator<android.service.notification.NotificationListenerService.RankingMap> CREATOR;
}
+ public final class NotificationStats implements android.os.Parcelable {
+ ctor public NotificationStats();
+ ctor protected NotificationStats(android.os.Parcel);
+ method public int describeContents();
+ method public int getDismissalSurface();
+ method public boolean hasDirectReplied();
+ method public boolean hasExpanded();
+ method public boolean hasInteracted();
+ method public boolean hasSeen();
+ method public boolean hasSnoozed();
+ method public boolean hasViewedSettings();
+ method public void setDirectReplied();
+ method public void setDismissalSurface(int);
+ method public void setExpanded();
+ method public void setSeen();
+ method public void setSnoozed();
+ method public void setViewedSettings();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.notification.NotificationStats> CREATOR;
+ field public static final int DISMISSAL_AOD = 2; // 0x2
+ field public static final int DISMISSAL_NOT_DISMISSED = -1; // 0xffffffff
+ field public static final int DISMISSAL_OTHER = 0; // 0x0
+ field public static final int DISMISSAL_PEEK = 1; // 0x1
+ field public static final int DISMISSAL_SHADE = 3; // 0x3
+ }
+
public final class SnoozeCriterion implements android.os.Parcelable {
ctor public SnoozeCriterion(java.lang.String, java.lang.CharSequence, java.lang.CharSequence);
ctor protected SnoozeCriterion(android.os.Parcel);
@@ -43506,8 +43552,12 @@
field public static final java.lang.String EXTRA_MBMS_FILE_INFO = "android.telephony.extra.MBMS_FILE_INFO";
field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_ACTION = "android.telephony.action.EmbmsDownload";
field public static final int RESULT_CANCELLED = 2; // 0x2
+ field public static final int RESULT_DOWNLOAD_FAILURE = 6; // 0x6
field public static final int RESULT_EXPIRED = 3; // 0x3
+ field public static final int RESULT_FILE_ROOT_UNREACHABLE = 8; // 0x8
field public static final int RESULT_IO_ERROR = 4; // 0x4
+ field public static final int RESULT_OUT_OF_STORAGE = 7; // 0x7
+ field public static final int RESULT_SERVICE_ID_NOT_DEFINED = 5; // 0x5
field public static final int RESULT_SUCCESSFUL = 1; // 0x1
field public static final int STATUS_ACTIVELY_DOWNLOADING = 1; // 0x1
field public static final int STATUS_PENDING_DOWNLOAD = 2; // 0x2
@@ -44293,13 +44343,12 @@
}
public static class DownloadRequest.Builder {
- ctor public DownloadRequest.Builder();
+ ctor public DownloadRequest.Builder(android.net.Uri);
method public android.telephony.mbms.DownloadRequest build();
method public android.telephony.mbms.DownloadRequest.Builder setAppIntent(android.content.Intent);
method public android.telephony.mbms.DownloadRequest.Builder setOpaqueData(byte[]);
method public android.telephony.mbms.DownloadRequest.Builder setServiceId(java.lang.String);
method public android.telephony.mbms.DownloadRequest.Builder setServiceInfo(android.telephony.mbms.FileServiceInfo);
- method public android.telephony.mbms.DownloadRequest.Builder setSource(android.net.Uri);
method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int);
}
@@ -49434,8 +49483,8 @@
method protected boolean awakenScrollBars(int);
method protected boolean awakenScrollBars(int, boolean);
method public void bringToFront();
- method public void buildDrawingCache();
- method public void buildDrawingCache(boolean);
+ method public deprecated void buildDrawingCache();
+ method public deprecated void buildDrawingCache(boolean);
method public void buildLayer();
method public boolean callOnClick();
method public boolean canResolveLayoutDirection();
@@ -49460,7 +49509,7 @@
method protected int computeVerticalScrollRange();
method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
method public void createContextMenu(android.view.ContextMenu);
- method public void destroyDrawingCache();
+ method public deprecated void destroyDrawingCache();
method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
method public boolean dispatchCapturedPointerEvent(android.view.MotionEvent);
method public void dispatchConfigurationChanged(android.content.res.Configuration);
@@ -49541,10 +49590,10 @@
method public static int getDefaultSize(int, int);
method public android.view.Display getDisplay();
method public final int[] getDrawableState();
- method public android.graphics.Bitmap getDrawingCache();
- method public android.graphics.Bitmap getDrawingCache(boolean);
- method public int getDrawingCacheBackgroundColor();
- method public int getDrawingCacheQuality();
+ method public deprecated android.graphics.Bitmap getDrawingCache();
+ method public deprecated android.graphics.Bitmap getDrawingCache(boolean);
+ method public deprecated int getDrawingCacheBackgroundColor();
+ method public deprecated int getDrawingCacheQuality();
method public void getDrawingRect(android.graphics.Rect);
method public long getDrawingTime();
method public float getElevation();
@@ -49672,8 +49721,8 @@
method public boolean hasTransientState();
method public boolean hasWindowFocus();
method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup);
- method public void invalidate(android.graphics.Rect);
- method public void invalidate(int, int, int, int);
+ method public deprecated void invalidate(android.graphics.Rect);
+ method public deprecated void invalidate(int, int, int, int);
method public void invalidate();
method public void invalidateDrawable(android.graphics.drawable.Drawable);
method public void invalidateOutline();
@@ -49683,7 +49732,7 @@
method public boolean isClickable();
method public boolean isContextClickable();
method public boolean isDirty();
- method public boolean isDrawingCacheEnabled();
+ method public deprecated boolean isDrawingCacheEnabled();
method public boolean isDuplicateParentStateEnabled();
method public boolean isEnabled();
method public final boolean isFocusable();
@@ -49854,9 +49903,9 @@
method public void setContentDescription(java.lang.CharSequence);
method public void setContextClickable(boolean);
method public void setDefaultFocusHighlightEnabled(boolean);
- method public void setDrawingCacheBackgroundColor(int);
- method public void setDrawingCacheEnabled(boolean);
- method public void setDrawingCacheQuality(int);
+ method public deprecated void setDrawingCacheBackgroundColor(int);
+ method public deprecated void setDrawingCacheEnabled(boolean);
+ method public deprecated void setDrawingCacheQuality(int);
method public void setDuplicateParentStateEnabled(boolean);
method public void setElevation(float);
method public void setEnabled(boolean);
@@ -50007,9 +50056,9 @@
field public static final int DRAG_FLAG_GLOBAL_URI_READ = 1; // 0x1
field public static final int DRAG_FLAG_GLOBAL_URI_WRITE = 2; // 0x2
field public static final int DRAG_FLAG_OPAQUE = 512; // 0x200
- field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
- field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
- field public static final int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
+ field public static final deprecated int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
+ field public static final deprecated int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
+ field public static final deprecated int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
field protected static final int[] EMPTY_STATE_SET;
field protected static final int[] ENABLED_FOCUSED_SELECTED_STATE_SET;
field protected static final int[] ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
@@ -50401,7 +50450,7 @@
method public android.animation.LayoutTransition getLayoutTransition();
method public int getNestedScrollAxes();
method public android.view.ViewGroupOverlay getOverlay();
- method public int getPersistentDrawingCache();
+ method public deprecated int getPersistentDrawingCache();
method public boolean getTouchscreenBlocksFocus();
method public int indexOfChild(android.view.View);
method public final deprecated void invalidateChild(android.view.View, android.graphics.Rect);
@@ -50454,7 +50503,7 @@
method public void setAddStatesFromChildren(boolean);
method public deprecated void setAlwaysDrawnWithCacheEnabled(boolean);
method public deprecated void setAnimationCacheEnabled(boolean);
- method protected void setChildrenDrawingCacheEnabled(boolean);
+ method protected deprecated void setChildrenDrawingCacheEnabled(boolean);
method protected void setChildrenDrawingOrderEnabled(boolean);
method protected deprecated void setChildrenDrawnWithCacheEnabled(boolean);
method public void setClipChildren(boolean);
@@ -50466,7 +50515,7 @@
method public void setLayoutTransition(android.animation.LayoutTransition);
method public void setMotionEventSplittingEnabled(boolean);
method public void setOnHierarchyChangeListener(android.view.ViewGroup.OnHierarchyChangeListener);
- method public void setPersistentDrawingCache(int);
+ method public deprecated void setPersistentDrawingCache(int);
method protected void setStaticTransformationsEnabled(boolean);
method public void setTouchscreenBlocksFocus(boolean);
method public void setTransitionGroup(boolean);
@@ -50484,10 +50533,10 @@
field public static final int FOCUS_BLOCK_DESCENDANTS = 393216; // 0x60000
field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0
field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1
- field public static final int PERSISTENT_ALL_CACHES = 3; // 0x3
- field public static final int PERSISTENT_ANIMATION_CACHE = 1; // 0x1
- field public static final int PERSISTENT_NO_CACHE = 0; // 0x0
- field public static final int PERSISTENT_SCROLLING_CACHE = 2; // 0x2
+ field public static final deprecated int PERSISTENT_ALL_CACHES = 3; // 0x3
+ field public static final deprecated int PERSISTENT_ANIMATION_CACHE = 1; // 0x1
+ field public static final deprecated int PERSISTENT_NO_CACHE = 0; // 0x0
+ field public static final deprecated int PERSISTENT_SCROLLING_CACHE = 2; // 0x2
}
public static class ViewGroup.LayoutParams {
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 7ee261e..639877f 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -275,6 +275,10 @@
method public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputManager.HardwareCallback, android.media.tv.TvInputInfo);
}
+ public static final class TvInputManager.Hardware {
+ method public boolean dispatchKeyEventToHdmi(android.view.KeyEvent);
+ }
+
public class TvView extends android.view.ViewGroup {
method public void requestUnblockContent(android.media.tv.TvContentRating);
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 92d0e016..3c61448 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -362,6 +362,7 @@
field public static final int canRequestTouchExplorationMode = 16843735; // 0x10103d7
field public static final int canRetrieveWindowContent = 16843653; // 0x1010385
field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230
+ field public static final int cantSaveState = 16844142; // 0x101056e
field public static final deprecated int capitalize = 16843113; // 0x1010169
field public static final int category = 16843752; // 0x10103e8
field public static final int centerBright = 16842956; // 0x10100cc
@@ -3866,7 +3867,9 @@
method public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
method public void removeStacksInWindowingModes(int[]) throws java.lang.SecurityException;
method public void removeStacksWithActivityTypes(int[]) throws java.lang.SecurityException;
+ method public void resizeStack(int, android.graphics.Rect) throws java.lang.SecurityException;
method public deprecated void restartPackage(java.lang.String);
+ method public void setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException;
method public static void setVrThread(int);
method public void setWatchHeapLimit(long);
method public static boolean supportsMultiWindow(android.content.Context);
@@ -4019,11 +4022,7 @@
}
public static class ActivityManager.StackId {
- field public static final int DOCKED_STACK_ID = 3; // 0x3
- field public static final int FREEFORM_WORKSPACE_STACK_ID = 2; // 0x2
- field public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1; // 0x1
field public static final int INVALID_STACK_ID = -1; // 0xffffffff
- field public static final int PINNED_STACK_ID = 4; // 0x4
}
public static class ActivityManager.TaskDescription implements android.os.Parcelable {
@@ -32108,6 +32107,7 @@
field public static final java.lang.String DISALLOW_SET_WALLPAPER = "no_set_wallpaper";
field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location";
field public static final java.lang.String DISALLOW_SMS = "no_sms";
+ field public static final java.lang.String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
@@ -37505,11 +37505,17 @@
}
public static class ImageTransformation.Builder {
- ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
- method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+ ctor public deprecated ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int);
+ ctor public ImageTransformation.Builder(android.view.autofill.AutofillId, java.util.regex.Pattern, int, java.lang.CharSequence);
+ method public deprecated android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int);
+ method public android.service.autofill.ImageTransformation.Builder addOption(java.util.regex.Pattern, int, java.lang.CharSequence);
method public android.service.autofill.ImageTransformation build();
}
+ public abstract class InternalSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
+ ctor public InternalSanitizer();
+ }
+
public final class LuhnChecksumValidator implements android.os.Parcelable android.service.autofill.Validator {
ctor public LuhnChecksumValidator(android.view.autofill.AutofillId...);
method public int describeContents();
@@ -37526,6 +37532,9 @@
field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR;
}
+ public abstract interface Sanitizer {
+ }
+
public final class SaveCallback {
method public void onFailure(java.lang.CharSequence);
method public void onSuccess();
@@ -37549,6 +37558,7 @@
public static final class SaveInfo.Builder {
ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
ctor public SaveInfo.Builder(int);
+ method public android.service.autofill.SaveInfo.Builder addSanitizer(android.service.autofill.Sanitizer, android.view.autofill.AutofillId...);
method public android.service.autofill.SaveInfo build();
method public android.service.autofill.SaveInfo.Builder setCustomDescription(android.service.autofill.CustomDescription);
method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
@@ -37567,6 +37577,14 @@
field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
}
+ public final class TextValueSanitizer extends android.service.autofill.InternalSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
+ ctor public TextValueSanitizer(java.util.regex.Pattern, java.lang.String);
+ method public int describeContents();
+ method public android.view.autofill.AutofillValue sanitize(android.view.autofill.AutofillValue);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.TextValueSanitizer> CREATOR;
+ }
+
public abstract interface Transformation {
}
@@ -37798,6 +37816,7 @@
field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
field public static final java.lang.String KEY_PEOPLE = "key_people";
field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
+ field public static final java.lang.String KEY_USER_SENTIMENT = "key_user_sentiment";
}
public final class Condition implements android.os.Parcelable {
@@ -37884,6 +37903,7 @@
method public void onNotificationRemoved(android.service.notification.StatusBarNotification);
method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, int);
+ method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int);
method public final void requestInterruptionFilter(int);
method public final void requestListenerHints(int);
method public static void requestRebind(android.content.ComponentName);
@@ -37936,8 +37956,12 @@
method public java.lang.String getOverrideGroupKey();
method public int getRank();
method public int getSuppressedVisualEffects();
+ method public int getUserSentiment();
method public boolean isAmbient();
method public boolean matchesInterruptionFilter();
+ field public static final int USER_SENTIMENT_NEGATIVE = -1; // 0xffffffff
+ field public static final int USER_SENTIMENT_NEUTRAL = 0; // 0x0
+ field public static final int USER_SENTIMENT_POSITIVE = 1; // 0x1
}
public static class NotificationListenerService.RankingMap implements android.os.Parcelable {
@@ -37948,6 +37972,32 @@
field public static final android.os.Parcelable.Creator<android.service.notification.NotificationListenerService.RankingMap> CREATOR;
}
+ public final class NotificationStats implements android.os.Parcelable {
+ ctor public NotificationStats();
+ ctor protected NotificationStats(android.os.Parcel);
+ method public int describeContents();
+ method public int getDismissalSurface();
+ method public boolean hasDirectReplied();
+ method public boolean hasExpanded();
+ method public boolean hasInteracted();
+ method public boolean hasSeen();
+ method public boolean hasSnoozed();
+ method public boolean hasViewedSettings();
+ method public void setDirectReplied();
+ method public void setDismissalSurface(int);
+ method public void setExpanded();
+ method public void setSeen();
+ method public void setSnoozed();
+ method public void setViewedSettings();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.notification.NotificationStats> CREATOR;
+ field public static final int DISMISSAL_AOD = 2; // 0x2
+ field public static final int DISMISSAL_NOT_DISMISSED = -1; // 0xffffffff
+ field public static final int DISMISSAL_OTHER = 0; // 0x0
+ field public static final int DISMISSAL_PEEK = 1; // 0x1
+ field public static final int DISMISSAL_SHADE = 3; // 0x3
+ }
+
public final class SnoozeCriterion implements android.os.Parcelable {
ctor public SnoozeCriterion(java.lang.String, java.lang.CharSequence, java.lang.CharSequence);
ctor protected SnoozeCriterion(android.os.Parcel);
@@ -40375,8 +40425,12 @@
field public static final java.lang.String EXTRA_MBMS_DOWNLOAD_RESULT = "android.telephony.extra.MBMS_DOWNLOAD_RESULT";
field public static final java.lang.String EXTRA_MBMS_FILE_INFO = "android.telephony.extra.MBMS_FILE_INFO";
field public static final int RESULT_CANCELLED = 2; // 0x2
+ field public static final int RESULT_DOWNLOAD_FAILURE = 6; // 0x6
field public static final int RESULT_EXPIRED = 3; // 0x3
+ field public static final int RESULT_FILE_ROOT_UNREACHABLE = 8; // 0x8
field public static final int RESULT_IO_ERROR = 4; // 0x4
+ field public static final int RESULT_OUT_OF_STORAGE = 7; // 0x7
+ field public static final int RESULT_SERVICE_ID_NOT_DEFINED = 5; // 0x5
field public static final int RESULT_SUCCESSFUL = 1; // 0x1
field public static final int STATUS_ACTIVELY_DOWNLOADING = 1; // 0x1
field public static final int STATUS_PENDING_DOWNLOAD = 2; // 0x2
@@ -41043,11 +41097,10 @@
}
public static class DownloadRequest.Builder {
- ctor public DownloadRequest.Builder();
+ ctor public DownloadRequest.Builder(android.net.Uri);
method public android.telephony.mbms.DownloadRequest build();
method public android.telephony.mbms.DownloadRequest.Builder setAppIntent(android.content.Intent);
method public android.telephony.mbms.DownloadRequest.Builder setServiceInfo(android.telephony.mbms.FileServiceInfo);
- method public android.telephony.mbms.DownloadRequest.Builder setSource(android.net.Uri);
method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int);
}
@@ -46276,8 +46329,8 @@
method protected boolean awakenScrollBars(int);
method protected boolean awakenScrollBars(int, boolean);
method public void bringToFront();
- method public void buildDrawingCache();
- method public void buildDrawingCache(boolean);
+ method public deprecated void buildDrawingCache();
+ method public deprecated void buildDrawingCache(boolean);
method public void buildLayer();
method public boolean callOnClick();
method public boolean canResolveLayoutDirection();
@@ -46302,7 +46355,7 @@
method protected int computeVerticalScrollRange();
method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
method public void createContextMenu(android.view.ContextMenu);
- method public void destroyDrawingCache();
+ method public deprecated void destroyDrawingCache();
method public android.view.WindowInsets dispatchApplyWindowInsets(android.view.WindowInsets);
method public boolean dispatchCapturedPointerEvent(android.view.MotionEvent);
method public void dispatchConfigurationChanged(android.content.res.Configuration);
@@ -46383,10 +46436,10 @@
method public static int getDefaultSize(int, int);
method public android.view.Display getDisplay();
method public final int[] getDrawableState();
- method public android.graphics.Bitmap getDrawingCache();
- method public android.graphics.Bitmap getDrawingCache(boolean);
- method public int getDrawingCacheBackgroundColor();
- method public int getDrawingCacheQuality();
+ method public deprecated android.graphics.Bitmap getDrawingCache();
+ method public deprecated android.graphics.Bitmap getDrawingCache(boolean);
+ method public deprecated int getDrawingCacheBackgroundColor();
+ method public deprecated int getDrawingCacheQuality();
method public void getDrawingRect(android.graphics.Rect);
method public long getDrawingTime();
method public float getElevation();
@@ -46515,8 +46568,8 @@
method public boolean hasTransientState();
method public boolean hasWindowFocus();
method public static android.view.View inflate(android.content.Context, int, android.view.ViewGroup);
- method public void invalidate(android.graphics.Rect);
- method public void invalidate(int, int, int, int);
+ method public deprecated void invalidate(android.graphics.Rect);
+ method public deprecated void invalidate(int, int, int, int);
method public void invalidate();
method public void invalidateDrawable(android.graphics.drawable.Drawable);
method public void invalidateOutline();
@@ -46527,7 +46580,7 @@
method public boolean isContextClickable();
method public boolean isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
method public boolean isDirty();
- method public boolean isDrawingCacheEnabled();
+ method public deprecated boolean isDrawingCacheEnabled();
method public boolean isDuplicateParentStateEnabled();
method public boolean isEnabled();
method public final boolean isFocusable();
@@ -46701,9 +46754,9 @@
method public void setContentDescription(java.lang.CharSequence);
method public void setContextClickable(boolean);
method public void setDefaultFocusHighlightEnabled(boolean);
- method public void setDrawingCacheBackgroundColor(int);
- method public void setDrawingCacheEnabled(boolean);
- method public void setDrawingCacheQuality(int);
+ method public deprecated void setDrawingCacheBackgroundColor(int);
+ method public deprecated void setDrawingCacheEnabled(boolean);
+ method public deprecated void setDrawingCacheQuality(int);
method public void setDuplicateParentStateEnabled(boolean);
method public void setElevation(float);
method public void setEnabled(boolean);
@@ -46855,9 +46908,9 @@
field public static final int DRAG_FLAG_GLOBAL_URI_READ = 1; // 0x1
field public static final int DRAG_FLAG_GLOBAL_URI_WRITE = 2; // 0x2
field public static final int DRAG_FLAG_OPAQUE = 512; // 0x200
- field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
- field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
- field public static final int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
+ field public static final deprecated int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
+ field public static final deprecated int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
+ field public static final deprecated int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
field protected static final int[] EMPTY_STATE_SET;
field protected static final int[] ENABLED_FOCUSED_SELECTED_STATE_SET;
field protected static final int[] ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
@@ -47253,7 +47306,7 @@
method public android.animation.LayoutTransition getLayoutTransition();
method public int getNestedScrollAxes();
method public android.view.ViewGroupOverlay getOverlay();
- method public int getPersistentDrawingCache();
+ method public deprecated int getPersistentDrawingCache();
method public boolean getTouchscreenBlocksFocus();
method public int indexOfChild(android.view.View);
method public final deprecated void invalidateChild(android.view.View, android.graphics.Rect);
@@ -47306,7 +47359,7 @@
method public void setAddStatesFromChildren(boolean);
method public deprecated void setAlwaysDrawnWithCacheEnabled(boolean);
method public deprecated void setAnimationCacheEnabled(boolean);
- method protected void setChildrenDrawingCacheEnabled(boolean);
+ method protected deprecated void setChildrenDrawingCacheEnabled(boolean);
method protected void setChildrenDrawingOrderEnabled(boolean);
method protected deprecated void setChildrenDrawnWithCacheEnabled(boolean);
method public void setClipChildren(boolean);
@@ -47318,7 +47371,7 @@
method public void setLayoutTransition(android.animation.LayoutTransition);
method public void setMotionEventSplittingEnabled(boolean);
method public void setOnHierarchyChangeListener(android.view.ViewGroup.OnHierarchyChangeListener);
- method public void setPersistentDrawingCache(int);
+ method public deprecated void setPersistentDrawingCache(int);
method protected void setStaticTransformationsEnabled(boolean);
method public void setTouchscreenBlocksFocus(boolean);
method public void setTransitionGroup(boolean);
@@ -47336,10 +47389,10 @@
field public static final int FOCUS_BLOCK_DESCENDANTS = 393216; // 0x60000
field public static final int LAYOUT_MODE_CLIP_BOUNDS = 0; // 0x0
field public static final int LAYOUT_MODE_OPTICAL_BOUNDS = 1; // 0x1
- field public static final int PERSISTENT_ALL_CACHES = 3; // 0x3
- field public static final int PERSISTENT_ANIMATION_CACHE = 1; // 0x1
- field public static final int PERSISTENT_NO_CACHE = 0; // 0x0
- field public static final int PERSISTENT_SCROLLING_CACHE = 2; // 0x2
+ field public static final deprecated int PERSISTENT_ALL_CACHES = 3; // 0x3
+ field public static final deprecated int PERSISTENT_ANIMATION_CACHE = 1; // 0x1
+ field public static final deprecated int PERSISTENT_NO_CACHE = 0; // 0x0
+ field public static final deprecated int PERSISTENT_SCROLLING_CACHE = 2; // 0x2
}
public static class ViewGroup.LayoutParams {
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index b16188e..e5d35b3 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -34,13 +34,6 @@
iot/BootAction.cpp \
iot/BootParameters.cpp \
-LOCAL_SHARED_LIBRARIES += \
- libandroidthings \
- libbase \
- libbinder \
-
-LOCAL_STATIC_LIBRARIES += cpufeatures
-
else
LOCAL_SRC_FILES += \
diff --git a/cmds/bootanimation/iot/iotbootanimation_main.cpp b/cmds/bootanimation/iot/iotbootanimation_main.cpp
index 742f9c24..00cef43 100644
--- a/cmds/bootanimation/iot/iotbootanimation_main.cpp
+++ b/cmds/bootanimation/iot/iotbootanimation_main.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "IotBootAnimation"
-#include <android-base/file.h>
+#include <base/files/file_util.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <binder/ProcessState.h>
@@ -31,13 +31,14 @@
#include "BootParameters.h"
using namespace android;
-using android::base::ReadFileToString;
// Create a typedef for readability.
typedef android::BootAnimation::Animation Animation;
namespace {
+constexpr const char* kDefaultLibName = "libbootaction.so";
+
class BootActionAnimationCallbacks : public android::BootAnimation::Callbacks {
public:
BootActionAnimationCallbacks(std::unique_ptr<BootParameters> bootParameters)
@@ -49,11 +50,13 @@
// This value is optionally provided by the user and will be written to
// /oem/oem.prop.
char property[PROP_VALUE_MAX] = {0};
- if (property_get("ro.oem.bootactions.lib", property, "") < 1) {
- ALOGI("No bootaction specified");
+ property_get("ro.oem.bootactions.lib", property, kDefaultLibName);
+ library_path += property;
+
+ if (!::base::PathExists(::base::FilePath(library_path))) {
+ ALOGI("Skipping boot actions: %s does not exist", library_path.c_str());
return;
}
- library_path += property;
mBootAction = new BootAction();
if (!mBootAction->init(library_path, mBootParameters->getParameters())) {
diff --git a/cmds/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp
index 07a064cf..37f6ed7 100644
--- a/cmds/incidentd/src/PrivacyBuffer.cpp
+++ b/cmds/incidentd/src/PrivacyBuffer.cpp
@@ -33,18 +33,18 @@
{
EncodedBuffer::Pointer snapshot = iter->rp()->copy();
size_t bytesToWrite = 0;
- uint32_t varint = 0;
+ uint64_t varint = 0;
switch (wireType) {
case WIRE_TYPE_VARINT:
varint = iter->readRawVarint();
- if(!skip) return buf->writeRawVarint(varint);
+ if(!skip) return buf->writeRawVarint64(varint);
break;
case WIRE_TYPE_FIXED64:
bytesToWrite = 8;
break;
case WIRE_TYPE_LENGTH_DELIMITED:
bytesToWrite = iter->readRawVarint();
- if(!skip) buf->writeRawVarint(bytesToWrite);
+ if(!skip) buf->writeRawVarint32(bytesToWrite);
break;
case WIRE_TYPE_FIXED32:
bytesToWrite = 4;
@@ -76,7 +76,6 @@
uint8_t wireType = read_wire_type(varint);
uint32_t fieldId = read_field_id(varint);
const Privacy* policy = parentPolicy->lookup(fieldId);
-
if (policy == NULL || !policy->IsMessageType() || !policy->HasChildren()) {
bool skip = !spec.CheckPremission(policy);
size_t amt = buf->size();
@@ -99,7 +98,7 @@
}
buf->writeHeader(fieldId, wireType);
- buf->writeRawVarint(finalSize);
+ buf->writeRawVarint32(finalSize);
while (!q.empty()) {
EncodedBuffer* subField = q.front();
EncodedBuffer::iterator it = subField->begin();
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 56f9512..4c95007 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -42,23 +42,7 @@
LOCAL_SRC_FILES := \
../../core/java/android/os/IStatsCompanionService.aidl \
../../core/java/android/os/IStatsManager.aidl \
- src/StatsService.cpp \
- src/AnomalyMonitor.cpp \
- src/StatsPuller.cpp \
- src/LogEntryPrinter.cpp \
- src/LogReader.cpp \
- src/main.cpp \
- src/DropboxWriter.cpp \
- src/parse_util.cpp \
- src/StatsLogProcessor.cpp \
- src/stats_log.proto \
- src/statsd_config.proto \
- src/DropboxReader.cpp \
- src/matchers/LogEntryMatcherManager.cpp \
- src/metrics/CountMetricProducer.cpp \
- src/metrics/ConditionTracker.cpp \
- src/metrics/MetricsManager.cpp \
-
+ $(call all-cpp-files-under,src) \
LOCAL_CFLAGS += \
-Wall \
@@ -128,13 +112,21 @@
../../core/java/android/os/IStatsCompanionService.aidl \
../../core/java/android/os/IStatsManager.aidl \
src/StatsService.cpp \
- tests/indexed_priority_queue_test.cpp \
- src/parse_util.cpp \
+ src/AnomalyMonitor.cpp \
+ src/stats_util.cpp \
src/LogEntryPrinter.cpp \
src/LogReader.cpp \
- src/matchers/LogEntryMatcherManager.cpp \
- tests/LogReader_test.cpp \
- tests/LogEntryMatcher_test.cpp \
+ src/matchers/matcher_util.cpp \
+ src/condition/SimpleConditionTracker.cpp \
+ src/condition/CombinationConditionTracker.cpp \
+ src/matchers/SimpleLogMatchingTracker.cpp \
+ src/matchers/CombinationLogMatchingTracker.cpp \
+ src/metrics/metrics_manager_util.cpp \
+ src/metrics/CountMetricProducer.cpp \
+ src/metrics/CountAnomalyTracker.cpp \
+ src/condition/condition_util.cpp \
+ src/UidMap.cpp \
+ $(call all-cpp-files-under, tests) \
LOCAL_STATIC_LIBRARIES := \
libgmock \
diff --git a/cmds/statsd/src/AnomalyMonitor.cpp b/cmds/statsd/src/AnomalyMonitor.cpp
index 92fe844..4fbbc7a 100644
--- a/cmds/statsd/src/AnomalyMonitor.cpp
+++ b/cmds/statsd/src/AnomalyMonitor.cpp
@@ -90,6 +90,36 @@
}
}
+// More efficient than repeatedly calling remove(mPq.top()) since it batches the
+// updates to the registered alarm.
+unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>
+ AnomalyMonitor::popSoonerThan(uint32_t timestampSec) {
+
+ if (DEBUG) ALOGD("Removing alarms with time <= %u", timestampSec);
+ unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> oldAlarms;
+ std::lock_guard<std::mutex> lock(mLock);
+
+ for (sp<const AnomalyAlarm> t = mPq.top();
+ t != nullptr && t->timestampSec <= timestampSec; t = mPq.top()) {
+ oldAlarms.insert(t);
+ mPq.pop(); // remove t
+ }
+ // Always update registered alarm time (if anything has changed).
+ if (!oldAlarms.empty()) {
+ if (mPq.empty()) {
+ if (DEBUG) ALOGD("Queue is empty. Cancel any alarm.");
+ mRegisteredAlarmTimeSec = 0;
+ if (mStatsCompanionService != nullptr) {
+ mStatsCompanionService->cancelAnomalyAlarm();
+ }
+ } else {
+ // Always update the registered alarm in this case (unlike remove()).
+ updateRegisteredAlarmTime_l(mPq.top()->timestampSec);
+ }
+ }
+ return oldAlarms;
+}
+
void AnomalyMonitor::updateRegisteredAlarmTime_l(uint32_t timestampSec) {
if (DEBUG) ALOGD("Updating reg alarm time to %u", timestampSec);
mRegisteredAlarmTimeSec = timestampSec;
diff --git a/cmds/statsd/src/AnomalyMonitor.h b/cmds/statsd/src/AnomalyMonitor.h
index d78be54..7c6e5e8 100644
--- a/cmds/statsd/src/AnomalyMonitor.h
+++ b/cmds/statsd/src/AnomalyMonitor.h
@@ -21,12 +21,14 @@
#include <indexed_priority_queue.h>
#include <utils/RefBase.h>
+#include <unordered_set>
#include <queue>
#include <vector>
using namespace android;
using android::os::IStatsCompanionService;
+using std::unordered_set;
namespace android {
namespace os {
@@ -86,6 +88,13 @@
void remove(sp<const AnomalyAlarm> alarm);
/**
+ * Returns and removes all alarms whose timestamp <= the given timestampSec.
+ * Always updates the registered alarm if return is non-empty.
+ */
+ unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>
+ popSoonerThan(uint32_t timestampSec);
+
+ /**
* Returns the projected alarm timestamp that is registered with
* StatsCompanionService. This may not be equal to the soonest alarm,
* but should be within minDiffToUpdateRegisteredAlarmTimeSec of it.
diff --git a/cmds/statsd/src/KernelWakelockPuller.cpp b/cmds/statsd/src/KernelWakelockPuller.cpp
new file mode 100644
index 0000000..1798f9d
--- /dev/null
+++ b/cmds/statsd/src/KernelWakelockPuller.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#include "KernelWakelockPuller.h"
+#include <android/os/IStatsCompanionService.h>
+#include <binder/IPCThreadState.h>
+#include <cutils/log.h>
+#include <private/android_filesystem_config.h>
+#include "StatsPuller.h"
+#include "StatsService.h"
+
+using namespace android;
+using namespace android::base;
+using namespace android::binder;
+using namespace android::os;
+using namespace std;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+const int KernelWakelockPuller::PULL_CODE_KERNEL_WAKELOCKS = 20;
+
+// The reading and parsing are implemented in Java. It is not difficult to port over. But for now
+// let StatsCompanionService handle that and send the data back.
+String16 KernelWakelockPuller::pull() {
+ sp<IStatsCompanionService> statsCompanion = StatsService::getStatsCompanionService();
+ String16 returned_value("");
+ if (statsCompanion != NULL) {
+ Status status = statsCompanion->pullData(KernelWakelockPuller::PULL_CODE_KERNEL_WAKELOCKS,
+ &returned_value);
+ if (!status.isOk()) {
+ ALOGW("error pulling kernel wakelock");
+ }
+ ALOGD("KernelWakelockPuller::pull succeeded!");
+ // TODO: remove this when we integrate into aggregation chain.
+ ALOGD("%s", String8(returned_value).string());
+ return returned_value;
+ } else {
+ ALOGW("statsCompanion not found!");
+ return String16();
+ }
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/parse_util.h b/cmds/statsd/src/KernelWakelockPuller.h
similarity index 65%
copy from cmds/statsd/src/parse_util.h
copy to cmds/statsd/src/KernelWakelockPuller.h
index 8b82e7b..1c16f87 100644
--- a/cmds/statsd/src/parse_util.h
+++ b/cmds/statsd/src/KernelWakelockPuller.h
@@ -13,24 +13,27 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef PARSE_UTIL_H
-#define PARSE_UTIL_H
-#include "DropboxWriter.h"
-#include "LogReader.h"
+#ifndef STATSD_KERNELWAKELOCKPULLER_H
+#define STATSD_KERNELWAKELOCKPULLER_H
-#include <log/logprint.h>
+#include <utils/String16.h>
+#include "StatsPuller.h"
namespace android {
namespace os {
namespace statsd {
-EventMetricData parse(log_msg msg);
-
-int getTagId(log_msg msg);
+class KernelWakelockPuller : public StatsPuller {
+public:
+ // a number of stats need to be pulled from StatsCompanionService
+ //
+ const static int PULL_CODE_KERNEL_WAKELOCKS;
+ String16 pull() override;
+};
} // namespace statsd
} // namespace os
} // namespace android
-#endif // PARSE_UTIL_H
+#endif // STATSD_KERNELWAKELOCKPULLER_H
diff --git a/cmds/statsd/src/PackageInfoListener.h b/cmds/statsd/src/PackageInfoListener.h
new file mode 100644
index 0000000..476c1d9
--- /dev/null
+++ b/cmds/statsd/src/PackageInfoListener.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef STATSD_PACKAGE_INFO_LISTENER_H
+#define STATSD_PACKAGE_INFO_LISTENER_H
+
+#include <utils/RefBase.h>
+#include <string>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class PackageInfoListener : public virtual android::RefBase {
+public:
+ // Uid map will notify this listener that the app with apk name and uid has been upgraded to
+ // the specified version.
+ virtual void notifyAppUpgrade(const std::string& apk, const int uid, const int version) = 0;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif //STATSD_PACKAGE_INFO_LISTENER_H
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 117fb5e..f877ef3 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -20,7 +20,6 @@
#include <frameworks/base/cmds/statsd/src/stats_log.pb.h>
#include <log/log_event_list.h>
#include <metrics/CountMetricProducer.h>
-#include <parse_util.h>
#include <utils/Errors.h>
using namespace android;
@@ -32,7 +31,9 @@
namespace os {
namespace statsd {
-StatsLogProcessor::StatsLogProcessor() : m_dropbox_writer("all-logs") {
+StatsLogProcessor::StatsLogProcessor(const sp<UidMap> &uidMap)
+ : m_dropbox_writer("all-logs"), m_UidMap(uidMap)
+{
// hardcoded config
// this should be called from StatsService when it receives a statsd_config
UpdateConfig(0, buildFakeConfig());
@@ -41,28 +42,6 @@
StatsLogProcessor::~StatsLogProcessor() {
}
-StatsdConfig StatsLogProcessor::buildFakeConfig() {
- // HACK: Hard code a test metric for counting screen on events...
- StatsdConfig config;
- config.set_config_id(12345L);
-
- CountMetric* metric = config.add_count_metric();
- metric->set_metric_id(20150717L);
- metric->set_what("SCREEN_IS_ON");
- metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
-
- LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
- eventMatcher->set_name("SCREEN_IS_ON");
-
- SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
- simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
- simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()
- ->set_key(1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
- simpleLogEntryMatcher->mutable_key_value_matcher(0)
- ->set_eq_int(2/*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
- return config;
-}
-
// TODO: what if statsd service restarts? How do we know what logs are already processed before?
void StatsLogProcessor::OnLogEvent(const log_msg& msg) {
// TODO: Use EventMetric to filter the events we want to log.
@@ -83,7 +62,14 @@
ALOGD("Updated configuration for source %i", config_source);
- mMetricsManagers.insert({config_source, std::make_unique<MetricsManager>(config)});
+ unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(config);
+ if (newMetricsManager->isConfigValid()) {
+ mMetricsManagers.insert({config_source, std::move(newMetricsManager)});
+ ALOGD("StatsdConfig valid");
+ } else {
+ // If there is any error in the config, don't use it.
+ ALOGD("StatsdConfig NOT valid");
+ }
}
} // namespace statsd
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 88c63fa..05e441c 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -16,11 +16,12 @@
#ifndef STATS_LOG_PROCESSOR_H
#define STATS_LOG_PROCESSOR_H
-#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "DropboxWriter.h"
#include "LogReader.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include "metrics/MetricsManager.h"
-#include "parse_util.h"
+#include "stats_util.h"
+#include "UidMap.h"
#include <log/logprint.h>
#include <stdio.h>
@@ -32,7 +33,7 @@
class StatsLogProcessor : public LogListener {
public:
- StatsLogProcessor();
+ StatsLogProcessor(const sp<UidMap> &uidMap);
virtual ~StatsLogProcessor();
virtual void OnLogEvent(const log_msg& msg);
@@ -45,7 +46,7 @@
std::unordered_map<int, std::unique_ptr<MetricsManager>> mMetricsManagers;
- static StatsdConfig buildFakeConfig();
+ sp<UidMap> m_UidMap; // Reference to the UidMap to lookup app name and version for each uid.
};
} // namespace statsd
diff --git a/cmds/statsd/src/StatsPuller.cpp b/cmds/statsd/src/StatsPuller.cpp
deleted file mode 100644
index 94e8361..0000000
--- a/cmds/statsd/src/StatsPuller.cpp
+++ /dev/null
@@ -1,60 +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.
- */
-
-#define LOG_TAG "StatsPuller"
-#define DEBUG true
-
-#include "StatsPuller.h"
-#include "StatsService.h"
-#include <android/os/IStatsCompanionService.h>
-#include <cutils/log.h>
-
-using namespace android;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-String16 StatsPuller::pull(int pullCode) {
- if (DEBUG) ALOGD("Initiating pulling %d", pullCode);
-
- switch (pullCode) {
- // All stats_companion_service cases go here with fallthroughs
- case PULL_CODE_KERNEL_WAKELOCKS: {
- // TODO: Consider caching the statsCompanion service
- sp <IStatsCompanionService>
- statsCompanion = StatsService::getStatsCompanionService();
- String16 returned_value("");
- Status status = statsCompanion->pullData(pullCode, &returned_value);
- if (DEBUG) ALOGD("Finished pulling the data");
- if (!status.isOk()) {
- ALOGW("error pulling data of type %d", pullCode);
- }
- return returned_value;
- }
-
- // case OTHER_TYPES: etc.
-
- default: {
- ALOGE("invalid pull code %d", pullCode);
- return String16("");
- }
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/StatsPuller.h b/cmds/statsd/src/StatsPuller.h
index 05343b5..5e556b8 100644
--- a/cmds/statsd/src/StatsPuller.h
+++ b/cmds/statsd/src/StatsPuller.h
@@ -25,19 +25,13 @@
class StatsPuller {
public:
- // Enums of pulled data types (pullCodes)
- // These values must be kept in sync with com/android/server/stats/StatsCompanionService.java.
- // TODO: pull the constant from stats_events.proto instead
- const static int PULL_CODE_KERNEL_WAKELOCKS = 20;
-
- StatsPuller();
- ~StatsPuller();
-
- static String16 pull(int pullCode);
+ virtual ~StatsPuller(){};
+ // use string for now, until we figure out how to integrate into the aggregation path
+ virtual String16 pull() = 0;
};
} // namespace statsd
} // namespace os
} // namespace android
-#endif //STATSD_STATSPULLER_H
+#endif // STATSD_STATSPULLER_H
diff --git a/cmds/statsd/src/StatsPullerManager.cpp b/cmds/statsd/src/StatsPullerManager.cpp
new file mode 100644
index 0000000..f4cf1ce
--- /dev/null
+++ b/cmds/statsd/src/StatsPullerManager.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "StatsPullerManager"
+#define DEBUG true
+
+#include "StatsPullerManager.h"
+#include <android/os/IStatsCompanionService.h>
+#include <cutils/log.h>
+#include "StatsService.h"
+#include "KernelWakelockPuller.h"
+
+
+using namespace android;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+const int StatsPullerManager::KERNEL_WAKELOCKS = 1;
+
+StatsPullerManager::StatsPullerManager() {
+ mStatsPullers.insert(
+ {static_cast<int>(KERNEL_WAKELOCKS), std::make_unique<KernelWakelockPuller>()});
+}
+
+String16 StatsPullerManager::pull(int pullCode) {
+ if (DEBUG) ALOGD("Initiating pulling %d", pullCode);
+ if (mStatsPullers.find(pullCode) != mStatsPullers.end()) {
+ return (mStatsPullers.find(pullCode)->second)->pull();
+ } else {
+ ALOGD("Unknown pull code %d", pullCode);
+ return String16();
+ }
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/StatsPullerManager.h b/cmds/statsd/src/StatsPullerManager.h
new file mode 100644
index 0000000..ab36df5
--- /dev/null
+++ b/cmds/statsd/src/StatsPullerManager.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+#ifndef STATSD_STATSPULLERMANAGER_H
+#define STATSD_STATSPULLERMANAGER_H
+
+#include <utils/String16.h>
+#include <unordered_map>
+#include "StatsPuller.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+const static int KERNEL_WAKELOCKS = 1;
+
+class StatsPullerManager {
+public:
+ // Enums of pulled data types (pullCodes)
+ // These values must be kept in sync with com/android/server/stats/StatsCompanionService.java.
+ // TODO: pull the constant from stats_events.proto instead
+ const static int KERNEL_WAKELOCKS;
+ StatsPullerManager();
+
+ String16 pull(const int pullCode);
+
+private:
+ std::unordered_map<int, std::unique_ptr<StatsPuller>> mStatsPullers;
+};
+
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif // STATSD_STATSPULLERMANAGER_H
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index ae7d66b..b496404 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -40,7 +40,8 @@
namespace statsd {
StatsService::StatsService(const sp<Looper>& handlerLooper)
- : mAnomalyMonitor(new AnomalyMonitor(2)) // TODO: Change this based on the config
+ : mAnomalyMonitor(new AnomalyMonitor(2)),m_UidMap(new UidMap()), mStatsPullerManager()
+ // TODO: Change AnomalyMonitor initialization based on the config
{
ALOGD("stats service constructed");
}
@@ -131,6 +132,9 @@
if (!args[0].compare(String8("config"))) {
return doLoadConfig(in);
}
+ if (!args[0].compare(String8("print-uid-map"))) {
+ return doPrintUidMap(out);
+ }
}
printCmdHelp(out);
@@ -153,6 +157,43 @@
}
}
+Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version,
+ const vector<String16>& app) {
+ if (DEBUG) ALOGD("StatsService::informAllUidData was called");
+
+ if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
+ return Status::fromExceptionCode(Status::EX_SECURITY,
+ "Only system uid can call informAllUidData");
+ }
+
+ m_UidMap->updateMap(uid, version, app);
+ if (DEBUG) ALOGD("StatsService::informAllUidData succeeded");
+
+ return Status::ok();
+}
+
+Status StatsService::informOnePackage(const String16& app, int32_t uid, int32_t version) {
+ if (DEBUG) ALOGD("StatsService::informOnePackage was called");
+
+ if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
+ return Status::fromExceptionCode(Status::EX_SECURITY,
+ "Only system uid can call informOnePackage");
+ }
+ m_UidMap->updateApp(app, uid, version);
+ return Status::ok();
+}
+
+Status StatsService::informOnePackageRemoved(const String16& app, int32_t uid) {
+ if (DEBUG) ALOGD("StatsService::informOnePackageRemoved was called");
+
+ if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
+ return Status::fromExceptionCode(Status::EX_SECURITY,
+ "Only system uid can call informOnePackageRemoved");
+ }
+ m_UidMap->removeApp(app, uid);
+ return Status::ok();
+}
+
Status StatsService::informAnomalyAlarmFired() {
if (DEBUG) ALOGD("StatsService::informAnomalyAlarmFired was called");
@@ -177,9 +218,10 @@
if (DEBUG) ALOGD("StatsService::informPollAlarmFired succeeded");
// TODO: determine what services to poll and poll (or ask StatsCompanionService to poll) them.
- String16 output = StatsPuller::pull(StatsPuller::PULL_CODE_KERNEL_WAKELOCKS);
+ String16 output = mStatsPullerManager.pull(StatsPullerManager::KERNEL_WAKELOCKS);
// TODO: do something useful with the output instead of writing a string to screen.
ALOGD("%s", String8(output).string());
+ ALOGD("%d", int(output.size()));
return Status::ok();
}
@@ -260,9 +302,15 @@
return DropboxReader::readStatsLogs(out, args[1].string(), msec);
}
+status_t StatsService::doPrintUidMap(FILE* out) {
+ m_UidMap->printUidMap(out);
+ return NO_ERROR;
+}
+
void StatsService::printCmdHelp(FILE* out) {
fprintf(out, "Usage:\n");
fprintf(out, "\t print-stats-log [tag_required] [timestamp_nsec_optional]\n");
+ fprintf(out, "\t print-uid-map Prints the UID, app name, version mapping.\n");
fprintf(out,
"\t config\t Loads a new config from command-line (must be proto in wire-encoded "
"format).\n");
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index a16b115..541f7e8 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -19,7 +19,9 @@
#include "AnomalyMonitor.h"
#include "StatsLogProcessor.h"
+#include "StatsPullerManager.h"
#include "StatsPuller.h"
+#include "UidMap.h"
#include <android/os/BnStatsManager.h>
#include <android/os/IStatsCompanionService.h>
@@ -60,6 +62,11 @@
virtual Status informPollAlarmFired();
+ virtual Status informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version,
+ const vector<String16>& app);
+ virtual Status informOnePackage(const String16& app, int32_t uid, int32_t version);
+ virtual Status informOnePackageRemoved(const String16& app, int32_t uid);
+
virtual status_t setProcessor(const sp<StatsLogProcessor>& main_processor);
// TODO: public for testing since statsd doesn't run when system starts. Change to private
@@ -71,10 +78,16 @@
// TODO: Should be private. Temporarily public for testing purposes only.
const sp<AnomalyMonitor> mAnomalyMonitor;
+ sp<UidMap> getUidMap() {
+ return m_UidMap;
+ }
+
/** Fetches and returns the StatsCompanionService. */
static sp<IStatsCompanionService> getStatsCompanionService();
- private:
+private:
+ sp<UidMap> m_UidMap; // Reference to the UID map needed for translating UID to app name/version.
+
sp<StatsLogProcessor> m_processor; // Reference to the processor for updating configs.
status_t doPrintStatsLog(FILE* out, const Vector<String8>& args);
@@ -82,6 +95,10 @@
void printCmdHelp(FILE* out);
status_t doLoadConfig(FILE* in);
+
+ StatsPullerManager mStatsPullerManager;
+
+ status_t doPrintUidMap(FILE* out);
};
// --- StatsdDeathRecipient ---
diff --git a/cmds/statsd/src/UidMap.cpp b/cmds/statsd/src/UidMap.cpp
new file mode 100644
index 0000000..76a7f3f
--- /dev/null
+++ b/cmds/statsd/src/UidMap.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, versionCode 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.
+ */
+
+#include "UidMap.h"
+#include <cutils/log.h>
+#include <utils/Errors.h>
+
+using namespace android;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+bool UidMap::hasApp(int uid, const string& packageName) const {
+ lock_guard<mutex> lock(mMutex);
+
+ auto range = mMap.equal_range(uid);
+ for (auto it = range.first; it != range.second; ++it) {
+ if (it->second.packageName == packageName) {
+ return true;
+ }
+ }
+ return false;
+}
+
+int UidMap::getAppVersion(int uid, const string& packageName) const {
+ lock_guard<mutex> lock(mMutex);
+
+ auto range = mMap.equal_range(uid);
+ for (auto it = range.first; it != range.second; ++it) {
+ if (it->second.packageName == packageName) {
+ return it->second.versionCode;
+ }
+ }
+ return 0;
+}
+
+void UidMap::updateMap(const vector <int32_t> &uid, const vector <int32_t> &versionCode,
+ const vector <String16> &packageName) {
+ lock_guard<mutex> lock(mMutex); // Exclusively lock for updates.
+
+ mMap.clear();
+ for (unsigned long j=0; j<uid.size(); j++) {
+ mMap.insert(make_pair(uid[j], AppData(string(String8(packageName[j]).string()),
+ versionCode[j])));
+ }
+
+ if (mOutput.initial_size() == 0) { // Provide the initial states in the mOutput proto
+ for (unsigned long j=0; j<uid.size(); j++) {
+ auto t = mOutput.add_initial();
+ t->set_app(string(String8(packageName[j]).string()));
+ t->set_version(int(versionCode[j]));
+ t->set_uid(uid[j]);
+ }
+ }
+}
+
+void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int32_t& versionCode){
+ lock_guard<mutex> lock(mMutex);
+
+ string app = string(String8(app_16).string());
+
+ // Notify any interested producers that this app has updated
+ for (auto it : mSubscribers) {
+ it->notifyAppUpgrade(app, uid, versionCode);
+ }
+
+ auto log = mOutput.add_changes();
+ log->set_deletion(false);
+ //log.timestamp = TODO: choose how timestamps are computed
+ log->set_app(app);
+ log->set_uid(uid);
+ log->set_version(versionCode);
+
+ auto range = mMap.equal_range(int(uid));
+ for (auto it = range.first; it != range.second; ++it) {
+ if (it->second.packageName == app) {
+ it->second.versionCode = int(versionCode);
+ return;
+ }
+ ALOGD("updateApp failed to find the app %s with uid %i to update", app.c_str(), uid);
+ return;
+ }
+
+ // Otherwise, we need to add an app at this uid.
+ mMap.insert(make_pair(uid, AppData(app, int(versionCode))));
+}
+
+
+void UidMap::removeApp(const String16& app_16, const int32_t& uid){
+ lock_guard<mutex> lock(mMutex);
+
+ string app = string(String8(app_16).string());
+
+ auto log = mOutput.add_changes();
+ log->set_deletion(true);
+ //log.timestamp = TODO: choose how timestamps are computed
+ log->set_app(app);
+ log->set_uid(uid);
+
+ auto range = mMap.equal_range(int(uid));
+ for (auto it = range.first; it != range.second; ++it) {
+ if (it->second.packageName == app) {
+ mMap.erase(it);
+ return;
+ }
+ }
+ ALOGD("removeApp failed to find the app %s with uid %i to remove", app.c_str(), uid);
+ return;
+}
+
+void UidMap::addListener(sp<PackageInfoListener> producer) {
+ lock_guard<mutex> lock(mMutex); // Lock for updates
+ mSubscribers.insert(producer);
+}
+
+void UidMap::removeListener(sp<PackageInfoListener> producer) {
+ lock_guard<mutex> lock(mMutex); // Lock for updates
+ mSubscribers.erase(producer);
+}
+
+UidMapping UidMap::getAndClearOutput() {
+ lock_guard<mutex> lock(mMutex); // Lock for updates
+
+ auto ret = UidMapping(mOutput); // Copy that will be returned.
+ mOutput.Clear();
+
+ // Re-initialize the initial state for the outputs. This results in extra data being uploaded
+ // but helps ensure we can't re-construct the UID->app name, versionCode mapping in server.
+ for (auto it : mMap) {
+ auto t = mOutput.add_initial();
+ t->set_app(it.second.packageName);
+ t->set_version(it.second.versionCode);
+ t->set_uid(it.first);
+ }
+
+ return ret;
+}
+
+void UidMap::printUidMap(FILE* out) {
+ lock_guard<mutex> lock(mMutex);
+
+ for (auto it : mMap) {
+ fprintf(out, "%s, v%d (%i)\n", it.second.packageName.c_str(), it.second.versionCode, it.first);
+ }
+}
+
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/UidMap.h b/cmds/statsd/src/UidMap.h
new file mode 100644
index 0000000..1481010
--- /dev/null
+++ b/cmds/statsd/src/UidMap.h
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+#ifndef STATSD_UIDMAP_H
+#define STATSD_UIDMAP_H
+
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "PackageInfoListener.h"
+
+#include <binder/IResultReceiver.h>
+#include <binder/IShellCallback.h>
+#include <log/logprint.h>
+#include <mutex>
+#include <string>
+#include <stdio.h>
+#include <set>
+#include <unordered_map>
+#include <utils/RefBase.h>
+
+using namespace std;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+struct AppData {
+ const string packageName;
+ int versionCode;
+
+ AppData(const string& a, const int v) : packageName(a), versionCode(v) {};
+};
+
+// UidMap keeps track of what the corresponding app name (APK name) and version code for every uid
+// at any given moment. This map must be updated by StatsCompanionService.
+class UidMap : public virtual android::RefBase {
+public:
+ /*
+ * All three inputs must be the same size, and the jth element in each array refers to the same
+ * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j].
+ */
+ void updateMap(const vector<int32_t>& uid, const vector<int32_t>& versionCode,
+ const vector<String16>& packageName);
+
+ // Returns true if the given uid contains the specified app (eg. com.google.android.gms).
+ bool hasApp(int uid, const string& packageName) const;
+
+ int getAppVersion(int uid, const string& packageName) const;
+
+ void updateApp(const String16& packageName, const int32_t& uid, const int32_t& versionCode);
+ void removeApp(const String16& packageName, const int32_t& uid);
+
+ // Helper for debugging contents of this uid map. Can be triggered with:
+ // adb shell cmd stats print-uid-map
+ void printUidMap(FILE* out);
+
+ // Commands for indicating to the map that a producer should be notified if an app is updated.
+ // This allows the metric producer to distinguish when the same uid or app represents a
+ // different version of an app.
+ void addListener(sp<PackageInfoListener> producer);
+ // Remove the listener from the set of metric producers that subscribe to updates.
+ void removeListener(sp<PackageInfoListener> producer);
+
+ // Grabs the current output contents and then clears it.
+ UidMapping getAndClearOutput();
+
+private:
+ // TODO: Use shared_mutex for improved read-locking if a library can be found in Android.
+ mutable mutex mMutex;
+
+ std::unordered_multimap<int, AppData> mMap;
+
+ // We prepare the output proto as apps are updated, so that we can grab the current output.
+ UidMapping mOutput;
+
+ // Metric producers that should be notified if there's an upgrade in any app.
+ set<sp<PackageInfoListener>> mSubscribers;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif //STATSD_UIDMAP_H
+
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
new file mode 100644
index 0000000..6188383
--- /dev/null
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "CombinationConditionTracker"
+#define DEBUG true // STOPSHIP if true
+#define VLOG(...) \
+ if (DEBUG) ALOGD(__VA_ARGS__);
+
+#include "CombinationConditionTracker.h"
+#include <cutils/log.h>
+#include <log/logprint.h>
+using std::string;
+using std::unique_ptr;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+CombinationConditionTracker::CombinationConditionTracker(const string& name, const int index)
+ : ConditionTracker(name, index) {
+ VLOG("creating CombinationConditionTracker %s", mName.c_str());
+}
+
+CombinationConditionTracker::~CombinationConditionTracker() {
+ VLOG("~CombinationConditionTracker() %s", mName.c_str());
+}
+
+bool CombinationConditionTracker::init(const vector<Condition>& allConditionConfig,
+ const vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<string, int>& conditionNameIndexMap,
+ vector<bool>& stack) {
+ VLOG("Combiniation condition init() %s", mName.c_str());
+ if (mInitialized) {
+ return true;
+ }
+
+ // mark this node as visited in the recursion stack.
+ stack[mIndex] = true;
+
+ Condition_Combination combinationCondition = allConditionConfig[mIndex].combination();
+
+ if (!combinationCondition.has_operation()) {
+ return false;
+ }
+ mLogicalOperation = combinationCondition.operation();
+
+ if (mLogicalOperation == LogicalOperation::NOT && combinationCondition.condition_size() != 1) {
+ return false;
+ }
+
+ for (string child : combinationCondition.condition()) {
+ auto it = conditionNameIndexMap.find(child);
+
+ if (it == conditionNameIndexMap.end()) {
+ ALOGW("Condition %s not found in the config", child.c_str());
+ return false;
+ }
+
+ int childIndex = it->second;
+ const auto& childTracker = allConditionTrackers[childIndex];
+ // if the child is a visited node in the recursion -> circle detected.
+ if (stack[childIndex]) {
+ ALOGW("Circle detected!!!");
+ return false;
+ }
+
+ bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers,
+ conditionNameIndexMap, stack);
+
+ if (!initChildSucceeded) {
+ ALOGW("Child initialization failed %s ", child.c_str());
+ return false;
+ } else {
+ ALOGW("Child initialization success %s ", child.c_str());
+ }
+
+ mChildren.push_back(childIndex);
+
+ mTrackerIndex.insert(childTracker->getLogTrackerIndex().begin(),
+ childTracker->getLogTrackerIndex().end());
+ }
+
+ // unmark this node in the recursion stack.
+ stack[mIndex] = false;
+
+ mInitialized = true;
+
+ return true;
+}
+
+bool CombinationConditionTracker::evaluateCondition(
+ const LogEventWrapper& event, const std::vector<MatchingState>& eventMatcherValues,
+ const std::vector<sp<ConditionTracker>>& mAllConditions,
+ std::vector<ConditionState>& conditionCache, std::vector<bool>& changedCache) {
+ // value is up to date.
+ if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
+ return false;
+ }
+
+ for (const int childIndex : mChildren) {
+ if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
+ const sp<ConditionTracker>& child = mAllConditions[childIndex];
+ child->evaluateCondition(event, eventMatcherValues, mAllConditions, conditionCache,
+ changedCache);
+ }
+ }
+
+ ConditionState newCondition =
+ evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
+
+ bool changed = (mConditionState != newCondition);
+ mConditionState = newCondition;
+
+ conditionCache[mIndex] = mConditionState;
+
+ changedCache[mIndex] = changed;
+ return changed;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
new file mode 100644
index 0000000..38780e7
--- /dev/null
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef COMBINATION_CONDITION_TRACKER_H
+#define COMBINATION_CONDITION_TRACKER_H
+
+#include "ConditionTracker.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class CombinationConditionTracker : public virtual ConditionTracker {
+public:
+ CombinationConditionTracker(const std::string& name, const int index);
+
+ ~CombinationConditionTracker();
+
+ bool init(const std::vector<Condition>& allConditionConfig,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<std::string, int>& conditionNameIndexMap,
+ std::vector<bool>& stack) override;
+
+ bool evaluateCondition(const LogEventWrapper& event,
+ const std::vector<MatchingState>& eventMatcherValues,
+ const std::vector<sp<ConditionTracker>>& mAllConditions,
+ std::vector<ConditionState>& conditionCache,
+ std::vector<bool>& changedCache) override;
+
+private:
+ LogicalOperation mLogicalOperation;
+ // Store index of the children Conditions.
+ // We don't store string name of the Children, because we want to get rid of the hash map to
+ // map the name to object. We don't want to store smart pointers to children, because it
+ // increases the risk of circular dependency and memory leak.
+ std::vector<int> mChildren;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif // COMBINATION_CONDITION_TRACKER_H
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
new file mode 100644
index 0000000..2da8fa0
--- /dev/null
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+#ifndef CONDITION_TRACKER_H
+#define CONDITION_TRACKER_H
+
+#include <cutils/log.h>
+#include <log/logprint.h>
+#include <utils/RefBase.h>
+#include <unordered_map>
+#include "../matchers/LogMatchingTracker.h"
+#include "../matchers/matcher_util.h"
+#include "condition_util.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class ConditionTracker : public virtual RefBase {
+public:
+ ConditionTracker(const std::string& name, const int index)
+ : mName(name),
+ mIndex(index),
+ mInitialized(false),
+ mConditionState(ConditionState::kUnknown),
+ mTrackerIndex(){};
+
+ virtual ~ConditionTracker(){};
+
+ // Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also
+ // be done in the constructor, but we do it separately because (1) easy to return a bool to
+ // indicate whether the initialization is successful. (2) makes unit test easier.
+ // allConditionConfig: the list of all Condition config from statsd_config.
+ // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also
+ // need to call init() on children conditions)
+ // conditionNameIndexMap: the mapping from condition name to its index.
+ // stack: a bit map to keep track which nodes have been visited on the stack in the recursion.
+ virtual bool init(const std::vector<Condition>& allConditionConfig,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<std::string, int>& conditionNameIndexMap,
+ std::vector<bool>& stack) = 0;
+
+ // evaluate current condition given the new event.
+ // return true if the condition state changed, false if the condition state is not changed.
+ // event: the new log event
+ // eventMatcherValues: the results of the LogMatcherTrackers. LogMatcherTrackers always process
+ // event before ConditionTrackers, because ConditionTracker depends on
+ // LogMatchingTrackers.
+ // mAllConditions: the list of all ConditionTracker
+ // conditionCache: the cached results of the ConditionTrackers for this new event.
+ // changedCache: the bit map to record whether the condition has changed.
+ virtual bool evaluateCondition(const LogEventWrapper& event,
+ const std::vector<MatchingState>& eventMatcherValues,
+ const std::vector<sp<ConditionTracker>>& mAllConditions,
+ std::vector<ConditionState>& conditionCache,
+ std::vector<bool>& changedCache) = 0;
+
+ // Return the current condition state.
+ virtual ConditionState isConditionMet() {
+ ALOGW("Condition %s value %d", mName.c_str(), mConditionState);
+ return mConditionState;
+ };
+
+ // return the list of LogMatchingTracker index that this ConditionTracker uses.
+ virtual const std::set<int>& getLogTrackerIndex() const {
+ return mTrackerIndex;
+ }
+
+protected:
+ // We don't really need the string name, but having a name here makes log messages
+ // easy to debug.
+ const std::string mName;
+
+ // the index of this condition in the manager's condition list.
+ const int mIndex;
+
+ // if it's properly initialized.
+ bool mInitialized;
+
+ // current condition state.
+ ConditionState mConditionState;
+
+ // the list of LogMatchingTracker index that this ConditionTracker uses.
+ std::set<int> mTrackerIndex;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif // CONDITION_TRACKER_H
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
new file mode 100644
index 0000000..e78c0de
--- /dev/null
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Stats_SimpleConditionTracker"
+#define DEBUG true // STOPSHIP if true
+#define VLOG(...) \
+ if (DEBUG) ALOGD(__VA_ARGS__);
+
+#include "SimpleConditionTracker.h"
+#include <cutils/log.h>
+#include <log/logprint.h>
+
+using std::string;
+using std::unique_ptr;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+SimpleConditionTracker::SimpleConditionTracker(
+ const string& name, const int index, const SimpleCondition& simpleCondition,
+ const unordered_map<string, int>& trackerNameIndexMap)
+ : ConditionTracker(name, index) {
+ VLOG("creating SimpleConditionTracker %s", mName.c_str());
+ mCountNesting = simpleCondition.count_nesting();
+
+ if (simpleCondition.has_start()) {
+ auto pair = trackerNameIndexMap.find(simpleCondition.start());
+ if (pair == trackerNameIndexMap.end()) {
+ ALOGW("Start matcher %s not found in the config", simpleCondition.start().c_str());
+ return;
+ }
+ mStartLogMatcherIndex = pair->second;
+ mTrackerIndex.insert(mStartLogMatcherIndex);
+ } else {
+ mStartLogMatcherIndex = -1;
+ }
+
+ if (simpleCondition.has_stop()) {
+ auto pair = trackerNameIndexMap.find(simpleCondition.stop());
+ if (pair == trackerNameIndexMap.end()) {
+ ALOGW("Stop matcher %s not found in the config", simpleCondition.stop().c_str());
+ return;
+ }
+ mStopLogMatcherIndex = pair->second;
+ mTrackerIndex.insert(mStopLogMatcherIndex);
+ } else {
+ mStopLogMatcherIndex = -1;
+ }
+
+ if (simpleCondition.has_stop_all()) {
+ auto pair = trackerNameIndexMap.find(simpleCondition.stop_all());
+ if (pair == trackerNameIndexMap.end()) {
+ ALOGW("Stop matcher %s not found in the config", simpleCondition.stop().c_str());
+ return;
+ }
+ mStopAllLogMatcherIndex = pair->second;
+ mTrackerIndex.insert(mStopAllLogMatcherIndex);
+ } else {
+ mStopAllLogMatcherIndex = -1;
+ }
+
+ mInitialized = true;
+}
+
+SimpleConditionTracker::~SimpleConditionTracker() {
+ VLOG("~SimpleConditionTracker()");
+}
+
+bool SimpleConditionTracker::init(const vector<Condition>& allConditionConfig,
+ const vector<sp<ConditionTracker>>& allConditionTrackers,
+ const unordered_map<string, int>& conditionNameIndexMap,
+ vector<bool>& stack) {
+ // SimpleConditionTracker does not have dependency on other conditions, thus we just return
+ // if the initialization was successful.
+ return mInitialized;
+}
+
+bool SimpleConditionTracker::evaluateCondition(const LogEventWrapper& event,
+ const vector<MatchingState>& eventMatcherValues,
+ const vector<sp<ConditionTracker>>& mAllConditions,
+ vector<ConditionState>& conditionCache,
+ vector<bool>& changedCache) {
+ if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
+ // it has been evaluated.
+ VLOG("Yes, already evaluated, %s %d", mName.c_str(), mConditionState);
+ return false;
+ }
+
+ // Ignore nesting, because we know we cannot trust ourselves on tracking nesting conditions.
+ ConditionState newCondition = mConditionState;
+ // Note: The order to evaluate the following start, stop, stop_all matters.
+ // The priority of overwrite is stop_all > stop > start.
+ if (mStartLogMatcherIndex >= 0 &&
+ eventMatcherValues[mStartLogMatcherIndex] == MatchingState::kMatched) {
+ newCondition = ConditionState::kTrue;
+ }
+
+ if (mStopLogMatcherIndex >= 0 &&
+ eventMatcherValues[mStopLogMatcherIndex] == MatchingState::kMatched) {
+ newCondition = ConditionState::kFalse;
+ }
+
+ if (mStopAllLogMatcherIndex >= 0 &&
+ eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
+ newCondition = ConditionState::kFalse;
+ }
+
+ bool changed = (mConditionState != newCondition);
+ mConditionState = newCondition;
+ conditionCache[mIndex] = mConditionState;
+ changedCache[mIndex] = changed;
+ return changed;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
new file mode 100644
index 0000000..41e1707
--- /dev/null
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#ifndef SIMPLE_CONDITION_TRACKER_H
+#define SIMPLE_CONDITION_TRACKER_H
+
+#include "ConditionTracker.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class SimpleConditionTracker : public virtual ConditionTracker {
+public:
+ SimpleConditionTracker(const std::string& name, const int index,
+ const SimpleCondition& simpleCondition,
+ const std::unordered_map<std::string, int>& trackerNameIndexMap);
+
+ ~SimpleConditionTracker();
+
+ bool init(const std::vector<Condition>& allConditionConfig,
+ const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ const std::unordered_map<std::string, int>& conditionNameIndexMap,
+ std::vector<bool>& stack) override;
+
+ bool evaluateCondition(const LogEventWrapper& event,
+ const std::vector<MatchingState>& eventMatcherValues,
+ const std::vector<sp<ConditionTracker>>& mAllConditions,
+ std::vector<ConditionState>& conditionCache,
+ std::vector<bool>& changedCache) override;
+
+private:
+ // The index of the LogEventMatcher which defines the start.
+ int mStartLogMatcherIndex;
+
+ // The index of the LogEventMatcher which defines the end.
+ int mStopLogMatcherIndex;
+
+ // if the start end needs to be nested.
+ bool mCountNesting;
+
+ // The index of the LogEventMatcher which defines the stop all.
+ int mStopAllLogMatcherIndex;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif // SIMPLE_CONDITION_TRACKER_H
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
new file mode 100644
index 0000000..cb07d15
--- /dev/null
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+#include "condition_util.h"
+
+#include <cutils/log.h>
+#include <log/event_tag_map.h>
+#include <log/log_event_list.h>
+#include <log/logprint.h>
+#include <utils/Errors.h>
+#include <unordered_map>
+#include "ConditionTracker.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "stats_util.h"
+
+using std::set;
+using std::string;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+ConditionState evaluateCombinationCondition(const std::vector<int>& children,
+ const LogicalOperation& operation,
+ const std::vector<ConditionState>& conditionCache) {
+ ConditionState newCondition;
+
+ bool hasUnknown = false;
+ bool hasFalse = false;
+ bool hasTrue = false;
+
+ for (auto childIndex : children) {
+ ConditionState childState = conditionCache[childIndex];
+ if (childState == ConditionState::kUnknown) {
+ hasUnknown = true;
+ break;
+ }
+ if (childState == ConditionState::kFalse) {
+ hasFalse = true;
+ }
+ if (childState == ConditionState::kTrue) {
+ hasTrue = true;
+ }
+ }
+
+ // If any child condition is in unknown state, the condition is unknown too.
+ if (hasUnknown) {
+ return ConditionState::kUnknown;
+ }
+
+ switch (operation) {
+ case LogicalOperation::AND: {
+ newCondition = hasFalse ? ConditionState::kFalse : ConditionState::kTrue;
+ break;
+ }
+ case LogicalOperation::OR: {
+ newCondition = hasTrue ? ConditionState::kTrue : ConditionState::kFalse;
+ break;
+ }
+ case LogicalOperation::NOT:
+ newCondition = (conditionCache[children[0]] == ConditionState::kFalse)
+ ? ConditionState::kTrue
+ : ConditionState::kFalse;
+ break;
+ case LogicalOperation::NAND:
+ newCondition = hasFalse ? ConditionState::kTrue : ConditionState::kFalse;
+ break;
+ case LogicalOperation::NOR:
+ newCondition = hasTrue ? ConditionState::kFalse : ConditionState::kTrue;
+ break;
+ }
+ return newCondition;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/metrics/ConditionTracker.h b/cmds/statsd/src/condition/condition_util.h
similarity index 60%
rename from cmds/statsd/src/metrics/ConditionTracker.h
rename to cmds/statsd/src/condition/condition_util.h
index b94d5ab..a4fcea3 100644
--- a/cmds/statsd/src/metrics/ConditionTracker.h
+++ b/cmds/statsd/src/condition/condition_util.h
@@ -14,38 +14,28 @@
* limitations under the License.
*/
-#ifndef CONDITION_TRACKER_H
-#define CONDITION_TRACKER_H
+#ifndef CONDITION_UTIL_H
+#define CONDITION_UTIL_H
-#include <utils/RefBase.h>
-#include "../matchers/LogEntryMatcherManager.h"
+#include <vector>
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
namespace android {
namespace os {
namespace statsd {
-class ConditionTracker : public RefBase {
-public:
- ConditionTracker();
-
- ConditionTracker(const Condition& condition);
-
- ~ConditionTracker();
-
- void evaluateCondition(const LogEventWrapper& event);
-
- bool isConditionMet() const;
-
-private:
- // this is the definition of the Condition.
- Condition mCondition;
-
- bool mIsConditionMet;
+enum ConditionState {
+ kNotEvaluated = -2,
+ kUnknown = -1,
+ kFalse = 0,
+ kTrue = 1,
};
+ConditionState evaluateCombinationCondition(const std::vector<int>& children,
+ const LogicalOperation& operation,
+ const std::vector<ConditionState>& conditionCache);
} // namespace statsd
} // namespace os
} // namespace android
-
-#endif // CONDITION_TRACKER_H
+#endif // CONDITION_UTIL_H
diff --git a/cmds/statsd/src/indexed_priority_queue.h b/cmds/statsd/src/indexed_priority_queue.h
index c749c3e..81e8b3d 100644
--- a/cmds/statsd/src/indexed_priority_queue.h
+++ b/cmds/statsd/src/indexed_priority_queue.h
@@ -55,6 +55,8 @@
void push(sp<const AA> a);
/** Removes a from the priority queue. If not present or a==nullptr, does nothing. */
void remove(sp<const AA> a);
+ /** Removes the top element, if there is one. */
+ void pop();
/** Removes all elements. */
void clear();
/** Returns whether priority queue contains a (not just a copy of a, but a itself). */
@@ -127,6 +129,28 @@
sift_down(idx);
}
+// The same as, but slightly more efficient than, remove(top()).
+template <class AA, class Comparator>
+void indexed_priority_queue<AA, Comparator>::pop() {
+ sp<const AA> a = top();
+ if (a == nullptr) return;
+ const size_t idx = 1;
+ if (idx == size()) { // if a is the last element
+ pq.pop_back();
+ indices.erase(a);
+ return;
+ }
+ // move last element (guaranteed not to be at idx) to idx, then delete a
+ sp<const AA> last_a = pq.back();
+ pq[idx] = last_a;
+ pq.pop_back();
+ indices[last_a] = idx;
+ indices.erase(a);
+
+ // get the heap back in order (since the element at idx is not in order)
+ sift_down(idx);
+}
+
template <class AA, class Comparator>
void indexed_priority_queue<AA, Comparator>::clear() {
pq.clear();
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index b303321..37477dc 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -20,6 +20,7 @@
#include "LogReader.h"
#include "StatsLogProcessor.h"
#include "StatsService.h"
+#include "UidMap.h"
#include <binder/IInterface.h>
#include <binder/IPCThreadState.h>
@@ -56,7 +57,7 @@
// Put the printer one first, so it will print before the real ones.
reader->AddListener(new LogEntryPrinter(STDOUT_FILENO));
- sp<StatsLogProcessor> main_processor = new StatsLogProcessor();
+ sp<StatsLogProcessor> main_processor = new StatsLogProcessor(data->service->getUidMap());
data->service->setProcessor(main_processor);
reader->AddListener(main_processor);
diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
new file mode 100644
index 0000000..9f9b648
--- /dev/null
+++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+#include "CombinationLogMatchingTracker.h"
+
+#include <cutils/log.h>
+#include "matcher_util.h"
+using std::set;
+using std::string;
+using std::unique_ptr;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+CombinationLogMatchingTracker::CombinationLogMatchingTracker(const string& name, const int index)
+ : LogMatchingTracker(name, index) {
+}
+
+CombinationLogMatchingTracker::~CombinationLogMatchingTracker() {
+}
+
+bool CombinationLogMatchingTracker::init(const vector<LogEntryMatcher>& allLogMatchers,
+ const vector<sp<LogMatchingTracker>>& allTrackers,
+ const unordered_map<string, int>& matcherMap,
+ vector<bool>& stack) {
+ if (mInitialized) {
+ return true;
+ }
+
+ // mark this node as visited in the recursion stack.
+ stack[mIndex] = true;
+
+ LogEntryMatcher_Combination matcher = allLogMatchers[mIndex].combination();
+
+ // LogicalOperation is missing in the config
+ if (!matcher.has_operation()) {
+ return false;
+ }
+
+ mLogicalOperation = matcher.operation();
+
+ if (mLogicalOperation == LogicalOperation::NOT && matcher.matcher_size() != 1) {
+ return false;
+ }
+
+ for (const string& child : matcher.matcher()) {
+ auto pair = matcherMap.find(child);
+ if (pair == matcherMap.end()) {
+ ALOGW("Matcher %s not found in the config", child.c_str());
+ return false;
+ }
+
+ int childIndex = pair->second;
+
+ // if the child is a visited node in the recursion -> circle detected.
+ if (stack[childIndex]) {
+ ALOGE("Circle detected in matcher config");
+ return false;
+ }
+
+ if (!allTrackers[childIndex]->init(allLogMatchers, allTrackers, matcherMap, stack)) {
+ ALOGW("child matcher init failed %s", child.c_str());
+ return false;
+ }
+
+ mChildren.push_back(childIndex);
+
+ const set<int>& childTagIds = allTrackers[childIndex]->getTagIds();
+ mTagIds.insert(childTagIds.begin(), childTagIds.end());
+ }
+
+ mInitialized = true;
+ // unmark this node in the recursion stack.
+ stack[mIndex] = false;
+ return true;
+}
+
+void CombinationLogMatchingTracker::onLogEvent(const LogEventWrapper& event,
+ const vector<sp<LogMatchingTracker>>& allTrackers,
+ vector<MatchingState>& matcherResults) {
+ // this event has been processed.
+ if (matcherResults[mIndex] != MatchingState::kNotComputed) {
+ return;
+ }
+
+ if (mTagIds.find(event.tagId) == mTagIds.end()) {
+ matcherResults[mIndex] = MatchingState::kNotMatched;
+ return;
+ }
+
+ // evaluate children matchers if they haven't been evaluated.
+ for (const int childIndex : mChildren) {
+ if (matcherResults[childIndex] == MatchingState::kNotComputed) {
+ const sp<LogMatchingTracker>& child = allTrackers[childIndex];
+ child->onLogEvent(event, allTrackers, matcherResults);
+ }
+ }
+
+ bool matched = combinationMatch(mChildren, mLogicalOperation, matcherResults);
+ matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
new file mode 100644
index 0000000..51ee232
--- /dev/null
+++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+#ifndef COMBINATION_LOG_MATCHING_TRACKER_H
+#define COMBINATION_LOG_MATCHING_TRACKER_H
+
+#include <log/log_read.h>
+#include <log/logprint.h>
+#include <set>
+#include <unordered_map>
+#include <vector>
+#include "LogMatchingTracker.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Represents a LogEntryMatcher_Combination in the StatsdConfig.
+class CombinationLogMatchingTracker : public virtual LogMatchingTracker {
+public:
+ CombinationLogMatchingTracker(const std::string& name, const int index);
+
+ bool init(const std::vector<LogEntryMatcher>& allLogMatchers,
+ const std::vector<sp<LogMatchingTracker>>& allTrackers,
+ const std::unordered_map<std::string, int>& matcherMap,
+ std::vector<bool>& stack);
+
+ ~CombinationLogMatchingTracker();
+
+ void onLogEvent(const LogEventWrapper& event,
+ const std::vector<sp<LogMatchingTracker>>& allTrackers,
+ std::vector<MatchingState>& matcherResults) override;
+
+private:
+ LogicalOperation mLogicalOperation;
+
+ std::vector<int> mChildren;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#endif // COMBINATION_LOG_MATCHING_TRACKER_H
diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h
new file mode 100644
index 0000000..4244bd5
--- /dev/null
+++ b/cmds/statsd/src/matchers/LogMatchingTracker.h
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+#ifndef LOG_MATCHING_TRACKER_H
+#define LOG_MATCHING_TRACKER_H
+
+#include <log/log_read.h>
+#include <log/logprint.h>
+#include <utils/RefBase.h>
+#include <set>
+#include <unordered_map>
+
+#include <vector>
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "matcher_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class LogMatchingTracker : public virtual RefBase {
+public:
+ LogMatchingTracker(const std::string& name, const int index)
+ : mName(name), mIndex(index), mInitialized(false){};
+
+ virtual ~LogMatchingTracker(){};
+
+ // Initialize this LogMatchingTracker.
+ // allLogMatchers: the list of the LogEntryMatcher proto config. This is needed because we don't
+ // store the proto object in memory. We only need it during initilization.
+ // allTrackers: the list of the LogMatchingTracker objects. It's a one-to-one mapping with
+ // allLogMatchers. This is needed because the initialization is done recursively
+ // for CombinationLogMatchingTrackers using DFS.
+ // stack: a bit map to record which matcher has been visited on the stack. This is for detecting
+ // circle dependency.
+ virtual bool init(const std::vector<LogEntryMatcher>& allLogMatchers,
+ const std::vector<sp<LogMatchingTracker>>& allTrackers,
+ const std::unordered_map<std::string, int>& matcherMap,
+ std::vector<bool>& stack) = 0;
+
+ // Called when a log event comes.
+ // event: the log event.
+ // allTrackers: the list of all LogMatchingTrackers. This is needed because the log processing
+ // is done recursively.
+ // matcherResults: The cached results for all matchers for this event. Parent matchers can
+ // directly access the children's matching results if they have been evaluated.
+ // Otherwise, call children matchers' onLogEvent.
+ virtual void onLogEvent(const LogEventWrapper& event,
+ const std::vector<sp<LogMatchingTracker>>& allTrackers,
+ std::vector<MatchingState>& matcherResults) = 0;
+
+ // Get the tagIds that this matcher cares about. The combined collection is stored
+ // in MetricMananger, so that we can pass any LogEvents that are not interest of us. It uses
+ // some memory but hopefully it can save us much CPU time when there is flood of events.
+ virtual const std::set<int>& getTagIds() const {
+ return mTagIds;
+ }
+
+protected:
+ // Name of this matching. We don't really need the name, but it makes log message easy to debug.
+ const std::string mName;
+
+ // Index of this LogMatchingTracker in MetricsManager's container.
+ const int mIndex;
+
+ // Whether this LogMatchingTracker has been properly initialized.
+ bool mInitialized;
+
+ // The collection of the event tag ids that this LogMatchingTracker cares. So we can quickly
+ // return kNotMatched when we receive an event with an id not in the list. This is especially
+ // useful when we have a complex CombinationLogMatcherTracker.
+ // TODO: Consider use an array instead of stl set. In reality, the number of the tag ids a
+ // LogMatchingTracker cares is only a few.
+ std::set<int> mTagIds;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+
+#endif // LOG_MATCHING_TRACKER_H
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
new file mode 100644
index 0000000..1c83039
--- /dev/null
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SimpleLogMatchingTracker"
+#define DEBUG true // STOPSHIP if true
+#define VLOG(...) \
+ if (DEBUG) ALOGD(__VA_ARGS__);
+
+#include "SimpleLogMatchingTracker.h"
+#include <cutils/log.h>
+#include <log/logprint.h>
+
+using std::string;
+using std::unique_ptr;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+SimpleLogMatchingTracker::SimpleLogMatchingTracker(const string& name, const int index,
+ const SimpleLogEntryMatcher& matcher)
+ : LogMatchingTracker(name, index), mMatcher(matcher) {
+ for (int i = 0; i < matcher.tag_size(); i++) {
+ mTagIds.insert(matcher.tag(i));
+ }
+ mInitialized = true;
+}
+
+SimpleLogMatchingTracker::~SimpleLogMatchingTracker() {
+}
+
+bool SimpleLogMatchingTracker::init(const vector<LogEntryMatcher>& allLogMatchers,
+ const vector<sp<LogMatchingTracker>>& allTrackers,
+ const unordered_map<string, int>& matcherMap,
+ vector<bool>& stack) {
+ // no need to do anything.
+ return true;
+}
+
+void SimpleLogMatchingTracker::onLogEvent(const LogEventWrapper& event,
+ const vector<sp<LogMatchingTracker>>& allTrackers,
+ vector<MatchingState>& matcherResults) {
+ if (matcherResults[mIndex] != MatchingState::kNotComputed) {
+ VLOG("Matcher %s already evaluated ", mName.c_str());
+ return;
+ }
+
+ if (mTagIds.find(event.tagId) == mTagIds.end()) {
+ matcherResults[mIndex] = MatchingState::kNotMatched;
+ return;
+ }
+
+ bool matched = matchesSimple(mMatcher, event);
+ matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
+ VLOG("Stats SimpleLogMatcher %s matched? %d", mName.c_str(), matched);
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
new file mode 100644
index 0000000..65dbe64
--- /dev/null
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#ifndef SIMPLE_LOG_MATCHING_TRACKER_H
+#define SIMPLE_LOG_MATCHING_TRACKER_H
+
+#include <log/log_read.h>
+#include <log/logprint.h>
+#include <set>
+#include <unordered_map>
+#include <vector>
+#include "LogMatchingTracker.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class SimpleLogMatchingTracker : public virtual LogMatchingTracker {
+public:
+ SimpleLogMatchingTracker(const std::string& name, const int index,
+ const SimpleLogEntryMatcher& matcher);
+
+ ~SimpleLogMatchingTracker();
+
+ bool init(const std::vector<LogEntryMatcher>& allLogMatchers,
+ const std::vector<sp<LogMatchingTracker>>& allTrackers,
+ const std::unordered_map<std::string, int>& matcherMap,
+ std::vector<bool>& stack) override;
+
+ void onLogEvent(const LogEventWrapper& event,
+ const std::vector<sp<LogMatchingTracker>>& allTrackers,
+ std::vector<MatchingState>& matcherResults) override;
+
+private:
+ const SimpleLogEntryMatcher mMatcher;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#endif // SIMPLE_LOG_MATCHING_TRACKER_H
diff --git a/cmds/statsd/src/matchers/LogEntryMatcherManager.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
similarity index 69%
rename from cmds/statsd/src/matchers/LogEntryMatcherManager.cpp
rename to cmds/statsd/src/matchers/matcher_util.cpp
index ab7b2b1d..557c032 100644
--- a/cmds/statsd/src/matchers/LogEntryMatcherManager.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -14,26 +14,28 @@
* limitations under the License.
*/
-#include "LogEntryMatcherManager.h"
+#include "matcher_util.h"
#include <cutils/log.h>
#include <log/event_tag_map.h>
#include <log/log_event_list.h>
#include <log/logprint.h>
#include <utils/Errors.h>
#include <unordered_map>
+#include "LogMatchingTracker.h"
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-#include "parse_util.h"
+#include "stats_util.h"
using std::set;
using std::string;
using std::unordered_map;
+using std::vector;
namespace android {
namespace os {
namespace statsd {
-LogEventWrapper LogEntryMatcherManager::parseLogEvent(log_msg msg) {
+LogEventWrapper parseLogEvent(log_msg msg) {
LogEventWrapper wrapper;
wrapper.timestamp_ns = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec;
wrapper.tagId = getTagId(msg);
@@ -67,7 +69,9 @@
break;
case EVENT_TYPE_STRING:
if (index % 2 == 1) {
- wrapper.strMap[key] = elem.data.string;
+ // without explicit calling string() constructor, there will be an
+ // additional 0 in the end of the string.
+ wrapper.strMap[key] = string(elem.data.string);
}
index++;
break;
@@ -99,57 +103,56 @@
return wrapper;
}
-bool LogEntryMatcherManager::matches(const LogEntryMatcher& matcher, const LogEventWrapper& event) {
- const int tagId = event.tagId;
- const unordered_map<int, long>& intMap = event.intMap;
- const unordered_map<int, string>& strMap = event.strMap;
- const unordered_map<int, float>& floatMap = event.floatMap;
- const unordered_map<int, bool>& boolMap = event.boolMap;
-
- if (matcher.has_combination()) { // Need to evaluate composite matching
- switch (matcher.combination().operation()) {
- case LogicalOperation::AND:
- for (auto nestedMatcher : matcher.combination().matcher()) {
- if (!matches(nestedMatcher, event)) {
- return false; // return false if any nested matcher is false;
- }
+bool combinationMatch(const vector<int>& children, const LogicalOperation& operation,
+ const vector<MatchingState>& matcherResults) {
+ bool matched;
+ switch (operation) {
+ case LogicalOperation::AND: {
+ matched = true;
+ for (const int childIndex : children) {
+ if (matcherResults[childIndex] != MatchingState::kMatched) {
+ matched = false;
+ break;
}
- return true; // Otherwise, return true.
- case LogicalOperation::OR:
- for (auto nestedMatcher : matcher.combination().matcher()) {
- if (matches(nestedMatcher, event)) {
- return true; // return true if any nested matcher is true;
- }
- }
- return false;
- case LogicalOperation::NOT:
- return !matches(matcher.combination().matcher(0), event);
-
- // Case NAND is just inverting the return statement of AND
- case LogicalOperation::NAND:
- for (auto nestedMatcher : matcher.combination().matcher()) {
- auto simple = nestedMatcher.simple_log_entry_matcher();
- if (!matches(nestedMatcher, event)) {
- return true; // return false if any nested matcher is false;
- }
- }
- return false; // Otherwise, return true.
- case LogicalOperation::NOR:
- for (auto nestedMatcher : matcher.combination().matcher()) {
- if (matches(nestedMatcher, event)) {
- return false; // return true if any nested matcher is true;
- }
- }
- return true;
+ }
+ break;
}
- return false;
- } else {
- return matchesSimple(matcher.simple_log_entry_matcher(), event);
+ case LogicalOperation::OR: {
+ matched = false;
+ for (const int childIndex : children) {
+ if (matcherResults[childIndex] == MatchingState::kMatched) {
+ matched = true;
+ break;
+ }
+ }
+ break;
+ }
+ case LogicalOperation::NOT:
+ matched = matcherResults[children[0]] == MatchingState::kNotMatched;
+ break;
+ case LogicalOperation::NAND:
+ matched = false;
+ for (const int childIndex : children) {
+ if (matcherResults[childIndex] != MatchingState::kMatched) {
+ matched = true;
+ break;
+ }
+ }
+ break;
+ case LogicalOperation::NOR:
+ matched = true;
+ for (const int childIndex : children) {
+ if (matcherResults[childIndex] == MatchingState::kMatched) {
+ matched = false;
+ break;
+ }
+ }
+ break;
}
+ return matched;
}
-bool LogEntryMatcherManager::matchesSimple(const SimpleLogEntryMatcher& simpleMatcher,
- const LogEventWrapper& event) {
+bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher, const LogEventWrapper& event) {
const int tagId = event.tagId;
const unordered_map<int, long>& intMap = event.intMap;
const unordered_map<int, string>& strMap = event.strMap;
@@ -249,26 +252,6 @@
return false;
}
-set<int> LogEntryMatcherManager::getTagIdsFromMatcher(const LogEntryMatcher& matcher) {
- set<int> result;
- switch (matcher.contents_case()) {
- case LogEntryMatcher::kCombination:
- for (auto sub_matcher : matcher.combination().matcher()) {
- set<int> tagSet = getTagIdsFromMatcher(sub_matcher);
- result.insert(tagSet.begin(), tagSet.end());
- }
- break;
- case LogEntryMatcher::kSimpleLogEntryMatcher:
- for (int i = 0; i < matcher.simple_log_entry_matcher().tag_size(); i++) {
- result.insert(matcher.simple_log_entry_matcher().tag(i));
- }
- break;
- case LogEntryMatcher::CONTENTS_NOT_SET:
- break;
- }
- return result;
-}
-
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/matchers/LogEntryMatcherManager.h b/cmds/statsd/src/matchers/matcher_util.h
similarity index 63%
rename from cmds/statsd/src/matchers/LogEntryMatcherManager.h
rename to cmds/statsd/src/matchers/matcher_util.h
index fc8e6a1..6d8e762 100644
--- a/cmds/statsd/src/matchers/LogEntryMatcherManager.h
+++ b/cmds/statsd/src/matchers/matcher_util.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef LOG_ENTRY_MATCHER_MANAGER_H
-#define LOG_ENTRY_MATCHER_MANAGER_H
+#ifndef MATCHER_UTIL_H
+#define MATCHER_UTIL_H
#include <log/log_read.h>
#include <log/logprint.h>
@@ -25,9 +25,6 @@
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
-using std::string;
-using std::unordered_map;
-
namespace android {
namespace os {
namespace statsd {
@@ -41,26 +38,20 @@
std::unordered_map<int, float> floatMap;
} LogEventWrapper;
-/**
- * Keeps track per log entry which simple log entry matchers match.
- */
-class LogEntryMatcherManager {
-public:
- LogEntryMatcherManager();
-
- ~LogEntryMatcherManager(){};
-
- static LogEventWrapper parseLogEvent(log_msg msg);
-
- static std::set<int> getTagIdsFromMatcher(const LogEntryMatcher& matcher);
-
- static bool matches(const LogEntryMatcher& matcher, const LogEventWrapper& wrapper);
-
- static bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher,
- const LogEventWrapper& wrapper);
+enum MatchingState {
+ kNotComputed = -1,
+ kNotMatched = 0,
+ kMatched = 1,
};
+LogEventWrapper parseLogEvent(log_msg msg);
+
+bool combinationMatch(const std::vector<int>& children, const LogicalOperation& operation,
+ const std::vector<MatchingState>& matcherResults);
+
+bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher, const LogEventWrapper& wrapper);
+
} // namespace statsd
} // namespace os
} // namespace android
-#endif // LOG_ENTRY_MATCHER_MANAGER_H
+#endif // MATCHER_UTIL_H
diff --git a/cmds/statsd/src/metrics/ConditionTracker.cpp b/cmds/statsd/src/metrics/ConditionTracker.cpp
deleted file mode 100644
index 684ffdb..0000000
--- a/cmds/statsd/src/metrics/ConditionTracker.cpp
+++ /dev/null
@@ -1,54 +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.
- */
-
-#define LOG_TAG "ConditionTracker"
-#define DEBUG true // STOPSHIP if true
-#define VLOG(...) \
- if (DEBUG) ALOGD(__VA_ARGS__);
-
-#include "ConditionTracker.h"
-#include <cutils/log.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-ConditionTracker::ConditionTracker() : mIsConditionMet(true) {
- VLOG("ConditionTracker()");
-}
-
-ConditionTracker::ConditionTracker(const Condition& condition)
- : mCondition(condition), mIsConditionMet(true) {
- VLOG("ConditionTracker()");
-}
-
-ConditionTracker::~ConditionTracker() {
- VLOG("~ConditionTracker()");
-}
-
-void ConditionTracker::evaluateCondition(const LogEventWrapper& event) {
- // modify condition.
- VLOG("evaluateCondition");
-}
-
-bool ConditionTracker::isConditionMet() const {
- VLOG("isConditionMet() %d", mIsConditionMet);
- return mIsConditionMet;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/metrics/CountAnomalyTracker.cpp b/cmds/statsd/src/metrics/CountAnomalyTracker.cpp
new file mode 100644
index 0000000..ebd53e0
--- /dev/null
+++ b/cmds/statsd/src/metrics/CountAnomalyTracker.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "CountAnomaly"
+#define DEBUG true // STOPSHIP if true
+#define VLOG(...) \
+ if (DEBUG) ALOGD(__VA_ARGS__);
+
+#include "CountAnomalyTracker.h"
+
+#include <cutils/log.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+CountAnomalyTracker::CountAnomalyTracker(size_t numBuckets, int thresholdGt)
+ : mNumPastBuckets(numBuckets > 0 ? numBuckets - 1 : 0),
+ mPastBuckets(mNumPastBuckets > 0 ? (new int[mNumPastBuckets]) : nullptr),
+ mThresholdGt(thresholdGt) {
+
+ VLOG("CountAnomalyTracker() called");
+ if (numBuckets < 1) {
+ ALOGE("Cannot create CountAnomalyTracker with %zu buckets", numBuckets);
+ }
+ reset(); // initialization
+}
+
+CountAnomalyTracker::~CountAnomalyTracker() {
+ VLOG("~CountAnomalyTracker() called");
+}
+
+void CountAnomalyTracker::addPastBucket(int pastBucketCount,
+ time_t numberOfBucketsAgo) {
+ VLOG("addPastBucket() called.");
+ if (numberOfBucketsAgo < 1) {
+ ALOGE("Cannot add a past bucket %ld units in past", numberOfBucketsAgo);
+ return;
+ }
+ // If past bucket was ancient, just empty out all past info.
+ // This always applies if mNumPastBuckets == 0 (i.e. store no past buckets).
+ if (numberOfBucketsAgo > (time_t) mNumPastBuckets) {
+ reset();
+ return;
+ }
+
+ // Empty out old mPastBuckets[i] values and update mSumPastCounters.
+ for (size_t i = mOldestBucketIndex;
+ i < mOldestBucketIndex + numberOfBucketsAgo; i++) {
+ mSumPastCounters -= mPastBuckets[index(i)];
+ mPastBuckets[index(i)] = 0;
+ }
+
+ // Replace the oldest bucket with the new bucket we are adding.
+ mPastBuckets[mOldestBucketIndex] = pastBucketCount;
+ mSumPastCounters += pastBucketCount;
+
+ // Advance the oldest bucket index by numberOfBucketsAgo units.
+ mOldestBucketIndex = index(mOldestBucketIndex + numberOfBucketsAgo);
+
+ // TODO: Once dimensions are added to mSumPastCounters:
+ // iterate through mSumPastCounters and remove any entries that are 0.
+}
+
+void CountAnomalyTracker::reset() {
+ VLOG("reset() called.");
+ for (size_t i = 0; i < mNumPastBuckets; i++) {
+ mPastBuckets[i] = 0;
+ }
+ mSumPastCounters = 0;
+ mOldestBucketIndex = 0;
+}
+
+void CountAnomalyTracker::checkAnomaly(int currentCount) {
+ // Note that this works even if mNumPastBuckets < 1 (since then
+ // mSumPastCounters = 0 so the comparison is based only on currentCount).
+
+ // TODO: Remove these extremely verbose debugging log.
+ VLOG("Checking whether %d + %d > %d",
+ mSumPastCounters, currentCount, mThresholdGt);
+
+ if (mSumPastCounters + currentCount > mThresholdGt) {
+ declareAnomaly();
+ }
+}
+
+void CountAnomalyTracker::declareAnomaly() {
+ // TODO: check that not in refractory period.
+ // TODO: Do something.
+ ALOGI("An anomaly has occurred!");
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/metrics/CountAnomalyTracker.h b/cmds/statsd/src/metrics/CountAnomalyTracker.h
new file mode 100644
index 0000000..449dee9
--- /dev/null
+++ b/cmds/statsd/src/metrics/CountAnomalyTracker.h
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#ifndef COUNT_ANOMALY_TRACKER_H
+#define COUNT_ANOMALY_TRACKER_H
+
+#include <stdlib.h>
+#include <memory> // unique_ptr
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class CountAnomalyTracker {
+public:
+ CountAnomalyTracker(size_t numBuckets, int thresholdGt);
+
+ virtual ~CountAnomalyTracker();
+
+
+ // Adds a new past bucket, holding pastBucketCount, and then advances the
+ // present by numberOfBucketsAgo buckets (filling any intervening buckets
+ // with 0s).
+ // Thus, the newly added bucket (which holds pastBucketCount) is stored
+ // numberOfBucketsAgo buckets ago.
+ void addPastBucket(int pastBucketCount, time_t numberOfBucketsAgo);
+
+ // Informs the anomaly tracker of the current bucket's count, so that it can
+ // determine whether an anomaly has occurred. This value is not stored.
+ void checkAnomaly(int currentCount);
+
+private:
+ // Number of past buckets. One less than the total number of buckets needed
+ // for the anomaly detection (since the current bucket is not in the past).
+ const size_t mNumPastBuckets;
+
+ // Count values for each of the past mNumPastBuckets buckets.
+ // TODO: Add dimensions. This parallels the type of CountMetricProducer.mCounter.
+ std::unique_ptr<int[]> mPastBuckets;
+
+ // Sum over all of mPastBuckets (cached).
+ // TODO: Add dimensions. This parallels the type of CountMetricProducer.mCounter.
+ // At that point, mSumPastCounters must never contain entries of 0.
+ int mSumPastCounters;
+
+ // Index of the oldest bucket (i.e. the next bucket to be overwritten).
+ size_t mOldestBucketIndex = 0;
+
+ // If mSumPastCounters + currentCount > mThresholdGt --> Anomaly!
+ const int mThresholdGt;
+
+ void declareAnomaly();
+
+ // Calculates the corresponding index within the circular array.
+ size_t index(size_t unsafeIndex) {
+ return unsafeIndex % mNumPastBuckets;
+ }
+
+ // Resets all data. For use when all the data gets stale.
+ void reset();
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#endif // COUNT_ANOMALY_TRACKER_H
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index fbd013e..e98999e 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -20,7 +20,7 @@
if (DEBUG) ALOGD(__VA_ARGS__);
#include "CountMetricProducer.h"
-#include "parse_util.h"
+#include "CountAnomalyTracker.h"
#include <cutils/log.h>
#include <limits.h>
@@ -32,13 +32,14 @@
namespace os {
namespace statsd {
-CountMetricProducer::CountMetricProducer(const CountMetric& metric,
- const sp<ConditionTracker> condition)
+CountMetricProducer::CountMetricProducer(const CountMetric& metric, const bool hasCondition)
: mMetric(metric),
- mConditionTracker(condition),
- mStartTime(std::time(nullptr)),
+ mStartTime(time(nullptr)),
mCounter(0),
- mCurrentBucketStartTime(mStartTime) {
+ mCurrentBucketStartTime(mStartTime),
+ // TODO: read mAnomalyTracker parameters from config file.
+ mAnomalyTracker(6, 10),
+ mCondition(hasCondition ? ConditionState::kUnknown : ConditionState::kTrue) {
// TODO: evaluate initial conditions. and set mConditionMet.
if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
mBucketSize_sec = metric.bucket().bucket_size_millis() / 1000;
@@ -49,10 +50,6 @@
VLOG("created. bucket size %lu start_time: %lu", mBucketSize_sec, mStartTime);
}
-CountMetricProducer::CountMetricProducer(const CountMetric& metric)
- : CountMetricProducer(metric, new ConditionTracker()) {
-}
-
CountMetricProducer::~CountMetricProducer() {
VLOG("~CountMetricProducer() called");
}
@@ -60,13 +57,17 @@
void CountMetricProducer::finish() {
// TODO: write the StatsLogReport to dropbox using
// DropboxWriter.
- onDumpReport();
}
void CountMetricProducer::onDumpReport() {
VLOG("dump report now...");
}
+void CountMetricProducer::onConditionChanged(const bool conditionMet) {
+ VLOG("onConditionChanged");
+ mCondition = conditionMet;
+}
+
void CountMetricProducer::onMatchedLogEvent(const LogEventWrapper& event) {
time_t eventTime = event.timestamp_ns / 1000000000;
@@ -75,31 +76,37 @@
return;
}
- if (mConditionTracker->isConditionMet()) {
+ if (mCondition == ConditionState::kTrue) {
flushCounterIfNeeded(eventTime);
mCounter++;
+ mAnomalyTracker.checkAnomaly(mCounter);
+ VLOG("metric %lld count %d", mMetric.metric_id(), mCounter);
}
}
-// When a new matched event comes in, we check if it falls into the current bucket. And flush the
-// counter to the StatsLogReport and adjust the bucket if needed.
+// When a new matched event comes in, we check if it falls into the current
+// bucket. And flush the counter to the StatsLogReport and adjust the bucket if
+// needed.
void CountMetricProducer::flushCounterIfNeeded(const time_t& eventTime) {
if (mCurrentBucketStartTime + mBucketSize_sec > eventTime) {
return;
}
// TODO: add a KeyValuePair to StatsLogReport.
- ALOGD("CountMetric: dump counter %d", mCounter);
-
- // reset counter
- mCounter = 0;
+ ALOGD("%lld: dump counter %d", mMetric.metric_id(), mCounter);
// adjust the bucket start time
- mCurrentBucketStartTime =
- mCurrentBucketStartTime +
- ((eventTime - mCurrentBucketStartTime) / mBucketSize_sec) * mBucketSize_sec;
+ time_t numBucketsForward = (eventTime - mCurrentBucketStartTime)
+ / mBucketSize_sec;
- VLOG("new bucket start time: %lu", mCurrentBucketStartTime);
+ mCurrentBucketStartTime = mCurrentBucketStartTime +
+ (numBucketsForward) * mBucketSize_sec;
+
+ // reset counter
+ mAnomalyTracker.addPastBucket(mCounter, numBucketsForward);
+ mCounter = 0;
+
+ VLOG("%lld: new bucket start time: %lu", mMetric.metric_id(), mCurrentBucketStartTime);
}
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 7665791..370cd468 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -17,47 +17,54 @@
#ifndef COUNT_METRIC_PRODUCER_H
#define COUNT_METRIC_PRODUCER_H
-#include <mutex>
-#include <thread>
#include <unordered_map>
-#include "../matchers/LogEntryMatcherManager.h"
-#include "ConditionTracker.h"
-#include "DropboxWriter.h"
+
+#include "CountAnomalyTracker.h"
+#include "../condition/ConditionTracker.h"
+#include "../matchers/matcher_util.h"
#include "MetricProducer.h"
#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+using namespace std;
+
namespace android {
namespace os {
namespace statsd {
class CountMetricProducer : public MetricProducer {
public:
- CountMetricProducer(const CountMetric& countMetric, const sp<ConditionTracker> condition);
-
- CountMetricProducer(const CountMetric& countMetric);
+ CountMetricProducer(const CountMetric& countMetric, const bool hasCondition);
virtual ~CountMetricProducer();
void onMatchedLogEvent(const LogEventWrapper& event) override;
+ void onConditionChanged(const bool conditionMet) override;
+
void finish() override;
void onDumpReport() override;
+ // TODO: Implement this later.
+ virtual void notifyAppUpgrade(const string& apk, const int uid, const int version) override {};
+
private:
const CountMetric mMetric;
- const sp<ConditionTracker> mConditionTracker;
-
const time_t mStartTime;
// TODO: Add dimensions.
+ // Counter value for the current bucket.
int mCounter;
time_t mCurrentBucketStartTime;
long mBucketSize_sec;
+ CountAnomalyTracker mAnomalyTracker;
+
+ bool mCondition;
+
void flushCounterIfNeeded(const time_t& newEventTime);
};
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 44a778b..b7e9656 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -18,21 +18,27 @@
#define METRIC_PRODUCER_H
#include <log/logprint.h>
-#include "../matchers/LogEntryMatcherManager.h"
+#include <utils/RefBase.h>
+#include "../matchers/matcher_util.h"
+#include "PackageInfoListener.h"
namespace android {
namespace os {
namespace statsd {
// A MetricProducer is responsible for compute one single metrics, creating stats log report, and
-// writing the report to dropbox.
-class MetricProducer {
+// writing the report to dropbox. MetricProducers should respond to package changes as required in
+// PackageInfoListener, but if none of the metrics are slicing by package name, then the update can
+// be a no-op.
+class MetricProducer : public virtual RefBase, public virtual PackageInfoListener {
public:
virtual ~MetricProducer(){};
- // Consume the stats log if it's interesting to this metric.
+ // Consume the parsed stats log entry that already matched the "what" of the metric.
virtual void onMatchedLogEvent(const LogEventWrapper& event) = 0;
+ virtual void onConditionChanged(const bool condition) = 0;
+
// This is called when the metric collecting is done, e.g., when there is a new configuration
// coming. MetricProducer should do the clean up, and dump existing data to dropbox.
virtual void finish() = 0;
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index cb74206..1e65f58 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -21,13 +21,17 @@
#include "MetricsManager.h"
#include <cutils/log.h>
#include <log/logprint.h>
+#include "../condition/CombinationConditionTracker.h"
+#include "../condition/SimpleConditionTracker.h"
+#include "../matchers/CombinationLogMatchingTracker.h"
+#include "../matchers/SimpleLogMatchingTracker.h"
#include "CountMetricProducer.h"
-#include "parse_util.h"
+#include "metrics_manager_util.h"
+#include "stats_util.h"
using std::make_unique;
using std::set;
using std::string;
-using std::unique_ptr;
using std::unordered_map;
using std::vector;
@@ -35,93 +39,90 @@
namespace os {
namespace statsd {
-MetricsManager::MetricsManager(const StatsdConfig& config) : mConfig(config), mLogMatchers() {
- std::unordered_map<string, LogEntryMatcher> matcherMap;
- std::unordered_map<string, sp<ConditionTracker>> conditionMap;
-
- for (int i = 0; i < config.log_entry_matcher_size(); i++) {
- const LogEntryMatcher& logMatcher = config.log_entry_matcher(i);
- mMatchers.push_back(logMatcher);
-
- matcherMap[config.log_entry_matcher(i).name()] = logMatcher;
-
- mLogMatchers[logMatcher.name()] = vector<unique_ptr<MetricProducer>>();
- // Collect all the tag ids that are interesting
- set<int> tagIds = LogEntryMatcherManager::getTagIdsFromMatcher(logMatcher);
-
- mTagIds.insert(tagIds.begin(), tagIds.end());
- }
-
- for (int i = 0; i < config.condition_size(); i++) {
- const Condition& condition = config.condition(i);
- conditionMap[condition.name()] = new ConditionTracker(condition);
- }
-
- // Build MetricProducers for each metric defined in config.
- // (1) build CountMetricProducer
- for (int i = 0; i < config.count_metric_size(); i++) {
- const CountMetric& metric = config.count_metric(i);
- auto it = mLogMatchers.find(metric.what());
- if (it == mLogMatchers.end()) {
- ALOGW("cannot find the LogEntryMatcher %s in config", metric.what().c_str());
- continue;
- }
-
- if (metric.has_condition()) {
- auto condition_it = conditionMap.find(metric.condition());
- if (condition_it == conditionMap.end()) {
- ALOGW("cannot find the Condition %s in the config", metric.condition().c_str());
- continue;
- }
- it->second.push_back(make_unique<CountMetricProducer>(metric, condition_it->second));
- } else {
- it->second.push_back(make_unique<CountMetricProducer>(metric));
- }
- }
-
- // TODO: build other types of metrics too.
+MetricsManager::MetricsManager(const StatsdConfig& config) {
+ mConfigValid = initStatsdConfig(config, mTagIds, mAllLogEntryMatchers, mAllConditionTrackers,
+ mAllMetricProducers, mConditionToMetricMap, mTrackerToMetricMap,
+ mTrackerToConditionMap);
}
MetricsManager::~MetricsManager() {
VLOG("~MetricManager()");
}
+bool MetricsManager::isConfigValid() const {
+ return mConfigValid;
+}
+
void MetricsManager::finish() {
- for (auto const& entryPair : mLogMatchers) {
- for (auto const& metric : entryPair.second) {
- metric->finish();
- }
+ for (auto& metricProducer : mAllMetricProducers) {
+ metricProducer->finish();
}
}
// Consume the stats log if it's interesting to this metric.
void MetricsManager::onLogEvent(const log_msg& logMsg) {
+ if (!mConfigValid) {
+ return;
+ }
+
int tagId = getTagId(logMsg);
if (mTagIds.find(tagId) == mTagIds.end()) {
// not interesting...
return;
}
- // Since at least one of the metrics is interested in this event, we parse it now.
- LogEventWrapper event = LogEntryMatcherManager::parseLogEvent(logMsg);
- // Evaluate the conditions. Order matters, this should happen
- // before sending the event to metrics
- for (auto& condition : mConditionTracker) {
- condition->evaluateCondition(event);
+ // Since at least one of the metrics is interested in this event, we parse it now.
+ LogEventWrapper event = parseLogEvent(logMsg);
+ vector<MatchingState> matcherCache(mAllLogEntryMatchers.size(), MatchingState::kNotComputed);
+
+ for (auto& matcher : mAllLogEntryMatchers) {
+ matcher->onLogEvent(event, mAllLogEntryMatchers, matcherCache);
}
- // Now find out which LogMatcher matches this event, and let relevant metrics know.
- for (auto matcher : mMatchers) {
- if (LogEntryMatcherManager::matches(matcher, event)) {
- auto it = mLogMatchers.find(matcher.name());
- if (it != mLogMatchers.end()) {
- for (auto const& it2 : it->second) {
- // Only metrics that matches this event get notified.
- it2->onMatchedLogEvent(event);
+ // A bitmap to see which ConditionTracker needs to be re-evaluated.
+ vector<bool> conditionToBeEvaluated(mAllConditionTrackers.size(), false);
+
+ for (const auto& pair : mTrackerToConditionMap) {
+ if (matcherCache[pair.first] == MatchingState::kMatched) {
+ const auto& conditionList = pair.second;
+ for (const int conditionIndex : conditionList) {
+ conditionToBeEvaluated[conditionIndex] = true;
+ }
+ }
+ }
+
+ vector<ConditionState> conditionCache(mAllConditionTrackers.size(),
+ ConditionState::kNotEvaluated);
+ // A bitmap to track if a condition has changed value.
+ vector<bool> changedCache(mAllConditionTrackers.size(), false);
+ for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
+ if (conditionToBeEvaluated[i] == false) {
+ continue;
+ }
+
+ sp<ConditionTracker>& condition = mAllConditionTrackers[i];
+ condition->evaluateCondition(event, matcherCache, mAllConditionTrackers, conditionCache,
+ changedCache);
+ if (changedCache[i]) {
+ auto pair = mConditionToMetricMap.find(i);
+ if (pair != mConditionToMetricMap.end()) {
+ auto& metricList = pair->second;
+ for (auto metricIndex : metricList) {
+ mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i]);
}
- } else {
- // TODO: we should remove any redundant matchers that the config provides.
- ALOGW("Matcher not used by any metrics.");
+ }
+ }
+ }
+
+ // For matched LogEntryMatchers, tell relevant metrics that a matched event has come.
+ for (size_t i = 0; i < mAllLogEntryMatchers.size(); i++) {
+ if (matcherCache[i] == MatchingState::kMatched) {
+ auto pair = mTrackerToMetricMap.find(i);
+ if (pair != mTrackerToMetricMap.end()) {
+ auto& metricList = pair->second;
+ for (const int metricIndex : metricList) {
+ mAllMetricProducers[metricIndex]->onMatchedLogEvent(event);
+ }
}
}
}
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 77d7535..70c34db 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -20,8 +20,8 @@
#include <cutils/log.h>
#include <log/logprint.h>
#include <unordered_map>
-#include "../matchers/LogEntryMatcherManager.h"
-#include "ConditionTracker.h"
+#include "../condition/ConditionTracker.h"
+#include "../matchers/LogMatchingTracker.h"
#include "MetricProducer.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
@@ -36,25 +36,58 @@
~MetricsManager();
- // Consume the stats log if it's interesting to this metric.
+ // Return whether the configuration is valid.
+ bool isConfigValid() const;
+
void onLogEvent(const log_msg& logMsg);
+ // Called when everything should wrap up. We are about to finish (e.g., new config comes).
void finish();
private:
- const StatsdConfig mConfig;
-
// All event tags that are interesting to my metrics.
std::set<int> mTagIds;
- // The matchers that my metrics share.
- std::vector<LogEntryMatcher> mMatchers;
+ // We only store the sp of LogMatchingTracker, MetricProducer, and ConditionTracker in
+ // MetricManager. There are relationship between them, and the relationship are denoted by index
+ // instead of poiters. The reasons for this are: (1) the relationship between them are
+ // complicated, store index instead of pointers reduce the risk of A holds B's sp, and B holds
+ // A's sp. (2) When we evaluate matcher results, or condition results, we can quickly get the
+ // related results from a cache using the index.
+ // TODO: using unique_ptr may be more appriopreate?
- // The conditions that my metrics share.
- std::vector<sp<ConditionTracker>> mConditionTracker;
+ // Hold all the log entry matchers from the config.
+ std::vector<sp<LogMatchingTracker>> mAllLogEntryMatchers;
- // the map from LogEntryMatcher names to the metrics that use this matcher.
- std::unordered_map<std::string, std::vector<std::unique_ptr<MetricProducer>>> mLogMatchers;
+ // Hold all the conditions from the config.
+ std::vector<sp<ConditionTracker>> mAllConditionTrackers;
+
+ // Hold all metrics from the config.
+ std::vector<sp<MetricProducer>> mAllMetricProducers;
+
+ // To make the log processing more efficient, we want to do as much filtering as possible
+ // before we go into individual trackers and conditions to match.
+
+ // 1st filter: check if the event tag id is in mTagIds.
+ // 2nd filter: if it is, we parse the event because there is at least one member is interested.
+ // then pass to all LogMatchingTrackers (itself also filter events by ids).
+ // 3nd filter: for LogMatchingTrackers that matched this event, we pass this event to the
+ // ConditionTrackers and MetricProducers that use this matcher.
+ // 4th filter: for ConditionTrackers that changed value due to this event, we pass
+ // new conditions to metrics that use this condition.
+
+ // The following map is initialized from the statsd_config.
+
+ // maps from the index of the LogMatchingTracker to index of MetricProducer.
+ std::unordered_map<int, std::vector<int>> mTrackerToMetricMap;
+
+ // maps from LogMatchingTracker to ConditionTracker
+ std::unordered_map<int, std::vector<int>> mTrackerToConditionMap;
+
+ // maps from ConditionTracker to MetricProducer
+ std::unordered_map<int, std::vector<int>> mConditionToMetricMap;
+
+ bool mConfigValid;
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
new file mode 100644
index 0000000..6fdd228
--- /dev/null
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -0,0 +1,200 @@
+/*
+ * 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.
+ */
+
+#include "../condition/CombinationConditionTracker.h"
+#include "../condition/SimpleConditionTracker.h"
+#include "../matchers/CombinationLogMatchingTracker.h"
+#include "../matchers/SimpleLogMatchingTracker.h"
+#include "CountMetricProducer.h"
+#include "stats_util.h"
+
+using std::set;
+using std::string;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+bool initLogTrackers(const StatsdConfig& config, unordered_map<string, int>& logTrackerMap,
+ vector<sp<LogMatchingTracker>>& allLogEntryMatchers, set<int>& allTagIds) {
+ vector<LogEntryMatcher> matcherConfigs;
+
+ for (int i = 0; i < config.log_entry_matcher_size(); i++) {
+ const LogEntryMatcher& logMatcher = config.log_entry_matcher(i);
+
+ int index = allLogEntryMatchers.size();
+ switch (logMatcher.contents_case()) {
+ case LogEntryMatcher::ContentsCase::kSimpleLogEntryMatcher:
+ allLogEntryMatchers.push_back(new SimpleLogMatchingTracker(
+ logMatcher.name(), index, logMatcher.simple_log_entry_matcher()));
+ break;
+ case LogEntryMatcher::ContentsCase::kCombination:
+ allLogEntryMatchers.push_back(
+ new CombinationLogMatchingTracker(logMatcher.name(), index));
+ break;
+ default:
+ ALOGE("Matcher %s malformed", logMatcher.name().c_str());
+ return false;
+ // continue;
+ }
+ if (logTrackerMap.find(logMatcher.name()) != logTrackerMap.end()) {
+ ALOGE("Duplicate LogEntryMatcher found!");
+ return false;
+ }
+ logTrackerMap[logMatcher.name()] = index;
+ matcherConfigs.push_back(logMatcher);
+ }
+
+ vector<bool> stackTracker2(allLogEntryMatchers.size(), false);
+ for (auto& matcher : allLogEntryMatchers) {
+ if (!matcher->init(matcherConfigs, allLogEntryMatchers, logTrackerMap, stackTracker2)) {
+ return false;
+ }
+ // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only.
+ const set<int>& tagIds = matcher->getTagIds();
+ allTagIds.insert(tagIds.begin(), tagIds.end());
+ }
+ return true;
+}
+
+bool initConditions(const StatsdConfig& config, const unordered_map<string, int>& logTrackerMap,
+ unordered_map<string, int>& conditionTrackerMap,
+ vector<sp<ConditionTracker>>& allConditionTrackers,
+ unordered_map<int, std::vector<int>>& trackerToConditionMap) {
+ vector<Condition> conditionConfigs;
+
+ for (int i = 0; i < config.condition_size(); i++) {
+ const Condition& condition = config.condition(i);
+ int index = allConditionTrackers.size();
+ switch (condition.contents_case()) {
+ case Condition::ContentsCase::kSimpleCondition: {
+ allConditionTrackers.push_back(new SimpleConditionTracker(
+ condition.name(), index, condition.simple_condition(), logTrackerMap));
+ break;
+ }
+ case Condition::ContentsCase::kCombination: {
+ allConditionTrackers.push_back(
+ new CombinationConditionTracker(condition.name(), index));
+ break;
+ }
+ default:
+ ALOGE("Condition %s malformed", condition.name().c_str());
+ return false;
+ }
+ if (conditionTrackerMap.find(condition.name()) != conditionTrackerMap.end()) {
+ ALOGE("Duplicate Condition found!");
+ return false;
+ }
+ conditionTrackerMap[condition.name()] = index;
+ conditionConfigs.push_back(condition);
+ }
+
+ vector<bool> stackTracker(allConditionTrackers.size(), false);
+ for (size_t i = 0; i < allConditionTrackers.size(); i++) {
+ auto& conditionTracker = allConditionTrackers[i];
+ if (!conditionTracker->init(conditionConfigs, allConditionTrackers, conditionTrackerMap,
+ stackTracker)) {
+ return false;
+ }
+ for (const int trackerIndex : conditionTracker->getLogTrackerIndex()) {
+ auto& conditionList = trackerToConditionMap[trackerIndex];
+ conditionList.push_back(i);
+ }
+ }
+ return true;
+}
+
+bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& logTrackerMap,
+ const unordered_map<string, int>& conditionTrackerMap,
+ vector<sp<MetricProducer>>& allMetricProducers,
+ unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ unordered_map<int, std::vector<int>>& trackerToMetricMap) {
+ // Build MetricProducers for each metric defined in config.
+ // (1) build CountMetricProducer
+ for (int i = 0; i < config.count_metric_size(); i++) {
+ const CountMetric& metric = config.count_metric(i);
+ if (!metric.has_what()) {
+ ALOGW("cannot find what in CountMetric %lld", metric.metric_id());
+ return false;
+ }
+
+ auto logTrackerIt = logTrackerMap.find(metric.what());
+ if (logTrackerIt == logTrackerMap.end()) {
+ ALOGW("cannot find the LogEntryMatcher %s in config", metric.what().c_str());
+ return false;
+ }
+
+ sp<MetricProducer> countProducer;
+ int metricIndex = allMetricProducers.size();
+ if (metric.has_condition()) {
+ auto condition_it = conditionTrackerMap.find(metric.condition());
+ if (condition_it == conditionTrackerMap.end()) {
+ ALOGW("cannot find the Condition %s in the config", metric.condition().c_str());
+ return false;
+ }
+ countProducer = new CountMetricProducer(metric, true /*has condition*/);
+ // will create new vector if not exist before.
+ auto& metricList = conditionToMetricMap[condition_it->second];
+ metricList.push_back(metricIndex);
+ } else {
+ countProducer = new CountMetricProducer(metric, false /*no condition*/);
+ }
+
+ int logTrackerIndex = logTrackerIt->second;
+ auto& metric_list = trackerToMetricMap[logTrackerIndex];
+ metric_list.push_back(metricIndex);
+ allMetricProducers.push_back(countProducer);
+ }
+
+ // TODO: build other types of metrics too.
+
+ return true;
+}
+
+bool initStatsdConfig(const StatsdConfig& config, set<int>& allTagIds,
+ vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
+ vector<sp<ConditionTracker>>& allConditionTrackers,
+ vector<sp<MetricProducer>>& allMetricProducers,
+ unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ unordered_map<int, std::vector<int>>& trackerToConditionMap) {
+ unordered_map<string, int> logTrackerMap;
+ unordered_map<string, int> conditionTrackerMap;
+
+ if (!initLogTrackers(config, logTrackerMap, allLogEntryMatchers, allTagIds)) {
+ ALOGE("initLogMatchingTrackers failed");
+ return false;
+ }
+
+ if (!initConditions(config, logTrackerMap, conditionTrackerMap, allConditionTrackers,
+ trackerToConditionMap)) {
+ ALOGE("initConditionTrackers failed");
+ return false;
+ }
+
+ if (!initMetrics(config, logTrackerMap, conditionTrackerMap, allMetricProducers,
+ conditionToMetricMap, trackerToMetricMap)) {
+ ALOGE("initMetricProducers failed");
+ return false;
+ }
+ return true;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
new file mode 100644
index 0000000..5f1f295
--- /dev/null
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+#ifndef METRIC_UTIL_H
+#define METRIC_UTIL_H
+#include <memory>
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+#include "../condition/ConditionTracker.h"
+#include "../matchers/LogMatchingTracker.h"
+#include "CountMetricProducer.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Helper functions for MetricsManager to initialize from StatsdConfig.
+// *Note*: only initStatsdConfig() should be called from outside.
+// All other functions are intermediate
+// steps, created to make unit tests easier. And most of the parameters in these
+// functions are temporary objects in the initialization phase.
+
+// Initialize the LogMatchingTrackers.
+// input:
+// [config]: the input StatsdConfig
+// output:
+// [logTrackerMap]: this map should contain matcher name to index mapping
+// [allLogEntryMatchers]: should store the sp to all the LogMatchingTracker
+// [allTagIds]: contains the set of all interesting tag ids to this config.
+bool initLogTrackers(const StatsdConfig& config,
+ std::unordered_map<std::string, int>& logTrackerMap,
+ std::vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
+ std::set<int>& allTagIds);
+
+// Initialize ConditionTrackers
+// input:
+// [config]: the input config
+// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
+// output:
+// [conditionTrackerMap]: this map should contain condition name to index mapping
+// [allConditionTrackers]: stores the sp to all the ConditionTrackers
+// [trackerToConditionMap]: contain the mapping from index of
+// log tracker to condition trackers that use the log tracker
+bool initConditions(const StatsdConfig& config,
+ const std::unordered_map<std::string, int>& logTrackerMap,
+ std::unordered_map<std::string, int>& conditionTrackerMap,
+ std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ std::unordered_map<int, std::vector<int>>& trackerToConditionMap);
+
+// Initialize MetricProducers.
+// input:
+// [config]: the input config
+// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
+// [conditionTrackerMap]: condition name to index mapping
+// output:
+// [allMetricProducers]: contains the list of sp to the MetricProducers created.
+// [conditionToMetricMap]: contains the mapping from condition tracker index to
+// the list of MetricProducer index
+// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
+bool initMetrics(const StatsdConfig& config,
+ const std::unordered_map<std::string, int>& logTrackerMap,
+ const std::unordered_map<std::string, int>& conditionTrackerMap,
+ std::vector<sp<MetricProducer>>& allMetricProducers,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap);
+
+// Initialize MetricManager from StatsdConfig.
+// Parameters are the members of MetricsManager. See MetricsManager for declaration.
+bool initStatsdConfig(const StatsdConfig& config, std::set<int>& allTagIds,
+ std::vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
+ std::vector<sp<ConditionTracker>>& allConditionTrackers,
+ std::vector<sp<MetricProducer>>& allMetricProducers,
+ std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+ std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+ std::unordered_map<int, std::vector<int>>& trackerToConditionMap);
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#endif // METRIC_UTIL_H
diff --git a/cmds/statsd/src/parse_util.cpp b/cmds/statsd/src/parse_util.cpp
deleted file mode 100644
index 61421880..0000000
--- a/cmds/statsd/src/parse_util.cpp
+++ /dev/null
@@ -1,132 +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.
- */
-
-#include <log/log_event_list.h>
-#include <parse_util.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-static inline uint32_t get4LE(const char* src) {
- return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
-}
-
-int getTagId(log_msg msg) {
- return get4LE(msg.msg());
-}
-
-EventMetricData parse(log_msg msg) {
- // dump all statsd logs to dropbox for now.
- // TODO: Add filtering, aggregation, etc.
- EventMetricData eventMetricData;
-
- // set tag.
- int tag = getTagId(msg);
- // TODO: Replace the following line when we can serialize on the fly.
- //eventMetricData.set_tag(tag);
-
- // set timestamp of the event.
- eventMetricData.set_timestamp_nanos(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec);
-
- // start iterating k,v pairs.
- android_log_context context =
- create_android_log_parser(const_cast<log_msg*>(&msg)->msg() + sizeof(uint32_t),
- const_cast<log_msg*>(&msg)->len() - sizeof(uint32_t));
- android_log_list_element elem;
-
- if (context) {
- memset(&elem, 0, sizeof(elem));
- size_t index = 0;
- int32_t key = -1;
-
- do {
- elem = android_log_read_next(context);
- switch ((int)elem.type) {
- case EVENT_TYPE_INT:
- if (index % 2 == 0) {
- key = elem.data.int32;
- } else {
- // TODO: Fix the following lines when we can serialize on the fly.
- /*
- int32_t val = elem.data.int32;
- KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
- keyValuePair->set_key(key);
- keyValuePair->set_value_int(val);
- */
- }
- index++;
- break;
- case EVENT_TYPE_FLOAT:
- if (index % 2 == 1) {
- // TODO: Fix the following lines when we can serialize on the fly.
- /*
- float val = elem.data.float32;
- KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
- keyValuePair->set_key(key);
- keyValuePair->set_value_float(val);
- */
- }
- index++;
- break;
- case EVENT_TYPE_STRING:
- if (index % 2 == 1) {
- // TODO: Fix the following lines when we can serialize on the fly.
- /*
- char* val = elem.data.string;
- KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
- keyValuePair->set_key(key);
- keyValuePair->set_value_str(val);
- */
- }
- index++;
- break;
- case EVENT_TYPE_LONG:
- if (index % 2 == 1) {
- // TODO: Fix the following lines when we can serialize on the fly.
- /*
- int64_t val = elem.data.int64;
- KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
- keyValuePair->set_key(key);
- keyValuePair->set_value_int(val);
- */
- }
- index++;
- break;
- case EVENT_TYPE_LIST:
- break;
- case EVENT_TYPE_LIST_STOP:
- break;
- case EVENT_TYPE_UNKNOWN:
- break;
- default:
- elem.complete = true;
- break;
- }
-
- if (elem.complete) {
- break;
- }
- } while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete);
-
- android_log_destroy(&context);
- }
-
- return eventMetricData;
-}
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 2dc0cc7..6421b70 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -55,6 +55,43 @@
repeated CountBucketInfo bucket_info = 2;
}
+message DurationBucketInfo {
+ optional int64 start_bucket_nanos = 1;
+
+ optional int64 end_bucket_nanos = 2;
+
+ optional int64 duration_nanos = 3;
+}
+
+message DurationMetricData {
+ repeated KeyValuePair dimension = 1;
+
+ repeated DurationBucketInfo bucket_info = 2;
+}
+
+message UidMapping {
+ message AppInfo {
+ optional string app = 1;
+
+ optional int32 version = 2;
+
+ optional int32 uid = 3;
+ }
+
+ repeated AppInfo initial = 1;
+
+ message Change {
+ optional bool deletion = 1;
+
+ optional int64 timestamp = 2;
+ optional string app = 3;
+ optional int32 uid = 4;
+
+ optional int32 version = 5;
+ }
+ repeated Change changes = 2;
+}
+
message StatsLogReport {
optional int32 metric_id = 1;
@@ -68,8 +105,12 @@
message CountMetricDataWrapper {
repeated CountMetricData data = 1;
}
+ message DurationMetricDataWrapper {
+ repeated CountMetricData data = 1;
+ }
oneof data {
EventMetricDataWrapper event_metrics = 4;
CountMetricDataWrapper count_metrics = 5;
+ DurationMetricDataWrapper duration_metrics = 6;
}
}
diff --git a/cmds/statsd/src/stats_util.cpp b/cmds/statsd/src/stats_util.cpp
new file mode 100644
index 0000000..978b228
--- /dev/null
+++ b/cmds/statsd/src/stats_util.cpp
@@ -0,0 +1,289 @@
+/*
+ * 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.
+ */
+
+#include <log/log_event_list.h>
+#include "stats_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+static inline uint32_t get4LE(const char* src) {
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+int getTagId(log_msg msg) {
+ return get4LE(msg.msg());
+}
+
+EventMetricData parse(log_msg msg) {
+ // dump all statsd logs to dropbox for now.
+ // TODO: Add filtering, aggregation, etc.
+ EventMetricData eventMetricData;
+
+ // set tag.
+ int tag = getTagId(msg);
+ // TODO: Replace the following line when we can serialize on the fly.
+ // eventMetricData.set_tag(tag);
+
+ // set timestamp of the event.
+ eventMetricData.set_timestamp_nanos(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec);
+
+ // start iterating k,v pairs.
+ android_log_context context =
+ create_android_log_parser(const_cast<log_msg*>(&msg)->msg() + sizeof(uint32_t),
+ const_cast<log_msg*>(&msg)->len() - sizeof(uint32_t));
+ android_log_list_element elem;
+
+ if (context) {
+ memset(&elem, 0, sizeof(elem));
+ size_t index = 0;
+ int32_t key = -1;
+
+ do {
+ elem = android_log_read_next(context);
+ switch ((int)elem.type) {
+ case EVENT_TYPE_INT:
+ if (index % 2 == 0) {
+ key = elem.data.int32;
+ } else {
+ // TODO: Fix the following lines when we can serialize on the fly.
+ /*
+ int32_t val = elem.data.int32;
+ KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
+ keyValuePair->set_key(key);
+ keyValuePair->set_value_int(val);
+ */
+ }
+ index++;
+ break;
+ case EVENT_TYPE_FLOAT:
+ if (index % 2 == 1) {
+ // TODO: Fix the following lines when we can serialize on the fly.
+ /*
+ float val = elem.data.float32;
+ KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
+ keyValuePair->set_key(key);
+ keyValuePair->set_value_float(val);
+ */
+ }
+ index++;
+ break;
+ case EVENT_TYPE_STRING:
+ if (index % 2 == 1) {
+ // TODO: Fix the following lines when we can serialize on the fly.
+ /*
+ char* val = elem.data.string;
+ KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
+ keyValuePair->set_key(key);
+ keyValuePair->set_value_str(val);
+ */
+ }
+ index++;
+ break;
+ case EVENT_TYPE_LONG:
+ if (index % 2 == 1) {
+ // TODO: Fix the following lines when we can serialize on the fly.
+ /*
+ int64_t val = elem.data.int64;
+ KeyValuePair* keyValuePair = eventMetricData.add_key_value_pair();
+ keyValuePair->set_key(key);
+ keyValuePair->set_value_int(val);
+ */
+ }
+ index++;
+ break;
+ case EVENT_TYPE_LIST:
+ break;
+ case EVENT_TYPE_LIST_STOP:
+ break;
+ case EVENT_TYPE_UNKNOWN:
+ break;
+ default:
+ elem.complete = true;
+ break;
+ }
+
+ if (elem.complete) {
+ break;
+ }
+ } while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete);
+
+ android_log_destroy(&context);
+ }
+
+ return eventMetricData;
+}
+
+StatsdConfig buildFakeConfig() {
+ // HACK: Hard code a test metric for counting screen on events...
+ StatsdConfig config;
+ config.set_config_id(12345L);
+
+ // One count metric to count screen on
+ CountMetric* metric = config.add_count_metric();
+ metric->set_metric_id(20150717L);
+ metric->set_what("SCREEN_IS_ON");
+ metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
+
+ // One count metric to count PHOTO_CHANGE_OR_CHROME_CRASH
+ metric = config.add_count_metric();
+ metric->set_metric_id(20150718L);
+ metric->set_what("PHOTO_PROCESS_STATE_CHANGE");
+ metric->mutable_bucket()->set_bucket_size_millis(60 * 1000L);
+ metric->set_condition("SCREEN_IS_ON");
+
+
+ LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_IS_ON");
+
+ SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+ simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+
+
+ eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_IS_OFF");
+
+ simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+ simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
+
+
+
+ LogEntryMatcher* procEventMatcher = config.add_log_entry_matcher();
+ procEventMatcher->set_name("PHOTO_CRASH");
+
+ SimpleLogEntryMatcher* simpleLogMatcher2 = procEventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogMatcher2->add_tag(1112 /*PROCESS_STATE_CHANGE*/);
+ KeyValueMatcher* keyValueMatcher = simpleLogMatcher2->add_key_value_matcher();
+ keyValueMatcher->mutable_key_matcher()->set_key(
+ 1002 /*pkg*/);
+ keyValueMatcher->set_eq_string(
+ "com.google.android.apps.photos" /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+ keyValueMatcher = simpleLogMatcher2->add_key_value_matcher();
+ keyValueMatcher->mutable_key_matcher()->set_key(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ keyValueMatcher->set_eq_int(2);
+
+
+ procEventMatcher = config.add_log_entry_matcher();
+ procEventMatcher->set_name("PHOTO_START");
+
+ simpleLogMatcher2 = procEventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogMatcher2->add_tag(1112 /*PROCESS_STATE_CHANGE*/);
+ keyValueMatcher = simpleLogMatcher2->add_key_value_matcher();
+ keyValueMatcher->mutable_key_matcher()->set_key(
+ 1002 /*pkg*/);
+ keyValueMatcher->set_eq_string(
+ "com.google.android.apps.photos" /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+ keyValueMatcher = simpleLogMatcher2->add_key_value_matcher();
+ keyValueMatcher->mutable_key_matcher()->set_key(
+ 1 /*STATE*/);
+ keyValueMatcher->set_eq_int(1);
+
+
+ procEventMatcher = config.add_log_entry_matcher();
+ procEventMatcher->set_name("PHOTO_PROCESS_STATE_CHANGE");
+ LogEntryMatcher_Combination* combinationMatcher = procEventMatcher->mutable_combination();
+ combinationMatcher->set_operation(LogicalOperation::OR);
+ combinationMatcher->add_matcher("PHOTO_START");
+ combinationMatcher->add_matcher("PHOTO_CRASH");
+
+
+ procEventMatcher = config.add_log_entry_matcher();
+ procEventMatcher->set_name("CHROME_CRASH");
+
+ simpleLogMatcher2 = procEventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogMatcher2->add_tag(1112 /*PROCESS_STATE_CHANGE*/);
+ keyValueMatcher = simpleLogMatcher2->add_key_value_matcher();
+ keyValueMatcher->mutable_key_matcher()->set_key(
+ 1002 /*pkg*/);
+ keyValueMatcher->set_eq_string(
+ "com.android.chrome" /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+ keyValueMatcher = simpleLogMatcher2->add_key_value_matcher();
+ keyValueMatcher->mutable_key_matcher()->set_key(
+ 1 /*STATE*/);
+ keyValueMatcher->set_eq_int(2);
+
+
+
+ procEventMatcher = config.add_log_entry_matcher();
+ procEventMatcher->set_name("PHOTO_CHANGE_OR_CHROME_CRASH");
+ combinationMatcher = procEventMatcher->mutable_combination();
+ combinationMatcher->set_operation(LogicalOperation::OR);
+ combinationMatcher->add_matcher("PHOTO_PROCESS_STATE_CHANGE");
+ combinationMatcher->add_matcher("CHROME_CRASH");
+
+
+
+ Condition* condition = config.add_condition();
+ condition->set_name("SCREEN_IS_ON");
+ SimpleCondition* simpleCondition = condition->mutable_simple_condition();
+ simpleCondition->set_start("SCREEN_IS_ON");
+ simpleCondition->set_stop("SCREEN_IS_OFF");
+
+
+ condition = config.add_condition();
+ condition->set_name("PHOTO_STARTED");
+
+ simpleCondition = condition->mutable_simple_condition();
+ simpleCondition->set_start("PHOTO_START");
+ simpleCondition->set_stop("PHOTO_CRASH");
+
+
+ condition = config.add_condition();
+ condition->set_name("SCREEN_IS_OFF");
+
+ simpleCondition = condition->mutable_simple_condition();
+ simpleCondition->set_start("SCREEN_IS_OFF");
+ simpleCondition->set_stop("SCREEN_IS_ON");
+
+
+ condition = config.add_condition();
+ condition->set_name("SCREEN_IS_EITHER_ON_OFF");
+
+ Condition_Combination* combination = condition->mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_condition("SCREEN_IS_ON");
+ combination->add_condition("SCREEN_IS_OFF");
+
+
+ condition = config.add_condition();
+ condition->set_name("SCREEN_IS_NEITHER_ON_OFF");
+
+ combination = condition->mutable_combination();
+ combination->set_operation(LogicalOperation::NOR);
+ combination->add_condition("SCREEN_IS_ON");
+ combination->add_condition("SCREEN_IS_OFF");
+
+ return config;
+}
+
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/parse_util.h b/cmds/statsd/src/stats_util.h
similarity index 90%
rename from cmds/statsd/src/parse_util.h
rename to cmds/statsd/src/stats_util.h
index 8b82e7b..25b9bba 100644
--- a/cmds/statsd/src/parse_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -20,6 +20,7 @@
#include "LogReader.h"
#include <log/logprint.h>
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
namespace android {
namespace os {
@@ -29,6 +30,8 @@
int getTagId(log_msg msg);
+StatsdConfig buildFakeConfig();
+
} // namespace statsd
} // namespace os
} // namespace android
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 3e4ebaf..d7702cd 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -65,7 +65,8 @@
message Combination {
optional LogicalOperation operation = 1;
- repeated LogEntryMatcher matcher = 2;
+
+ repeated string matcher = 2;
}
oneof contents {
SimpleLogEntryMatcher simple_log_entry_matcher = 2;
@@ -122,6 +123,24 @@
optional Bucket bucket = 5;
}
+message DurationMetric {
+ optional int64 metric_id = 1;
+
+ enum AggregationType {
+ DURATION_SUM = 1;
+
+ DURATION_MAX_SPARSE = 2;
+ DURATION_MIN_SPARSE = 3;
+ }
+ optional AggregationType type = 2;
+
+ optional string predicate = 3;
+
+ repeated KeyMatcher dimension = 4;
+
+ optional Bucket bucket = 5;
+}
+
message StatsdConfig {
optional int64 config_id = 1;
diff --git a/cmds/statsd/tests/AnomalyMonitor_test.cpp b/cmds/statsd/tests/AnomalyMonitor_test.cpp
new file mode 100644
index 0000000..d5b6811
--- /dev/null
+++ b/cmds/statsd/tests/AnomalyMonitor_test.cpp
@@ -0,0 +1,66 @@
+// 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.
+
+#define LOG_TAG "statsd_test"
+
+#include "../src/AnomalyMonitor.h"
+
+#include <gtest/gtest.h>
+
+using namespace android::os::statsd;
+
+#ifdef __ANDROID__
+TEST(AnomalyMonitor, popSoonerThan) {
+ unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>> set;
+ AnomalyMonitor am(2);
+
+ set = am.popSoonerThan(5);
+ EXPECT_TRUE(set.empty());
+
+ sp<const AnomalyAlarm> a = new AnomalyAlarm{10};
+ sp<const AnomalyAlarm> b = new AnomalyAlarm{20};
+ sp<const AnomalyAlarm> c = new AnomalyAlarm{20};
+ sp<const AnomalyAlarm> d = new AnomalyAlarm{30};
+ sp<const AnomalyAlarm> e = new AnomalyAlarm{40};
+ sp<const AnomalyAlarm> f = new AnomalyAlarm{50};
+
+ am.add(a);
+ am.add(b);
+ am.add(c);
+ am.add(d);
+ am.add(e);
+ am.add(f);
+
+ set = am.popSoonerThan(5);
+ EXPECT_TRUE(set.empty());
+
+ set = am.popSoonerThan(30);
+ EXPECT_EQ(4u, set.size());
+ EXPECT_EQ(1u, set.count(a));
+ EXPECT_EQ(1u, set.count(b));
+ EXPECT_EQ(1u, set.count(c));
+ EXPECT_EQ(1u, set.count(d));
+
+ set = am.popSoonerThan(60);
+ EXPECT_EQ(2u, set.size());
+ EXPECT_EQ(1u, set.count(e));
+ EXPECT_EQ(1u, set.count(f));
+
+ set = am.popSoonerThan(80);
+ EXPECT_EQ(0u, set.size());
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/ConditionTracker_test.cpp b/cmds/statsd/tests/ConditionTracker_test.cpp
new file mode 100644
index 0000000..f8b0fd0
--- /dev/null
+++ b/cmds/statsd/tests/ConditionTracker_test.cpp
@@ -0,0 +1,162 @@
+// 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.
+
+#define LOG_TAG "statsd_test"
+
+#include <gtest/gtest.h>
+#include "../src/condition/condition_util.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+#include <stdio.h>
+#include <vector>
+
+using namespace android::os::statsd;
+using std::vector;
+
+
+#ifdef __ANDROID__
+TEST(ConditionTrackerTest, TestUnknownCondition) {
+ LogicalOperation operation = LogicalOperation::AND;
+
+ vector<int> children;
+ children.push_back(0);
+ children.push_back(1);
+ children.push_back(2);
+
+ vector<ConditionState> conditionResults;
+ conditionResults.push_back(ConditionState::kUnknown);
+ conditionResults.push_back(ConditionState::kFalse);
+ conditionResults.push_back(ConditionState::kTrue);
+
+ EXPECT_EQ(evaluateCombinationCondition(children, operation, conditionResults),
+ ConditionState::kUnknown);
+}
+TEST(ConditionTrackerTest, TestAndCondition) {
+ // Set up the matcher
+ LogicalOperation operation = LogicalOperation::AND;
+
+ vector<int> children;
+ children.push_back(0);
+ children.push_back(1);
+ children.push_back(2);
+
+ vector<ConditionState> conditionResults;
+ conditionResults.push_back(ConditionState::kTrue);
+ conditionResults.push_back(ConditionState::kFalse);
+ conditionResults.push_back(ConditionState::kTrue);
+
+ EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
+
+ conditionResults.clear();
+ conditionResults.push_back(ConditionState::kTrue);
+ conditionResults.push_back(ConditionState::kTrue);
+ conditionResults.push_back(ConditionState::kTrue);
+
+ EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
+}
+
+TEST(ConditionTrackerTest, TestOrCondition) {
+ // Set up the matcher
+ LogicalOperation operation = LogicalOperation::OR;
+
+ vector<int> children;
+ children.push_back(0);
+ children.push_back(1);
+ children.push_back(2);
+
+ vector<ConditionState> conditionResults;
+ conditionResults.push_back(ConditionState::kTrue);
+ conditionResults.push_back(ConditionState::kFalse);
+ conditionResults.push_back(ConditionState::kTrue);
+
+ EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
+
+ conditionResults.clear();
+ conditionResults.push_back(ConditionState::kFalse);
+ conditionResults.push_back(ConditionState::kFalse);
+ conditionResults.push_back(ConditionState::kFalse);
+
+ EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
+}
+
+TEST(ConditionTrackerTest, TestNotCondition) {
+ // Set up the matcher
+ LogicalOperation operation = LogicalOperation::NOT;
+
+ vector<int> children;
+ children.push_back(0);
+
+ vector<ConditionState> conditionResults;
+ conditionResults.push_back(ConditionState::kTrue);
+
+ EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
+
+ conditionResults.clear();
+ conditionResults.push_back(ConditionState::kFalse);
+ EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
+}
+
+TEST(ConditionTrackerTest, TestNandCondition) {
+ // Set up the matcher
+ LogicalOperation operation = LogicalOperation::NAND;
+
+ vector<int> children;
+ children.push_back(0);
+ children.push_back(1);
+
+ vector<ConditionState> conditionResults;
+ conditionResults.push_back(ConditionState::kTrue);
+ conditionResults.push_back(ConditionState::kFalse);
+
+ EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
+
+ conditionResults.clear();
+ conditionResults.push_back(ConditionState::kFalse);
+ conditionResults.push_back(ConditionState::kFalse);
+ EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
+
+ conditionResults.clear();
+ conditionResults.push_back(ConditionState::kTrue);
+ conditionResults.push_back(ConditionState::kTrue);
+ EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
+}
+
+TEST(ConditionTrackerTest, TestNorCondition) {
+ // Set up the matcher
+ LogicalOperation operation = LogicalOperation::NOR;
+
+ vector<int> children;
+ children.push_back(0);
+ children.push_back(1);
+
+ vector<ConditionState> conditionResults;
+ conditionResults.push_back(ConditionState::kTrue);
+ conditionResults.push_back(ConditionState::kFalse);
+
+ EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
+
+ conditionResults.clear();
+ conditionResults.push_back(ConditionState::kFalse);
+ conditionResults.push_back(ConditionState::kFalse);
+ EXPECT_TRUE(evaluateCombinationCondition(children, operation, conditionResults));
+
+ conditionResults.clear();
+ conditionResults.push_back(ConditionState::kTrue);
+ conditionResults.push_back(ConditionState::kTrue);
+ EXPECT_FALSE(evaluateCombinationCondition(children, operation, conditionResults));
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 473704a..6069801 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -18,14 +18,15 @@
#include <log/log_event_list.h>
#include <log/log_read.h>
#include <log/logprint.h>
-#include "../src/matchers/LogEntryMatcherManager.h"
-#include "../src/parse_util.h"
+#include "../src/matchers/matcher_util.h"
+#include "../src/stats_util.h"
#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
#include <stdio.h>
using namespace android::os::statsd;
using std::unordered_map;
+using std::vector;
const int kTagIdWakelock = 123;
const int kKeyIdState = 45;
@@ -41,7 +42,7 @@
LogEventWrapper wrapper;
wrapper.tagId = kTagIdWakelock;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
}
TEST(LogEntryMatcherTest, TestBoolMatcher) {
@@ -57,13 +58,13 @@
keyValue->set_eq_bool(true);
wrapper.boolMap[kKeyIdState] = true;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
keyValue->set_eq_bool(false);
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
- wrapper.boolMap[kTagIdWakelock] = false;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ wrapper.boolMap[kKeyIdState] = false;
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
}
TEST(LogEntryMatcherTest, TestStringMatcher) {
@@ -80,7 +81,7 @@
wrapper.strMap[kKeyIdState] = "wakelock_name";
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
}
TEST(LogEntryMatcherTest, TestIntComparisonMatcher) {
@@ -96,19 +97,19 @@
keyValue->set_lt_int(10);
wrapper.intMap[kKeyIdState] = 11;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
wrapper.intMap[kKeyIdState] = 10;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
wrapper.intMap[kKeyIdState] = 9;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
keyValue->set_gt_int(10);
wrapper.intMap[kKeyIdState] = 11;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
wrapper.intMap[kKeyIdState] = 10;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
wrapper.intMap[kKeyIdState] = 9;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
}
TEST(LogEntryMatcherTest, TestIntWithEqualityComparisonMatcher) {
@@ -124,19 +125,19 @@
keyValue->set_lte_int(10);
wrapper.intMap[kKeyIdState] = 11;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
wrapper.intMap[kKeyIdState] = 10;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
wrapper.intMap[kKeyIdState] = 9;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
keyValue->set_gte_int(10);
wrapper.intMap[kKeyIdState] = 11;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
wrapper.intMap[kKeyIdState] = 10;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
wrapper.intMap[kKeyIdState] = 9;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
}
TEST(LogEntryMatcherTest, TestFloatComparisonMatcher) {
@@ -152,15 +153,15 @@
keyValue->set_lt_float(10.0);
wrapper.floatMap[kKeyIdState] = 10.1;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
wrapper.floatMap[kKeyIdState] = 9.9;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
keyValue->set_gt_float(10.0);
wrapper.floatMap[kKeyIdState] = 10.1;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(matchesSimple(*simpleMatcher, wrapper));
wrapper.floatMap[kKeyIdState] = 9.9;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(matchesSimple(*simpleMatcher, wrapper));
}
// Helper for the composite matchers.
@@ -173,141 +174,117 @@
TEST(LogEntryMatcherTest, TestAndMatcher) {
// Set up the matcher
- LogEntryMatcher matcher;
- auto combination = matcher.mutable_combination();
- combination->set_operation(LogicalOperation::AND);
+ LogicalOperation operation = LogicalOperation::AND;
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdState, 3);
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdPackageVersion, 4);
+ vector<int> children;
+ children.push_back(0);
+ children.push_back(1);
+ children.push_back(2);
- LogEventWrapper wrapper;
- wrapper.tagId = kTagIdWakelock;
+ vector<MatchingState> matcherResults;
+ matcherResults.push_back(MatchingState::kMatched);
+ matcherResults.push_back(MatchingState::kNotMatched);
+ matcherResults.push_back(MatchingState::kMatched);
- wrapper.intMap[1003] = 4;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap.clear();
- wrapper.intMap[1] = 3;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap.clear();
- wrapper.intMap[1] = 3;
- wrapper.intMap[1003] = 4;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
+
+ matcherResults.clear();
+ matcherResults.push_back(MatchingState::kMatched);
+ matcherResults.push_back(MatchingState::kMatched);
+ matcherResults.push_back(MatchingState::kMatched);
+
+ EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
}
TEST(LogEntryMatcherTest, TestOrMatcher) {
// Set up the matcher
- LogEntryMatcher matcher;
- auto combination = matcher.mutable_combination();
- combination->set_operation(LogicalOperation::OR);
+ LogicalOperation operation = LogicalOperation::OR;
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdState, 3);
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdPackageVersion, 4);
+ vector<int> children;
+ children.push_back(0);
+ children.push_back(1);
+ children.push_back(2);
- LogEventWrapper wrapper;
- wrapper.tagId = kTagIdWakelock;
+ vector<MatchingState> matcherResults;
+ matcherResults.push_back(MatchingState::kMatched);
+ matcherResults.push_back(MatchingState::kNotMatched);
+ matcherResults.push_back(MatchingState::kMatched);
- // Don't set any key-value pairs.
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap[1003] = 4;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap.clear();
- wrapper.intMap[1] = 3;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap.clear();
- wrapper.intMap[1] = 3;
- wrapper.intMap[1003] = 4;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
+
+ matcherResults.clear();
+ matcherResults.push_back(MatchingState::kNotMatched);
+ matcherResults.push_back(MatchingState::kNotMatched);
+ matcherResults.push_back(MatchingState::kNotMatched);
+
+ EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
}
TEST(LogEntryMatcherTest, TestNotMatcher) {
// Set up the matcher
- LogEntryMatcher matcher;
- auto combination = matcher.mutable_combination();
- combination->set_operation(LogicalOperation::NOT);
+ LogicalOperation operation = LogicalOperation::NOT;
- // Define first simpleMatcher
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdState, 3);
+ vector<int> children;
+ children.push_back(0);
- LogEventWrapper wrapper;
- wrapper.tagId = kTagIdWakelock;
+ vector<MatchingState> matcherResults;
+ matcherResults.push_back(MatchingState::kMatched);
- // Don't set any key-value pairs.
- wrapper.intMap[kKeyIdState] = 3;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
+
+ matcherResults.clear();
+ matcherResults.push_back(MatchingState::kNotMatched);
+ EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
}
-TEST(LogEntryMatcherTest, TestNANDMatcher) {
+TEST(LogEntryMatcherTest, TestNandMatcher) {
// Set up the matcher
- LogEntryMatcher matcher;
- auto combination = matcher.mutable_combination();
- combination->set_operation(LogicalOperation::NAND);
+ LogicalOperation operation = LogicalOperation::NAND;
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdState, 3);
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdPackageVersion, 4);
+ vector<int> children;
+ children.push_back(0);
+ children.push_back(1);
- LogEventWrapper wrapper;
- wrapper.tagId = kTagIdWakelock;
+ vector<MatchingState> matcherResults;
+ matcherResults.push_back(MatchingState::kMatched);
+ matcherResults.push_back(MatchingState::kNotMatched);
- // Don't set any key-value pairs.
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap[kKeyIdState] = 3;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap[kKeyIdPackageVersion] = 4;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
+
+ matcherResults.clear();
+ matcherResults.push_back(MatchingState::kNotMatched);
+ matcherResults.push_back(MatchingState::kNotMatched);
+ EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
+
+ matcherResults.clear();
+ matcherResults.push_back(MatchingState::kMatched);
+ matcherResults.push_back(MatchingState::kMatched);
+ EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
}
-TEST(LogEntryMatcherTest, TestNORMatcher) {
+TEST(LogEntryMatcherTest, TestNorMatcher) {
// Set up the matcher
- LogEntryMatcher matcher;
- auto combination = matcher.mutable_combination();
- combination->set_operation(LogicalOperation::NOR);
+ LogicalOperation operation = LogicalOperation::NOR;
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdState, 3);
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdPackageVersion, 4);
+ vector<int> children;
+ children.push_back(0);
+ children.push_back(1);
- LogEventWrapper wrapper;
- wrapper.tagId = kTagIdWakelock;
+ vector<MatchingState> matcherResults;
+ matcherResults.push_back(MatchingState::kMatched);
+ matcherResults.push_back(MatchingState::kNotMatched);
- // Don't set any key-value pairs.
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap[kKeyIdState] = 3;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap[kKeyIdPackageVersion] = 4;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
-}
+ EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
-// Tests that a NOT on top of AND is the same as NAND
-TEST(LogEntryMatcherTest, TestMultipleLayerMatcher) {
- LogEntryMatcher matcher;
- auto not_combination = matcher.mutable_combination();
- not_combination->set_operation(LogicalOperation::NOT);
+ matcherResults.clear();
+ matcherResults.push_back(MatchingState::kNotMatched);
+ matcherResults.push_back(MatchingState::kNotMatched);
+ EXPECT_TRUE(combinationMatch(children, operation, matcherResults));
- // Now add the AND
- auto combination = not_combination->add_matcher()->mutable_combination();
- combination->set_operation(LogicalOperation::AND);
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdState, 3);
- addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
- kTagIdWakelock, kKeyIdPackageVersion, 4);
-
- LogEventWrapper wrapper;
- wrapper.tagId = kTagIdWakelock;
-
- // Don't set any key-value pairs.
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap[kKeyIdState] = 3;
- EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, wrapper));
- wrapper.intMap[kKeyIdPackageVersion] = 4;
- EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, wrapper));
+ matcherResults.clear();
+ matcherResults.push_back(MatchingState::kMatched);
+ matcherResults.push_back(MatchingState::kMatched);
+ EXPECT_FALSE(combinationMatch(children, operation, matcherResults));
}
#else
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
new file mode 100644
index 0000000..673c156
--- /dev/null
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -0,0 +1,231 @@
+// 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.
+
+#define LOG_TAG "statsd_test"
+
+#include <gtest/gtest.h>
+#include "../src/condition/ConditionTracker.h"
+#include "../src/matchers/LogMatchingTracker.h"
+#include "../src/metrics/CountMetricProducer.h"
+#include "../src/metrics/MetricProducer.h"
+#include "../src/metrics/metrics_manager_util.h"
+
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+#include <stdio.h>
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+using namespace android::os::statsd;
+using android::sp;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+// TODO: ADD MORE TEST CASES.
+
+StatsdConfig buildGoodConfig() {
+ StatsdConfig config;
+ config.set_config_id(12345L);
+
+ LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_IS_ON");
+
+ SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+ simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+ eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_IS_OFF");
+
+ simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+ simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
+
+ eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_ON_OR_OFF");
+
+ LogEntryMatcher_Combination* combination = eventMatcher->mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_matcher("SCREEN_IS_ON");
+ combination->add_matcher("SCREEN_IS_OFF");
+
+ return config;
+}
+
+StatsdConfig buildCircleMatchers() {
+ StatsdConfig config;
+ config.set_config_id(12345L);
+
+ LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_IS_ON");
+
+ SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+ simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+ eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_ON_OR_OFF");
+
+ LogEntryMatcher_Combination* combination = eventMatcher->mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_matcher("SCREEN_IS_ON");
+ // Circle dependency
+ combination->add_matcher("SCREEN_ON_OR_OFF");
+
+ return config;
+}
+
+StatsdConfig buildMissingMatchers() {
+ StatsdConfig config;
+ config.set_config_id(12345L);
+
+ LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_IS_ON");
+
+ SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+ simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+ eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_ON_OR_OFF");
+
+ LogEntryMatcher_Combination* combination = eventMatcher->mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_matcher("SCREEN_IS_ON");
+ // undefined matcher
+ combination->add_matcher("ABC");
+
+ return config;
+}
+
+StatsdConfig buildCircleConditions() {
+ StatsdConfig config;
+ config.set_config_id(12345L);
+
+ LogEntryMatcher* eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_IS_ON");
+
+ SimpleLogEntryMatcher* simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+ simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+ eventMatcher = config.add_log_entry_matcher();
+ eventMatcher->set_name("SCREEN_IS_OFF");
+
+ simpleLogEntryMatcher = eventMatcher->mutable_simple_log_entry_matcher();
+ simpleLogEntryMatcher->add_tag(2 /*SCREEN_STATE_CHANGE*/);
+ simpleLogEntryMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+ simpleLogEntryMatcher->mutable_key_value_matcher(0)->set_eq_int(
+ 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
+
+ Condition* condition = config.add_condition();
+ condition->set_name("SCREEN_IS_ON");
+ SimpleCondition* simpleCondition = condition->mutable_simple_condition();
+ simpleCondition->set_start("SCREEN_IS_ON");
+ simpleCondition->set_stop("SCREEN_IS_OFF");
+
+ condition = config.add_condition();
+ condition->set_name("SCREEN_IS_EITHER_ON_OFF");
+
+ Condition_Combination* combination = condition->mutable_combination();
+ combination->set_operation(LogicalOperation::OR);
+ combination->add_condition("SCREEN_IS_ON");
+ combination->add_condition("SCREEN_IS_EITHER_ON_OFF");
+
+ return config;
+}
+
+TEST(MetricsManagerTest, TestGoodConfig) {
+ StatsdConfig config = buildGoodConfig();
+ set<int> allTagIds;
+ vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ vector<sp<MetricProducer>> allMetricProducers;
+ unordered_map<int, std::vector<int>> conditionToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToConditionMap;
+
+ EXPECT_TRUE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
+ allMetricProducers, conditionToMetricMap, trackerToMetricMap,
+ trackerToConditionMap));
+}
+
+TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
+ StatsdConfig config = buildCircleMatchers();
+ set<int> allTagIds;
+ vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ vector<sp<MetricProducer>> allMetricProducers;
+ unordered_map<int, std::vector<int>> conditionToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToConditionMap;
+
+ EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
+ allMetricProducers, conditionToMetricMap, trackerToMetricMap,
+ trackerToConditionMap));
+}
+
+TEST(MetricsManagerTest, TestMissingMatchers) {
+ StatsdConfig config = buildMissingMatchers();
+ set<int> allTagIds;
+ vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ vector<sp<MetricProducer>> allMetricProducers;
+ unordered_map<int, std::vector<int>> conditionToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToConditionMap;
+
+ EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
+ allMetricProducers, conditionToMetricMap, trackerToMetricMap,
+ trackerToConditionMap));
+}
+
+TEST(MetricsManagerTest, TestCircleConditionDependency) {
+ StatsdConfig config = buildCircleConditions();
+ set<int> allTagIds;
+ vector<sp<LogMatchingTracker>> allLogEntryMatchers;
+ vector<sp<ConditionTracker>> allConditionTrackers;
+ vector<sp<MetricProducer>> allMetricProducers;
+ unordered_map<int, std::vector<int>> conditionToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToMetricMap;
+ unordered_map<int, std::vector<int>> trackerToConditionMap;
+
+ EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
+ allMetricProducers, conditionToMetricMap, trackerToMetricMap,
+ trackerToConditionMap));
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
new file mode 100644
index 0000000..b6f1449
--- /dev/null
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -0,0 +1,69 @@
+// 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.
+
+#define LOG_TAG "statsd_test"
+
+#include <gtest/gtest.h>
+#include "../src/UidMap.h"
+#include <stdio.h>
+
+using namespace android;
+using namespace android::os::statsd;
+
+#ifdef __ANDROID__
+const string kApp1 = "app1.sharing.1";
+const string kApp2 = "app2.sharing.1";
+
+TEST(UidMapTest, TestMatching) {
+ UidMap m;
+ vector<int32_t> uids;
+ vector<int32_t> versions;
+ vector<String16> apps;
+
+ uids.push_back(1000);
+ uids.push_back(1000);
+ apps.push_back(String16(kApp1.c_str()));
+ apps.push_back(String16(kApp2.c_str()));
+ versions.push_back(4);
+ versions.push_back(5);
+ m.updateMap(uids, versions, apps);
+ EXPECT_TRUE(m.hasApp(1000, kApp1));
+ EXPECT_TRUE(m.hasApp(1000, kApp2));
+ EXPECT_FALSE(m.hasApp(1000, "not.app"));
+}
+
+TEST(UidMapTest, TestAddAndRemove) {
+ UidMap m;
+ vector<int32_t> uids;
+ vector<int32_t> versions;
+ vector<String16> apps;
+
+ uids.push_back(1000);
+ uids.push_back(1000);
+ apps.push_back(String16(kApp1.c_str()));
+ apps.push_back(String16(kApp2.c_str()));
+ versions.push_back(4);
+ versions.push_back(5);
+ m.updateMap(uids, versions, apps);
+
+ m.updateApp(String16(kApp1.c_str()), 1000, 40);
+ EXPECT_EQ(40, m.getAppVersion(1000, kApp1));
+
+ m.removeApp(String16(kApp1.c_str()), 1000);
+ EXPECT_FALSE(m.hasApp(1000, kApp1));
+ EXPECT_TRUE(m.hasApp(1000, kApp2));
+}
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
\ No newline at end of file
diff --git a/cmds/statsd/tests/indexed_priority_queue_test.cpp b/cmds/statsd/tests/indexed_priority_queue_test.cpp
index e4d4d25..74a482e 100644
--- a/cmds/statsd/tests/indexed_priority_queue_test.cpp
+++ b/cmds/statsd/tests/indexed_priority_queue_test.cpp
@@ -182,6 +182,40 @@
EXPECT_FALSE(ipq.contains(nullptr));
}
+TEST(indexed_priority_queue, pop) {
+ indexed_priority_queue<AATest, AATest::Smaller> ipq;
+ sp<const AATest> a = new AATest{1};
+ sp<const AATest> b = new AATest{2};
+ sp<const AATest> c = new AATest{3};
+
+ ipq.push(c);
+ ipq.push(b);
+ ipq.push(a);
+ EXPECT_EQ(3u, ipq.size());
+
+ ipq.pop();
+ EXPECT_EQ(2u, ipq.size());
+ EXPECT_FALSE(ipq.contains(a));
+ EXPECT_TRUE(ipq.contains(b));
+ EXPECT_TRUE(ipq.contains(c));
+
+ ipq.pop();
+ EXPECT_EQ(1u, ipq.size());
+ EXPECT_FALSE(ipq.contains(a));
+ EXPECT_FALSE(ipq.contains(b));
+ EXPECT_TRUE(ipq.contains(c));
+
+ ipq.pop();
+ EXPECT_EQ(0u, ipq.size());
+ EXPECT_FALSE(ipq.contains(a));
+ EXPECT_FALSE(ipq.contains(b));
+ EXPECT_FALSE(ipq.contains(c));
+ EXPECT_TRUE(ipq.empty());
+
+ ipq.pop(); // pop an empty queue
+ EXPECT_TRUE(ipq.empty());
+}
+
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index e0ac911..d988a42 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -542,9 +542,9 @@
* <ul>
* <li> <p>When creating a new document, the backing database entry or file for
* it is created immediately. For example, if the user chooses to write
- * a new e-mail, a new entry for that e-mail is created as soon as they
+ * a new email, a new entry for that email is created as soon as they
* start entering data, so that if they go to any other activity after
- * that point this e-mail will now appear in the list of drafts.</p>
+ * that point this email will now appear in the list of drafts.</p>
* <li> <p>When an activity's <code>onPause()</code> method is called, it should
* commit to the backing content provider or file any changes the user
* has made. This ensures that those changes will be seen by any other
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 26f96fb..2ba6e01 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -16,10 +16,10 @@
package android.app;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -670,134 +670,6 @@
/** Invalid stack ID. */
public static final int INVALID_STACK_ID = -1;
- /** First static stack ID.
- * @hide */
- private static final int FIRST_STATIC_STACK_ID = 0;
-
- /** ID of stack where fullscreen activities are normally launched into. */
- public static final int FULLSCREEN_WORKSPACE_STACK_ID = 1;
-
- /** ID of stack where freeform/resized activities are normally launched into. */
- public static final int FREEFORM_WORKSPACE_STACK_ID = FULLSCREEN_WORKSPACE_STACK_ID + 1;
-
- /** ID of stack that occupies a dedicated region of the screen. */
- public static final int DOCKED_STACK_ID = FREEFORM_WORKSPACE_STACK_ID + 1;
-
- /** ID of stack that always on top (always visible) when it exist. */
- public static final int PINNED_STACK_ID = DOCKED_STACK_ID + 1;
-
- /** Last static stack stack ID.
- * @hide */
- private static final int LAST_STATIC_STACK_ID = PINNED_STACK_ID;
-
- /** Start of ID range used by stacks that are created dynamically.
- * @hide */
- public static final int FIRST_DYNAMIC_STACK_ID = LAST_STATIC_STACK_ID + 1;
-
- // TODO: Figure-out a way to remove this.
- /** @hide */
- public static boolean isStaticStack(int stackId) {
- return stackId >= FIRST_STATIC_STACK_ID && stackId <= LAST_STATIC_STACK_ID;
- }
-
- // TODO: It seems this mostly means a stack on a secondary display now. Need to see if
- // there are other meanings. If not why not just use information from the display?
- /** @hide */
- public static boolean isDynamicStack(int stackId) {
- return stackId >= FIRST_DYNAMIC_STACK_ID;
- }
-
- /**
- * Returns true if we try to maintain focus in the current stack when the top activity
- * finishes.
- * @hide
- */
- // TODO: Figure-out a way to remove. Probably isn't needed in the new world...
- public static boolean keepFocusInStackIfPossible(int stackId) {
- return stackId == FREEFORM_WORKSPACE_STACK_ID
- || stackId == DOCKED_STACK_ID || stackId == PINNED_STACK_ID;
- }
-
- /**
- * Returns true if the windows of tasks being moved to the target stack from the source
- * stack should be replaced, meaning that window manager will keep the old window around
- * until the new is ready.
- * @hide
- */
- public static boolean replaceWindowsOnTaskMove(int sourceStackId, int targetStackId) {
- return sourceStackId == FREEFORM_WORKSPACE_STACK_ID
- || targetStackId == FREEFORM_WORKSPACE_STACK_ID;
- }
-
- /**
- * Returns true if the top task in the task is allowed to return home when finished and
- * there are other tasks in the stack.
- * @hide
- */
- public static boolean allowTopTaskToReturnHome(int stackId) {
- return stackId != PINNED_STACK_ID;
- }
-
- /**
- * Returns true if the stack should be resized to match the bounds specified by
- * {@link ActivityOptions#setLaunchBounds} when launching an activity into the stack.
- * @hide
- */
- public static boolean resizeStackWithLaunchBounds(int stackId) {
- return stackId == PINNED_STACK_ID;
- }
-
- /**
- * Returns true if a window from the specified stack with {@param stackId} are normally
- * fullscreen, i. e. they can become the top opaque fullscreen window, meaning that it
- * controls system bars, lockscreen occluded/dismissing state, screen rotation animation,
- * etc.
- * @hide
- */
- // TODO: What about the other side of docked stack if we move this to WindowConfiguration?
- public static boolean normallyFullscreenWindows(int stackId) {
- return stackId != PINNED_STACK_ID && stackId != FREEFORM_WORKSPACE_STACK_ID
- && stackId != DOCKED_STACK_ID;
- }
-
- /** Returns the stack id for the input windowing mode.
- * @hide */
- // TODO: To be removed once we are not using stack id for stuff...
- public static int getStackIdForWindowingMode(int windowingMode) {
- switch (windowingMode) {
- case WINDOWING_MODE_PINNED: return PINNED_STACK_ID;
- case WINDOWING_MODE_FREEFORM: return FREEFORM_WORKSPACE_STACK_ID;
- case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: return DOCKED_STACK_ID;
- case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return FULLSCREEN_WORKSPACE_STACK_ID;
- case WINDOWING_MODE_FULLSCREEN: return FULLSCREEN_WORKSPACE_STACK_ID;
- default: return INVALID_STACK_ID;
- }
- }
-
- /** Returns the windowing mode that should be used for this input stack id.
- * @hide */
- // TODO: To be removed once we are not using stack id for stuff...
- public static int getWindowingModeForStackId(int stackId, boolean inSplitScreenMode) {
- final int windowingMode;
- switch (stackId) {
- case FULLSCREEN_WORKSPACE_STACK_ID:
- windowingMode = inSplitScreenMode
- ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY : WINDOWING_MODE_FULLSCREEN;
- break;
- case PINNED_STACK_ID:
- windowingMode = WINDOWING_MODE_PINNED;
- break;
- case DOCKED_STACK_ID:
- windowingMode = WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
- break;
- case FREEFORM_WORKSPACE_STACK_ID:
- windowingMode = WINDOWING_MODE_FREEFORM;
- break;
- default :
- windowingMode = WINDOWING_MODE_UNDEFINED;
- }
- return windowingMode;
- }
}
/**
@@ -1531,6 +1403,12 @@
*/
public int resizeMode;
+ /**
+ * The current configuration this task is in.
+ * @hide
+ */
+ final public Configuration configuration = new Configuration();
+
public RecentTaskInfo() {
}
@@ -1576,6 +1454,7 @@
}
dest.writeInt(supportsSplitScreenMultiWindow ? 1 : 0);
dest.writeInt(resizeMode);
+ configuration.writeToParcel(dest, flags);
}
public void readFromParcel(Parcel source) {
@@ -1600,6 +1479,7 @@
Rect.CREATOR.createFromParcel(source) : null;
supportsSplitScreenMultiWindow = source.readInt() == 1;
resizeMode = source.readInt();
+ configuration.readFromParcel(source);
}
public static final Creator<RecentTaskInfo> CREATOR
@@ -1798,7 +1678,7 @@
* The full configuration the task is currently running in.
* @hide
*/
- public Configuration configuration = new Configuration();
+ final public Configuration configuration = new Configuration();
public RunningTaskInfo() {
}
@@ -2025,12 +1905,49 @@
}
/**
+ * Sets the windowing mode for a specific task. Only works on tasks of type
+ * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}
+ * @param taskId The id of the task to set the windowing mode for.
+ * @param windowingMode The windowing mode to set for the task.
+ * @param toTop If the task should be moved to the top once the windowing mode changes.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+ public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop)
+ throws SecurityException {
+ try {
+ getService().setTaskWindowingMode(taskId, windowingMode, toTop);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Resizes the input stack id to the given bounds.
+ * @param stackId Id of the stack to resize.
+ * @param bounds Bounds to resize the stack to or {@code null} for fullscreen.
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+ public void resizeStack(int stackId, Rect bounds) throws SecurityException {
+ try {
+ getService().resizeStack(stackId, bounds, false /* allowResizeInDockedMode */,
+ false /* preserveWindows */, false /* animate */, -1 /* animationDuration */);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Removes stacks in the windowing modes from the system if they are of activity type
* ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
*
* @hide
*/
@TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
public void removeStacksInWindowingModes(int[] windowingModes) throws SecurityException {
try {
getService().removeStacksInWindowingModes(windowingModes);
@@ -2045,6 +1962,7 @@
* @hide
*/
@TestApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
public void removeStacksWithActivityTypes(int[] activityTypes) throws SecurityException {
try {
getService().removeStacksWithActivityTypes(activityTypes);
@@ -2535,7 +2453,7 @@
* The full configuration the stack is currently running in.
* @hide
*/
- public Configuration configuration = new Configuration();
+ final public Configuration configuration = new Configuration();
@Override
public int describeContents() {
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index a68c3a5..f14831d 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -17,7 +17,6 @@
package android.app;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.INVALID_DISPLAY;
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 955b463..f039516 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -361,6 +361,15 @@
void killUid(int appId, int userId, in String reason);
void setUserIsMonkey(boolean monkey);
void hang(in IBinder who, boolean allowRestart);
+
+ /**
+ * Sets the windowing mode for a specific task. Only works on tasks of type
+ * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}
+ * @param taskId The id of the task to set the windowing mode for.
+ * @param windowingMode The windowing mode to set for the task.
+ * @param toTop If the task should be moved to the top once the windowing mode changes.
+ */
+ void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop);
void moveTaskToStack(int taskId, int stackId, boolean toTop);
/**
* Resizes the input stack id to the given bounds.
@@ -490,6 +499,18 @@
in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);
boolean moveTaskToDockedStack(int taskId, int createMode, boolean toTop, boolean animate,
in Rect initialBounds);
+ /**
+ * Dismisses split-screen multi-window mode.
+ * {@param toTop} If true the current primary split-screen stack will be placed or left on top.
+ */
+ void dismissSplitScreenMode(boolean toTop);
+ /**
+ * Dismisses PiP
+ * @param animate True if the dismissal should be animated.
+ * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
+ * default animation duration should be used.
+ */
+ void dismissPip(boolean animate, int animationDuration);
void suppressResizeConfigChanges(boolean suppress);
void moveTasksToFullscreenStack(int fromStackId, boolean onTop);
boolean moveTopActivityToPinnedStack(int stackId, in Rect bounds);
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index a56965b..2e1e988 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -30,7 +30,7 @@
void onTaskStackChanged();
/** Called whenever an Activity is moved to the pinned stack from another stack. */
- void onActivityPinned(String packageName, int userId, int taskId);
+ void onActivityPinned(String packageName, int userId, int taskId, int stackId);
/** Called whenever an Activity is moved from the pinned stack to another stack. */
void onActivityUnpinned();
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 64b9ae8..c06ad3f 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -15,8 +15,11 @@
*/
package android.app;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.app.NotificationManager.Importance;
+import android.content.ContentResolver;
+import android.content.Context;
import android.content.Intent;
import android.media.AudioAttributes;
import android.net.Uri;
@@ -27,6 +30,10 @@
import android.text.TextUtils;
import android.util.proto.ProtoOutputStream;
+import com.android.internal.util.Preconditions;
+
+import com.android.internal.util.Preconditions;
+
import org.json.JSONException;
import org.json.JSONObject;
import org.xmlpull.v1.XmlPullParser;
@@ -569,14 +576,35 @@
/**
* @hide
*/
+ public void populateFromXmlForRestore(XmlPullParser parser, Context context) {
+ populateFromXml(parser, true, context);
+ }
+
+ /**
+ * @hide
+ */
@SystemApi
public void populateFromXml(XmlPullParser parser) {
+ populateFromXml(parser, false, null);
+ }
+
+ /**
+ * If {@param forRestore} is true, {@param Context} MUST be non-null.
+ */
+ private void populateFromXml(XmlPullParser parser, boolean forRestore,
+ @Nullable Context context) {
+ Preconditions.checkArgument(!forRestore || context != null,
+ "forRestore is true but got null context");
+
// Name, id, and importance are set in the constructor.
setDescription(parser.getAttributeValue(null, ATT_DESC));
setBypassDnd(Notification.PRIORITY_DEFAULT
!= safeInt(parser, ATT_PRIORITY, Notification.PRIORITY_DEFAULT));
setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY));
- setSound(safeUri(parser, ATT_SOUND), safeAudioAttributes(parser));
+
+ Uri sound = safeUri(parser, ATT_SOUND);
+ setSound(forRestore ? restoreSoundUri(context, sound) : sound, safeAudioAttributes(parser));
+
enableLights(safeBool(parser, ATT_LIGHTS, false));
setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR));
setVibrationPattern(safeLongArray(parser, ATT_VIBRATION, null));
@@ -588,11 +616,62 @@
setBlockableSystem(safeBool(parser, ATT_BLOCKABLE_SYSTEM, false));
}
+ @Nullable
+ private Uri restoreSoundUri(Context context, @Nullable Uri uri) {
+ if (uri == null) {
+ return null;
+ }
+ ContentResolver contentResolver = context.getContentResolver();
+ // There are backups out there with uncanonical uris (because we fixed this after
+ // shipping). If uncanonical uris are given to MediaProvider.uncanonicalize it won't
+ // verify the uri against device storage and we'll possibly end up with a broken uri.
+ // We then canonicalize the uri to uncanonicalize it back, which means we properly check
+ // the uri and in the case of not having the resource we end up with the default - better
+ // than broken. As a side effect we'll canonicalize already canonicalized uris, this is fine
+ // according to the docs because canonicalize method has to handle canonical uris as well.
+ Uri canonicalizedUri = contentResolver.canonicalize(uri);
+ if (canonicalizedUri == null) {
+ // We got a null because the uri in the backup does not exist here, so we return default
+ return Settings.System.DEFAULT_NOTIFICATION_URI;
+ }
+ return contentResolver.uncanonicalize(canonicalizedUri);
+ }
+
/**
* @hide
*/
@SystemApi
public void writeXml(XmlSerializer out) throws IOException {
+ writeXml(out, false, null);
+ }
+
+ /**
+ * @hide
+ */
+ public void writeXmlForBackup(XmlSerializer out, Context context) throws IOException {
+ writeXml(out, true, context);
+ }
+
+ private Uri getSoundForBackup(Context context) {
+ Uri sound = getSound();
+ if (sound == null) {
+ return null;
+ }
+ Uri canonicalSound = context.getContentResolver().canonicalize(sound);
+ if (canonicalSound == null) {
+ // The content provider does not support canonical uris so we backup the default
+ return Settings.System.DEFAULT_NOTIFICATION_URI;
+ }
+ return canonicalSound;
+ }
+
+ /**
+ * If {@param forBackup} is true, {@param Context} MUST be non-null.
+ */
+ private void writeXml(XmlSerializer out, boolean forBackup, @Nullable Context context)
+ throws IOException {
+ Preconditions.checkArgument(!forBackup || context != null,
+ "forBackup is true but got null context");
out.startTag(null, TAG_CHANNEL);
out.attribute(null, ATT_ID, getId());
if (getName() != null) {
@@ -613,8 +692,9 @@
out.attribute(null, ATT_VISIBILITY,
Integer.toString(getLockscreenVisibility()));
}
- if (getSound() != null) {
- out.attribute(null, ATT_SOUND, getSound().toString());
+ Uri sound = forBackup ? getSoundForBackup(context) : getSound();
+ if (sound != null) {
+ out.attribute(null, ATT_SOUND, sound.toString());
}
if (getAudioAttributes() != null) {
out.attribute(null, ATT_USAGE, Integer.toString(getAudioAttributes().getUsage()));
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 8987bc0..23c4166 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -73,15 +73,16 @@
public static final int DISABLE2_QUICK_SETTINGS = 1;
public static final int DISABLE2_SYSTEM_ICONS = 1 << 1;
public static final int DISABLE2_NOTIFICATION_SHADE = 1 << 2;
+ public static final int DISABLE2_GLOBAL_ACTIONS = 1 << 3;
public static final int DISABLE2_NONE = 0x00000000;
public static final int DISABLE2_MASK = DISABLE2_QUICK_SETTINGS | DISABLE2_SYSTEM_ICONS
- | DISABLE2_NOTIFICATION_SHADE;
+ | DISABLE2_NOTIFICATION_SHADE | DISABLE2_GLOBAL_ACTIONS;
@IntDef(flag = true,
value = {DISABLE2_NONE, DISABLE2_MASK, DISABLE2_QUICK_SETTINGS, DISABLE2_SYSTEM_ICONS,
- DISABLE2_NOTIFICATION_SHADE})
+ DISABLE2_NOTIFICATION_SHADE, DISABLE2_GLOBAL_ACTIONS})
@Retention(RetentionPolicy.SOURCE)
public @interface Disable2Flags {}
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 4674c9c..402e209 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -31,7 +31,7 @@
}
@Override
- public void onActivityPinned(String packageName, int userId, int taskId)
+ public void onActivityPinned(String packageName, int userId, int taskId, int stackId)
throws RemoteException {
}
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 6b40538..251863c 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -511,7 +511,8 @@
return windowingMode != WINDOWING_MODE_FREEFORM && windowingMode != WINDOWING_MODE_PINNED;
}
- private static String windowingModeToString(@WindowingMode int windowingMode) {
+ /** @hide */
+ public static String windowingModeToString(@WindowingMode int windowingMode) {
switch (windowingMode) {
case WINDOWING_MODE_UNDEFINED: return "undefined";
case WINDOWING_MODE_FULLSCREEN: return "fullscreen";
diff --git a/core/java/android/app/job/JobScheduler.java b/core/java/android/app/job/JobScheduler.java
index 3868439..0deb2e1 100644
--- a/core/java/android/app/job/JobScheduler.java
+++ b/core/java/android/app/job/JobScheduler.java
@@ -24,7 +24,6 @@
import android.annotation.SystemService;
import android.content.ClipData;
import android.content.Context;
-import android.content.Intent;
import android.os.Bundle;
import android.os.PersistableBundle;
@@ -40,16 +39,18 @@
* and how to construct them. You will construct these JobInfo objects and pass them to the
* JobScheduler with {@link #schedule(JobInfo)}. When the criteria declared are met, the
* system will execute this job on your application's {@link android.app.job.JobService}.
- * You identify which JobService is meant to execute the logic for your job when you create the
- * JobInfo with
+ * You identify the service component that implements the logic for your job when you
+ * construct the JobInfo using
* {@link android.app.job.JobInfo.Builder#JobInfo.Builder(int,android.content.ComponentName)}.
* </p>
* <p>
- * The framework will be intelligent about when you receive your callbacks, and attempt to batch
- * and defer them as much as possible. Typically if you don't specify a deadline on your job, it
- * can be run at any moment depending on the current state of the JobScheduler's internal queue,
- * however it might be deferred as long as until the next time the device is connected to a power
- * source.
+ * The framework will be intelligent about when it executes jobs, and attempt to batch
+ * and defer them as much as possible. Typically if you don't specify a deadline on a job, it
+ * can be run at any moment depending on the current state of the JobScheduler's internal queue.
+ * <p>
+ * While a job is running, the system holds a wakelock on behalf of your app. For this reason,
+ * you do not need to take any action to guarantee that the device stays awake for the
+ * duration of the job.
* </p>
* <p>You do not
* instantiate this class directly; instead, retrieve it through
@@ -141,30 +142,34 @@
int userId, String tag);
/**
- * Cancel a job that is pending in the JobScheduler.
- * @param jobId unique identifier for this job. Obtain this value from the jobs returned by
- * {@link #getAllPendingJobs()}.
+ * Cancel the specified job. If the job is currently executing, it is stopped
+ * immediately and the return value from its {@link JobService#onStopJob(JobParameters)}
+ * method is ignored.
+ *
+ * @param jobId unique identifier for the job to be canceled, as supplied to
+ * {@link JobInfo.Builder#JobInfo.Builder(int, android.content.ComponentName)
+ * JobInfo.Builder(int, android.content.ComponentName)}.
*/
public abstract void cancel(int jobId);
/**
- * Cancel all jobs that have been registered with the JobScheduler by this package.
+ * Cancel <em>all</em> jobs that have been scheduled by the calling application.
*/
public abstract void cancelAll();
/**
- * Retrieve all jobs for this package that are pending in the JobScheduler.
+ * Retrieve all jobs that have been scheduled by the calling application.
*
- * @return a list of all the jobs registered by this package that have not
- * yet been executed.
+ * @return a list of all of the app's scheduled jobs. This includes jobs that are
+ * currently started as well as those that are still waiting to run.
*/
public abstract @NonNull List<JobInfo> getAllPendingJobs();
/**
- * Retrieve a specific job for this package that is pending in the
- * JobScheduler.
+ * Look up the description of a scheduled job.
*
- * @return job registered by this package that has not yet been executed.
+ * @return The {@link JobInfo} description of the given scheduled job, or {@code null}
+ * if the supplied job ID does not correspond to any job.
*/
public abstract @Nullable JobInfo getPendingJob(int jobId);
}
diff --git a/core/java/android/app/job/JobService.java b/core/java/android/app/job/JobService.java
index 9096b47..69afed2 100644
--- a/core/java/android/app/job/JobService.java
+++ b/core/java/android/app/job/JobService.java
@@ -18,16 +18,7 @@
import android.app.Service;
import android.content.Intent;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.lang.ref.WeakReference;
/**
* <p>Entry point for the callback from the {@link android.app.job.JobScheduler}.</p>
@@ -55,7 +46,7 @@
* </pre>
*
* <p>If a job service is declared in the manifest but not protected with this
- * permission, that service will be ignored by the OS.
+ * permission, that service will be ignored by the system.
*/
public static final String PERMISSION_BIND =
"android.permission.BIND_JOB_SERVICE";
@@ -81,14 +72,36 @@
}
/**
- * Override this method with the callback logic for your job. Any such logic needs to be
- * performed on a separate thread, as this function is executed on your application's main
- * thread.
+ * Called to indicate that the job has begun executing. Override this method with the
+ * logic for your job. Like all other component lifecycle callbacks, this method executes
+ * on your application's main thread.
+ * <p>
+ * Return {@code true} from this method if your job needs to continue running. If you
+ * do this, the job remains active until you call
+ * {@link #jobFinished(JobParameters, boolean)} to tell the system that it has completed
+ * its work, or until the job's required constraints are no longer satisfied. For
+ * example, if the job was scheduled using
+ * {@link JobInfo.Builder#setRequiresCharging(boolean) setRequiresCharging(true)},
+ * it will be immediately halted by the system if the user unplugs the device from power,
+ * the job's {@link #onStopJob(JobParameters)} callback will be invoked, and the app
+ * will be expected to shut down all ongoing work connected with that job.
+ * <p>
+ * The system holds a wakelock on behalf of your app as long as your job is executing.
+ * This wakelock is acquired before this method is invoked, and is not released until either
+ * you call {@link #jobFinished(JobParameters, boolean)}, or after the system invokes
+ * {@link #onStopJob(JobParameters)} to notify your job that it is being shut down
+ * prematurely.
+ * <p>
+ * Returning {@code false} from this method means your job is already finished. The
+ * system's wakelock for the job will be released, and {@link #onStopJob(JobParameters)}
+ * will not be invoked.
*
- * @param params Parameters specifying info about this job, including the extras bundle you
- * optionally provided at job-creation time.
- * @return True if your service needs to process the work (on a separate thread). False if
- * there's no more work to be done for this job.
+ * @param params Parameters specifying info about this job, including the optional
+ * extras configured with {@link JobInfo.Builder#setExtras(android.os.PersistableBundle).
+ * This object serves to identify this specific running job instance when calling
+ * {@link #jobFinished(JobParameters, boolean)}.
+ * @return {@code true} if your service will continue running, using a separate thread
+ * when appropriate. {@code false} means that this job has completed its work.
*/
public abstract boolean onStartJob(JobParameters params);
@@ -101,37 +114,44 @@
* {@link android.app.job.JobInfo.Builder#setRequiredNetworkType(int)}, yet while your
* job was executing the user toggled WiFi. Another example is if you had specified
* {@link android.app.job.JobInfo.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
- * idle maintenance window. You are solely responsible for the behaviour of your application
- * upon receipt of this message; your app will likely start to misbehave if you ignore it. One
- * immediate repercussion is that the system will cease holding a wakelock for you.</p>
+ * idle maintenance window. You are solely responsible for the behavior of your application
+ * upon receipt of this message; your app will likely start to misbehave if you ignore it.
+ * <p>
+ * Once this method returns, the system releases the wakelock that it is holding on
+ * behalf of the job.</p>
*
- * @param params Parameters specifying info about this job.
- * @return True to indicate to the JobManager whether you'd like to reschedule this job based
- * on the retry criteria provided at job creation-time. False to drop the job. Regardless of
- * the value returned, your job must stop executing.
+ * @param params The parameters identifying this job, as supplied to
+ * the job in the {@link #onStartJob(JobParameters)} callback.
+ * @return {@code true} to indicate to the JobManager whether you'd like to reschedule
+ * this job based on the retry criteria provided at job creation-time; or {@code false}
+ * to end the job entirely. Regardless of the value returned, your job must stop executing.
*/
public abstract boolean onStopJob(JobParameters params);
/**
- * Call this to inform the JobManager you've finished executing. This can be called from any
- * thread, as it will ultimately be run on your application's main thread. When the system
- * receives this message it will release the wakelock being held.
+ * Call this to inform the JobScheduler that the job has finished its work. When the
+ * system receives this message, it releases the wakelock being held for the job.
* <p>
- * You can specify post-execution behaviour to the scheduler here with
- * <code>needsReschedule </code>. This will apply a back-off timer to your job based on
- * the default, or what was set with
- * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)}. The original
- * requirements are always honoured even for a backed-off job. Note that a job running in
- * idle mode will not be backed-off. Instead what will happen is the job will be re-added
- * to the queue and re-executed within a future idle maintenance window.
+ * You can request that the job be scheduled again by passing {@code true} as
+ * the <code>wantsReschedule</code> parameter. This will apply back-off policy
+ * for the job; this policy can be adjusted through the
+ * {@link android.app.job.JobInfo.Builder#setBackoffCriteria(long, int)} method
+ * when the job is originally scheduled. The job's initial
+ * requirements are preserved when jobs are rescheduled, regardless of backed-off
+ * policy.
+ * <p class="note">
+ * A job running while the device is dozing will not be rescheduled with the normal back-off
+ * policy. Instead, the job will be re-added to the queue and executed again during
+ * a future idle maintenance window.
* </p>
*
- * @param params Parameters specifying system-provided info about this job, this was given to
- * your application in {@link #onStartJob(JobParameters)}.
- * @param needsReschedule True if this job should be rescheduled according to the back-off
- * criteria specified at schedule-time. False otherwise.
+ * @param params The parameters identifying this job, as supplied to
+ * the job in the {@link #onStartJob(JobParameters)} callback.
+ * @param wantsReschedule {@code true} if this job should be rescheduled according
+ * to the back-off criteria specified when it was first scheduled; {@code false}
+ * otherwise.
*/
- public final void jobFinished(JobParameters params, boolean needsReschedule) {
- mEngine.jobFinished(params, needsReschedule);
+ public final void jobFinished(JobParameters params, boolean wantsReschedule) {
+ mEngine.jobFinished(params, wantsReschedule);
}
-}
\ No newline at end of file
+}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index cdeaea3..5b2bf45 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -2099,7 +2099,8 @@
public static Uri maybeAddUserId(Uri uri, int userId) {
if (uri == null) return null;
if (userId != UserHandle.USER_CURRENT
- && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+ && (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())
+ || ContentResolver.SCHEME_SLICE.equals(uri.getScheme()))) {
if (!uriHasUserId(uri)) {
//We don't add the user Id if there's already one
Uri.Builder builder = uri.buildUpon();
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index be7f921..143c51d 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -467,6 +467,7 @@
/** Updates the flags for the given permission. */
public abstract void updatePermissionFlagsTEMP(@NonNull String permName,
@NonNull String packageName, int flagMask, int flagValues, int userId);
- /** temporary until mPermissionTrees is moved to PermissionManager */
- public abstract Object enforcePermissionTreeTEMP(@NonNull String permName, int callingUid);
+ /** Returns a PermissionGroup. */
+ public abstract @Nullable PackageParser.PermissionGroup getPermissionGroupTEMP(
+ @NonNull String groupName);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 6c7c8a07..ad36139a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3711,17 +3711,15 @@
ai.flags |= ApplicationInfo.FLAG_IS_GAME;
}
- if (false) {
- if (sa.getBoolean(
- com.android.internal.R.styleable.AndroidManifestApplication_cantSaveState,
- false)) {
- ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
+ if (sa.getBoolean(
+ com.android.internal.R.styleable.AndroidManifestApplication_cantSaveState,
+ false)) {
+ ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE;
- // A heavy-weight application can not be in a custom process.
- // We can do direct compare because we intern all strings.
- if (ai.processName != null && ai.processName != ai.packageName) {
- outError[0] = "cantSaveState applications can not use custom processes";
- }
+ // A heavy-weight application can not be in a custom process.
+ // We can do direct compare because we intern all strings.
+ if (ai.processName != null && !ai.processName.equals(ai.packageName)) {
+ outError[0] = "cantSaveState applications can not use custom processes";
}
}
}
@@ -6849,6 +6847,11 @@
dest.writeParcelable(group, flags);
}
+ /** @hide */
+ public boolean isAppOp() {
+ return info.isAppOp();
+ }
+
private Permission(Parcel in) {
super(in);
final ClassLoader boot = Object.class.getClassLoader();
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index b45c26c..5dd7aed 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -353,6 +353,11 @@
return size;
}
+ /** @hide */
+ public boolean isAppOp() {
+ return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0;
+ }
+
public static final Creator<PermissionInfo> CREATOR =
new Creator<PermissionInfo>() {
@Override
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index a8b8c4b..386239c 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -796,7 +796,7 @@
dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
is.close();
}
- } catch (Exception e) {
+ } catch (Exception | StackOverflowError e) {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
final NotFoundException rnf = new NotFoundException(
"File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 6fbacaf..b2af44e 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -278,6 +278,19 @@
*/
public static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 1 << 7;
+ /**
+ * Virtual display flag: Indicates that the contents will be destroyed once
+ * the display is removed.
+ *
+ * Public virtual displays without this flag will move their content to main display
+ * stack once they're removed. Private vistual displays will always destroy their
+ * content on removal even without this flag.
+ *
+ * @see #createVirtualDisplay
+ * @hide
+ */
+ public static final int VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 8;
+
/** @hide */
public DisplayManager(Context context) {
mContext = context;
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 2c9fb23..4e474c8 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -683,9 +683,9 @@
*/
public boolean hasIPv4Address() {
for (LinkAddress address : mLinkAddresses) {
- if (address.getAddress() instanceof Inet4Address) {
- return true;
- }
+ if (address.getAddress() instanceof Inet4Address) {
+ return true;
+ }
}
return false;
}
@@ -725,9 +725,9 @@
*/
public boolean hasIPv4DefaultRoute() {
for (RouteInfo r : mRoutes) {
- if (r.isIPv4Default()) {
- return true;
- }
+ if (r.isIPv4Default()) {
+ return true;
+ }
}
return false;
}
@@ -740,9 +740,9 @@
*/
public boolean hasIPv6DefaultRoute() {
for (RouteInfo r : mRoutes) {
- if (r.isIPv6Default()) {
- return true;
- }
+ if (r.isIPv6Default()) {
+ return true;
+ }
}
return false;
}
@@ -755,9 +755,9 @@
*/
public boolean hasIPv4DnsServer() {
for (InetAddress ia : mDnses) {
- if (ia instanceof Inet4Address) {
- return true;
- }
+ if (ia instanceof Inet4Address) {
+ return true;
+ }
}
return false;
}
@@ -770,9 +770,9 @@
*/
public boolean hasIPv6DnsServer() {
for (InetAddress ia : mDnses) {
- if (ia instanceof Inet6Address) {
- return true;
- }
+ if (ia instanceof Inet6Address) {
+ return true;
+ }
}
return false;
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 66b6b47..450ced4 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -19,6 +19,7 @@
import android.app.job.JobParameters;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.service.batterystats.BatteryStatsServiceDumpProto;
import android.telephony.SignalStrength;
import android.text.format.DateFormat;
import android.util.ArrayMap;
@@ -29,11 +30,13 @@
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
import android.view.Display;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
+import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
@@ -78,17 +81,17 @@
* A constant indicating a sensor timer.
*/
public static final int SENSOR = 3;
-
+
/**
* A constant indicating a a wifi running timer
*/
public static final int WIFI_RUNNING = 4;
-
+
/**
* A constant indicating a full wifi lock timer
*/
public static final int FULL_WIFI_LOCK = 5;
-
+
/**
* A constant indicating a wifi scan
*/
@@ -190,7 +193,7 @@
public static final int STATS_SINCE_UNPLUGGED = 2;
// NOTE: Update this list if you add/change any stats above.
- // These characters are supposed to represent "total", "last", "current",
+ // These characters are supposed to represent "total", "last", "current",
// and "unplugged". They were shortened for efficiency sake.
private static final String[] STAT_NAMES = { "l", "c", "u" };
@@ -217,8 +220,10 @@
* - Package wakeup alarms are now on screen-off timebase
* New in version 26:
* - Resource power manager (rpm) states [but screenOffRpm is disabled from working properly]
+ * New in version 27:
+ * - Always On Display (screen doze mode) time and power
*/
- static final String CHECKIN_VERSION = "26";
+ static final int CHECKIN_VERSION = 27;
/**
* Old version, we hit 9 and ran out of room, need to remove.
@@ -1381,12 +1386,12 @@
public static final int STATE_PHONE_SCANNING_FLAG = 1<<21;
public static final int STATE_SCREEN_ON_FLAG = 1<<20; // consider moving to states2
public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19; // consider moving to states2
- // empty slot
+ public static final int STATE_SCREEN_DOZE_FLAG = 1 << 18;
// empty slot
public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<16;
public static final int MOST_INTERESTING_STATES =
- STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG;
+ STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG | STATE_SCREEN_DOZE_FLAG;
public static final int SETTLE_TO_ZERO_STATES = 0xffff0000 & ~MOST_INTERESTING_STATES;
@@ -1414,8 +1419,8 @@
public static final int STATE2_BLUETOOTH_SCAN_FLAG = 1 << 20;
public static final int MOST_INTERESTING_STATES2 =
- STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_MASK
- | STATE2_CHARGING_FLAG | STATE2_PHONE_IN_CALL_FLAG | STATE2_BLUETOOTH_ON_FLAG;
+ STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_MASK
+ | STATE2_CHARGING_FLAG | STATE2_PHONE_IN_CALL_FLAG | STATE2_BLUETOOTH_ON_FLAG;
public static final int SETTLE_TO_ZERO_STATES2 = 0xffff0000 & ~MOST_INTERESTING_STATES2;
@@ -1863,6 +1868,21 @@
*/
public abstract int getScreenOnCount(int which);
+ /**
+ * Returns the time in microseconds that the screen has been dozing while the device was
+ * running on battery.
+ *
+ * {@hide}
+ */
+ public abstract long getScreenDozeTime(long elapsedRealtimeUs, int which);
+
+ /**
+ * Returns the number of times the screen was turned dozing.
+ *
+ * {@hide}
+ */
+ public abstract int getScreenDozeCount(int which);
+
public abstract long getInteractiveTime(long elapsedRealtimeUs, int which);
public static final int SCREEN_BRIGHTNESS_DARK = 0;
@@ -1891,6 +1911,13 @@
long elapsedRealtimeUs, int which);
/**
+ * Returns the {@link Timer} object that tracks the given screen brightness.
+ *
+ * {@hide}
+ */
+ public abstract Timer getScreenBrightnessTimer(int brightnessBin);
+
+ /**
* Returns the time in microseconds that power save mode has been enabled while the device was
* running on battery.
*
@@ -1999,6 +2026,14 @@
long elapsedRealtimeUs, int which);
/**
+ * Returns the {@link Timer} object that tracks how much the phone has been trying to
+ * acquire a signal.
+ *
+ * {@hide}
+ */
+ public abstract Timer getPhoneSignalScanningTimer();
+
+ /**
* Returns the number of times the phone has entered the given signal strength.
*
* {@hide}
@@ -2006,6 +2041,12 @@
public abstract int getPhoneSignalStrengthCount(int strengthBin, int which);
/**
+ * Return the {@link Timer} object used to track the given signal strength's duration and
+ * counts.
+ */
+ protected abstract Timer getPhoneSignalStrengthTimer(int strengthBin);
+
+ /**
* Returns the time in microseconds that the mobile network has been active
* (in a high power state).
*
@@ -2088,6 +2129,11 @@
*/
public abstract int getPhoneDataConnectionCount(int dataType, int which);
+ /**
+ * Returns the {@link Timer} object that tracks the phone's data connection type stats.
+ */
+ public abstract Timer getPhoneDataConnectionTimer(int dataType);
+
public static final int WIFI_SUPPL_STATE_INVALID = 0;
public static final int WIFI_SUPPL_STATE_DISCONNECTED = 1;
public static final int WIFI_SUPPL_STATE_INTERFACE_DISABLED = 2;
@@ -2116,8 +2162,7 @@
"group", "compl", "dorm", "uninit"
};
- public static final BitDescription[] HISTORY_STATE_DESCRIPTIONS
- = new BitDescription[] {
+ public static final BitDescription[] HISTORY_STATE_DESCRIPTIONS = new BitDescription[] {
new BitDescription(HistoryItem.STATE_CPU_RUNNING_FLAG, "running", "r"),
new BitDescription(HistoryItem.STATE_WAKE_LOCK_FLAG, "wake_lock", "w"),
new BitDescription(HistoryItem.STATE_SENSOR_ON_FLAG, "sensor", "s"),
@@ -2131,6 +2176,7 @@
new BitDescription(HistoryItem.STATE_AUDIO_ON_FLAG, "audio", "a"),
new BitDescription(HistoryItem.STATE_SCREEN_ON_FLAG, "screen", "S"),
new BitDescription(HistoryItem.STATE_BATTERY_PLUGGED_FLAG, "plugged", "BP"),
+ new BitDescription(HistoryItem.STATE_SCREEN_DOZE_FLAG, "screen_doze", "Sd"),
new BitDescription(HistoryItem.STATE_DATA_CONNECTION_MASK,
HistoryItem.STATE_DATA_CONNECTION_SHIFT, "data_conn", "Pcn",
DATA_CONNECTION_NAMES, DATA_CONNECTION_NAMES),
@@ -2247,6 +2293,13 @@
public abstract int getWifiStateCount(int wifiState, int which);
/**
+ * Returns the {@link Timer} object that tracks the given WiFi state.
+ *
+ * {@hide}
+ */
+ public abstract Timer getWifiStateTimer(int wifiState);
+
+ /**
* Returns the time in microseconds that the wifi supplicant has been
* in a given state.
*
@@ -2262,6 +2315,13 @@
*/
public abstract int getWifiSupplStateCount(int state, int which);
+ /**
+ * Returns the {@link Timer} object that tracks the given wifi supplicant state.
+ *
+ * {@hide}
+ */
+ public abstract Timer getWifiSupplStateTimer(int state);
+
public static final int NUM_WIFI_SIGNAL_STRENGTH_BINS = 5;
/**
@@ -2281,6 +2341,13 @@
public abstract int getWifiSignalStrengthCount(int strengthBin, int which);
/**
+ * Returns the {@link Timer} object that tracks the given WIFI signal strength.
+ *
+ * {@hide}
+ */
+ public abstract Timer getWifiSignalStrengthTimer(int strengthBin);
+
+ /**
* Returns the time in microseconds that the flashlight has been on while the device was
* running on battery.
*
@@ -2467,6 +2534,18 @@
public abstract int getDischargeAmountScreenOffSinceCharge();
/**
+ * Get the amount the battery has discharged while the screen was dozing,
+ * since the last time power was unplugged.
+ */
+ public abstract int getDischargeAmountScreenDoze();
+
+ /**
+ * Get the amount the battery has discharged while the screen was dozing,
+ * since the last time the device was charged.
+ */
+ public abstract int getDischargeAmountScreenDozeSinceCharge();
+
+ /**
* Returns the total, last, or current battery uptime in microseconds.
*
* @param curTime the elapsed realtime in microseconds.
@@ -2483,7 +2562,7 @@
public abstract long computeBatteryRealtime(long curTime, int which);
/**
- * Returns the total, last, or current battery screen off uptime in microseconds.
+ * Returns the total, last, or current battery screen off/doze uptime in microseconds.
*
* @param curTime the elapsed realtime in microseconds.
* @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
@@ -2491,7 +2570,7 @@
public abstract long computeBatteryScreenOffUptime(long curTime, int which);
/**
- * Returns the total, last, or current battery screen off realtime in microseconds.
+ * Returns the total, last, or current battery screen off/doze realtime in microseconds.
*
* @param curTime the current elapsed realtime in microseconds.
* @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT.
@@ -2590,18 +2669,24 @@
};
/**
- * Return the counter keeping track of the amount of battery discharge while the screen was off,
- * measured in micro-Ampere-hours. This will be non-zero only if the device's battery has
- * a coulomb counter.
- */
- public abstract LongCounter getDischargeScreenOffCoulombCounter();
-
- /**
- * Return the counter keeping track of the amount of battery discharge measured in
+ * Return the amount of battery discharge while the screen was off, measured in
* micro-Ampere-hours. This will be non-zero only if the device's battery has
* a coulomb counter.
*/
- public abstract LongCounter getDischargeCoulombCounter();
+ public abstract long getUahDischargeScreenOff(int which);
+
+ /**
+ * Return the amount of battery discharge while the screen was in doze mode, measured in
+ * micro-Ampere-hours. This will be non-zero only if the device's battery has
+ * a coulomb counter.
+ */
+ public abstract long getUahDischargeScreenDoze(int which);
+
+ /**
+ * Return the amount of battery discharge measured in micro-Ampere-hours. This will be
+ * non-zero only if the device's battery has a coulomb counter.
+ */
+ public abstract long getUahDischarge(int which);
/**
* Returns the estimated real battery capacity, which may be less than the capacity
@@ -2946,13 +3031,38 @@
final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500)
/ 1000;
final int count = timer.getCountLocked(which);
- if (totalTime != 0) {
+ if (totalTime != 0 || count != 0) {
dumpLine(pw, uid, category, type, totalTime, count);
}
}
}
/**
+ * Dump a given timer stat to the proto stream.
+ *
+ * @param proto the ProtoOutputStream to log to
+ * @param fieldId type of data, the field to save to (e.g. AggregatedBatteryStats.WAKELOCK)
+ * @param timer a {@link Timer} to dump stats for
+ * @param rawRealtimeUs the current elapsed realtime of the system in microseconds
+ * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT
+ */
+ private static void dumpTimer(ProtoOutputStream proto, long fieldId,
+ Timer timer, long rawRealtimeUs, int which) {
+ if (timer == null) {
+ return;
+ }
+ // Convert from microseconds to milliseconds with rounding
+ final long totalTimeMs = (timer.getTotalTimeLocked(rawRealtimeUs, which) + 500) / 1000;
+ final int count = timer.getCountLocked(which);
+ if (totalTimeMs != 0 || count != 0) {
+ final long token = proto.start(fieldId);
+ proto.write(TimerProto.DURATION_MS, totalTimeMs);
+ proto.write(TimerProto.COUNT, count);
+ proto.end(token);
+ }
+ }
+
+ /**
* Checks if the ControllerActivityCounter has any data worth dumping.
*/
private static boolean controllerActivityHasData(ControllerActivityCounter counter, int which) {
@@ -3004,6 +3114,38 @@
pw.println();
}
+ /**
+ * Dumps the ControllerActivityCounter if it has any data worth dumping.
+ */
+ private static void dumpControllerActivityProto(ProtoOutputStream proto, long fieldId,
+ ControllerActivityCounter counter,
+ int which) {
+ if (!controllerActivityHasData(counter, which)) {
+ return;
+ }
+
+ final long cToken = proto.start(fieldId);
+
+ proto.write(ControllerActivityProto.IDLE_DURATION_MS,
+ counter.getIdleTimeCounter().getCountLocked(which));
+ proto.write(ControllerActivityProto.RX_DURATION_MS,
+ counter.getRxTimeCounter().getCountLocked(which));
+ proto.write(ControllerActivityProto.POWER_MAH,
+ counter.getPowerCounter().getCountLocked(which) / (1000 * 60 * 60));
+
+ long tToken;
+ LongCounter[] txCounters = counter.getTxTimeCounters();
+ for (int i = 0; i < txCounters.length; ++i) {
+ LongCounter c = txCounters[i];
+ tToken = proto.start(ControllerActivityProto.TX);
+ proto.write(ControllerActivityProto.TxLevel.LEVEL, i);
+ proto.write(ControllerActivityProto.TxLevel.DURATION_MS, c.getCountLocked(which));
+ proto.end(tToken);
+ }
+
+ proto.end(cToken);
+ }
+
private final void printControllerActivityIfInteresting(PrintWriter pw, StringBuilder sb,
String prefix, String controllerName,
ControllerActivityCounter counter,
@@ -3096,13 +3238,13 @@
/**
* Checkin server version of dump to produce more compact, computer-readable log.
*
- * NOTE: all times are expressed in 'ms'.
+ * NOTE: all times are expressed in microseconds, unless specified otherwise.
*/
public final void dumpCheckinLocked(Context context, PrintWriter pw, int which, int reqUid,
boolean wifiOnly) {
final long rawUptime = SystemClock.uptimeMillis() * 1000;
- final long rawRealtime = SystemClock.elapsedRealtime() * 1000;
- final long rawRealtimeMs = (rawRealtime + 500) / 1000;
+ final long rawRealtimeMs = SystemClock.elapsedRealtime();
+ final long rawRealtime = rawRealtimeMs * 1000;
final long batteryUptime = getBatteryUptime(rawUptime);
final long whichBatteryUptime = computeBatteryUptime(rawUptime, which);
final long whichBatteryRealtime = computeBatteryRealtime(rawRealtime, which);
@@ -3112,6 +3254,7 @@
final long totalRealtime = computeRealtime(rawRealtime, which);
final long totalUptime = computeUptime(rawUptime, which);
final long screenOnTime = getScreenOnTime(rawRealtime, which);
+ final long screenDozeTime = getScreenDozeTime(rawRealtime, which);
final long interactiveTime = getInteractiveTime(rawRealtime, which);
final long powerSaveModeEnabledTime = getPowerSaveModeEnabledTime(rawRealtime, which);
final long deviceIdleModeLightTime = getDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT,
@@ -3124,9 +3267,9 @@
rawRealtime, which);
final int connChanges = getNumConnectivityChange(which);
final long phoneOnTime = getPhoneOnTime(rawRealtime, which);
- final long dischargeCount = getDischargeCoulombCounter().getCountLocked(which);
- final long dischargeScreenOffCount = getDischargeScreenOffCoulombCounter()
- .getCountLocked(which);
+ final long dischargeCount = getUahDischarge(which);
+ final long dischargeScreenOffCount = getUahDischargeScreenOff(which);
+ final long dischargeScreenDozeCount = getUahDischargeScreenDoze(which);
final StringBuilder sb = new StringBuilder(128);
@@ -3143,7 +3286,8 @@
getStartClockTime(),
whichBatteryScreenOffRealtime / 1000, whichBatteryScreenOffUptime / 1000,
getEstimatedBatteryCapacity(),
- getMinLearnedBatteryCapacity(), getMaxLearnedBatteryCapacity());
+ getMinLearnedBatteryCapacity(), getMaxLearnedBatteryCapacity(),
+ screenDozeTime / 1000);
// Calculate wakelock times across all uids.
@@ -3295,13 +3439,15 @@
getDischargeStartLevel()-getDischargeCurrentLevel(),
getDischargeStartLevel()-getDischargeCurrentLevel(),
getDischargeAmountScreenOn(), getDischargeAmountScreenOff(),
- dischargeCount / 1000, dischargeScreenOffCount / 1000);
+ dischargeCount / 1000, dischargeScreenOffCount / 1000,
+ getDischargeAmountScreenDoze(), dischargeScreenDozeCount / 1000);
} else {
dumpLine(pw, 0 /* uid */, category, BATTERY_DISCHARGE_DATA,
getLowDischargeAmountSinceCharge(), getHighDischargeAmountSinceCharge(),
getDischargeAmountScreenOnSinceCharge(),
getDischargeAmountScreenOffSinceCharge(),
- dischargeCount / 1000, dischargeScreenOffCount / 1000);
+ dischargeCount / 1000, dischargeScreenOffCount / 1000,
+ getDischargeAmountScreenDozeSinceCharge(), dischargeScreenDozeCount / 1000);
}
if (reqUid < 0) {
@@ -3361,9 +3507,9 @@
BatteryStatsHelper.makemAh(helper.getComputedPower()),
BatteryStatsHelper.makemAh(helper.getMinDrainedPower()),
BatteryStatsHelper.makemAh(helper.getMaxDrainedPower()));
+ int uid = 0;
for (int i=0; i<sippers.size(); i++) {
final BatterySipper bs = sippers.get(i);
- int uid = 0;
String label;
switch (bs.drainType) {
case IDLE:
@@ -3404,6 +3550,9 @@
case CAMERA:
label = "camera";
break;
+ case MEMORY:
+ label = "memory";
+ break;
default:
label = "???";
}
@@ -3424,6 +3573,7 @@
dumpLine(pw, 0 /* uid */, category, GLOBAL_CPU_FREQ_DATA, sb.toString());
}
+ // Dump stats per UID.
for (int iu = 0; iu < NU; iu++) {
final int uid = uidStats.keyAt(iu);
if (reqUid >= 0 && uid != reqUid) {
@@ -3831,6 +3981,7 @@
which);
final long batteryTimeRemaining = computeBatteryTimeRemaining(rawRealtime);
final long chargeTimeRemaining = computeChargeTimeRemaining(rawRealtime);
+ final long screenDozeTime = getScreenDozeTime(rawRealtime, which);
final StringBuilder sb = new StringBuilder(128);
@@ -3868,25 +4019,35 @@
sb.setLength(0);
sb.append(prefix);
- sb.append(" Time on battery: ");
- formatTimeMs(sb, whichBatteryRealtime / 1000); sb.append("(");
- sb.append(formatRatioLocked(whichBatteryRealtime, totalRealtime));
- sb.append(") realtime, ");
- formatTimeMs(sb, whichBatteryUptime / 1000);
- sb.append("("); sb.append(formatRatioLocked(whichBatteryUptime, totalRealtime));
- sb.append(") uptime");
+ sb.append(" Time on battery: ");
+ formatTimeMs(sb, whichBatteryRealtime / 1000); sb.append("(");
+ sb.append(formatRatioLocked(whichBatteryRealtime, totalRealtime));
+ sb.append(") realtime, ");
+ formatTimeMs(sb, whichBatteryUptime / 1000);
+ sb.append("("); sb.append(formatRatioLocked(whichBatteryUptime, whichBatteryRealtime));
+ sb.append(") uptime");
pw.println(sb.toString());
+
sb.setLength(0);
sb.append(prefix);
- sb.append(" Time on battery screen off: ");
- formatTimeMs(sb, whichBatteryScreenOffRealtime / 1000); sb.append("(");
- sb.append(formatRatioLocked(whichBatteryScreenOffRealtime, totalRealtime));
- sb.append(") realtime, ");
- formatTimeMs(sb, whichBatteryScreenOffUptime / 1000);
- sb.append("(");
- sb.append(formatRatioLocked(whichBatteryScreenOffUptime, totalRealtime));
- sb.append(") uptime");
+ sb.append(" Time on battery screen off: ");
+ formatTimeMs(sb, whichBatteryScreenOffRealtime / 1000); sb.append("(");
+ sb.append(formatRatioLocked(whichBatteryScreenOffRealtime, whichBatteryRealtime));
+ sb.append(") realtime, ");
+ formatTimeMs(sb, whichBatteryScreenOffUptime / 1000);
+ sb.append("(");
+ sb.append(formatRatioLocked(whichBatteryScreenOffUptime, whichBatteryRealtime));
+ sb.append(") uptime");
pw.println(sb.toString());
+
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Time on battery screen doze: ");
+ formatTimeMs(sb, screenDozeTime / 1000); sb.append("(");
+ sb.append(formatRatioLocked(screenDozeTime, whichBatteryRealtime));
+ sb.append(")");
+ pw.println(sb.toString());
+
sb.setLength(0);
sb.append(prefix);
sb.append(" Total run time: ");
@@ -3910,8 +4071,7 @@
pw.println(sb.toString());
}
- final LongCounter dischargeCounter = getDischargeCoulombCounter();
- final long dischargeCount = dischargeCounter.getCountLocked(which);
+ final long dischargeCount = getUahDischarge(which);
if (dischargeCount >= 0) {
sb.setLength(0);
sb.append(prefix);
@@ -3921,8 +4081,7 @@
pw.println(sb.toString());
}
- final LongCounter dischargeScreenOffCounter = getDischargeScreenOffCoulombCounter();
- final long dischargeScreenOffCount = dischargeScreenOffCounter.getCountLocked(which);
+ final long dischargeScreenOffCount = getUahDischargeScreenOff(which);
if (dischargeScreenOffCount >= 0) {
sb.setLength(0);
sb.append(prefix);
@@ -3932,7 +4091,18 @@
pw.println(sb.toString());
}
- final long dischargeScreenOnCount = dischargeCount - dischargeScreenOffCount;
+ final long dischargeScreenDozeCount = getUahDischargeScreenDoze(which);
+ if (dischargeScreenDozeCount >= 0) {
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Screen doze discharge: ");
+ sb.append(BatteryStatsHelper.makemAh(dischargeScreenDozeCount / 1000.0));
+ sb.append(" mAh");
+ pw.println(sb.toString());
+ }
+
+ final long dischargeScreenOnCount =
+ dischargeCount - dischargeScreenOffCount - dischargeScreenDozeCount;
if (dischargeScreenOnCount >= 0) {
sb.setLength(0);
sb.append(prefix);
@@ -4340,20 +4510,24 @@
pw.println(getDischargeCurrentLevel());
}
pw.print(prefix); pw.print(" Amount discharged while screen on: ");
- pw.println(getDischargeAmountScreenOn());
+ pw.println(getDischargeAmountScreenOn());
pw.print(prefix); pw.print(" Amount discharged while screen off: ");
- pw.println(getDischargeAmountScreenOff());
+ pw.println(getDischargeAmountScreenOff());
+ pw.print(prefix); pw.print(" Amount discharged while screen doze: ");
+ pw.println(getDischargeAmountScreenDoze());
pw.println(" ");
} else {
pw.print(prefix); pw.println(" Device battery use since last full charge");
pw.print(prefix); pw.print(" Amount discharged (lower bound): ");
- pw.println(getLowDischargeAmountSinceCharge());
+ pw.println(getLowDischargeAmountSinceCharge());
pw.print(prefix); pw.print(" Amount discharged (upper bound): ");
- pw.println(getHighDischargeAmountSinceCharge());
+ pw.println(getHighDischargeAmountSinceCharge());
pw.print(prefix); pw.print(" Amount discharged while screen on: ");
- pw.println(getDischargeAmountScreenOnSinceCharge());
+ pw.println(getDischargeAmountScreenOnSinceCharge());
pw.print(prefix); pw.print(" Amount discharged while screen off: ");
- pw.println(getDischargeAmountScreenOffSinceCharge());
+ pw.println(getDischargeAmountScreenOffSinceCharge());
+ pw.print(prefix); pw.print(" Amount discharged while screen doze: ");
+ pw.println(getDischargeAmountScreenDozeSinceCharge());
pw.println();
}
@@ -5426,8 +5600,9 @@
}
}
}
-
+
public void prepareForDumpLocked() {
+ // We don't need to require subclasses implement this.
}
public static class HistoryPrinter {
@@ -5914,6 +6089,61 @@
return true;
}
+ private static void dumpDurationSteps(ProtoOutputStream proto, long fieldId,
+ LevelStepTracker steps) {
+ if (steps == null) {
+ return;
+ }
+ int count = steps.mNumStepDurations;
+ long token;
+ for (int i = 0; i < count; ++i) {
+ token = proto.start(fieldId);
+ proto.write(SystemProto.BatteryLevelStep.DURATION_MS, steps.getDurationAt(i));
+ proto.write(SystemProto.BatteryLevelStep.LEVEL, steps.getLevelAt(i));
+
+ final long initMode = steps.getInitModeAt(i);
+ final long modMode = steps.getModModeAt(i);
+
+ int ds = SystemProto.BatteryLevelStep.DS_MIXED;
+ if ((modMode & STEP_LEVEL_MODE_SCREEN_STATE) == 0) {
+ switch ((int) (initMode & STEP_LEVEL_MODE_SCREEN_STATE) + 1) {
+ case Display.STATE_OFF:
+ ds = SystemProto.BatteryLevelStep.DS_OFF;
+ break;
+ case Display.STATE_ON:
+ ds = SystemProto.BatteryLevelStep.DS_ON;
+ break;
+ case Display.STATE_DOZE:
+ ds = SystemProto.BatteryLevelStep.DS_DOZE;
+ break;
+ case Display.STATE_DOZE_SUSPEND:
+ ds = SystemProto.BatteryLevelStep.DS_DOZE_SUSPEND;
+ break;
+ default:
+ ds = SystemProto.BatteryLevelStep.DS_ERROR;
+ break;
+ }
+ }
+ proto.write(SystemProto.BatteryLevelStep.DISPLAY_STATE, ds);
+
+ int psm = SystemProto.BatteryLevelStep.PSM_MIXED;
+ if ((modMode & STEP_LEVEL_MODE_POWER_SAVE) == 0) {
+ psm = (initMode & STEP_LEVEL_MODE_POWER_SAVE) != 0
+ ? SystemProto.BatteryLevelStep.PSM_ON : SystemProto.BatteryLevelStep.PSM_OFF;
+ }
+ proto.write(SystemProto.BatteryLevelStep.POWER_SAVE_MODE, psm);
+
+ int im = SystemProto.BatteryLevelStep.IM_MIXED;
+ if ((modMode & STEP_LEVEL_MODE_DEVICE_IDLE) == 0) {
+ im = (initMode & STEP_LEVEL_MODE_DEVICE_IDLE) != 0
+ ? SystemProto.BatteryLevelStep.IM_ON : SystemProto.BatteryLevelStep.IM_OFF;
+ }
+ proto.write(SystemProto.BatteryLevelStep.IDLE_MODE, im);
+
+ proto.end(token);
+ }
+ }
+
public static final int DUMP_CHARGED_ONLY = 1<<1;
public static final int DUMP_DAILY_ONLY = 1<<2;
public static final int DUMP_HISTORY_ONLY = 1<<3;
@@ -6248,7 +6478,8 @@
pw.println();
}
}
-
+
+ // This is called from BatteryStatsService.
@SuppressWarnings("unused")
public void dumpCheckinLocked(Context context, PrintWriter pw,
List<ApplicationInfo> apps, int flags, long histStart) {
@@ -6260,10 +6491,7 @@
long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
- final boolean filtering = (flags &
- (DUMP_HISTORY_ONLY|DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) != 0;
-
- if ((flags&DUMP_INCLUDE_HISTORY) != 0 || (flags&DUMP_HISTORY_ONLY) != 0) {
+ if ((flags & (DUMP_INCLUDE_HISTORY | DUMP_HISTORY_ONLY)) != 0) {
if (startIteratingHistoryLocked()) {
try {
for (int i=0; i<getHistoryStringPoolSize(); i++) {
@@ -6287,7 +6515,7 @@
}
}
- if (filtering && (flags&(DUMP_CHARGED_ONLY|DUMP_DAILY_ONLY)) == 0) {
+ if ((flags & DUMP_HISTORY_ONLY) != 0) {
return;
}
@@ -6320,7 +6548,7 @@
}
}
}
- if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) {
+ if ((flags & DUMP_DAILY_ONLY) == 0) {
dumpDurationSteps(pw, "", DISCHARGE_STEP_DATA, getDischargeLevelStepTracker(), true);
String[] lineArgs = new String[1];
long timeRemaining = computeBatteryTimeRemaining(SystemClock.elapsedRealtime() * 1000);
@@ -6340,4 +6568,399 @@
(flags&DUMP_DEVICE_WIFI_ONLY) != 0);
}
}
+
+ /** Dump #STATS_SINCE_CHARGED batterystats data to a proto. @hide */
+ public void dumpProtoLocked(Context context, FileDescriptor fd, List<ApplicationInfo> apps,
+ int flags, long historyStart) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ final long bToken = proto.start(BatteryStatsServiceDumpProto.BATTERYSTATS);
+ prepareForDumpLocked();
+
+ proto.write(BatteryStatsProto.REPORT_VERSION, CHECKIN_VERSION);
+ proto.write(BatteryStatsProto.PARCEL_VERSION, getParcelVersion());
+ proto.write(BatteryStatsProto.START_PLATFORM_VERSION, getStartPlatformVersion());
+ proto.write(BatteryStatsProto.END_PLATFORM_VERSION, getEndPlatformVersion());
+
+ long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
+
+ if ((flags & (DUMP_INCLUDE_HISTORY | DUMP_HISTORY_ONLY)) != 0) {
+ if (startIteratingHistoryLocked()) {
+ // TODO: implement dumpProtoHistoryLocked(proto);
+ }
+ }
+
+ if ((flags & (DUMP_HISTORY_ONLY | DUMP_DAILY_ONLY)) == 0) {
+ // TODO: implement dumpProtoAppsLocked(proto, apps);
+ dumpProtoSystemLocked(context, proto, (flags & DUMP_DEVICE_WIFI_ONLY) != 0);
+ }
+
+ proto.end(bToken);
+ proto.flush();
+ }
+
+ private void dumpProtoSystemLocked(Context context, ProtoOutputStream proto, boolean wifiOnly) {
+ final long sToken = proto.start(BatteryStatsProto.SYSTEM);
+ final long rawUptimeUs = SystemClock.uptimeMillis() * 1000;
+ final long rawRealtimeMs = SystemClock.elapsedRealtime();
+ final long rawRealtimeUs = rawRealtimeMs * 1000;
+ final int which = STATS_SINCE_CHARGED;
+
+ // Battery data (BATTERY_DATA)
+ long token = proto.start(SystemProto.BATTERY);
+ proto.write(SystemProto.Battery.START_CLOCK_TIME_MS, getStartClockTime());
+ proto.write(SystemProto.Battery.START_COUNT, getStartCount());
+ proto.write(SystemProto.Battery.TOTAL_REALTIME_MS,
+ computeRealtime(rawRealtimeUs, which) / 1000);
+ proto.write(SystemProto.Battery.TOTAL_UPTIME_MS,
+ computeUptime(rawUptimeUs, which) / 1000);
+ proto.write(SystemProto.Battery.BATTERY_REALTIME_MS,
+ computeBatteryRealtime(rawRealtimeUs, which) / 1000);
+ proto.write(SystemProto.Battery.BATTERY_UPTIME_MS,
+ computeBatteryUptime(rawUptimeUs, which) / 1000);
+ proto.write(SystemProto.Battery.SCREEN_OFF_REALTIME_MS,
+ computeBatteryScreenOffRealtime(rawRealtimeUs, which) / 1000);
+ proto.write(SystemProto.Battery.SCREEN_OFF_UPTIME_MS,
+ computeBatteryScreenOffUptime(rawUptimeUs, which) / 1000);
+ proto.write(SystemProto.Battery.SCREEN_DOZE_DURATION_MS,
+ getScreenDozeTime(rawRealtimeUs, which) / 1000);
+ proto.write(SystemProto.Battery.ESTIMATED_BATTERY_CAPACITY_MAH,
+ getEstimatedBatteryCapacity());
+ proto.write(SystemProto.Battery.MIN_LEARNED_BATTERY_CAPACITY_UAH,
+ getMinLearnedBatteryCapacity());
+ proto.write(SystemProto.Battery.MAX_LEARNED_BATTERY_CAPACITY_UAH,
+ getMaxLearnedBatteryCapacity());
+ proto.end(token);
+
+ // Battery discharge (BATTERY_DISCHARGE_DATA)
+ token = proto.start(SystemProto.BATTERY_DISCHARGE);
+ proto.write(SystemProto.BatteryDischarge.LOWER_BOUND_SINCE_CHARGE,
+ getLowDischargeAmountSinceCharge());
+ proto.write(SystemProto.BatteryDischarge.UPPER_BOUND_SINCE_CHARGE,
+ getHighDischargeAmountSinceCharge());
+ proto.write(SystemProto.BatteryDischarge.SCREEN_ON_SINCE_CHARGE,
+ getDischargeAmountScreenOnSinceCharge());
+ proto.write(SystemProto.BatteryDischarge.SCREEN_OFF_SINCE_CHARGE,
+ getDischargeAmountScreenOffSinceCharge());
+ proto.write(SystemProto.BatteryDischarge.SCREEN_DOZE_SINCE_CHARGE,
+ getDischargeAmountScreenDozeSinceCharge());
+ proto.write(SystemProto.BatteryDischarge.TOTAL_MAH,
+ getUahDischarge(which) / 1000);
+ proto.write(SystemProto.BatteryDischarge.TOTAL_MAH_SCREEN_OFF,
+ getUahDischargeScreenOff(which) / 1000);
+ proto.write(SystemProto.BatteryDischarge.TOTAL_MAH_SCREEN_DOZE,
+ getUahDischargeScreenDoze(which) / 1000);
+ proto.end(token);
+
+ // Time remaining
+ long timeRemainingUs = computeChargeTimeRemaining(rawRealtimeUs);
+ if (timeRemainingUs >= 0) {
+ // Charge time remaining (CHARGE_TIME_REMAIN_DATA)
+ proto.write(SystemProto.CHARGE_TIME_REMAINING_MS, timeRemainingUs / 1000);
+ } else {
+ timeRemainingUs = computeBatteryTimeRemaining(rawRealtimeUs);
+ // Discharge time remaining (DISCHARGE_TIME_REMAIN_DATA)
+ if (timeRemainingUs >= 0) {
+ proto.write(SystemProto.DISCHARGE_TIME_REMAINING_MS, timeRemainingUs / 1000);
+ } else {
+ proto.write(SystemProto.DISCHARGE_TIME_REMAINING_MS, -1);
+ }
+ }
+
+ // Charge step (CHARGE_STEP_DATA)
+ dumpDurationSteps(proto, SystemProto.CHARGE_STEP, getChargeLevelStepTracker());
+
+ // Phone data connection (DATA_CONNECTION_TIME_DATA and DATA_CONNECTION_COUNT_DATA)
+ for (int i = 0; i < NUM_DATA_CONNECTION_TYPES; ++i) {
+ token = proto.start(SystemProto.DATA_CONNECTION);
+ proto.write(SystemProto.DataConnection.NAME, i);
+ dumpTimer(proto, SystemProto.DataConnection.TOTAL, getPhoneDataConnectionTimer(i),
+ rawRealtimeUs, which);
+ proto.end(token);
+ }
+
+ // Discharge step (DISCHARGE_STEP_DATA)
+ dumpDurationSteps(proto, SystemProto.DISCHARGE_STEP, getDischargeLevelStepTracker());
+
+ // CPU frequencies (GLOBAL_CPU_FREQ_DATA)
+ final long[] cpuFreqs = getCpuFreqs();
+ if (cpuFreqs != null) {
+ for (long i : cpuFreqs) {
+ proto.write(SystemProto.CPU_FREQUENCY, i);
+ }
+ }
+
+ // Bluetooth controller (GLOBAL_BLUETOOTH_CONTROLLER_DATA)
+ dumpControllerActivityProto(proto, SystemProto.GLOBAL_BLUETOOTH_CONTROLLER,
+ getBluetoothControllerActivity(), which);
+
+ // Modem controller (GLOBAL_MODEM_CONTROLLER_DATA)
+ dumpControllerActivityProto(proto, SystemProto.GLOBAL_MODEM_CONTROLLER,
+ getModemControllerActivity(), which);
+
+ // Global network data (GLOBAL_NETWORK_DATA)
+ token = proto.start(SystemProto.GLOBAL_NETWORK);
+ proto.write(SystemProto.GlobalNetwork.MOBILE_BYTES_RX,
+ getNetworkActivityBytes(NETWORK_MOBILE_RX_DATA, which));
+ proto.write(SystemProto.GlobalNetwork.MOBILE_BYTES_TX,
+ getNetworkActivityBytes(NETWORK_MOBILE_TX_DATA, which));
+ proto.write(SystemProto.GlobalNetwork.MOBILE_PACKETS_RX,
+ getNetworkActivityPackets(NETWORK_MOBILE_RX_DATA, which));
+ proto.write(SystemProto.GlobalNetwork.MOBILE_PACKETS_TX,
+ getNetworkActivityPackets(NETWORK_MOBILE_TX_DATA, which));
+ proto.write(SystemProto.GlobalNetwork.WIFI_BYTES_RX,
+ getNetworkActivityBytes(NETWORK_WIFI_RX_DATA, which));
+ proto.write(SystemProto.GlobalNetwork.WIFI_BYTES_TX,
+ getNetworkActivityBytes(NETWORK_WIFI_TX_DATA, which));
+ proto.write(SystemProto.GlobalNetwork.WIFI_PACKETS_RX,
+ getNetworkActivityPackets(NETWORK_WIFI_RX_DATA, which));
+ proto.write(SystemProto.GlobalNetwork.WIFI_PACKETS_TX,
+ getNetworkActivityPackets(NETWORK_WIFI_TX_DATA, which));
+ proto.write(SystemProto.GlobalNetwork.BT_BYTES_RX,
+ getNetworkActivityBytes(NETWORK_BT_RX_DATA, which));
+ proto.write(SystemProto.GlobalNetwork.BT_BYTES_TX,
+ getNetworkActivityBytes(NETWORK_BT_TX_DATA, which));
+ proto.end(token);
+
+ // Wifi controller (GLOBAL_WIFI_CONTROLLER_DATA)
+ dumpControllerActivityProto(proto, SystemProto.GLOBAL_WIFI_CONTROLLER,
+ getWifiControllerActivity(), which);
+
+
+ // Global wifi (GLOBAL_WIFI_DATA)
+ token = proto.start(SystemProto.GLOBAL_WIFI);
+ proto.write(SystemProto.GlobalWifi.ON_DURATION_MS,
+ getWifiOnTime(rawRealtimeUs, which) / 1000);
+ proto.write(SystemProto.GlobalWifi.RUNNING_DURATION_MS,
+ getGlobalWifiRunningTime(rawRealtimeUs, which) / 1000);
+ proto.end(token);
+
+ // Kernel wakelock (KERNEL_WAKELOCK_DATA)
+ final Map<String, ? extends Timer> kernelWakelocks = getKernelWakelockStats();
+ for (Map.Entry<String, ? extends Timer> ent : kernelWakelocks.entrySet()) {
+ token = proto.start(SystemProto.KERNEL_WAKELOCK);
+ proto.write(SystemProto.KernelWakelock.NAME, ent.getKey());
+ dumpTimer(proto, SystemProto.KernelWakelock.TOTAL, ent.getValue(),
+ rawRealtimeUs, which);
+ proto.end(token);
+ }
+
+ // Misc (MISC_DATA)
+ // Calculate wakelock times across all uids.
+ long fullWakeLockTimeTotalUs = 0;
+ long partialWakeLockTimeTotalUs = 0;
+
+ final SparseArray<? extends Uid> uidStats = getUidStats();
+ for (int iu = 0; iu < uidStats.size(); iu++) {
+ final Uid u = uidStats.valueAt(iu);
+
+ final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelocks =
+ u.getWakelockStats();
+ for (int iw = wakelocks.size() - 1; iw >= 0; --iw) {
+ final Uid.Wakelock wl = wakelocks.valueAt(iw);
+
+ final Timer fullWakeTimer = wl.getWakeTime(WAKE_TYPE_FULL);
+ if (fullWakeTimer != null) {
+ fullWakeLockTimeTotalUs += fullWakeTimer.getTotalTimeLocked(rawRealtimeUs,
+ which);
+ }
+
+ final Timer partialWakeTimer = wl.getWakeTime(WAKE_TYPE_PARTIAL);
+ if (partialWakeTimer != null) {
+ partialWakeLockTimeTotalUs += partialWakeTimer.getTotalTimeLocked(
+ rawRealtimeUs, which);
+ }
+ }
+ }
+ token = proto.start(SystemProto.MISC);
+ proto.write(SystemProto.Misc.SCREEN_ON_DURATION_MS,
+ getScreenOnTime(rawRealtimeUs, which) / 1000);
+ proto.write(SystemProto.Misc.PHONE_ON_DURATION_MS,
+ getPhoneOnTime(rawRealtimeUs, which) / 1000);
+ proto.write(SystemProto.Misc.FULL_WAKELOCK_TOTAL_DURATION_MS,
+ fullWakeLockTimeTotalUs / 1000);
+ proto.write(SystemProto.Misc.PARTIAL_WAKELOCK_TOTAL_DURATION_MS,
+ partialWakeLockTimeTotalUs / 1000);
+ proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_DURATION_MS,
+ getMobileRadioActiveTime(rawRealtimeUs, which) / 1000);
+ proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_ADJUSTED_TIME_MS,
+ getMobileRadioActiveAdjustedTime(which) / 1000);
+ proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_COUNT,
+ getMobileRadioActiveCount(which));
+ proto.write(SystemProto.Misc.MOBILE_RADIO_ACTIVE_UNKNOWN_DURATION_MS,
+ getMobileRadioActiveUnknownTime(which) / 1000);
+ proto.write(SystemProto.Misc.INTERACTIVE_DURATION_MS,
+ getInteractiveTime(rawRealtimeUs, which) / 1000);
+ proto.write(SystemProto.Misc.BATTERY_SAVER_MODE_ENABLED_DURATION_MS,
+ getPowerSaveModeEnabledTime(rawRealtimeUs, which) / 1000);
+ proto.write(SystemProto.Misc.NUM_CONNECTIVITY_CHANGES,
+ getNumConnectivityChange(which));
+ proto.write(SystemProto.Misc.DEEP_DOZE_ENABLED_DURATION_MS,
+ getDeviceIdleModeTime(DEVICE_IDLE_MODE_DEEP, rawRealtimeUs, which) / 1000);
+ proto.write(SystemProto.Misc.DEEP_DOZE_COUNT,
+ getDeviceIdleModeCount(DEVICE_IDLE_MODE_DEEP, which));
+ proto.write(SystemProto.Misc.DEEP_DOZE_IDLING_DURATION_MS,
+ getDeviceIdlingTime(DEVICE_IDLE_MODE_DEEP, rawRealtimeUs, which) / 1000);
+ proto.write(SystemProto.Misc.DEEP_DOZE_IDLING_COUNT,
+ getDeviceIdlingCount(DEVICE_IDLE_MODE_DEEP, which));
+ proto.write(SystemProto.Misc.LONGEST_DEEP_DOZE_DURATION_MS,
+ getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_DEEP));
+ proto.write(SystemProto.Misc.LIGHT_DOZE_ENABLED_DURATION_MS,
+ getDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT, rawRealtimeUs, which) / 1000);
+ proto.write(SystemProto.Misc.LIGHT_DOZE_COUNT,
+ getDeviceIdleModeCount(DEVICE_IDLE_MODE_LIGHT, which));
+ proto.write(SystemProto.Misc.LIGHT_DOZE_IDLING_DURATION_MS,
+ getDeviceIdlingTime(DEVICE_IDLE_MODE_LIGHT, rawRealtimeUs, which) / 1000);
+ proto.write(SystemProto.Misc.LIGHT_DOZE_IDLING_COUNT,
+ getDeviceIdlingCount(DEVICE_IDLE_MODE_LIGHT, which));
+ proto.write(SystemProto.Misc.LONGEST_LIGHT_DOZE_DURATION_MS,
+ getLongestDeviceIdleModeTime(DEVICE_IDLE_MODE_LIGHT));
+ proto.end(token);
+
+ final BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly);
+ helper.create(this);
+ helper.refreshStats(which, UserHandle.USER_ALL);
+
+ // Power use item (POWER_USE_ITEM_DATA)
+ final List<BatterySipper> sippers = helper.getUsageList();
+ if (sippers != null) {
+ for (int i = 0; i < sippers.size(); ++i) {
+ final BatterySipper bs = sippers.get(i);
+ int n = SystemProto.PowerUseItem.UNKNOWN_SIPPER;
+ int uid = 0;
+ switch (bs.drainType) {
+ case IDLE:
+ n = SystemProto.PowerUseItem.IDLE;
+ break;
+ case CELL:
+ n = SystemProto.PowerUseItem.CELL;
+ break;
+ case PHONE:
+ n = SystemProto.PowerUseItem.PHONE;
+ break;
+ case WIFI:
+ n = SystemProto.PowerUseItem.WIFI;
+ break;
+ case BLUETOOTH:
+ n = SystemProto.PowerUseItem.BLUETOOTH;
+ break;
+ case SCREEN:
+ n = SystemProto.PowerUseItem.SCREEN;
+ break;
+ case FLASHLIGHT:
+ n = SystemProto.PowerUseItem.FLASHLIGHT;
+ break;
+ case APP:
+ // dumpProtoAppLocked will handle this.
+ continue;
+ case USER:
+ n = SystemProto.PowerUseItem.USER;
+ uid = UserHandle.getUid(bs.userId, 0);
+ break;
+ case UNACCOUNTED:
+ n = SystemProto.PowerUseItem.UNACCOUNTED;
+ break;
+ case OVERCOUNTED:
+ n = SystemProto.PowerUseItem.OVERCOUNTED;
+ break;
+ case CAMERA:
+ n = SystemProto.PowerUseItem.CAMERA;
+ break;
+ case MEMORY:
+ n = SystemProto.PowerUseItem.MEMORY;
+ break;
+ }
+ token = proto.start(SystemProto.POWER_USE_ITEM);
+ proto.write(SystemProto.PowerUseItem.NAME, n);
+ proto.write(SystemProto.PowerUseItem.UID, uid);
+ proto.write(SystemProto.PowerUseItem.COMPUTED_POWER_MAH, bs.totalPowerMah);
+ proto.write(SystemProto.PowerUseItem.SHOULD_HIDE, bs.shouldHide);
+ proto.write(SystemProto.PowerUseItem.SCREEN_POWER_MAH, bs.screenPowerMah);
+ proto.write(SystemProto.PowerUseItem.PROPORTIONAL_SMEAR_MAH,
+ bs.proportionalSmearMah);
+ proto.end(token);
+ }
+ }
+
+ // Power use summary (POWER_USE_SUMMARY_DATA)
+ token = proto.start(SystemProto.POWER_USE_SUMMARY);
+ proto.write(SystemProto.PowerUseSummary.BATTERY_CAPACITY_MAH,
+ helper.getPowerProfile().getBatteryCapacity());
+ proto.write(SystemProto.PowerUseSummary.COMPUTED_POWER_MAH, helper.getComputedPower());
+ proto.write(SystemProto.PowerUseSummary.MIN_DRAINED_POWER_MAH, helper.getMinDrainedPower());
+ proto.write(SystemProto.PowerUseSummary.MAX_DRAINED_POWER_MAH, helper.getMaxDrainedPower());
+ proto.end(token);
+
+ // RPM stats (RESOURCE_POWER_MANAGER_DATA)
+ final Map<String, ? extends Timer> rpmStats = getRpmStats();
+ final Map<String, ? extends Timer> screenOffRpmStats = getScreenOffRpmStats();
+ for (Map.Entry<String, ? extends Timer> ent : rpmStats.entrySet()) {
+ token = proto.start(SystemProto.RESOURCE_POWER_MANAGER);
+ proto.write(SystemProto.ResourcePowerManager.NAME, ent.getKey());
+ dumpTimer(proto, SystemProto.ResourcePowerManager.TOTAL,
+ ent.getValue(), rawRealtimeUs, which);
+ dumpTimer(proto, SystemProto.ResourcePowerManager.SCREEN_OFF,
+ screenOffRpmStats.get(ent.getKey()), rawRealtimeUs, which);
+ proto.end(token);
+ }
+
+ // Screen brightness (SCREEN_BRIGHTNESS_DATA)
+ for (int i = 0; i < NUM_SCREEN_BRIGHTNESS_BINS; ++i) {
+ token = proto.start(SystemProto.SCREEN_BRIGHTNESS);
+ proto.write(SystemProto.ScreenBrightness.NAME, i);
+ dumpTimer(proto, SystemProto.ScreenBrightness.TOTAL, getScreenBrightnessTimer(i),
+ rawRealtimeUs, which);
+ proto.end(token);
+ }
+
+ // Signal scanning time (SIGNAL_SCANNING_TIME_DATA)
+ dumpTimer(proto, SystemProto.SIGNAL_SCANNING, getPhoneSignalScanningTimer(), rawRealtimeUs,
+ which);
+
+ // Phone signal strength (SIGNAL_STRENGTH_TIME_DATA and SIGNAL_STRENGTH_COUNT_DATA)
+ for (int i = 0; i < SignalStrength.NUM_SIGNAL_STRENGTH_BINS; ++i) {
+ token = proto.start(SystemProto.PHONE_SIGNAL_STRENGTH);
+ proto.write(SystemProto.PhoneSignalStrength.NAME, i);
+ dumpTimer(proto, SystemProto.PhoneSignalStrength.TOTAL, getPhoneSignalStrengthTimer(i),
+ rawRealtimeUs, which);
+ proto.end(token);
+ }
+
+ // Wakeup reasons (WAKEUP_REASON_DATA)
+ final Map<String, ? extends Timer> wakeupReasons = getWakeupReasonStats();
+ for (Map.Entry<String, ? extends Timer> ent : wakeupReasons.entrySet()) {
+ token = proto.start(SystemProto.WAKEUP_REASON);
+ proto.write(SystemProto.WakeupReason.NAME, ent.getKey());
+ dumpTimer(proto, SystemProto.WakeupReason.TOTAL, ent.getValue(), rawRealtimeUs, which);
+ proto.end(token);
+ }
+
+ // Wifi signal strength (WIFI_SIGNAL_STRENGTH_TIME_DATA and WIFI_SIGNAL_STRENGTH_COUNT_DATA)
+ for (int i = 0; i < NUM_WIFI_SIGNAL_STRENGTH_BINS; ++i) {
+ token = proto.start(SystemProto.WIFI_SIGNAL_STRENGTH);
+ proto.write(SystemProto.WifiSignalStrength.NAME, i);
+ dumpTimer(proto, SystemProto.WifiSignalStrength.TOTAL, getWifiSignalStrengthTimer(i),
+ rawRealtimeUs, which);
+ proto.end(token);
+ }
+
+ // Wifi state (WIFI_STATE_TIME_DATA and WIFI_STATE_COUNT_DATA)
+ for (int i = 0; i < NUM_WIFI_STATES; ++i) {
+ token = proto.start(SystemProto.WIFI_STATE);
+ proto.write(SystemProto.WifiState.NAME, i);
+ dumpTimer(proto, SystemProto.WifiState.TOTAL, getWifiStateTimer(i),
+ rawRealtimeUs, which);
+ proto.end(token);
+ }
+
+ // Wifi supplicant state (WIFI_SUPPL_STATE_TIME_DATA and WIFI_SUPPL_STATE_COUNT_DATA)
+ for (int i = 0; i < NUM_WIFI_SUPPL_STATES; ++i) {
+ token = proto.start(SystemProto.WIFI_SUPPLICANT_STATE);
+ proto.write(SystemProto.WifiSupplicantState.NAME, i);
+ dumpTimer(proto, SystemProto.WifiSupplicantState.TOTAL, getWifiSupplStateTimer(i),
+ rawRealtimeUs, which);
+ proto.end(token);
+ }
+
+ proto.end(sToken);
+ }
}
diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl
index cc2af21..8271701 100644
--- a/core/java/android/os/IDeviceIdleController.aidl
+++ b/core/java/android/os/IDeviceIdleController.aidl
@@ -23,6 +23,11 @@
interface IDeviceIdleController {
void addPowerSaveWhitelistApp(String name);
void removePowerSaveWhitelistApp(String name);
+ /* Removes an app from the system whitelist. Calling restoreSystemPowerWhitelistApp will add
+ the app back into the system whitelist */
+ void removeSystemPowerWhitelistApp(String name);
+ void restoreSystemPowerWhitelistApp(String name);
+ String[] getRemovedSystemPowerWhitelistApps();
String[] getSystemPowerWhitelistExceptIdle();
String[] getSystemPowerWhitelist();
String[] getUserPowerWhitelist();
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index f8f2813..daacc4e 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -45,4 +45,20 @@
* Two-way binder call so that caller's method (and corresponding wakelocks) will linger.
*/
void informPollAlarmFired();
+
+ /**
+ * Inform statsd what the version and package are for each uid. Note that each array should
+ * have the same number of elements, and version[i] and package[i] correspond to uid[i].
+ */
+ oneway void informAllUidData(in int[] uid, in int[] version, in String[] app);
+
+ /**
+ * Inform statsd what the uid and version are for one app that was updated.
+ */
+ oneway void informOnePackage(in String app, in int uid, in int version);
+
+ /**
+ * Inform stats that an app was removed.
+ */
+ oneway void informOnePackageRemoved(in String app, in int uid);
}
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index c091420..7f588ad 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -737,7 +737,9 @@
private void closeWithStatus(int status, String msg) {
if (mClosed) return;
mClosed = true;
- mGuard.close();
+ if (mGuard != null) {
+ mGuard.close();
+ }
// Status MUST be sent before closing actual descriptor
writeCommStatusAndClose(status, msg);
IoUtils.closeQuietly(mFd);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 430a5e3..8c68871 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -574,6 +574,25 @@
public static final String DISALLOW_CREATE_WINDOWS = "no_create_windows";
/**
+ * Specifies that system error dialogs for crashed or unresponsive apps should not be shown.
+ * In this case, the system will force-stop the app as if the user chooses the "close app"
+ * option on the UI. No feedback report will be collected as there is no way for the user to
+ * provide explicit consent.
+ *
+ * When this user restriction is set by device owners, it's applied to all users; when it's set
+ * by profile owners, it's only applied to the relevant profiles.
+ * The default value is <code>false</code>.
+ *
+ * <p>This user restriction has no effect on managed profiles.
+ * <p>Key for user restrictions.
+ * <p>Type: Boolean
+ * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+ * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+ * @see #getUserRestrictions()
+ */
+ public static final String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
+
+ /**
* Specifies if what is copied in the clipboard of this profile can
* be pasted in related profiles. Does not restrict if the clipboard of related profiles can be
* pasted in this profile.
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index ee8eed1..3d2e1d1 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -206,8 +206,7 @@
try {
mRingtone.setAudioAttributes(new AudioAttributes.Builder(mRingtone
.getAudioAttributes())
- .setFlags(AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY |
- AudioAttributes.FLAG_BYPASS_MUTE)
+ .setFlags(AudioAttributes.FLAG_BYPASS_MUTE)
.build());
mRingtone.play();
} catch (Throwable e) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index eca2fcd..a062db4 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9369,16 +9369,6 @@
public static final String DEVICE_IDLE_CONSTANTS = "device_idle_constants";
/**
- * Device Idle (Doze) specific settings for watches. See {@code #DEVICE_IDLE_CONSTANTS}
- *
- * <p>
- * Type: string
- * @hide
- * @see com.android.server.DeviceIdleController.Constants
- */
- public static final String DEVICE_IDLE_CONSTANTS_WATCH = "device_idle_constants_watch";
-
- /**
* Battery Saver specific settings
* This is encoded as a key=value list, separated by commas. Ex:
*
@@ -9404,9 +9394,12 @@
/**
* Battery anomaly detection specific settings
- * This is encoded as a key=value list, separated by commas. Ex:
+ * This is encoded as a key=value list, separated by commas.
+ * wakeup_blacklisted_tags is a string, encoded as a set of tags, encoded via
+ * {@link Uri#encode(String)}, separated by colons. Ex:
*
- * "anomaly_detection_enabled=true,wakelock_threshold=2000"
+ * "anomaly_detection_enabled=true,wakelock_threshold=2000,wakeup_alarm_enabled=true,"
+ * "wakeup_alarm_threshold=10,wakeup_blacklisted_tags=tag1:tag2:with%2Ccomma:with%3Acolon"
*
* The following keys are supported:
*
@@ -9414,6 +9407,11 @@
* anomaly_detection_enabled (boolean)
* wakelock_enabled (boolean)
* wakelock_threshold (long)
+ * wakeup_alarm_enabled (boolean)
+ * wakeup_alarm_threshold (long)
+ * wakeup_blacklisted_tags (string)
+ * bluetooth_scan_enabled (boolean)
+ * bluetooth_scan_threshold (long)
* </pre>
* @hide
*/
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 1521e7e..9a25f5b 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -187,7 +187,7 @@
* protect a dataset that contains sensitive information by requiring dataset authentication
* (see {@link Dataset.Builder#setAuthentication(android.content.IntentSender)}), and to include
* info about the "primary" field of the partition in the custom presentation for "secondary"
- * fields — that would prevent a malicious app from getting the "primary" fields without the
+ * fields—that would prevent a malicious app from getting the "primary" fields without the
* user realizing they're being released (for example, a malicious app could have fields for a
* credit card number, verification code, and expiration date crafted in a way that just the latter
* is visible; by explicitly indicating the expiration date is related to a given credit card
@@ -365,6 +365,81 @@
* <p><b>Note:</b> The autofill service could also whitelist well-known browser apps and skip the
* verifications above, as long as the service can verify the authenticity of the browser app by
* checking its signing certificate.
+ *
+ * <a name="MultipleStepsSave"></a>
+ * <h3>Saving when data is split in multiple screens</h3>
+ *
+ * Apps often split the user data in multiple screens in the same activity, specially in
+ * activities used to create a new user account. For example, the first screen asks for a username,
+ * and if the username is available, it moves to a second screen, which asks for a password.
+ *
+ * <p>It's tricky to handle save for autofill in these situations, because the autofill service must
+ * wait until the user enters both fields before the autofill save UI can be shown. But it can be
+ * done by following the steps below:
+ *
+ * <ol>
+ * <li>In the first
+ * {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback) fill request}, the service
+ * adds a {@link FillResponse.Builder#setClientState(android.os.Bundle) client state bundle} in
+ * the response, containing the autofill ids of the partial fields present in the screen.
+ * <li>In the second
+ * {@link #onFillRequest(FillRequest, CancellationSignal, FillCallback) fill request}, the service
+ * retrieves the {@link FillRequest#getClientState() client state bundle}, gets the autofill ids
+ * set in the previous request from the client state, and adds these ids and the
+ * {@link SaveInfo#FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE} to the {@link SaveInfo} used in the second
+ * response.
+ * <li>In the {@link #onSaveRequest(SaveRequest, SaveCallback) save request}, the service uses the
+ * proper {@link FillContext fill contexts} to get the value of each field (there is one fill
+ * context per fill request).
+ * </ol>
+ *
+ * <p>For example, in an app that uses 2 steps for the username and password fields, the workflow
+ * would be:
+ * <pre class="prettyprint">
+ * // On first fill request
+ * AutofillId usernameId = // parse from AssistStructure;
+ * Bundle clientState = new Bundle();
+ * clientState.putParcelable("usernameId", usernameId);
+ * fillCallback.onSuccess(
+ * new FillResponse.Builder()
+ * .setClientState(clientState)
+ * .setSaveInfo(new SaveInfo
+ * .Builder(SaveInfo.SAVE_DATA_TYPE_USERNAME, new AutofillId[] {usernameId})
+ * .build())
+ * .build());
+ *
+ * // On second fill request
+ * Bundle clientState = fillRequest.getClientState();
+ * AutofillId usernameId = clientState.getParcelable("usernameId");
+ * AutofillId passwordId = // parse from AssistStructure
+ * clientState.putParcelable("passwordId", passwordId);
+ * fillCallback.onSuccess(
+ * new FillResponse.Builder()
+ * .setClientState(clientState)
+ * .setSaveInfo(new SaveInfo
+ * .Builder(SaveInfo.SAVE_DATA_TYPE_USERNAME | SaveInfo.SAVE_DATA_TYPE_PASSWORD,
+ * new AutofillId[] {usernameId, passwordId})
+ * .setFlags(SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE)
+ * .build())
+ * .build());
+ *
+ * // On save request
+ * Bundle clientState = saveRequest.getClientState();
+ * AutofillId usernameId = clientState.getParcelable("usernameId");
+ * AutofillId passwordId = clientState.getParcelable("passwordId");
+ * List<FillContext> fillContexts = saveRequest.getFillContexts();
+ *
+ * FillContext usernameContext = fillContexts.get(0);
+ * ViewNode usernameNode = findNodeByAutofillId(usernameContext.getStructure(), usernameId);
+ * AutofillValue username = usernameNode.getAutofillValue().getTextValue().toString();
+ *
+ * FillContext passwordContext = fillContexts.get(1);
+ * ViewNode passwordNode = findNodeByAutofillId(passwordContext.getStructure(), passwordId);
+ * AutofillValue password = passwordNode.getAutofillValue().getTextValue().toString();
+ *
+ * save(username, password);
+ *
+ * </pre>
*/
public abstract class AutofillService extends Service {
private static final String TAG = "AutofillService";
diff --git a/core/java/android/service/autofill/ImageTransformation.java b/core/java/android/service/autofill/ImageTransformation.java
index 2151f74..4afda24 100644
--- a/core/java/android/service/autofill/ImageTransformation.java
+++ b/core/java/android/service/autofill/ImageTransformation.java
@@ -20,11 +20,12 @@
import android.annotation.DrawableRes;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import android.util.Log;
-import android.util.Pair;
import android.view.autofill.AutofillId;
import android.widget.ImageView;
import android.widget.RemoteViews;
@@ -43,9 +44,9 @@
*
* <pre class="prettyprint">
* new ImageTransformation.Builder(ccNumberId, Pattern.compile("^4815.*$"),
- * R.drawable.ic_credit_card_logo1)
- * .addOption(Pattern.compile("^1623.*$"), R.drawable.ic_credit_card_logo2)
- * .addOption(Pattern.compile("^42.*$"), R.drawable.ic_credit_card_logo3)
+ * R.drawable.ic_credit_card_logo1, "Brand 1")
+ * .addOption(Pattern.compile("^1623.*$"), R.drawable.ic_credit_card_logo2, "Brand 2")
+ * .addOption(Pattern.compile("^42.*$"), R.drawable.ic_credit_card_logo3, "Brand 3")
* .build();
* </pre>
*
@@ -59,7 +60,7 @@
private static final String TAG = "ImageTransformation";
private final AutofillId mId;
- private final ArrayList<Pair<Pattern, Integer>> mOptions;
+ private final ArrayList<Option> mOptions;
private ImageTransformation(Builder builder) {
mId = builder.mId;
@@ -82,17 +83,21 @@
}
for (int i = 0; i < size; i++) {
- final Pair<Pattern, Integer> option = mOptions.get(i);
+ final Option option = mOptions.get(i);
try {
- if (option.first.matcher(value).matches()) {
+ if (option.pattern.matcher(value).matches()) {
Log.d(TAG, "Found match at " + i + ": " + option);
- parentTemplate.setImageViewResource(childViewId, option.second);
+ parentTemplate.setImageViewResource(childViewId, option.resId);
+ if (option.contentDescription != null) {
+ parentTemplate.setContentDescription(childViewId,
+ option.contentDescription);
+ }
return;
}
} catch (Exception e) {
// Do not log full exception to avoid PII leaking
- Log.w(TAG, "Error matching regex #" + i + "(" + option.first.pattern() + ") on id "
- + option.second + ": " + e.getClass());
+ Log.w(TAG, "Error matching regex #" + i + "(" + option.pattern + ") on id "
+ + option.resId + ": " + e.getClass());
throw e;
}
@@ -105,25 +110,44 @@
*/
public static class Builder {
private final AutofillId mId;
- private final ArrayList<Pair<Pattern, Integer>> mOptions = new ArrayList<>();
+ private final ArrayList<Option> mOptions = new ArrayList<>();
private boolean mDestroyed;
/**
- * Create a new builder for a autofill id and add a first option.
+ * Creates a new builder for a autofill id and add a first option.
*
* @param id id of the screen field that will be used to evaluate whether the image should
* be used.
* @param regex regular expression defining what should be matched to use this image.
* @param resId resource id of the image (in the autofill service's package). The
* {@link RemoteViews presentation} must contain a {@link ImageView} child with that id.
+ *
+ * @deprecated use
+ * {@link #ImageTransformation.Builder(AutofillId, Pattern, int, CharSequence)} instead.
*/
+ @Deprecated
public Builder(@NonNull AutofillId id, @NonNull Pattern regex, @DrawableRes int resId) {
mId = Preconditions.checkNotNull(id);
-
addOption(regex, resId);
}
/**
+ * Creates a new builder for a autofill id and add a first option.
+ *
+ * @param id id of the screen field that will be used to evaluate whether the image should
+ * be used.
+ * @param regex regular expression defining what should be matched to use this image.
+ * @param resId resource id of the image (in the autofill service's package). The
+ * {@link RemoteViews presentation} must contain a {@link ImageView} child with that id.
+ * @param contentDescription content description to be applied in the child view.
+ */
+ public Builder(@NonNull AutofillId id, @NonNull Pattern regex, @DrawableRes int resId,
+ @NonNull CharSequence contentDescription) {
+ mId = Preconditions.checkNotNull(id);
+ addOption(regex, resId, contentDescription);
+ }
+
+ /**
* Adds an option to replace the child view with a different image when the regex matches.
*
* @param regex regular expression defining what should be matched to use this image.
@@ -131,17 +155,43 @@
* {@link RemoteViews presentation} must contain a {@link ImageView} child with that id.
*
* @return this build
+ *
+ * @deprecated use {@link #addOption(Pattern, int, CharSequence)} instead.
*/
+ @Deprecated
public Builder addOption(@NonNull Pattern regex, @DrawableRes int resId) {
+ addOptionInternal(regex, resId, null);
+ return this;
+ }
+
+ /**
+ * Adds an option to replace the child view with a different image and content description
+ * when the regex matches.
+ *
+ * @param regex regular expression defining what should be matched to use this image.
+ * @param resId resource id of the image (in the autofill service's package). The
+ * {@link RemoteViews presentation} must contain a {@link ImageView} child with that id.
+ * @param contentDescription content description to be applied in the child view.
+ *
+ * @return this build
+ */
+ public Builder addOption(@NonNull Pattern regex, @DrawableRes int resId,
+ @NonNull CharSequence contentDescription) {
+ addOptionInternal(regex, resId, Preconditions.checkNotNull(contentDescription));
+ return this;
+ }
+
+ private void addOptionInternal(@NonNull Pattern regex, @DrawableRes int resId,
+ @Nullable CharSequence contentDescription) {
throwIfDestroyed();
Preconditions.checkNotNull(regex);
Preconditions.checkArgument(resId != 0);
- mOptions.add(new Pair<>(regex, resId));
- return this;
+ mOptions.add(new Option(regex, resId, contentDescription));
}
+
/**
* Creates a new {@link ImageTransformation} instance.
*/
@@ -178,15 +228,18 @@
parcel.writeParcelable(mId, flags);
final int size = mOptions.size();
- final Pattern[] regexs = new Pattern[size];
+ final Pattern[] patterns = new Pattern[size];
final int[] resIds = new int[size];
+ final CharSequence[] contentDescriptions = new String[size];
for (int i = 0; i < size; i++) {
- Pair<Pattern, Integer> regex = mOptions.get(i);
- regexs[i] = regex.first;
- resIds[i] = regex.second;
+ final Option option = mOptions.get(i);
+ patterns[i] = option.pattern;
+ resIds[i] = option.resId;
+ contentDescriptions[i] = option.contentDescription;
}
- parcel.writeSerializable(regexs);
+ parcel.writeSerializable(patterns);
parcel.writeIntArray(resIds);
+ parcel.writeCharSequenceArray(contentDescriptions);
}
public static final Parcelable.Creator<ImageTransformation> CREATOR =
@@ -197,15 +250,22 @@
final Pattern[] regexs = (Pattern[]) parcel.readSerializable();
final int[] resIds = parcel.createIntArray();
+ final CharSequence[] contentDescriptions = parcel.readCharSequenceArray();
// Always go through the builder to ensure the data ingested by the system obeys the
// contract of the builder to avoid attacks using specially crafted parcels.
- final ImageTransformation.Builder builder = new ImageTransformation.Builder(id,
- regexs[0], resIds[0]);
+ final CharSequence contentDescription = contentDescriptions[0];
+ final ImageTransformation.Builder builder = (contentDescription != null)
+ ? new ImageTransformation.Builder(id, regexs[0], resIds[0], contentDescription)
+ : new ImageTransformation.Builder(id, regexs[0], resIds[0]);
final int size = regexs.length;
for (int i = 1; i < size; i++) {
- builder.addOption(regexs[i], resIds[i]);
+ if (contentDescriptions[i] != null) {
+ builder.addOption(regexs[i], resIds[i], contentDescriptions[i]);
+ } else {
+ builder.addOption(regexs[i], resIds[i]);
+ }
}
return builder.build();
@@ -216,4 +276,16 @@
return new ImageTransformation[size];
}
};
+
+ private static final class Option {
+ public final Pattern pattern;
+ public final int resId;
+ public final CharSequence contentDescription;
+
+ Option(Pattern pattern, int resId, CharSequence contentDescription) {
+ this.pattern = pattern;
+ this.resId = resId;
+ this.contentDescription = TextUtils.trimNoCopySpans(contentDescription);
+ }
+ }
}
diff --git a/core/java/android/service/autofill/InternalSanitizer.java b/core/java/android/service/autofill/InternalSanitizer.java
new file mode 100644
index 0000000..95d2f66
--- /dev/null
+++ b/core/java/android/service/autofill/InternalSanitizer.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcelable;
+import android.view.autofill.AutofillValue;
+
+/**
+ * Superclass of all sanitizers the system understands. As this is not public all public subclasses
+ * have to implement {@link Sanitizer} again.
+ *
+ * @hide
+ */
+@TestApi
+public abstract class InternalSanitizer implements Sanitizer, Parcelable {
+
+ /**
+ * Sanitizes an {@link AutofillValue}.
+ *
+ * @hide
+ */
+ public abstract AutofillValue sanitize(@NonNull AutofillValue value);
+}
diff --git a/core/java/android/service/autofill/Sanitizer.java b/core/java/android/service/autofill/Sanitizer.java
new file mode 100644
index 0000000..38757ac
--- /dev/null
+++ b/core/java/android/service/autofill/Sanitizer.java
@@ -0,0 +1,26 @@
+/*
+ * 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.service.autofill;
+
+/**
+ * Helper class used to sanitize user input before using it in a save request.
+ *
+ * <p>Typically used to avoid displaying the save UI for values that are autofilled but reformatted
+ * by the app—for example, if the autofill service sends a credit card number
+ * value as "004815162342108" and the app automatically changes it to "0048 1516 2342 108".
+ */
+public interface Sanitizer {
+}
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index e0a0730..1b9240c 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -25,6 +25,8 @@
import android.content.IntentSender;
import android.os.Parcel;
import android.os.Parcelable;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.DebugUtils;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
@@ -232,6 +234,8 @@
private final int mFlags;
private final CustomDescription mCustomDescription;
private final InternalValidator mValidator;
+ private final InternalSanitizer[] mSanitizerKeys;
+ private final AutofillId[][] mSanitizerValues;
private SaveInfo(Builder builder) {
mType = builder.mType;
@@ -243,6 +247,18 @@
mFlags = builder.mFlags;
mCustomDescription = builder.mCustomDescription;
mValidator = builder.mValidator;
+ if (builder.mSanitizers == null) {
+ mSanitizerKeys = null;
+ mSanitizerValues = null;
+ } else {
+ final int size = builder.mSanitizers.size();
+ mSanitizerKeys = new InternalSanitizer[size];
+ mSanitizerValues = new AutofillId[size][];
+ for (int i = 0; i < size; i++) {
+ mSanitizerKeys[i] = builder.mSanitizers.keyAt(i);
+ mSanitizerValues[i] = builder.mSanitizers.valueAt(i);
+ }
+ }
}
/** @hide */
@@ -292,6 +308,18 @@
return mValidator;
}
+ /** @hide */
+ @Nullable
+ public InternalSanitizer[] getSanitizerKeys() {
+ return mSanitizerKeys;
+ }
+
+ /** @hide */
+ @Nullable
+ public AutofillId[][] getSanitizerValues() {
+ return mSanitizerValues;
+ }
+
/**
* A builder for {@link SaveInfo} objects.
*/
@@ -307,6 +335,9 @@
private int mFlags;
private CustomDescription mCustomDescription;
private InternalValidator mValidator;
+ private ArrayMap<InternalSanitizer, AutofillId[]> mSanitizers;
+ // Set used to validate against duplicate ids.
+ private ArraySet<AutofillId> mSanitizerIds;
/**
* Creates a new builder.
@@ -530,6 +561,61 @@
}
/**
+ * Adds a sanitizer for one or more field.
+ *
+ * <p>When a sanitizer is set for a field, the {@link AutofillValue} is sent to the
+ * sanitizer before a save request is <a href="#TriggeringSaveRequest">triggered</a>.
+ *
+ * <p>Typically used to avoid displaying the save UI for values that are autofilled but
+ * reformattedby the app. For example, to remove spaces between every 4 digits of a
+ * credit card number:
+ *
+ * <pre class="prettyprint">
+ * builder.addSanitizer(
+ * new TextValueSanitizer(Pattern.compile("^(\\d{4}\s?\\d{4}\s?\\d{4}\s?\\d{4})$"),
+ * "$1$2$3$4"), ccNumberId);
+ * </pre>
+ *
+ * <p>The same sanitizer can be reused to sanitize multiple fields. For example, to trim
+ * both the username and password fields:
+ *
+ * <pre class="prettyprint">
+ * builder.addSanitizer(
+ * new TextValueSanitizer(Pattern.compile("^\\s*(.*)\\s*$"), "$1"),
+ * usernameId, passwordId);
+ * </pre>
+ *
+ * @param sanitizer an implementation provided by the Android System.
+ * @param ids id of fields whose value will be sanitized.
+ * @return this builder.
+ *
+ * @throws IllegalArgumentException if a sanitizer for any of the {@code ids} has already
+ * been added or if {@code ids} is empty.
+ */
+ public @NonNull Builder addSanitizer(@NonNull Sanitizer sanitizer,
+ @NonNull AutofillId... ids) {
+ throwIfDestroyed();
+ Preconditions.checkArgument(!ArrayUtils.isEmpty(ids), "ids cannot be empty or null");
+ Preconditions.checkArgument((sanitizer instanceof InternalSanitizer),
+ "not provided by Android System: " + sanitizer);
+
+ if (mSanitizers == null) {
+ mSanitizers = new ArrayMap<>();
+ mSanitizerIds = new ArraySet<>(ids.length);
+ }
+
+ // Check for duplicates first.
+ for (AutofillId id : ids) {
+ Preconditions.checkArgument(!mSanitizerIds.contains(id), "already added %s", id);
+ mSanitizerIds.add(id);
+ }
+
+ mSanitizers.put((InternalSanitizer) sanitizer, ids);
+
+ return this;
+ }
+
+ /**
* Builds a new {@link SaveInfo} instance.
*
* @throws IllegalStateException if no
@@ -569,6 +655,10 @@
.append(", mFlags=").append(mFlags)
.append(", mCustomDescription=").append(mCustomDescription)
.append(", validation=").append(mValidator)
+ .append(", sanitizerKeys=")
+ .append(mSanitizerKeys == null ? "N/A:" : mSanitizerKeys.length)
+ .append(", sanitizerValues=")
+ .append(mSanitizerValues == null ? "N/A:" : mSanitizerValues.length)
.append("]").toString();
}
@@ -591,6 +681,12 @@
parcel.writeCharSequence(mDescription);
parcel.writeParcelable(mCustomDescription, flags);
parcel.writeParcelable(mValidator, flags);
+ parcel.writeParcelableArray(mSanitizerKeys, flags);
+ if (mSanitizerKeys != null) {
+ for (int i = 0; i < mSanitizerValues.length; i++) {
+ parcel.writeParcelableArray(mSanitizerValues[i], flags);
+ }
+ }
parcel.writeInt(mFlags);
}
@@ -621,6 +717,16 @@
if (validator != null) {
builder.setValidator(validator);
}
+ final InternalSanitizer[] sanitizers =
+ parcel.readParcelableArray(null, InternalSanitizer.class);
+ if (sanitizers != null) {
+ final int size = sanitizers.length;
+ for (int i = 0; i < size; i++) {
+ final AutofillId[] autofillIds =
+ parcel.readParcelableArray(null, AutofillId.class);
+ builder.addSanitizer(sanitizers[i], autofillIds);
+ }
+ }
builder.setFlags(parcel.readInt());
return builder.build();
}
diff --git a/core/java/android/service/autofill/TextValueSanitizer.java b/core/java/android/service/autofill/TextValueSanitizer.java
new file mode 100644
index 0000000..12e85b1
--- /dev/null
+++ b/core/java/android/service/autofill/TextValueSanitizer.java
@@ -0,0 +1,122 @@
+/*
+ * 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.service.autofill;
+
+import static android.view.autofill.Helper.sDebug;
+
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Slog;
+import android.view.autofill.AutofillValue;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Sanitizes a text {@link AutofillValue} using a regular expression (regex) substitution.
+ *
+ * <p>For example, to remove spaces from groups of 4-digits in a credit card:
+ *
+ * <pre class="prettyprint">
+ * new TextValueSanitizer(Pattern.compile("^(\\d{4}\s?\\d{4}\s?\\d{4}\s?\\d{4})$"), "$1$2$3$4")
+ * </pre>
+ */
+public final class TextValueSanitizer extends InternalSanitizer implements
+ Sanitizer, Parcelable {
+ private static final String TAG = "TextValueSanitizer";
+
+ private final Pattern mRegex;
+ private final String mSubst;
+
+ /**
+ * Default constructor.
+ *
+ * @param regex regular expression with groups (delimited by {@code (} and {@code (}) that
+ * are used to substitute parts of the {@link AutofillValue#getTextValue() text value}.
+ * @param subst the string that substitutes the matched regex, using {@code $} for
+ * group substitution ({@code $1} for 1st group match, {@code $2} for 2nd, etc).
+ */
+ public TextValueSanitizer(@NonNull Pattern regex, @NonNull String subst) {
+ mRegex = Preconditions.checkNotNull(regex);
+ mSubst = Preconditions.checkNotNull(subst);
+ }
+
+ /** @hide */
+ @Override
+ @TestApi
+ public AutofillValue sanitize(@NonNull AutofillValue value) {
+ if (value == null) {
+ Slog.w(TAG, "sanitize() called with null value");
+ return null;
+ }
+ if (!value.isText()) return value;
+
+ final CharSequence text = value.getTextValue();
+
+ try {
+ final Matcher matcher = mRegex.matcher(text);
+ if (!matcher.matches()) return value;
+
+ final CharSequence sanitized = matcher.replaceAll(mSubst);
+ return AutofillValue.forText(sanitized);
+ } catch (Exception e) {
+ Slog.w(TAG, "Exception evaluating " + mRegex + "/" + mSubst + ": " + e);
+ return value;
+ }
+ }
+
+ /////////////////////////////////////
+ // Object "contract" methods. //
+ /////////////////////////////////////
+ @Override
+ public String toString() {
+ if (!sDebug) return super.toString();
+
+ return "TextValueSanitizer: [regex=" + mRegex + ", subst=" + mSubst + "]";
+ }
+
+ /////////////////////////////////////
+ // Parcelable "contract" methods. //
+ /////////////////////////////////////
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeSerializable(mRegex);
+ parcel.writeString(mSubst);
+ }
+
+ public static final Parcelable.Creator<TextValueSanitizer> CREATOR =
+ new Parcelable.Creator<TextValueSanitizer>() {
+ @Override
+ public TextValueSanitizer createFromParcel(Parcel parcel) {
+ return new TextValueSanitizer((Pattern) parcel.readSerializable(), parcel.readString());
+ }
+
+ @Override
+ public TextValueSanitizer[] newArray(int size) {
+ return new TextValueSanitizer[size];
+ }
+ };
+}
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index ce678fc..7348cf6 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -56,6 +56,15 @@
public static final String KEY_GROUP_KEY = "key_group_key";
/**
+ * Data type: int, one of {@link NotificationListenerService.Ranking#USER_SENTIMENT_POSITIVE},
+ * {@link NotificationListenerService.Ranking#USER_SENTIMENT_NEUTRAL},
+ * {@link NotificationListenerService.Ranking#USER_SENTIMENT_NEGATIVE}. Used to express how
+ * a user feels about notifications in the same {@link android.app.NotificationChannel} as
+ * the notification represented by {@link #getKey()}.
+ */
+ public static final String KEY_USER_SENTIMENT = "key_user_sentiment";
+
+ /**
* Create a notification adjustment.
*
* @param pkg The package of the notification.
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index ed44f25..c388367 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -19,6 +19,7 @@
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.os.UserHandle;
+import android.service.notification.NotificationStats;
import android.service.notification.IStatusBarNotificationHolder;
import android.service.notification.StatusBarNotification;
import android.service.notification.NotificationRankingUpdate;
@@ -26,12 +27,13 @@
/** @hide */
oneway interface INotificationListener
{
- // listeners and rankers
+ // listeners and assistant
void onListenerConnected(in NotificationRankingUpdate update);
void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder,
in NotificationRankingUpdate update);
+ // stats only for assistant
void onNotificationRemoved(in IStatusBarNotificationHolder notificationHolder,
- in NotificationRankingUpdate update, int reason);
+ in NotificationRankingUpdate update, in NotificationStats stats, int reason);
void onNotificationRankingUpdate(in NotificationRankingUpdate update);
void onListenerHintsChanged(int hints);
void onInterruptionFilterChanged(int interruptionFilter);
@@ -40,7 +42,7 @@
void onNotificationChannelModification(String pkgName, in UserHandle user, in NotificationChannel channel, int modificationType);
void onNotificationChannelGroupModification(String pkgName, in UserHandle user, in NotificationChannelGroup group, int modificationType);
- // rankers only
+ // assistants only
void onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder);
void onNotificationSnoozedUntilContext(in IStatusBarNotificationHolder notificationHolder, String snoozeCriterionId);
}
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index d94017c..8e52bfa 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -16,12 +16,9 @@
package android.service.notification;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.app.NotificationChannel;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
@@ -30,9 +27,9 @@
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
+
import com.android.internal.os.SomeArgs;
-import java.util.ArrayList;
import java.util.List;
/**
@@ -79,7 +76,7 @@
String snoozeCriterionId);
/**
- * A notification was posted by an app. Called before alert.
+ * A notification was posted by an app. Called before post.
*
* @param sbn the new notification
* @return an adjustment or null to take no action, within 100ms.
@@ -87,6 +84,34 @@
abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn);
/**
+ * Implement this method to learn when notifications are removed, how they were interacted with
+ * before removal, and why they were removed.
+ * <p>
+ * This might occur because the user has dismissed the notification using system UI (or another
+ * notification listener) or because the app has withdrawn the notification.
+ * <p>
+ * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the
+ * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight
+ * fields such as {@link android.app.Notification#contentView} and
+ * {@link android.app.Notification#largeIcon}. However, all other fields on
+ * {@link StatusBarNotification}, sufficient to match this call with a prior call to
+ * {@link #onNotificationPosted(StatusBarNotification)}, will be intact.
+ *
+ ** @param sbn A data structure encapsulating at least the original information (tag and id)
+ * and source (package name) used to post the {@link android.app.Notification} that
+ * was just removed.
+ * @param rankingMap The current ranking map that can be used to retrieve ranking information
+ * for active notifications.
+ * @param stats Stats about how the user interacted with the notification before it was removed.
+ * @param reason see {@link #REASON_LISTENER_CANCEL}, etc.
+ */
+ @Override
+ public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
+ NotificationStats stats, int reason) {
+ onNotificationRemoved(sbn, rankingMap, reason);
+ }
+
+ /**
* Updates a notification. N.B. this won’t cause
* an existing notification to alert, but might allow a future update to
* this notification to alert.
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index a5223fd..08d3118 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.app.INotificationManager;
import android.app.Notification;
import android.app.Notification.Builder;
@@ -265,7 +266,10 @@
@GuardedBy("mLock")
private RankingMap mRankingMap;
- private INotificationManager mNoMan;
+ /**
+ * @hide
+ */
+ protected INotificationManager mNoMan;
/**
* Only valid after a successful call to (@link registerAsService}.
@@ -389,6 +393,18 @@
}
/**
+ * NotificationStats are not populated for notification listeners, so fall back to
+ * {@link #onNotificationRemoved(StatusBarNotification, RankingMap, int)}.
+ *
+ * @hide
+ */
+ @TestApi
+ public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
+ NotificationStats stats, int reason) {
+ onNotificationRemoved(sbn, rankingMap, reason);
+ }
+
+ /**
* Implement this method to learn about when the listener is enabled and connected to
* the notification manager. You are safe to call {@link #getActiveNotifications()}
* at this time.
@@ -1200,7 +1216,7 @@
@Override
public void onNotificationRemoved(IStatusBarNotificationHolder sbnHolder,
- NotificationRankingUpdate update, int reason) {
+ NotificationRankingUpdate update, NotificationStats stats, int reason) {
StatusBarNotification sbn;
try {
sbn = sbnHolder.get();
@@ -1215,6 +1231,7 @@
args.arg1 = sbn;
args.arg2 = mRankingMap;
args.arg3 = reason;
+ args.arg4 = stats;
mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_REMOVED,
args).sendToTarget();
}
@@ -1324,6 +1341,26 @@
* @hide */
public static final int VISIBILITY_NO_OVERRIDE = NotificationManager.VISIBILITY_NO_OVERRIDE;
+ /**
+ * The user is likely to have a negative reaction to this notification.
+ */
+ public static final int USER_SENTIMENT_NEGATIVE = -1;
+ /**
+ * It is not known how the user will react to this notification.
+ */
+ public static final int USER_SENTIMENT_NEUTRAL = 0;
+ /**
+ * The user is likely to have a positive reaction to this notification.
+ */
+ public static final int USER_SENTIMENT_POSITIVE = 1;
+
+ /** @hide */
+ @IntDef(prefix = { "USER_SENTIMENT_" }, value = {
+ USER_SENTIMENT_NEGATIVE, USER_SENTIMENT_NEUTRAL, USER_SENTIMENT_POSITIVE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UserSentiment {}
+
private String mKey;
private int mRank = -1;
private boolean mIsAmbient;
@@ -1341,6 +1378,7 @@
// Notification assistant snooze criteria.
private ArrayList<SnoozeCriterion> mSnoozeCriteria;
private boolean mShowBadge;
+ private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL;
public Ranking() {}
@@ -1436,6 +1474,17 @@
}
/**
+ * Returns how the system thinks the user feels about notifications from the
+ * channel provided by {@link #getChannel()}. You can use this information to expose
+ * controls to help the user block this channel's notifications, if the sentiment is
+ * {@link #USER_SENTIMENT_NEGATIVE}, or emphasize this notification if the sentiment is
+ * {@link #USER_SENTIMENT_POSITIVE}.
+ */
+ public int getUserSentiment() {
+ return mUserSentiment;
+ }
+
+ /**
* If the {@link NotificationAssistantService} has added people to this notification, then
* this will be non-null.
* @hide
@@ -1471,7 +1520,8 @@
int visibilityOverride, int suppressedVisualEffects, int importance,
CharSequence explanation, String overrideGroupKey,
NotificationChannel channel, ArrayList<String> overridePeople,
- ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge) {
+ ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
+ int userSentiment) {
mKey = key;
mRank = rank;
mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1485,6 +1535,7 @@
mOverridePeople = overridePeople;
mSnoozeCriteria = snoozeCriteria;
mShowBadge = showBadge;
+ mUserSentiment = userSentiment;
}
/**
@@ -1532,6 +1583,7 @@
private ArrayMap<String, ArrayList<String>> mOverridePeople;
private ArrayMap<String, ArrayList<SnoozeCriterion>> mSnoozeCriteria;
private ArrayMap<String, Boolean> mShowBadge;
+ private ArrayMap<String, Integer> mUserSentiment;
private RankingMap(NotificationRankingUpdate rankingUpdate) {
mRankingUpdate = rankingUpdate;
@@ -1560,7 +1612,7 @@
getVisibilityOverride(key), getSuppressedVisualEffects(key),
getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
getChannel(key), getOverridePeople(key), getSnoozeCriteria(key),
- getShowBadge(key));
+ getShowBadge(key), getUserSentiment(key));
return rank >= 0;
}
@@ -1677,6 +1729,17 @@
return showBadge == null ? false : showBadge.booleanValue();
}
+ private int getUserSentiment(String key) {
+ synchronized (this) {
+ if (mUserSentiment == null) {
+ buildUserSentimentLocked();
+ }
+ }
+ Integer userSentiment = mUserSentiment.get(key);
+ return userSentiment == null
+ ? Ranking.USER_SENTIMENT_NEUTRAL : userSentiment.intValue();
+ }
+
// Locked by 'this'
private void buildRanksLocked() {
String[] orderedKeys = mRankingUpdate.getOrderedKeys();
@@ -1776,6 +1839,15 @@
}
}
+ // Locked by 'this'
+ private void buildUserSentimentLocked() {
+ Bundle userSentiment = mRankingUpdate.getUserSentiment();
+ mUserSentiment = new ArrayMap<>(userSentiment.size());
+ for (String key : userSentiment.keySet()) {
+ mUserSentiment.put(key, userSentiment.getInt(key));
+ }
+ }
+
// ----------- Parcelable
@Override
@@ -1835,8 +1907,9 @@
StatusBarNotification sbn = (StatusBarNotification) args.arg1;
RankingMap rankingMap = (RankingMap) args.arg2;
int reason = (int) args.arg3;
+ NotificationStats stats = (NotificationStats) args.arg4;
args.recycle();
- onNotificationRemoved(sbn, rankingMap, reason);
+ onNotificationRemoved(sbn, rankingMap, stats, reason);
} break;
case MSG_ON_LISTENER_CONNECTED: {
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 326b212..6d51db0 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -35,12 +35,13 @@
private final Bundle mOverridePeople;
private final Bundle mSnoozeCriteria;
private final Bundle mShowBadge;
+ private final Bundle mUserSentiment;
public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
Bundle visibilityOverrides, Bundle suppressedVisualEffects,
int[] importance, Bundle explanation, Bundle overrideGroupKeys,
Bundle channels, Bundle overridePeople, Bundle snoozeCriteria,
- Bundle showBadge) {
+ Bundle showBadge, Bundle userSentiment) {
mKeys = keys;
mInterceptedKeys = interceptedKeys;
mVisibilityOverrides = visibilityOverrides;
@@ -52,6 +53,7 @@
mOverridePeople = overridePeople;
mSnoozeCriteria = snoozeCriteria;
mShowBadge = showBadge;
+ mUserSentiment = userSentiment;
}
public NotificationRankingUpdate(Parcel in) {
@@ -67,6 +69,7 @@
mOverridePeople = in.readBundle();
mSnoozeCriteria = in.readBundle();
mShowBadge = in.readBundle();
+ mUserSentiment = in.readBundle();
}
@Override
@@ -87,6 +90,7 @@
out.writeBundle(mOverridePeople);
out.writeBundle(mSnoozeCriteria);
out.writeBundle(mShowBadge);
+ out.writeBundle(mUserSentiment);
}
public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -143,4 +147,8 @@
public Bundle getShowBadge() {
return mShowBadge;
}
+
+ public Bundle getUserSentiment() {
+ return mUserSentiment;
+ }
}
diff --git a/core/java/android/service/notification/NotificationStats.aidl b/core/java/android/service/notification/NotificationStats.aidl
new file mode 100644
index 0000000..40f5548
--- /dev/null
+++ b/core/java/android/service/notification/NotificationStats.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.service.notification;
+
+parcelable NotificationStats;
\ No newline at end of file
diff --git a/core/java/android/service/notification/NotificationStats.java b/core/java/android/service/notification/NotificationStats.java
new file mode 100644
index 0000000..76d5328
--- /dev/null
+++ b/core/java/android/service/notification/NotificationStats.java
@@ -0,0 +1,256 @@
+/**
+ * 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.service.notification;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.app.RemoteInput;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * @hide
+ */
+@TestApi
+@SystemApi
+public final class NotificationStats implements Parcelable {
+
+ private boolean mSeen;
+ private boolean mExpanded;
+ private boolean mDirectReplied;
+ private boolean mSnoozed;
+ private boolean mViewedSettings;
+ private boolean mInteracted;
+
+ /** @hide */
+ @IntDef(prefix = { "DISMISSAL_SURFACE_" }, value = {
+ DISMISSAL_NOT_DISMISSED, DISMISSAL_OTHER, DISMISSAL_PEEK, DISMISSAL_AOD, DISMISSAL_SHADE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DismissalSurface {}
+
+
+ private @DismissalSurface int mDismissalSurface = DISMISSAL_NOT_DISMISSED;
+
+ /**
+ * Notification has not been dismissed yet.
+ */
+ public static final int DISMISSAL_NOT_DISMISSED = -1;
+ /**
+ * Notification has been dismissed from a {@link NotificationListenerService} or the app
+ * itself.
+ */
+ public static final int DISMISSAL_OTHER = 0;
+ /**
+ * Notification has been dismissed while peeking.
+ */
+ public static final int DISMISSAL_PEEK = 1;
+ /**
+ * Notification has been dismissed from always on display.
+ */
+ public static final int DISMISSAL_AOD = 2;
+ /**
+ * Notification has been dismissed from the notification shade.
+ */
+ public static final int DISMISSAL_SHADE = 3;
+
+ public NotificationStats() {
+ }
+
+ protected NotificationStats(Parcel in) {
+ mSeen = in.readByte() != 0;
+ mExpanded = in.readByte() != 0;
+ mDirectReplied = in.readByte() != 0;
+ mSnoozed = in.readByte() != 0;
+ mViewedSettings = in.readByte() != 0;
+ mInteracted = in.readByte() != 0;
+ mDismissalSurface = in.readInt();
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeByte((byte) (mSeen ? 1 : 0));
+ dest.writeByte((byte) (mExpanded ? 1 : 0));
+ dest.writeByte((byte) (mDirectReplied ? 1 : 0));
+ dest.writeByte((byte) (mSnoozed ? 1 : 0));
+ dest.writeByte((byte) (mViewedSettings ? 1 : 0));
+ dest.writeByte((byte) (mInteracted ? 1 : 0));
+ dest.writeInt(mDismissalSurface);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<NotificationStats> CREATOR = new Creator<NotificationStats>() {
+ @Override
+ public NotificationStats createFromParcel(Parcel in) {
+ return new NotificationStats(in);
+ }
+
+ @Override
+ public NotificationStats[] newArray(int size) {
+ return new NotificationStats[size];
+ }
+ };
+
+ /**
+ * Returns whether the user has seen this notification at least once.
+ */
+ public boolean hasSeen() {
+ return mSeen;
+ }
+
+ /**
+ * Records that the user as seen this notification at least once.
+ */
+ public void setSeen() {
+ mSeen = true;
+ }
+
+ /**
+ * Returns whether the user has expanded this notification at least once.
+ */
+ public boolean hasExpanded() {
+ return mExpanded;
+ }
+
+ /**
+ * Records that the user has expanded this notification at least once.
+ */
+ public void setExpanded() {
+ mExpanded = true;
+ mInteracted = true;
+ }
+
+ /**
+ * Returns whether the user has replied to a notification that has a
+ * {@link android.app.Notification.Action.Builder#addRemoteInput(RemoteInput) direct reply} at
+ * least once.
+ */
+ public boolean hasDirectReplied() {
+ return mDirectReplied;
+ }
+
+ /**
+ * Records that the user has replied to a notification that has a
+ * {@link android.app.Notification.Action.Builder#addRemoteInput(RemoteInput) direct reply}
+ * at least once.
+ */
+ public void setDirectReplied() {
+ mDirectReplied = true;
+ mInteracted = true;
+ }
+
+ /**
+ * Returns whether the user has snoozed this notification at least once.
+ */
+ public boolean hasSnoozed() {
+ return mSnoozed;
+ }
+
+ /**
+ * Records that the user has snoozed this notification at least once.
+ */
+ public void setSnoozed() {
+ mSnoozed = true;
+ mInteracted = true;
+ }
+
+ /**
+ * Returns whether the user has viewed the in-shade settings for this notification at least
+ * once.
+ */
+ public boolean hasViewedSettings() {
+ return mViewedSettings;
+ }
+
+ /**
+ * Records that the user has viewed the in-shade settings for this notification at least once.
+ */
+ public void setViewedSettings() {
+ mViewedSettings = true;
+ mInteracted = true;
+ }
+
+ /**
+ * Returns whether the user has interacted with this notification beyond having viewed it.
+ */
+ public boolean hasInteracted() {
+ return mInteracted;
+ }
+
+ /**
+ * Returns from which surface the notification was dismissed.
+ */
+ public @DismissalSurface int getDismissalSurface() {
+ return mDismissalSurface;
+ }
+
+ /**
+ * Returns from which surface the notification was dismissed.
+ */
+ public void setDismissalSurface(@DismissalSurface int dismissalSurface) {
+ mDismissalSurface = dismissalSurface;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ NotificationStats that = (NotificationStats) o;
+
+ if (mSeen != that.mSeen) return false;
+ if (mExpanded != that.mExpanded) return false;
+ if (mDirectReplied != that.mDirectReplied) return false;
+ if (mSnoozed != that.mSnoozed) return false;
+ if (mViewedSettings != that.mViewedSettings) return false;
+ if (mInteracted != that.mInteracted) return false;
+ return mDismissalSurface == that.mDismissalSurface;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = (mSeen ? 1 : 0);
+ result = 31 * result + (mExpanded ? 1 : 0);
+ result = 31 * result + (mDirectReplied ? 1 : 0);
+ result = 31 * result + (mSnoozed ? 1 : 0);
+ result = 31 * result + (mViewedSettings ? 1 : 0);
+ result = 31 * result + (mInteracted ? 1 : 0);
+ result = 31 * result + mDismissalSurface;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("NotificationStats{");
+ sb.append("mSeen=").append(mSeen);
+ sb.append(", mExpanded=").append(mExpanded);
+ sb.append(", mDirectReplied=").append(mDirectReplied);
+ sb.append(", mSnoozed=").append(mSnoozed);
+ sb.append(", mViewedSettings=").append(mViewedSettings);
+ sb.append(", mInteracted=").append(mInteracted);
+ sb.append(", mDismissalSurface=").append(mDismissalSurface);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/core/java/android/slice/Slice.java b/core/java/android/slice/Slice.java
index bb810e6..5768654 100644
--- a/core/java/android/slice/Slice.java
+++ b/core/java/android/slice/Slice.java
@@ -35,6 +35,8 @@
import android.os.Parcelable;
import android.widget.RemoteViews;
+import com.android.internal.util.ArrayUtils;
+
import java.util.ArrayList;
import java.util.Arrays;
@@ -136,7 +138,7 @@
}
/**
- * @return The Uri that this slice represents.
+ * @return The Uri that this Slice represents.
*/
public Uri getUri() {
return mUri;
@@ -191,6 +193,13 @@
}
/**
+ * @hide
+ */
+ public boolean hasHint(@SliceHint String hint) {
+ return ArrayUtils.contains(mHints, hint);
+ }
+
+ /**
* A Builder used to construct {@link Slice}s
*/
public static class Builder {
@@ -308,4 +317,31 @@
return new Slice[size];
}
};
+
+ /**
+ * @hide
+ * @return A string representation of this slice.
+ */
+ public String getString() {
+ return getString("");
+ }
+
+ private String getString(String indent) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < mItems.length; i++) {
+ sb.append(indent);
+ if (mItems[i].getType() == TYPE_SLICE) {
+ sb.append("slice:\n");
+ sb.append(mItems[i].getSlice().getString(indent + " "));
+ } else if (mItems[i].getType() == TYPE_TEXT) {
+ sb.append("text: ");
+ sb.append(mItems[i].getText());
+ sb.append("\n");
+ } else {
+ sb.append(SliceItem.typeToString(mItems[i].getType()));
+ sb.append("\n");
+ }
+ }
+ return sb.toString();
+ }
}
diff --git a/core/java/android/slice/SliceItem.java b/core/java/android/slice/SliceItem.java
index 16f7dc6..2827ab9 100644
--- a/core/java/android/slice/SliceItem.java
+++ b/core/java/android/slice/SliceItem.java
@@ -132,6 +132,13 @@
mHints = ArrayUtils.appendElement(String.class, mHints, hint);
}
+ /**
+ * @hide
+ */
+ public void removeHint(String hint) {
+ ArrayUtils.removeElement(String.class, mHints, hint);
+ }
+
public @SliceType int getType() {
return mType;
}
@@ -230,7 +237,7 @@
public boolean hasHints(@SliceHint String[] hints) {
if (hints == null) return true;
for (String hint : hints) {
- if (!ArrayUtils.contains(mHints, hint)) {
+ if (!TextUtils.isEmpty(hint) && !ArrayUtils.contains(mHints, hint)) {
return false;
}
}
@@ -241,7 +248,7 @@
* @hide
*/
public boolean hasAnyHints(@SliceHint String[] hints) {
- if (hints == null) return true;
+ if (hints == null) return false;
for (String hint : hints) {
if (ArrayUtils.contains(mHints, hint)) {
return true;
@@ -309,4 +316,29 @@
return new SliceItem[size];
}
};
+
+ /**
+ * @hide
+ */
+ public static String typeToString(int type) {
+ switch (type) {
+ case TYPE_SLICE:
+ return "Slice";
+ case TYPE_TEXT:
+ return "Text";
+ case TYPE_IMAGE:
+ return "Image";
+ case TYPE_ACTION:
+ return "Action";
+ case TYPE_REMOTE_VIEW:
+ return "RemoteView";
+ case TYPE_COLOR:
+ return "Color";
+ case TYPE_TIMESTAMP:
+ return "Timestamp";
+ case TYPE_REMOTE_INPUT:
+ return "RemoteInput";
+ }
+ return "Unrecognized type: " + type;
+ }
}
diff --git a/core/java/android/slice/SliceQuery.java b/core/java/android/slice/SliceQuery.java
index edac0cc..d99b26a 100644
--- a/core/java/android/slice/SliceQuery.java
+++ b/core/java/android/slice/SliceQuery.java
@@ -61,6 +61,13 @@
/**
* @hide
*/
+ public static List<SliceItem> findAll(SliceItem s, int type) {
+ return findAll(s, type, (String[]) null, null);
+ }
+
+ /**
+ * @hide
+ */
public static List<SliceItem> findAll(SliceItem s, int type, String hints, String nonHints) {
return findAll(s, type, new String[]{ hints }, new String[]{ nonHints });
}
@@ -85,6 +92,13 @@
/**
* @hide
*/
+ public static SliceItem find(Slice s, int type) {
+ return find(s, type, (String[]) null, null);
+ }
+
+ /**
+ * @hide
+ */
public static SliceItem find(SliceItem s, int type) {
return find(s, type, (String[]) null, null);
}
diff --git a/core/java/android/slice/views/ActionRow.java b/core/java/android/slice/views/ActionRow.java
new file mode 100644
index 0000000..93e9c03
--- /dev/null
+++ b/core/java/android/slice/views/ActionRow.java
@@ -0,0 +1,201 @@
+/*
+ * 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.slice.views;
+
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.app.RemoteInput;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.drawable.Icon;
+import android.os.AsyncTask;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewParent;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * @hide
+ */
+public class ActionRow extends FrameLayout {
+
+ private static final int MAX_ACTIONS = 5;
+ private final int mSize;
+ private final int mIconPadding;
+ private final LinearLayout mActionsGroup;
+ private final boolean mFullActions;
+ private int mColor = Color.BLACK;
+
+ public ActionRow(Context context, boolean fullActions) {
+ super(context);
+ mFullActions = fullActions;
+ mSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 48,
+ context.getResources().getDisplayMetrics());
+ mIconPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 12,
+ context.getResources().getDisplayMetrics());
+ mActionsGroup = new LinearLayout(context);
+ mActionsGroup.setOrientation(LinearLayout.HORIZONTAL);
+ mActionsGroup.setLayoutParams(
+ new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ addView(mActionsGroup);
+ }
+
+ private void setColor(int color) {
+ mColor = color;
+ for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
+ View view = mActionsGroup.getChildAt(i);
+ SliceItem item = (SliceItem) view.getTag();
+ boolean tint = !item.hasHint(Slice.HINT_NO_TINT);
+ if (tint) {
+ ((ImageView) view).setImageTintList(ColorStateList.valueOf(mColor));
+ }
+ }
+ }
+
+ private ImageView addAction(Icon icon, boolean allowTint, SliceItem image) {
+ ImageView imageView = new ImageView(getContext());
+ imageView.setPadding(mIconPadding, mIconPadding, mIconPadding, mIconPadding);
+ imageView.setScaleType(ScaleType.FIT_CENTER);
+ imageView.setImageIcon(icon);
+ if (allowTint) {
+ imageView.setImageTintList(ColorStateList.valueOf(mColor));
+ }
+ imageView.setBackground(SliceViewUtil.getDrawable(getContext(),
+ android.R.attr.selectableItemBackground));
+ imageView.setTag(image);
+ addAction(imageView);
+ return imageView;
+ }
+
+ /**
+ * Set the actions and color for this action row.
+ */
+ public void setActions(SliceItem actionRow, SliceItem defColor) {
+ removeAllViews();
+ mActionsGroup.removeAllViews();
+ addView(mActionsGroup);
+
+ SliceItem color = SliceQuery.find(actionRow, SliceItem.TYPE_COLOR);
+ if (color == null) {
+ color = defColor;
+ }
+ if (color != null) {
+ setColor(color.getColor());
+ }
+ SliceQuery.findAll(actionRow, SliceItem.TYPE_ACTION).forEach(action -> {
+ if (mActionsGroup.getChildCount() >= MAX_ACTIONS) {
+ return;
+ }
+ SliceItem image = SliceQuery.find(action, SliceItem.TYPE_IMAGE);
+ if (image == null) {
+ return;
+ }
+ boolean tint = !image.hasHint(Slice.HINT_NO_TINT);
+ SliceItem input = SliceQuery.find(action, SliceItem.TYPE_REMOTE_INPUT);
+ if (input != null && input.getRemoteInput().getAllowFreeFormInput()) {
+ addAction(image.getIcon(), tint, image).setOnClickListener(
+ v -> handleRemoteInputClick(v, action.getAction(), input.getRemoteInput()));
+ createRemoteInputView(mColor, getContext());
+ } else {
+ addAction(image.getIcon(), tint, image).setOnClickListener(v -> AsyncTask.execute(
+ () -> {
+ try {
+ action.getAction().send();
+ } catch (CanceledException e) {
+ e.printStackTrace();
+ }
+ }));
+ }
+ });
+ setVisibility(getChildCount() != 0 ? View.VISIBLE : View.GONE);
+ }
+
+ private void addAction(View child) {
+ mActionsGroup.addView(child, new LinearLayout.LayoutParams(mSize, mSize, 1));
+ }
+
+ private void createRemoteInputView(int color, Context context) {
+ View riv = RemoteInputView.inflate(context, this);
+ riv.setVisibility(View.INVISIBLE);
+ addView(riv, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ riv.setBackgroundColor(color);
+ }
+
+ private boolean handleRemoteInputClick(View view, PendingIntent pendingIntent,
+ RemoteInput input) {
+ if (input == null) {
+ return false;
+ }
+
+ ViewParent p = view.getParent().getParent();
+ RemoteInputView riv = null;
+ while (p != null) {
+ if (p instanceof View) {
+ View pv = (View) p;
+ riv = findRemoteInputView(pv);
+ if (riv != null) {
+ break;
+ }
+ }
+ p = p.getParent();
+ }
+ if (riv == null) {
+ return false;
+ }
+
+ int width = view.getWidth();
+ if (view instanceof TextView) {
+ // Center the reveal on the text which might be off-center from the TextView
+ TextView tv = (TextView) view;
+ if (tv.getLayout() != null) {
+ int innerWidth = (int) tv.getLayout().getLineWidth(0);
+ innerWidth += tv.getCompoundPaddingLeft() + tv.getCompoundPaddingRight();
+ width = Math.min(width, innerWidth);
+ }
+ }
+ int cx = view.getLeft() + width / 2;
+ int cy = view.getTop() + view.getHeight() / 2;
+ int w = riv.getWidth();
+ int h = riv.getHeight();
+ int r = Math.max(
+ Math.max(cx + cy, cx + (h - cy)),
+ Math.max((w - cx) + cy, (w - cx) + (h - cy)));
+
+ riv.setRevealParameters(cx, cy, r);
+ riv.setPendingIntent(pendingIntent);
+ riv.setRemoteInput(new RemoteInput[] {
+ input
+ }, input);
+ riv.focusAnimated();
+ return true;
+ }
+
+ private RemoteInputView findRemoteInputView(View v) {
+ if (v == null) {
+ return null;
+ }
+ return (RemoteInputView) v.findViewWithTag(RemoteInputView.VIEW_TAG);
+ }
+}
diff --git a/core/java/android/slice/views/GridView.java b/core/java/android/slice/views/GridView.java
new file mode 100644
index 0000000..18a90f7
--- /dev/null
+++ b/core/java/android/slice/views/GridView.java
@@ -0,0 +1,186 @@
+/*
+ * 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.slice.views;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.views.LargeSliceAdapter.SliceListView;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * @hide
+ */
+public class GridView extends LinearLayout implements SliceListView {
+
+ private static final String TAG = "GridView";
+
+ private static final int MAX_IMAGES = 3;
+ private static final int MAX_ALL = 5;
+ private boolean mIsAllImages;
+
+ public GridView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mIsAllImages) {
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int height = width / getChildCount();
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(MeasureSpec.EXACTLY,
+ height);
+ getLayoutParams().height = height;
+ for (int i = 0; i < getChildCount(); i++) {
+ getChildAt(i).getLayoutParams().height = height;
+ }
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ public void setSliceItem(SliceItem slice) {
+ mIsAllImages = true;
+ removeAllViews();
+ int total = 1;
+ if (slice.getType() == SliceItem.TYPE_SLICE) {
+ SliceItem[] items = slice.getSlice().getItems();
+ total = items.length;
+ for (int i = 0; i < total; i++) {
+ SliceItem item = items[i];
+ if (isFull()) {
+ continue;
+ }
+ if (!addItem(item)) {
+ mIsAllImages = false;
+ }
+ }
+ } else {
+ if (!isFull()) {
+ if (!addItem(slice)) {
+ mIsAllImages = false;
+ }
+ }
+ }
+ if (total > getChildCount() && mIsAllImages) {
+ addExtraCount(total - getChildCount());
+ }
+ }
+
+ private void addExtraCount(int numExtra) {
+ View last = getChildAt(getChildCount() - 1);
+ FrameLayout frame = new FrameLayout(getContext());
+ frame.setLayoutParams(last.getLayoutParams());
+
+ removeView(last);
+ frame.addView(last, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
+ TextView v = new TextView(getContext());
+ v.setTextColor(Color.WHITE);
+ v.setBackgroundColor(0x4d000000);
+ v.setText(getResources().getString(R.string.slice_more_content, numExtra));
+ v.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);
+ v.setGravity(Gravity.CENTER);
+ frame.addView(v, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
+
+ addView(frame);
+ }
+
+ private boolean isFull() {
+ return getChildCount() >= (mIsAllImages ? MAX_IMAGES : MAX_ALL);
+ }
+
+ /**
+ * Returns true if this item is just an image.
+ */
+ private boolean addItem(SliceItem item) {
+ if (item.getType() == SliceItem.TYPE_IMAGE) {
+ ImageView v = new ImageView(getContext());
+ v.setImageIcon(item.getIcon());
+ v.setScaleType(ScaleType.CENTER_CROP);
+ addView(v, new LayoutParams(0, MATCH_PARENT, 1));
+ return true;
+ } else {
+ LinearLayout v = new LinearLayout(getContext());
+ int s = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ 12, getContext().getResources().getDisplayMetrics());
+ v.setPadding(0, s, 0, 0);
+ v.setOrientation(LinearLayout.VERTICAL);
+ v.setGravity(Gravity.CENTER_HORIZONTAL);
+ // TODO: Unify sporadic inflates that happen throughout the code.
+ ArrayList<SliceItem> items = new ArrayList<>();
+ if (item.getType() == SliceItem.TYPE_SLICE) {
+ items.addAll(Arrays.asList(item.getSlice().getItems()));
+ }
+ items.forEach(i -> {
+ Context context = getContext();
+ switch (i.getType()) {
+ case SliceItem.TYPE_TEXT:
+ boolean title = false;
+ if ((item.hasAnyHints(new String[] {
+ Slice.HINT_LARGE, Slice.HINT_TITLE
+ }))) {
+ title = true;
+ }
+ TextView tv = (TextView) LayoutInflater.from(context).inflate(
+ title ? R.layout.slice_title : R.layout.slice_secondary_text, null);
+ tv.setText(i.getText());
+ v.addView(tv);
+ break;
+ case SliceItem.TYPE_IMAGE:
+ ImageView iv = new ImageView(context);
+ iv.setImageIcon(i.getIcon());
+ if (item.hasHint(Slice.HINT_LARGE)) {
+ iv.setLayoutParams(new LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+ } else {
+ int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ 48, context.getResources().getDisplayMetrics());
+ iv.setLayoutParams(new LayoutParams(size, size));
+ }
+ v.addView(iv);
+ break;
+ case SliceItem.TYPE_REMOTE_VIEW:
+ v.addView(i.getRemoteView().apply(context, v));
+ break;
+ case SliceItem.TYPE_COLOR:
+ // TODO: Support color to tint stuff here.
+ break;
+ }
+ });
+ addView(v, new LayoutParams(0, WRAP_CONTENT, 1));
+ return false;
+ }
+ }
+}
diff --git a/core/java/android/slice/views/LargeSliceAdapter.java b/core/java/android/slice/views/LargeSliceAdapter.java
new file mode 100644
index 0000000..e77a1b2
--- /dev/null
+++ b/core/java/android/slice/views/LargeSliceAdapter.java
@@ -0,0 +1,224 @@
+/*
+ * 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.slice.views;
+
+import android.content.Context;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
+import android.slice.views.LargeSliceAdapter.SliceViewHolder;
+import android.util.ArrayMap;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.FrameLayout;
+
+import com.android.internal.R;
+import com.android.internal.widget.RecyclerView;
+import com.android.internal.widget.RecyclerView.ViewHolder;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @hide
+ */
+public class LargeSliceAdapter extends RecyclerView.Adapter<SliceViewHolder> {
+
+ public static final int TYPE_DEFAULT = 1;
+ public static final int TYPE_HEADER = 2;
+ public static final int TYPE_GRID = 3;
+ public static final int TYPE_MESSAGE = 4;
+ public static final int TYPE_MESSAGE_LOCAL = 5;
+ public static final int TYPE_REMOTE_VIEWS = 6;
+
+ private final IdGenerator mIdGen = new IdGenerator();
+ private final Context mContext;
+ private List<SliceWrapper> mSlices = new ArrayList<>();
+ private SliceItem mColor;
+
+ public LargeSliceAdapter(Context context) {
+ mContext = context;
+ setHasStableIds(true);
+ }
+
+ /**
+ * Set the {@link SliceItem}'s to be displayed in the adapter and the accent color.
+ */
+ public void setSliceItems(List<SliceItem> slices, SliceItem color) {
+ mColor = color;
+ mIdGen.resetUsage();
+ mSlices = slices.stream().map(s -> new SliceWrapper(s, mIdGen))
+ .collect(Collectors.toList());
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public SliceViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View v = inflateforType(viewType);
+ v.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ return new SliceViewHolder(v);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return mSlices.get(position).mType;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return mSlices.get(position).mId;
+ }
+
+ @Override
+ public int getItemCount() {
+ return mSlices.size();
+ }
+
+ @Override
+ public void onBindViewHolder(SliceViewHolder holder, int position) {
+ SliceWrapper slice = mSlices.get(position);
+ if (holder.mSliceView != null) {
+ holder.mSliceView.setColor(mColor);
+ holder.mSliceView.setSliceItem(slice.mItem);
+ } else if (slice.mType == TYPE_REMOTE_VIEWS) {
+ FrameLayout frame = (FrameLayout) holder.itemView;
+ frame.removeAllViews();
+ frame.addView(slice.mItem.getRemoteView().apply(mContext, frame));
+ }
+ }
+
+ private View inflateforType(int viewType) {
+ switch (viewType) {
+ case TYPE_REMOTE_VIEWS:
+ return new FrameLayout(mContext);
+ case TYPE_GRID:
+ return LayoutInflater.from(mContext).inflate(R.layout.slice_grid, null);
+ case TYPE_MESSAGE:
+ return LayoutInflater.from(mContext).inflate(R.layout.slice_message, null);
+ case TYPE_MESSAGE_LOCAL:
+ return LayoutInflater.from(mContext).inflate(R.layout.slice_message_local, null);
+ }
+ return new SmallTemplateView(mContext);
+ }
+
+ protected static class SliceWrapper {
+ private final SliceItem mItem;
+ private final int mType;
+ private final long mId;
+
+ public SliceWrapper(SliceItem item, IdGenerator idGen) {
+ mItem = item;
+ mType = getType(item);
+ mId = idGen.getId(item);
+ }
+
+ public static int getType(SliceItem item) {
+ if (item.getType() == SliceItem.TYPE_REMOTE_VIEW) {
+ return TYPE_REMOTE_VIEWS;
+ }
+ if (item.hasHint(Slice.HINT_MESSAGE)) {
+ // TODO: Better way to determine me or not? Something more like Messaging style.
+ if (SliceQuery.find(item, -1, Slice.HINT_SOURCE, null) != null) {
+ return TYPE_MESSAGE;
+ } else {
+ return TYPE_MESSAGE_LOCAL;
+ }
+ }
+ if (item.hasHint(Slice.HINT_HORIZONTAL)) {
+ return TYPE_GRID;
+ }
+ return TYPE_DEFAULT;
+ }
+ }
+
+ /**
+ * A {@link ViewHolder} for presenting slices in {@link LargeSliceAdapter}.
+ */
+ public static class SliceViewHolder extends ViewHolder {
+ public final SliceListView mSliceView;
+
+ public SliceViewHolder(View itemView) {
+ super(itemView);
+ mSliceView = itemView instanceof SliceListView ? (SliceListView) itemView : null;
+ }
+ }
+
+ /**
+ * View slices being displayed in {@link LargeSliceAdapter}.
+ */
+ public interface SliceListView {
+ /**
+ * Set the slice item for this view.
+ */
+ void setSliceItem(SliceItem slice);
+
+ /**
+ * Set the color for the items in this view.
+ */
+ default void setColor(SliceItem color) {
+
+ }
+ }
+
+ private static class IdGenerator {
+ private long mNextLong = 0;
+ private final ArrayMap<String, Long> mCurrentIds = new ArrayMap<>();
+ private final ArrayMap<String, Integer> mUsedIds = new ArrayMap<>();
+
+ public long getId(SliceItem item) {
+ String str = genString(item);
+ if (!mCurrentIds.containsKey(str)) {
+ mCurrentIds.put(str, mNextLong++);
+ }
+ long id = mCurrentIds.get(str);
+ int index = mUsedIds.getOrDefault(str, 0);
+ mUsedIds.put(str, index + 1);
+ return id + index * 10000;
+ }
+
+ private String genString(SliceItem item) {
+ StringBuilder builder = new StringBuilder();
+ SliceQuery.stream(item).forEach(i -> {
+ builder.append(i.getType());
+ i.removeHint(Slice.HINT_SELECTED);
+ builder.append(i.getHints());
+ switch (i.getType()) {
+ case SliceItem.TYPE_REMOTE_VIEW:
+ builder.append(i.getRemoteView());
+ break;
+ case SliceItem.TYPE_IMAGE:
+ builder.append(i.getIcon());
+ break;
+ case SliceItem.TYPE_TEXT:
+ builder.append(i.getText());
+ break;
+ case SliceItem.TYPE_COLOR:
+ builder.append(i.getColor());
+ break;
+ }
+ });
+ return builder.toString();
+ }
+
+ public void resetUsage() {
+ mUsedIds.clear();
+ }
+ }
+}
diff --git a/core/java/android/slice/views/LargeTemplateView.java b/core/java/android/slice/views/LargeTemplateView.java
new file mode 100644
index 0000000..d53e8fc
--- /dev/null
+++ b/core/java/android/slice/views/LargeTemplateView.java
@@ -0,0 +1,116 @@
+/*
+ * 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.slice.views;
+
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.content.Context;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
+import android.slice.views.SliceView.SliceModeView;
+import android.util.TypedValue;
+
+import com.android.internal.widget.LinearLayoutManager;
+import com.android.internal.widget.RecyclerView;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @hide
+ */
+public class LargeTemplateView extends SliceModeView {
+ private final LargeSliceAdapter mAdapter;
+ private final RecyclerView mRecyclerView;
+ private final int mDefaultHeight;
+ private final int mMaxHeight;
+ private Slice mSlice;
+
+ public LargeTemplateView(Context context) {
+ super(context);
+
+ mRecyclerView = new RecyclerView(getContext());
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+ mAdapter = new LargeSliceAdapter(context);
+ mRecyclerView.setAdapter(mAdapter);
+ addView(mRecyclerView);
+ int width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 300,
+ getResources().getDisplayMetrics());
+ setLayoutParams(new LayoutParams(width, WRAP_CONTENT));
+ mDefaultHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200,
+ getResources().getDisplayMetrics());
+ mMaxHeight = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200,
+ getResources().getDisplayMetrics());
+ }
+
+ @Override
+ public String getMode() {
+ return SliceView.MODE_LARGE;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ mRecyclerView.getLayoutParams().height = WRAP_CONTENT;
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mRecyclerView.getMeasuredHeight() > mMaxHeight
+ || mSlice.hasHint(Slice.HINT_PARTIAL)) {
+ mRecyclerView.getLayoutParams().height = mDefaultHeight;
+ } else {
+ mRecyclerView.getLayoutParams().height = mRecyclerView.getMeasuredHeight();
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ public void setSlice(Slice slice) {
+ SliceItem color = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
+ mSlice = slice;
+ List<SliceItem> items = new ArrayList<>();
+ boolean[] hasHeader = new boolean[1];
+ if (slice.hasHint(Slice.HINT_LIST)) {
+ addList(slice, items);
+ } else {
+ Arrays.asList(slice.getItems()).forEach(item -> {
+ if (item.hasHint(Slice.HINT_ACTIONS)) {
+ return;
+ } else if (item.getType() == SliceItem.TYPE_COLOR) {
+ return;
+ } else if (item.getType() == SliceItem.TYPE_SLICE
+ && item.hasHint(Slice.HINT_LIST)) {
+ addList(item.getSlice(), items);
+ } else if (item.hasHint(Slice.HINT_LIST_ITEM)) {
+ items.add(item);
+ } else if (!hasHeader[0]) {
+ hasHeader[0] = true;
+ items.add(0, item);
+ } else {
+ item.addHint(Slice.HINT_LIST_ITEM);
+ items.add(item);
+ }
+ });
+ }
+ mAdapter.setSliceItems(items, color);
+ }
+
+ private void addList(Slice slice, List<SliceItem> items) {
+ List<SliceItem> sliceItems = Arrays.asList(slice.getItems());
+ sliceItems.forEach(i -> i.addHint(Slice.HINT_LIST_ITEM));
+ items.addAll(sliceItems);
+ }
+}
diff --git a/core/java/android/slice/views/MessageView.java b/core/java/android/slice/views/MessageView.java
new file mode 100644
index 0000000..7b03e0b
--- /dev/null
+++ b/core/java/android/slice/views/MessageView.java
@@ -0,0 +1,77 @@
+/*
+ * 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.slice.views;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
+import android.slice.views.LargeSliceAdapter.SliceListView;
+import android.text.SpannableStringBuilder;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+/**
+ * @hide
+ */
+public class MessageView extends LinearLayout implements SliceListView {
+
+ private TextView mDetails;
+ private ImageView mIcon;
+
+ public MessageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mDetails = findViewById(android.R.id.summary);
+ mIcon = findViewById(android.R.id.icon);
+ }
+
+ @Override
+ public void setSliceItem(SliceItem slice) {
+ SliceItem source = SliceQuery.find(slice, SliceItem.TYPE_IMAGE, Slice.HINT_SOURCE, null);
+ if (source != null) {
+ final int iconSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ 24, getContext().getResources().getDisplayMetrics());
+ // TODO try and turn this into a drawable
+ Bitmap iconBm = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
+ Canvas iconCanvas = new Canvas(iconBm);
+ Drawable d = source.getIcon().loadDrawable(getContext());
+ d.setBounds(0, 0, iconSize, iconSize);
+ d.draw(iconCanvas);
+ mIcon.setImageBitmap(SliceViewUtil.getCircularBitmap(iconBm));
+ }
+ SpannableStringBuilder builder = new SpannableStringBuilder();
+ SliceQuery.findAll(slice, SliceItem.TYPE_TEXT).forEach(text -> {
+ if (builder.length() != 0) {
+ builder.append('\n');
+ }
+ builder.append(text.getText());
+ });
+ mDetails.setText(builder.toString());
+ }
+
+}
diff --git a/core/java/android/slice/views/RemoteInputView.java b/core/java/android/slice/views/RemoteInputView.java
new file mode 100644
index 0000000..a29bb5c
--- /dev/null
+++ b/core/java/android/slice/views/RemoteInputView.java
@@ -0,0 +1,445 @@
+/*
+ * 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.slice.views;
+
+import android.animation.Animator;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ShortcutManager;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.internal.R;
+
+/**
+ * Host for the remote input.
+ *
+ * @hide
+ */
+// TODO this should be unified with SystemUI RemoteInputView (b/67527720)
+public class RemoteInputView extends LinearLayout implements View.OnClickListener, TextWatcher {
+
+ private static final String TAG = "RemoteInput";
+
+ /**
+ * A marker object that let's us easily find views of this class.
+ */
+ public static final Object VIEW_TAG = new Object();
+
+ private RemoteEditText mEditText;
+ private ImageButton mSendButton;
+ private ProgressBar mProgressBar;
+ private PendingIntent mPendingIntent;
+ private RemoteInput[] mRemoteInputs;
+ private RemoteInput mRemoteInput;
+
+ private int mRevealCx;
+ private int mRevealCy;
+ private int mRevealR;
+ private boolean mResetting;
+
+ public RemoteInputView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mProgressBar = findViewById(R.id.remote_input_progress);
+ mSendButton = findViewById(R.id.remote_input_send);
+ mSendButton.setOnClickListener(this);
+
+ mEditText = (RemoteEditText) getChildAt(0);
+ mEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ final boolean isSoftImeEvent = event == null
+ && (actionId == EditorInfo.IME_ACTION_DONE
+ || actionId == EditorInfo.IME_ACTION_NEXT
+ || actionId == EditorInfo.IME_ACTION_SEND);
+ final boolean isKeyboardEnterKey = event != null
+ && KeyEvent.isConfirmKey(event.getKeyCode())
+ && event.getAction() == KeyEvent.ACTION_DOWN;
+
+ if (isSoftImeEvent || isKeyboardEnterKey) {
+ if (mEditText.length() > 0) {
+ sendRemoteInput();
+ }
+ // Consume action to prevent IME from closing.
+ return true;
+ }
+ return false;
+ }
+ });
+ mEditText.addTextChangedListener(this);
+ mEditText.setInnerFocusable(false);
+ mEditText.mRemoteInputView = this;
+ }
+
+ private void sendRemoteInput() {
+ Bundle results = new Bundle();
+ results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString());
+ Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent,
+ results);
+
+ mEditText.setEnabled(false);
+ mSendButton.setVisibility(INVISIBLE);
+ mProgressBar.setVisibility(VISIBLE);
+ mEditText.mShowImeOnInputConnection = false;
+
+ // Tell ShortcutManager that this package has been "activated". ShortcutManager
+ // will reset the throttling for this package.
+ // Strictly speaking, the intent receiver may be different from the intent creator,
+ // but that's an edge case, and also because we can't always know which package will receive
+ // an intent, so we just reset for the creator.
+ getContext().getSystemService(ShortcutManager.class).onApplicationActive(
+ mPendingIntent.getCreatorPackage(),
+ getContext().getUserId());
+
+ try {
+ mPendingIntent.send(mContext, 0, fillInIntent);
+ reset();
+ } catch (PendingIntent.CanceledException e) {
+ Log.i(TAG, "Unable to send remote input result", e);
+ Toast.makeText(mContext, "Failure sending pending intent for inline reply :(",
+ Toast.LENGTH_SHORT).show();
+ reset();
+ }
+ }
+
+ /**
+ * Creates a remote input view.
+ */
+ public static RemoteInputView inflate(Context context, ViewGroup root) {
+ RemoteInputView v = (RemoteInputView) LayoutInflater.from(context).inflate(
+ R.layout.slice_remote_input, root, false);
+ v.setTag(VIEW_TAG);
+ return v;
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v == mSendButton) {
+ sendRemoteInput();
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ super.onTouchEvent(event);
+
+ // We never want for a touch to escape to an outer view or one we covered.
+ return true;
+ }
+
+ private void onDefocus() {
+ setVisibility(INVISIBLE);
+ }
+
+ /**
+ * Set the pending intent for remote input.
+ */
+ public void setPendingIntent(PendingIntent pendingIntent) {
+ mPendingIntent = pendingIntent;
+ }
+
+ /**
+ * Set the remote inputs for this view.
+ */
+ public void setRemoteInput(RemoteInput[] remoteInputs, RemoteInput remoteInput) {
+ mRemoteInputs = remoteInputs;
+ mRemoteInput = remoteInput;
+ mEditText.setHint(mRemoteInput.getLabel());
+ }
+
+ /**
+ * Focuses the remote input view.
+ */
+ public void focusAnimated() {
+ if (getVisibility() != VISIBLE) {
+ Animator animator = ViewAnimationUtils.createCircularReveal(
+ this, mRevealCx, mRevealCy, 0, mRevealR);
+ animator.setDuration(200);
+ animator.start();
+ }
+ focus();
+ }
+
+ private void focus() {
+ setVisibility(VISIBLE);
+ mEditText.setInnerFocusable(true);
+ mEditText.mShowImeOnInputConnection = true;
+ mEditText.setSelection(mEditText.getText().length());
+ mEditText.requestFocus();
+ updateSendButton();
+ }
+
+ private void reset() {
+ mResetting = true;
+
+ mEditText.getText().clear();
+ mEditText.setEnabled(true);
+ mSendButton.setVisibility(VISIBLE);
+ mProgressBar.setVisibility(INVISIBLE);
+ updateSendButton();
+ onDefocus();
+
+ mResetting = false;
+ }
+
+ @Override
+ public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
+ if (mResetting && child == mEditText) {
+ // Suppress text events if it happens during resetting. Ideally this would be
+ // suppressed by the text view not being shown, but that doesn't work here because it
+ // needs to stay visible for the animation.
+ return false;
+ }
+ return super.onRequestSendAccessibilityEvent(child, event);
+ }
+
+ private void updateSendButton() {
+ mSendButton.setEnabled(mEditText.getText().length() != 0);
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ updateSendButton();
+ }
+
+ /**
+ * Tries to find an action that matches the current pending intent of this view and updates its
+ * state to that of the found action
+ *
+ * @return true if a matching action was found, false otherwise
+ */
+ public boolean updatePendingIntentFromActions(Notification.Action[] actions) {
+ if (mPendingIntent == null || actions == null) {
+ return false;
+ }
+ Intent current = mPendingIntent.getIntent();
+ if (current == null) {
+ return false;
+ }
+
+ for (Notification.Action a : actions) {
+ RemoteInput[] inputs = a.getRemoteInputs();
+ if (a.actionIntent == null || inputs == null) {
+ continue;
+ }
+ Intent candidate = a.actionIntent.getIntent();
+ if (!current.filterEquals(candidate)) {
+ continue;
+ }
+
+ RemoteInput input = null;
+ for (RemoteInput i : inputs) {
+ if (i.getAllowFreeFormInput()) {
+ input = i;
+ }
+ }
+ if (input == null) {
+ continue;
+ }
+ setPendingIntent(a.actionIntent);
+ setRemoteInput(inputs, input);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public void setRevealParameters(int cx, int cy, int r) {
+ mRevealCx = cx;
+ mRevealCy = cy;
+ mRevealR = r;
+ }
+
+ @Override
+ public void dispatchStartTemporaryDetach() {
+ super.dispatchStartTemporaryDetach();
+ // Detach the EditText temporarily such that it doesn't get onDetachedFromWindow and
+ // won't lose IME focus.
+ detachViewFromParent(mEditText);
+ }
+
+ @Override
+ public void dispatchFinishTemporaryDetach() {
+ if (isAttachedToWindow()) {
+ attachViewToParent(mEditText, 0, mEditText.getLayoutParams());
+ } else {
+ removeDetachedView(mEditText, false /* animate */);
+ }
+ super.dispatchFinishTemporaryDetach();
+ }
+
+ /**
+ * An EditText that changes appearance based on whether it's focusable and becomes un-focusable
+ * whenever the user navigates away from it or it becomes invisible.
+ */
+ public static class RemoteEditText extends EditText {
+
+ private final Drawable mBackground;
+ private RemoteInputView mRemoteInputView;
+ boolean mShowImeOnInputConnection;
+
+ public RemoteEditText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mBackground = getBackground();
+ }
+
+ private void defocusIfNeeded(boolean animate) {
+ if (mRemoteInputView != null || isTemporarilyDetached()) {
+ if (isTemporarilyDetached()) {
+ // We might get reattached but then the other one of HUN / expanded might steal
+ // our focus, so we'll need to save our text here.
+ }
+ return;
+ }
+ if (isFocusable() && isEnabled()) {
+ setInnerFocusable(false);
+ if (mRemoteInputView != null) {
+ mRemoteInputView.onDefocus();
+ }
+ mShowImeOnInputConnection = false;
+ }
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+
+ if (!isShown()) {
+ defocusIfNeeded(false /* animate */);
+ }
+ }
+
+ @Override
+ protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+ super.onFocusChanged(focused, direction, previouslyFocusedRect);
+ if (!focused) {
+ defocusIfNeeded(true /* animate */);
+ }
+ }
+
+ @Override
+ public void getFocusedRect(Rect r) {
+ super.getFocusedRect(r);
+ r.top = mScrollY;
+ r.bottom = mScrollY + (mBottom - mTop);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ // Eat the DOWN event here to prevent any default behavior.
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ defocusIfNeeded(true /* animate */);
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
+
+ if (mShowImeOnInputConnection && inputConnection != null) {
+ final InputMethodManager imm = InputMethodManager.getInstance();
+ if (imm != null) {
+ // onCreateInputConnection is called by InputMethodManager in the middle of
+ // setting up the connection to the IME; wait with requesting the IME until that
+ // work has completed.
+ post(new Runnable() {
+ @Override
+ public void run() {
+ imm.viewClicked(RemoteEditText.this);
+ imm.showSoftInput(RemoteEditText.this, 0);
+ }
+ });
+ }
+ }
+
+ return inputConnection;
+ }
+
+ @Override
+ public void onCommitCompletion(CompletionInfo text) {
+ clearComposingText();
+ setText(text.getText());
+ setSelection(getText().length());
+ }
+
+ void setInnerFocusable(boolean focusable) {
+ setFocusableInTouchMode(focusable);
+ setFocusable(focusable);
+ setCursorVisible(focusable);
+
+ if (focusable) {
+ requestFocus();
+ setBackground(mBackground);
+ } else {
+ setBackground(null);
+ }
+
+ }
+ }
+}
diff --git a/core/java/android/slice/views/ShortcutView.java b/core/java/android/slice/views/ShortcutView.java
new file mode 100644
index 0000000..8fe2f1a
--- /dev/null
+++ b/core/java/android/slice/views/ShortcutView.java
@@ -0,0 +1,110 @@
+/*
+ * 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.slice.views;
+
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.OvalShape;
+import android.net.Uri;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
+import android.slice.views.SliceView.SliceModeView;
+import android.view.ViewGroup;
+
+import com.android.internal.R;
+
+/**
+ * @hide
+ */
+public class ShortcutView extends SliceModeView {
+
+ private static final String TAG = "ShortcutView";
+
+ private PendingIntent mAction;
+ private Uri mUri;
+ private int mLargeIconSize;
+ private int mSmallIconSize;
+
+ public ShortcutView(Context context) {
+ super(context);
+ mLargeIconSize = getContext().getResources()
+ .getDimensionPixelSize(R.dimen.slice_shortcut_size);
+ mSmallIconSize = getContext().getResources().getDimensionPixelSize(R.dimen.slice_icon_size);
+ setLayoutParams(new ViewGroup.LayoutParams(mLargeIconSize, mLargeIconSize));
+ }
+
+ @Override
+ public void setSlice(Slice slice) {
+ removeAllViews();
+ SliceItem sliceItem = SliceQuery.find(slice, SliceItem.TYPE_ACTION);
+ SliceItem iconItem = slice.getPrimaryIcon();
+ SliceItem textItem = sliceItem != null
+ ? SliceQuery.find(sliceItem, SliceItem.TYPE_TEXT)
+ : SliceQuery.find(slice, SliceItem.TYPE_TEXT);
+ SliceItem colorItem = sliceItem != null
+ ? SliceQuery.find(sliceItem, SliceItem.TYPE_COLOR)
+ : SliceQuery.find(slice, SliceItem.TYPE_COLOR);
+ if (colorItem == null) {
+ colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
+ }
+ // TODO: pick better default colour
+ final int color = colorItem != null ? colorItem.getColor() : Color.GRAY;
+ ShapeDrawable circle = new ShapeDrawable(new OvalShape());
+ circle.setTint(color);
+ setBackground(circle);
+ if (iconItem != null) {
+ final boolean isLarge = iconItem.hasHint(Slice.HINT_LARGE);
+ final int iconSize = isLarge ? mLargeIconSize : mSmallIconSize;
+ SliceViewUtil.createCircledIcon(getContext(), color, iconSize, iconItem.getIcon(),
+ isLarge, this /* parent */);
+ mAction = sliceItem != null ? sliceItem.getAction()
+ : null;
+ mUri = slice.getUri();
+ setClickable(true);
+ } else {
+ setClickable(false);
+ }
+ }
+
+ @Override
+ public String getMode() {
+ return SliceView.MODE_SHORTCUT;
+ }
+
+ @Override
+ public boolean performClick() {
+ if (!callOnClick()) {
+ try {
+ if (mAction != null) {
+ mAction.send();
+ } else {
+ Intent intent = new Intent(Intent.ACTION_VIEW).setData(mUri);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ getContext().startActivity(intent);
+ }
+ } catch (CanceledException e) {
+ e.printStackTrace();
+ }
+ }
+ return true;
+ }
+}
diff --git a/core/java/android/slice/views/SliceView.java b/core/java/android/slice/views/SliceView.java
new file mode 100644
index 0000000..f379248
--- /dev/null
+++ b/core/java/android/slice/views/SliceView.java
@@ -0,0 +1,249 @@
+/*
+ * 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.slice.views;
+
+import android.annotation.StringDef;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.ColorDrawable;
+import android.net.Uri;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+
+/**
+ * A view that can display a {@link Slice} in different {@link SliceMode}'s.
+ *
+ * @hide
+ */
+public class SliceView extends LinearLayout {
+
+ private static final String TAG = "SliceView";
+
+ /**
+ * @hide
+ */
+ public abstract static class SliceModeView extends FrameLayout {
+
+ public SliceModeView(Context context) {
+ super(context);
+ }
+
+ /**
+ * @return the {@link SliceMode} of the slice being presented.
+ */
+ public abstract String getMode();
+
+ /**
+ * @param slice the slice to show in this view.
+ */
+ public abstract void setSlice(Slice slice);
+ }
+
+ /**
+ * @hide
+ */
+ @StringDef({
+ MODE_SMALL, MODE_LARGE, MODE_SHORTCUT
+ })
+ public @interface SliceMode {}
+
+ /**
+ * Mode indicating this slice should be presented in small template format.
+ */
+ public static final String MODE_SMALL = "SLICE_SMALL";
+ /**
+ * Mode indicating this slice should be presented in large template format.
+ */
+ public static final String MODE_LARGE = "SLICE_LARGE";
+ /**
+ * Mode indicating this slice should be presented as an icon.
+ */
+ public static final String MODE_SHORTCUT = "SLICE_ICON";
+
+ /**
+ * Will select the type of slice binding based on size of the View. TODO: Put in some info about
+ * that selection.
+ */
+ private static final String MODE_AUTO = "auto";
+
+ private String mMode = MODE_AUTO;
+ private SliceModeView mCurrentView;
+ private final ActionRow mActions;
+ private Slice mCurrentSlice;
+ private boolean mShowActions = true;
+
+ /**
+ * Simple constructor to create a slice view from code.
+ *
+ * @param context The context the view is running in.
+ */
+ public SliceView(Context context) {
+ super(context);
+ setOrientation(LinearLayout.VERTICAL);
+ mActions = new ActionRow(mContext, true);
+ mActions.setBackground(new ColorDrawable(0xffeeeeee));
+ mCurrentView = new LargeTemplateView(mContext);
+ addView(mCurrentView);
+ addView(mActions);
+ }
+
+ /**
+ * @hide
+ */
+ public void bindSlice(Intent intent) {
+ // TODO
+ }
+
+ /**
+ * Binds this view to the {@link Slice} associated with the provided {@link Uri}.
+ */
+ public void bindSlice(Uri sliceUri) {
+ validate(sliceUri);
+ Slice s = mContext.getContentResolver().bindSlice(sliceUri);
+ bindSlice(s);
+ }
+
+ /**
+ * Binds this view to the provided {@link Slice}.
+ */
+ public void bindSlice(Slice slice) {
+ mCurrentSlice = slice;
+ if (mCurrentSlice != null) {
+ reinflate();
+ }
+ }
+
+ /**
+ * Call to clean up the view.
+ */
+ public void unbindSlice() {
+ mCurrentSlice = null;
+ }
+
+ /**
+ * Set the {@link SliceMode} this view should present in.
+ */
+ public void setMode(@SliceMode String mode) {
+ setMode(mode, false /* animate */);
+ }
+
+ /**
+ * @hide
+ */
+ public void setMode(@SliceMode String mode, boolean animate) {
+ if (animate) {
+ Log.e(TAG, "Animation not supported yet");
+ }
+ mMode = mode;
+ reinflate();
+ }
+
+ /**
+ * @return the {@link SliceMode} this view is presenting in.
+ */
+ public @SliceMode String getMode() {
+ if (mMode.equals(MODE_AUTO)) {
+ return MODE_LARGE;
+ }
+ return mMode;
+ }
+
+ /**
+ * @hide
+ *
+ * Whether this view should show a row of actions with it.
+ */
+ public void setShowActionRow(boolean show) {
+ mShowActions = show;
+ reinflate();
+ }
+
+ private SliceModeView createView(String mode) {
+ switch (mode) {
+ case MODE_SHORTCUT:
+ return new ShortcutView(getContext());
+ case MODE_SMALL:
+ return new SmallTemplateView(getContext());
+ }
+ return new LargeTemplateView(getContext());
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ unbindSlice();
+ }
+
+ private void reinflate() {
+ if (mCurrentSlice == null) {
+ return;
+ }
+ // TODO: Smarter mapping here from one state to the next.
+ SliceItem color = SliceQuery.find(mCurrentSlice, SliceItem.TYPE_COLOR);
+ SliceItem[] items = mCurrentSlice.getItems();
+ SliceItem actionRow = SliceQuery.find(mCurrentSlice, SliceItem.TYPE_SLICE,
+ Slice.HINT_ACTIONS,
+ Slice.HINT_ALT);
+ String mode = getMode();
+ if (!mode.equals(mCurrentView.getMode())) {
+ removeAllViews();
+ mCurrentView = createView(mode);
+ addView(mCurrentView);
+ addView(mActions);
+ }
+ if (items.length > 1 || (items.length != 0 && items[0] != actionRow)) {
+ mCurrentView.setVisibility(View.VISIBLE);
+ mCurrentView.setSlice(mCurrentSlice);
+ } else {
+ mCurrentView.setVisibility(View.GONE);
+ }
+
+ boolean showActions = mShowActions && actionRow != null
+ && !mode.equals(MODE_SHORTCUT);
+ if (showActions) {
+ mActions.setActions(actionRow, color);
+ mActions.setVisibility(View.VISIBLE);
+ } else {
+ mActions.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ // TODO -- may need to rethink for AGSA
+ if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ requestDisallowInterceptTouchEvent(true);
+ }
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ private static void validate(Uri sliceUri) {
+ if (!ContentResolver.SCHEME_SLICE.equals(sliceUri.getScheme())) {
+ throw new RuntimeException("Invalid uri " + sliceUri);
+ }
+ if (sliceUri.getPathSegments().size() == 0) {
+ throw new RuntimeException("Invalid uri " + sliceUri);
+ }
+ }
+}
diff --git a/core/java/android/slice/views/SliceViewUtil.java b/core/java/android/slice/views/SliceViewUtil.java
new file mode 100644
index 0000000..1b5a6d1
--- /dev/null
+++ b/core/java/android/slice/views/SliceViewUtil.java
@@ -0,0 +1,182 @@
+/*
+ * 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.slice.views;
+
+import android.annotation.ColorInt;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.view.Gravity;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+/**
+ * A bunch of utilities for slice UI.
+ *
+ * @hide
+ */
+public class SliceViewUtil {
+
+ /**
+ * @hide
+ */
+ @ColorInt
+ public static int getColorAccent(Context context) {
+ return getColorAttr(context, android.R.attr.colorAccent);
+ }
+
+ /**
+ * @hide
+ */
+ @ColorInt
+ public static int getColorError(Context context) {
+ return getColorAttr(context, android.R.attr.colorError);
+ }
+
+ /**
+ * @hide
+ */
+ @ColorInt
+ public static int getDefaultColor(Context context, int resId) {
+ final ColorStateList list = context.getResources().getColorStateList(resId,
+ context.getTheme());
+
+ return list.getDefaultColor();
+ }
+
+ /**
+ * @hide
+ */
+ @ColorInt
+ public static int getDisabled(Context context, int inputColor) {
+ return applyAlphaAttr(context, android.R.attr.disabledAlpha, inputColor);
+ }
+
+ /**
+ * @hide
+ */
+ @ColorInt
+ public static int applyAlphaAttr(Context context, int attr, int inputColor) {
+ TypedArray ta = context.obtainStyledAttributes(new int[] {
+ attr
+ });
+ float alpha = ta.getFloat(0, 0);
+ ta.recycle();
+ return applyAlpha(alpha, inputColor);
+ }
+
+ /**
+ * @hide
+ */
+ @ColorInt
+ public static int applyAlpha(float alpha, int inputColor) {
+ alpha *= Color.alpha(inputColor);
+ return Color.argb((int) (alpha), Color.red(inputColor), Color.green(inputColor),
+ Color.blue(inputColor));
+ }
+
+ /**
+ * @hide
+ */
+ @ColorInt
+ public static int getColorAttr(Context context, int attr) {
+ TypedArray ta = context.obtainStyledAttributes(new int[] {
+ attr
+ });
+ @ColorInt
+ int colorAccent = ta.getColor(0, 0);
+ ta.recycle();
+ return colorAccent;
+ }
+
+ /**
+ * @hide
+ */
+ public static int getThemeAttr(Context context, int attr) {
+ TypedArray ta = context.obtainStyledAttributes(new int[] {
+ attr
+ });
+ int theme = ta.getResourceId(0, 0);
+ ta.recycle();
+ return theme;
+ }
+
+ /**
+ * @hide
+ */
+ public static Drawable getDrawable(Context context, int attr) {
+ TypedArray ta = context.obtainStyledAttributes(new int[] {
+ attr
+ });
+ Drawable drawable = ta.getDrawable(0);
+ ta.recycle();
+ return drawable;
+ }
+
+ /**
+ * @hide
+ */
+ public static void createCircledIcon(Context context, int color, int iconSize, Icon icon,
+ boolean isLarge, ViewGroup parent) {
+ ImageView v = new ImageView(context);
+ v.setImageIcon(icon);
+ parent.addView(v);
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams();
+ if (isLarge) {
+ // XXX better way to convert from icon -> bitmap or crop an icon (?)
+ Bitmap iconBm = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
+ Canvas iconCanvas = new Canvas(iconBm);
+ v.layout(0, 0, iconSize, iconSize);
+ v.draw(iconCanvas);
+ v.setImageBitmap(getCircularBitmap(iconBm));
+ } else {
+ v.setColorFilter(Color.WHITE);
+ }
+ lp.width = iconSize;
+ lp.height = iconSize;
+ lp.gravity = Gravity.CENTER;
+ }
+
+ /**
+ * @hide
+ */
+ public static Bitmap getCircularBitmap(Bitmap bitmap) {
+ Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
+ bitmap.getHeight(), Config.ARGB_8888);
+ Canvas canvas = new Canvas(output);
+ final Paint paint = new Paint();
+ final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+ paint.setAntiAlias(true);
+ canvas.drawARGB(0, 0, 0, 0);
+ canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2,
+ bitmap.getWidth() / 2, paint);
+ paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
+ canvas.drawBitmap(bitmap, rect, rect, paint);
+ return output;
+ }
+}
diff --git a/core/java/android/slice/views/SmallTemplateView.java b/core/java/android/slice/views/SmallTemplateView.java
new file mode 100644
index 0000000..b0b181e
--- /dev/null
+++ b/core/java/android/slice/views/SmallTemplateView.java
@@ -0,0 +1,211 @@
+/*
+ * 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.slice.views;
+
+import android.app.PendingIntent.CanceledException;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.slice.Slice;
+import android.slice.SliceItem;
+import android.slice.SliceQuery;
+import android.slice.views.LargeSliceAdapter.SliceListView;
+import android.slice.views.SliceView.SliceModeView;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+import java.text.Format;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Small template is also used to construct list items for use with {@link LargeTemplateView}.
+ *
+ * @hide
+ */
+public class SmallTemplateView extends SliceModeView implements SliceListView {
+
+ private static final String TAG = "SmallTemplateView";
+
+ private int mIconSize;
+ private int mPadding;
+
+ private LinearLayout mStartContainer;
+ private TextView mTitleText;
+ private TextView mSecondaryText;
+ private LinearLayout mEndContainer;
+
+ public SmallTemplateView(Context context) {
+ super(context);
+ inflate(context, R.layout.slice_small_template, this);
+ mIconSize = getContext().getResources().getDimensionPixelSize(R.dimen.slice_icon_size);
+ mPadding = getContext().getResources().getDimensionPixelSize(R.dimen.slice_padding);
+
+ mStartContainer = (LinearLayout) findViewById(android.R.id.icon_frame);
+ mTitleText = (TextView) findViewById(android.R.id.title);
+ mSecondaryText = (TextView) findViewById(android.R.id.summary);
+ mEndContainer = (LinearLayout) findViewById(android.R.id.widget_frame);
+ }
+
+ @Override
+ public String getMode() {
+ return SliceView.MODE_SMALL;
+ }
+
+ @Override
+ public void setSliceItem(SliceItem slice) {
+ resetViews();
+ SliceItem colorItem = SliceQuery.find(slice, SliceItem.TYPE_COLOR);
+ int color = colorItem != null ? colorItem.getColor() : -1;
+
+ // Look for any title elements
+ List<SliceItem> titleItems = SliceQuery.findAll(slice, -1, Slice.HINT_TITLE,
+ null);
+ boolean hasTitleText = false;
+ boolean hasTitleItem = false;
+ for (int i = 0; i < titleItems.size(); i++) {
+ SliceItem item = titleItems.get(i);
+ if (!hasTitleItem) {
+ // icon, action icon, or timestamp
+ if (item.getType() == SliceItem.TYPE_ACTION) {
+ hasTitleItem = addIcon(item, color, mStartContainer);
+ } else if (item.getType() == SliceItem.TYPE_IMAGE) {
+ addIcon(item, color, mStartContainer);
+ hasTitleItem = true;
+ } else if (item.getType() == SliceItem.TYPE_TIMESTAMP) {
+ TextView tv = new TextView(getContext());
+ tv.setText(convertTimeToString(item.getTimestamp()));
+ hasTitleItem = true;
+ }
+ }
+ if (!hasTitleText && item.getType() == SliceItem.TYPE_TEXT) {
+ mTitleText.setText(item.getText());
+ hasTitleText = true;
+ }
+ if (hasTitleText && hasTitleItem) {
+ break;
+ }
+ }
+ mTitleText.setVisibility(hasTitleText ? View.VISIBLE : View.GONE);
+ mStartContainer.setVisibility(hasTitleItem ? View.VISIBLE : View.GONE);
+
+ if (slice.getType() != SliceItem.TYPE_SLICE) {
+ return;
+ }
+
+ // Deal with remaining items
+ int itemCount = 0;
+ boolean hasSummary = false;
+ ArrayList<SliceItem> sliceItems = new ArrayList<SliceItem>(
+ Arrays.asList(slice.getSlice().getItems()));
+ for (int i = 0; i < sliceItems.size(); i++) {
+ SliceItem item = sliceItems.get(i);
+ if (!hasSummary && item.getType() == SliceItem.TYPE_TEXT
+ && !item.hasHint(Slice.HINT_TITLE)) {
+ // TODO -- Should combine all text items?
+ mSecondaryText.setText(item.getText());
+ hasSummary = true;
+ }
+ if (itemCount <= 3) {
+ if (item.getType() == SliceItem.TYPE_ACTION) {
+ if (addIcon(item, color, mEndContainer)) {
+ itemCount++;
+ }
+ } else if (item.getType() == SliceItem.TYPE_IMAGE) {
+ addIcon(item, color, mEndContainer);
+ itemCount++;
+ } else if (item.getType() == SliceItem.TYPE_TIMESTAMP) {
+ TextView tv = new TextView(getContext());
+ tv.setText(convertTimeToString(item.getTimestamp()));
+ mEndContainer.addView(tv);
+ itemCount++;
+ } else if (item.getType() == SliceItem.TYPE_SLICE) {
+ SliceItem[] subItems = item.getSlice().getItems();
+ for (int j = 0; j < subItems.length; j++) {
+ sliceItems.add(subItems[j]);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setSlice(Slice slice) {
+ setSliceItem(new SliceItem(slice, SliceItem.TYPE_SLICE, slice.getHints()));
+ }
+
+ /**
+ * @return Whether an icon was added.
+ */
+ private boolean addIcon(SliceItem sliceItem, int color, LinearLayout container) {
+ SliceItem image = null;
+ SliceItem action = null;
+ if (sliceItem.getType() == SliceItem.TYPE_ACTION) {
+ image = SliceQuery.find(sliceItem.getSlice(), SliceItem.TYPE_IMAGE);
+ action = sliceItem;
+ } else if (sliceItem.getType() == SliceItem.TYPE_IMAGE) {
+ image = sliceItem;
+ }
+ if (image != null) {
+ ImageView iv = new ImageView(getContext());
+ iv.setImageIcon(image.getIcon());
+ if (action != null) {
+ final SliceItem sliceAction = action;
+ iv.setOnClickListener(v -> AsyncTask.execute(
+ () -> {
+ try {
+ sliceAction.getAction().send();
+ } catch (CanceledException e) {
+ e.printStackTrace();
+ }
+ }));
+ iv.setBackground(SliceViewUtil.getDrawable(getContext(),
+ android.R.attr.selectableItemBackground));
+ }
+ if (color != -1 && !sliceItem.hasHint(Slice.HINT_NO_TINT)) {
+ iv.setColorFilter(color);
+ }
+ container.addView(iv);
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) iv.getLayoutParams();
+ lp.width = mIconSize;
+ lp.height = mIconSize;
+ lp.setMarginStart(mPadding);
+ return true;
+ }
+ return false;
+ }
+
+ private String convertTimeToString(long time) {
+ // TODO -- figure out what format(s) we support
+ Date date = new Date(time);
+ Format format = new SimpleDateFormat("MM dd yyyy HH:mm:ss");
+ return format.format(date);
+ }
+
+ private void resetViews() {
+ mStartContainer.removeAllViews();
+ mEndContainer.removeAllViews();
+ mTitleText.setText(null);
+ mSecondaryText.setText(null);
+ }
+}
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 24260c4..fba358c 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -299,7 +299,7 @@
private final Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
- private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<Builder>(3);
+ private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<>(3);
}
/**
@@ -440,7 +440,7 @@
mEllipsizeAt = null;
}
- mObjects = new PackedObjectVector<Directions>(1);
+ mObjects = new PackedObjectVector<>(1);
// Initial state is a single line with 0 characters (0 to 0), with top at 0 and bottom at
// whatever is natural, and undefined ellipsis.
@@ -1050,7 +1050,7 @@
private static class ChangeWatcher implements TextWatcher, SpanWatcher {
public ChangeWatcher(DynamicLayout layout) {
- mLayout = new WeakReference<DynamicLayout>(layout);
+ mLayout = new WeakReference<>(layout);
}
private void reflow(CharSequence s, int where, int before, int after) {
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 60fff73..ac5c2e9 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -319,8 +319,6 @@
private float getJustifyWidth(int lineNum) {
Alignment paraAlign = mAlignment;
- TabStops tabStops = null;
- boolean tabStopsIsInitialized = false;
int left = 0;
int right = mWidth;
@@ -371,10 +369,6 @@
}
}
- if (getLineContainsTab(lineNum)) {
- tabStops = new TabStops(TAB_INCREMENT, spans);
- }
-
final Alignment align;
if (paraAlign == Alignment.ALIGN_LEFT) {
align = (dir == DIR_LEFT_TO_RIGHT) ? Alignment.ALIGN_NORMAL : Alignment.ALIGN_OPPOSITE;
@@ -1423,7 +1417,6 @@
float dist = Math.abs(getHorizontal(max, primary) - horiz);
if (dist <= bestdist) {
- bestdist = dist;
best = max;
}
@@ -1570,7 +1563,7 @@
// XXX: we don't care about tabs
tl.set(mPaint, mText, lineStart, lineEnd, lineDir, directions, false, null);
caret = lineStart + tl.getOffsetToLeftRightOf(caret - lineStart, toLeft);
- tl = TextLine.recycle(tl);
+ TextLine.recycle(tl);
return caret;
}
@@ -1894,10 +1887,7 @@
int margin = 0;
- boolean isFirstParaLine = lineStart == 0 ||
- spanned.charAt(lineStart - 1) == '\n';
-
- boolean useFirstLineMargin = isFirstParaLine;
+ boolean useFirstLineMargin = lineStart == 0 || spanned.charAt(lineStart - 1) == '\n';
for (int i = 0; i < spans.length; i++) {
if (spans[i] instanceof LeadingMarginSpan2) {
int spStart = spanned.getSpanStart(spans[i]);
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 961cd8ee..4b6b6ae 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -433,7 +433,6 @@
* + addStyleRun (a text run, to be measured in native code)
* + addReplacementRun (a replacement run, width is given)
*
- * After measurement, nGetWidths() is valid if the widths are needed (eg for ellipsis).
* Run nComputeLineBreaks() to obtain line breaks for the paragraph.
*
* After all paragraphs, call finish() to release expensive buffers.
@@ -441,8 +440,6 @@
private Pair<String, long[]> getLocaleAndHyphenatorIfChanged(TextPaint paint) {
final LocaleList locales = paint.getTextLocales();
- final String languageTags;
- long[] hyphenators;
if (!locales.equals(mLocales)) {
mLocales = locales;
return new Pair(locales.toLanguageTags(), getHyphenators(locales));
@@ -521,7 +518,7 @@
private LocaleList mLocales;
- private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<Builder>(3);
+ private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<>(3);
}
public StaticLayout(CharSequence source, TextPaint paint,
@@ -866,10 +863,9 @@
spanEndCacheCount++;
}
- nGetWidths(b.mNativePtr, widths);
int breakCount = nComputeLineBreaks(b.mNativePtr, lineBreaks, lineBreaks.breaks,
lineBreaks.widths, lineBreaks.ascents, lineBreaks.descents, lineBreaks.flags,
- lineBreaks.breaks.length);
+ lineBreaks.breaks.length, widths);
final int[] breaks = lineBreaks.breaks;
final float[] lineWidths = lineBreaks.widths;
@@ -947,10 +943,10 @@
boolean moreChars = (endPos < bufEnd);
final int ascent = fallbackLineSpacing
- ? Math.min(fmAscent, (int) Math.round(ascents[breakIndex]))
+ ? Math.min(fmAscent, Math.round(ascents[breakIndex]))
: fmAscent;
final int descent = fallbackLineSpacing
- ? Math.max(fmDescent, (int) Math.round(descents[breakIndex]))
+ ? Math.max(fmDescent, Math.round(descents[breakIndex]))
: fmDescent;
v = out(source, here, endPos,
ascent, descent, fmTop, fmBottom,
@@ -1177,7 +1173,7 @@
mWorkPaint.set(paint);
do {
final float ellipsizedWidth = guessEllipsis(text, lineStart, lineEnd, widths,
- widthStart, tempAvail, where, line, textWidth, mWorkPaint, forceEllipsis, dir);
+ widthStart, tempAvail, where, line, mWorkPaint, forceEllipsis, dir);
if (ellipsizedWidth <= avail) {
lineFits = true;
} else {
@@ -1207,7 +1203,7 @@
// This method temporarily modifies the TextPaint passed to it, so the TextPaint passed to it
// should not be accessed while the method is running.
private float guessEllipsis(CharSequence text, int lineStart, int lineEnd, float[] widths,
- int widthStart, float avail, TextUtils.TruncateAt where, int line, float textWidth,
+ int widthStart, float avail, TextUtils.TruncateAt where, int line,
TextPaint paint, boolean forceEllipsis, int dir) {
final int savedHyphenEdit = paint.getHyphenEdit();
paint.setHyphenEdit(0);
@@ -1551,16 +1547,17 @@
@FloatRange(from = 0.0f) float width, @Nullable String languageTags,
@Nullable long[] hyphenators);
- private static native void nGetWidths(long nativePtr, float[] widths);
-
// populates LineBreaks and returns the number of breaks found
//
// the arrays inside the LineBreaks objects are passed in as well
// to reduce the number of JNI calls in the common case where the
// arrays do not have to be resized
+ // The individual character widths will be returned in charWidths. The length of charWidths must
+ // be at least the length of the text.
private static native int nComputeLineBreaks(long nativePtr, LineBreaks recycle,
int[] recycleBreaks, float[] recycleWidths, float[] recycleAscents,
- float[] recycleDescents, int[] recycleFlags, int recycleLength);
+ float[] recycleDescents, int[] recycleFlags, int recycleLength,
+ float[] charWidths);
private int mLineCount;
private int mTopPadding, mBottomPadding;
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 2dbff10..20c0ed8 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -73,7 +73,7 @@
new SpanSet<ReplacementSpan>(ReplacementSpan.class);
private final DecorationInfo mDecorationInfo = new DecorationInfo();
- private final ArrayList<DecorationInfo> mDecorations = new ArrayList();
+ private final ArrayList<DecorationInfo> mDecorations = new ArrayList<>();
private static final TextLine[] sCached = new TextLine[3];
@@ -340,14 +340,14 @@
boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
if (inSegment && advance) {
- return h += measureRun(segstart, offset, j, runIsRtl, fmi);
+ return h + measureRun(segstart, offset, j, runIsRtl, fmi);
}
float w = measureRun(segstart, j, j, runIsRtl, fmi);
h += advance ? w : -w;
if (inSegment) {
- return h += measureRun(segstart, offset, j, runIsRtl, null);
+ return h + measureRun(segstart, offset, j, runIsRtl, null);
}
if (codept == '\t') {
@@ -828,14 +828,14 @@
}
if (info.isUnderlineText) {
final float thickness =
- Math.max(((Paint) wp).getUnderlineThickness(), 1.0f);
+ Math.max(wp.getUnderlineThickness(), 1.0f);
drawStroke(wp, c, wp.getColor(), wp.getUnderlinePosition(), thickness,
decorationXLeft, decorationXRight, y);
}
if (info.isStrikeThruText) {
final float thickness =
- Math.max(((Paint) wp).getStrikeThruThickness(), 1.0f);
+ Math.max(wp.getStrikeThruThickness(), 1.0f);
drawStroke(wp, c, wp.getColor(), wp.getStrikeThruPosition(), thickness,
decorationXLeft, decorationXRight, y);
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3f710a8..0d1258d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1448,17 +1448,59 @@
/**
* <p>Enables low quality mode for the drawing cache.</p>
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
public static final int DRAWING_CACHE_QUALITY_LOW = 0x00080000;
/**
* <p>Enables high quality mode for the drawing cache.</p>
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
public static final int DRAWING_CACHE_QUALITY_HIGH = 0x00100000;
/**
* <p>Enables automatic quality mode for the drawing cache.</p>
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
public static final int DRAWING_CACHE_QUALITY_AUTO = 0x00000000;
private static final int[] DRAWING_CACHE_QUALITY_FLAGS = {
@@ -8907,7 +8949,21 @@
* @see #isDrawingCacheEnabled()
*
* @attr ref android.R.styleable#View_drawingCacheQuality
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
@DrawingCacheQuality
public int getDrawingCacheQuality() {
return mViewFlags & DRAWING_CACHE_QUALITY_MASK;
@@ -8925,7 +8981,21 @@
* @see #isDrawingCacheEnabled()
*
* @attr ref android.R.styleable#View_drawingCacheQuality
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
public void setDrawingCacheQuality(@DrawingCacheQuality int quality) {
setFlags(quality, DRAWING_CACHE_QUALITY_MASK);
}
@@ -15529,7 +15599,13 @@
* {@code dirty}.
*
* @param dirty the rectangle representing the bounds of the dirty region
+ *
+ * @deprecated The switch to hardware accelerated rendering in API 14 reduced
+ * the importance of the dirty rectangle. In API 21 the given rectangle is
+ * ignored entirely in favor of an internally-calculated area instead.
+ * Because of this, clients are encouraged to just call {@link #invalidate()}.
*/
+ @Deprecated
public void invalidate(Rect dirty) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
@@ -15550,7 +15626,13 @@
* @param t the top position of the dirty region
* @param r the right position of the dirty region
* @param b the bottom position of the dirty region
+ *
+ * @deprecated The switch to hardware accelerated rendering in API 14 reduced
+ * the importance of the dirty rectangle. In API 21 the given rectangle is
+ * ignored entirely in favor of an internally-calculated area instead.
+ * Because of this, clients are encouraged to just call {@link #invalidate()}.
*/
+ @Deprecated
public void invalidate(int l, int t, int r, int b) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
@@ -18091,7 +18173,21 @@
* @see #getDrawingCache()
* @see #buildDrawingCache()
* @see #setLayerType(int, android.graphics.Paint)
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
public void setDrawingCacheEnabled(boolean enabled) {
mCachingFailed = false;
setFlags(enabled ? DRAWING_CACHE_ENABLED : 0, DRAWING_CACHE_ENABLED);
@@ -18104,7 +18200,21 @@
*
* @see #setDrawingCacheEnabled(boolean)
* @see #getDrawingCache()
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
@ViewDebug.ExportedProperty(category = "drawing")
public boolean isDrawingCacheEnabled() {
return (mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED;
@@ -18245,7 +18355,21 @@
* @return A non-scaled bitmap representing this view or null if cache is disabled.
*
* @see #getDrawingCache(boolean)
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
public Bitmap getDrawingCache() {
return getDrawingCache(false);
}
@@ -18276,7 +18400,21 @@
* @see #isDrawingCacheEnabled()
* @see #buildDrawingCache(boolean)
* @see #destroyDrawingCache()
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
public Bitmap getDrawingCache(boolean autoScale) {
if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) {
return null;
@@ -18296,7 +18434,21 @@
* @see #setDrawingCacheEnabled(boolean)
* @see #buildDrawingCache()
* @see #getDrawingCache()
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
public void destroyDrawingCache() {
if (mDrawingCache != null) {
mDrawingCache.recycle();
@@ -18318,7 +18470,21 @@
* @see #setDrawingCacheEnabled(boolean)
* @see #buildDrawingCache()
* @see #getDrawingCache()
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
public void setDrawingCacheBackgroundColor(@ColorInt int color) {
if (color != mDrawingCacheBackgroundColor) {
mDrawingCacheBackgroundColor = color;
@@ -18330,7 +18496,21 @@
* @see #setDrawingCacheBackgroundColor(int)
*
* @return The background color to used for the drawing cache's bitmap
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
@ColorInt
public int getDrawingCacheBackgroundColor() {
return mDrawingCacheBackgroundColor;
@@ -18340,7 +18520,21 @@
* <p>Calling this method is equivalent to calling <code>buildDrawingCache(false)</code>.</p>
*
* @see #buildDrawingCache(boolean)
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
public void buildDrawingCache() {
buildDrawingCache(false);
}
@@ -18367,7 +18561,21 @@
*
* @see #getDrawingCache()
* @see #destroyDrawingCache()
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
public void buildDrawingCache(boolean autoScale) {
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || (autoScale ?
mDrawingCache == null : mUnscaledDrawingCache == null)) {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index b2e5a16..929beae 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -421,22 +421,78 @@
/**
* Used to indicate that no drawing cache should be kept in memory.
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
public static final int PERSISTENT_NO_CACHE = 0x0;
/**
* Used to indicate that the animation drawing cache should be kept in memory.
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
public static final int PERSISTENT_ANIMATION_CACHE = 0x1;
/**
* Used to indicate that the scrolling drawing cache should be kept in memory.
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
public static final int PERSISTENT_SCROLLING_CACHE = 0x2;
/**
* Used to indicate that all drawing caches should be kept in memory.
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
public static final int PERSISTENT_ALL_CACHES = 0x3;
// Layout Modes
@@ -3769,7 +3825,21 @@
* Enables or disables the drawing cache for each child of this view group.
*
* @param enabled true to enable the cache, false to dispose of it
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
protected void setChildrenDrawingCacheEnabled(boolean enabled) {
if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) {
final View[] children = mChildren;
@@ -6331,7 +6401,21 @@
* @return one or a combination of {@link #PERSISTENT_NO_CACHE},
* {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
* and {@link #PERSISTENT_ALL_CACHES}
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
@ViewDebug.ExportedProperty(category = "drawing", mapping = {
@ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"),
@ViewDebug.IntToString(from = PERSISTENT_ANIMATION_CACHE, to = "ANIMATION"),
@@ -6352,7 +6436,21 @@
* @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
* {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
* and {@link #PERSISTENT_ALL_CACHES}
+ *
+ * @deprecated The view drawing cache was largely made obsolete with the introduction of
+ * hardware-accelerated rendering in API 11. With hardware-acceleration, intermediate cache
+ * layers are largely unnecessary and can easily result in a net loss in performance due to the
+ * cost of creating and updating the layer. In the rare cases where caching layers are useful,
+ * such as for alpha animations, {@link #setLayerType(int, Paint)} handles this with hardware
+ * rendering. For software-rendered snapshots of a small part of the View hierarchy or
+ * individual Views it is recommended to create a {@link Canvas} from either a {@link Bitmap} or
+ * {@link android.graphics.Picture} and call {@link #draw(Canvas)} on the View. However these
+ * software-rendered usages are discouraged and have compatibility issues with hardware-only
+ * rendering features such as {@link android.graphics.Bitmap.Config#HARDWARE Config.HARDWARE}
+ * bitmaps, real-time shadows, and outline clipping. For screenshots of the UI for feedback
+ * reports or unit testing the {@link PixelCopy} API is recommended.
*/
+ @Deprecated
public void setPersistentDrawingCache(int drawingCacheToKeep) {
mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
}
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index 98f8dc8..69cc100 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -335,8 +335,8 @@
public abstract void setOnHardKeyboardStatusChangeListener(
OnHardKeyboardStatusChangeListener listener);
- /** Returns true if the stack with the input Id is currently visible. */
- public abstract boolean isStackVisible(int stackId);
+ /** Returns true if a stack in the windowing mode is currently visible. */
+ public abstract boolean isStackVisible(int windowingMode);
/**
* @return True if and only if the docked divider is currently in resize mode.
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index da72535..137e551 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -467,11 +467,8 @@
*/
public boolean isDimming();
- /**
- * @return the stack id this windows belongs to, or {@link StackId#INVALID_STACK_ID} if
- * not attached to any stack.
- */
- int getStackId();
+ /** @return the current windowing mode of this window. */
+ int getWindowingMode();
/**
* Returns true if the window is current in multi-windowing mode. i.e. it shares the
diff --git a/core/java/android/widget/SmartSelectSprite.java b/core/java/android/widget/SmartSelectSprite.java
index 7cbc494b..a391c6e 100644
--- a/core/java/android/widget/SmartSelectSprite.java
+++ b/core/java/android/widget/SmartSelectSprite.java
@@ -206,7 +206,7 @@
if (mRectangleBorderType == RectangleBorderType.OVERSHOOT) {
mDrawRect.left -= cornerRadius / 2;
- mDrawRect.right -= cornerRadius / 2;
+ mDrawRect.right += cornerRadius / 2;
} else {
switch (mExpansionDirection) {
case ExpansionDirection.CENTER:
@@ -437,6 +437,7 @@
RectangleWithTextSelectionLayout centerRectangle = null;
int startingOffset = 0;
+ int startingRectangleIndex = 0;
for (int index = 0; index < rectangleCount; ++index) {
final RectangleWithTextSelectionLayout rectangleWithTextSelectionLayout =
destinationRectangles.get(index);
@@ -446,6 +447,7 @@
break;
}
startingOffset += rectangle.width();
+ ++startingRectangleIndex;
}
if (centerRectangle == null) {
@@ -454,10 +456,6 @@
startingOffset += start.x - centerRectangle.getRectangle().left;
- final float centerRectangleHalfHeight = centerRectangle.getRectangle().height() / 2;
- final float startingOffsetLeft = startingOffset - centerRectangleHalfHeight;
- final float startingOffsetRight = startingOffset + centerRectangleHalfHeight;
-
final @RoundedRectangleShape.ExpansionDirection int[] expansionDirections =
generateDirections(centerRectangle, destinationRectangles);
@@ -482,6 +480,30 @@
final RectangleList rectangleList = new RectangleList(shapes);
final ShapeDrawable shapeDrawable = new ShapeDrawable(rectangleList);
+ final float startingOffsetLeft;
+ final float startingOffsetRight;
+
+ final RoundedRectangleShape startingRectangleShape = shapes.get(startingRectangleIndex);
+ final float cornerRadius = startingRectangleShape.getCornerRadius();
+ if (startingRectangleShape.mRectangleBorderType
+ == RoundedRectangleShape.RectangleBorderType.FIT) {
+ switch (startingRectangleShape.mExpansionDirection) {
+ case RoundedRectangleShape.ExpansionDirection.LEFT:
+ startingOffsetLeft = startingOffsetRight = startingOffset - cornerRadius / 2;
+ break;
+ case RoundedRectangleShape.ExpansionDirection.RIGHT:
+ startingOffsetLeft = startingOffsetRight = startingOffset + cornerRadius / 2;
+ break;
+ case RoundedRectangleShape.ExpansionDirection.CENTER: // fall through
+ default:
+ startingOffsetLeft = startingOffset - cornerRadius / 2;
+ startingOffsetRight = startingOffset + cornerRadius / 2;
+ break;
+ }
+ } else {
+ startingOffsetLeft = startingOffsetRight = startingOffset;
+ }
+
final Paint paint = shapeDrawable.getPaint();
paint.setColor(mStrokeColor);
paint.setStyle(Paint.Style.STROKE);
diff --git a/core/java/com/android/internal/alsa/AlsaDevicesParser.java b/core/java/com/android/internal/alsa/AlsaDevicesParser.java
index 7cdd897..6e3d596 100644
--- a/core/java/com/android/internal/alsa/AlsaDevicesParser.java
+++ b/core/java/com/android/internal/alsa/AlsaDevicesParser.java
@@ -258,7 +258,7 @@
return line.charAt(kIndex_CardDeviceField) == '[';
}
- public void scan() {
+ public boolean scan() {
mDeviceRecords.clear();
File devicesFile = new File(kDevicesFilePath);
@@ -274,11 +274,13 @@
}
}
reader.close();
+ return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
+ return false;
}
//
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index d64c9a1..4a181b2 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -20,6 +20,7 @@
import android.app.NotificationManager;
import android.content.Context;
import android.content.pm.ParceledListSlice;
+import android.media.AudioAttributes;
import android.os.RemoteException;
import android.provider.Settings;
@@ -47,6 +48,7 @@
public static String RETAIL_MODE = "RETAIL_MODE";
public static String USB = "USB";
public static String FOREGROUND_SERVICE = "FOREGROUND_SERVICE";
+ public static String HEAVY_WEIGHT_APP = "HEAVY_WEIGHT_APP";
public static void createAll(Context context) {
final NotificationManager nm = context.getSystemService(NotificationManager.class);
@@ -139,6 +141,17 @@
foregroundChannel.setBlockableSystem(true);
channelsList.add(foregroundChannel);
+ NotificationChannel heavyWeightChannel = new NotificationChannel(
+ HEAVY_WEIGHT_APP,
+ context.getString(R.string.notification_channel_heavy_weight_app),
+ NotificationManager.IMPORTANCE_DEFAULT);
+ heavyWeightChannel.setShowBadge(false);
+ heavyWeightChannel.setSound(null, new AudioAttributes.Builder()
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
+ .setUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT)
+ .build());
+ channelsList.add(heavyWeightChannel);
+
nm.createNotificationChannels(channelsList);
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index f085e29..15dc6f5 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -143,6 +143,9 @@
public static boolean checkWifiOnly(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(
Context.CONNECTIVITY_SERVICE);
+ if (cm == null) {
+ return false;
+ }
return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 0bd2981..dd07ddb 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -119,7 +119,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 166 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 167 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS;
@@ -341,8 +341,8 @@
protected final TimeBase mOnBatteryTimeBase = new TimeBase();
// These are the objects that will want to do something when the device
- // is unplugged from power *and* the screen is off.
- final TimeBase mOnBatteryScreenOffTimeBase = new TimeBase();
+ // is unplugged from power *and* the screen is off or doze.
+ protected final TimeBase mOnBatteryScreenOffTimeBase = new TimeBase();
// Set to true when we want to distribute CPU across wakelocks for the next
// CPU update, even if we aren't currently running wake locks.
@@ -436,8 +436,12 @@
public boolean mRecordAllHistory;
boolean mNoAutoReset;
- int mScreenState = Display.STATE_UNKNOWN;
- StopwatchTimer mScreenOnTimer;
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected int mScreenState = Display.STATE_UNKNOWN;
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected StopwatchTimer mScreenOnTimer;
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ protected StopwatchTimer mScreenDozeTimer;
int mScreenBrightnessBin = -1;
final StopwatchTimer[] mScreenBrightnessTimer = new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS];
@@ -583,12 +587,16 @@
int mHighDischargeAmountSinceCharge;
int mDischargeScreenOnUnplugLevel;
int mDischargeScreenOffUnplugLevel;
+ int mDischargeScreenDozeUnplugLevel;
int mDischargeAmountScreenOn;
int mDischargeAmountScreenOnSinceCharge;
int mDischargeAmountScreenOff;
int mDischargeAmountScreenOffSinceCharge;
+ int mDischargeAmountScreenDoze;
+ int mDischargeAmountScreenDozeSinceCharge;
private LongSamplingCounter mDischargeScreenOffCounter;
+ private LongSamplingCounter mDischargeScreenDozeCounter;
private LongSamplingCounter mDischargeCounter;
static final int MAX_LEVEL_STEPS = 200;
@@ -673,13 +681,18 @@
}
@Override
- public LongCounter getDischargeScreenOffCoulombCounter() {
- return mDischargeScreenOffCounter;
+ public long getUahDischarge(int which) {
+ return mDischargeCounter.getCountLocked(which);
}
@Override
- public LongCounter getDischargeCoulombCounter() {
- return mDischargeCounter;
+ public long getUahDischargeScreenOff(int which) {
+ return mDischargeScreenOffCounter.getCountLocked(which);
+ }
+
+ @Override
+ public long getUahDischargeScreenDoze(int which) {
+ return mDischargeScreenDozeCounter.getCountLocked(which);
}
@Override
@@ -3573,8 +3586,9 @@
mActiveHistoryStates2 = 0xffffffff;
}
- public void updateTimeBasesLocked(boolean unplugged, boolean screenOff, long uptime,
+ public void updateTimeBasesLocked(boolean unplugged, int screenState, long uptime,
long realtime) {
+ final boolean screenOff = !isScreenOn(screenState);
final boolean updateOnBatteryTimeBase = unplugged != mOnBatteryTimeBase.isRunning();
final boolean updateOnBatteryScreenOffTimeBase =
(unplugged && screenOff) != mOnBatteryScreenOffTimeBase.isRunning();
@@ -3591,20 +3605,22 @@
updateRpmStatsLocked(); // if either OnBattery or OnBatteryScreenOff timebase changes.
}
if (DEBUG_ENERGY_CPU) {
- Slog.d(TAG, "Updating cpu time because screen is now " + (screenOff ? "off" : "on")
+ Slog.d(TAG, "Updating cpu time because screen is now "
+ + Display.stateToString(screenState)
+ " and battery is " + (unplugged ? "on" : "off"));
}
updateCpuTimeLocked();
mOnBatteryTimeBase.setRunning(unplugged, uptime, realtime);
- mOnBatteryScreenOffTimeBase.setRunning(unplugged && screenOff, uptime, realtime);
- for (int i = mUidStats.size() - 1; i >= 0; --i) {
- final Uid u = mUidStats.valueAt(i);
- if (updateOnBatteryTimeBase) {
- u.updateOnBatteryBgTimeBase(uptime, realtime);
+ if (updateOnBatteryTimeBase) {
+ for (int i = mUidStats.size() - 1; i >= 0; --i) {
+ mUidStats.valueAt(i).updateOnBatteryBgTimeBase(uptime, realtime);
}
- if (updateOnBatteryScreenOffTimeBase) {
- u.updateOnBatteryScreenOffBgTimeBase(uptime, realtime);
+ }
+ if (updateOnBatteryScreenOffTimeBase) {
+ mOnBatteryScreenOffTimeBase.setRunning(unplugged && screenOff, uptime, realtime);
+ for (int i = mUidStats.size() - 1; i >= 0; --i) {
+ mUidStats.valueAt(i).updateOnBatteryScreenOffBgTimeBase(uptime, realtime);
}
}
}
@@ -3864,8 +3880,10 @@
}
public void setPretendScreenOff(boolean pretendScreenOff) {
- mPretendScreenOff = pretendScreenOff;
- noteScreenStateLocked(pretendScreenOff ? Display.STATE_OFF : Display.STATE_ON);
+ if (mPretendScreenOff != pretendScreenOff) {
+ mPretendScreenOff = pretendScreenOff;
+ noteScreenStateLocked(pretendScreenOff ? Display.STATE_OFF : Display.STATE_ON);
+ }
}
private String mInitialAcquireWakeName;
@@ -4195,54 +4213,58 @@
}
}
- if (state == Display.STATE_ON) {
- // Screen turning on.
- final long elapsedRealtime = mClocks.elapsedRealtime();
- final long uptime = mClocks.uptimeMillis();
+ final long elapsedRealtime = mClocks.elapsedRealtime();
+ final long uptime = mClocks.uptimeMillis();
+
+ boolean updateHistory = false;
+ if (isScreenDoze(state)) {
+ mHistoryCur.states |= HistoryItem.STATE_SCREEN_DOZE_FLAG;
+ mScreenDozeTimer.startRunningLocked(elapsedRealtime);
+ updateHistory = true;
+ } else if (isScreenDoze(oldState)) {
+ mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_DOZE_FLAG;
+ mScreenDozeTimer.stopRunningLocked(elapsedRealtime);
+ updateHistory = true;
+ }
+ if (isScreenOn(state)) {
mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime, uptime);
mScreenOnTimer.startRunningLocked(elapsedRealtime);
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin].startRunningLocked(elapsedRealtime);
}
-
- updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), false,
- mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000);
-
- // Fake a wake lock, so we consider the device waked as long
- // as the screen is on.
- noteStartWakeLocked(-1, -1, "screen", null, WAKE_TYPE_PARTIAL, false,
- elapsedRealtime, uptime);
-
- // Update discharge amounts.
- if (mOnBatteryInternal) {
- updateDischargeScreenLevelsLocked(false, true);
- }
- } else if (oldState == Display.STATE_ON) {
- // Screen turning off or dozing.
- final long elapsedRealtime = mClocks.elapsedRealtime();
- final long uptime = mClocks.uptimeMillis();
+ updateHistory = true;
+ } else if (isScreenOn(oldState)) {
mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: "
+ Integer.toHexString(mHistoryCur.states));
- addHistoryRecordLocked(elapsedRealtime, uptime);
mScreenOnTimer.stopRunningLocked(elapsedRealtime);
if (mScreenBrightnessBin >= 0) {
mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(elapsedRealtime);
}
-
+ updateHistory = true;
+ }
+ if (updateHistory) {
+ if (DEBUG_HISTORY) Slog.v(TAG, "Screen state to: "
+ + Display.stateToString(state));
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ }
+ if (isScreenOn(state)) {
+ updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state,
+ mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000);
+ // Fake a wake lock, so we consider the device waked as long as the screen is on.
+ noteStartWakeLocked(-1, -1, "screen", null, WAKE_TYPE_PARTIAL, false,
+ elapsedRealtime, uptime);
+ } else if (isScreenOn(oldState)) {
noteStopWakeLocked(-1, -1, "screen", "screen", WAKE_TYPE_PARTIAL,
elapsedRealtime, uptime);
-
- updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), true,
+ updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state,
mClocks.uptimeMillis() * 1000, elapsedRealtime * 1000);
-
- // Update discharge amounts.
- if (mOnBatteryInternal) {
- updateDischargeScreenLevelsLocked(true, false);
- }
+ }
+ // Update discharge amounts.
+ if (mOnBatteryInternal) {
+ updateDischargeScreenLevelsLocked(oldState, state);
}
}
}
@@ -5391,12 +5413,24 @@
return mScreenOnTimer.getCountLocked(which);
}
+ @Override public long getScreenDozeTime(long elapsedRealtimeUs, int which) {
+ return mScreenDozeTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
+ }
+
+ @Override public int getScreenDozeCount(int which) {
+ return mScreenDozeTimer.getCountLocked(which);
+ }
+
@Override public long getScreenBrightnessTime(int brightnessBin,
long elapsedRealtimeUs, int which) {
return mScreenBrightnessTimer[brightnessBin].getTotalTimeLocked(
elapsedRealtimeUs, which);
}
+ @Override public Timer getScreenBrightnessTimer(int brightnessBin) {
+ return mScreenBrightnessTimer[brightnessBin];
+ }
+
@Override public long getInteractiveTime(long elapsedRealtimeUs, int which) {
return mInteractiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@@ -5490,10 +5524,18 @@
elapsedRealtimeUs, which);
}
+ @Override public Timer getPhoneSignalScanningTimer() {
+ return mPhoneSignalScanningTimer;
+ }
+
@Override public int getPhoneSignalStrengthCount(int strengthBin, int which) {
return mPhoneSignalStrengthsTimer[strengthBin].getCountLocked(which);
}
+ @Override public Timer getPhoneSignalStrengthTimer(int strengthBin) {
+ return mPhoneSignalStrengthsTimer[strengthBin];
+ }
+
@Override public long getPhoneDataConnectionTime(int dataType,
long elapsedRealtimeUs, int which) {
return mPhoneDataConnectionsTimer[dataType].getTotalTimeLocked(
@@ -5504,6 +5546,10 @@
return mPhoneDataConnectionsTimer[dataType].getCountLocked(which);
}
+ @Override public Timer getPhoneDataConnectionTimer(int dataType) {
+ return mPhoneDataConnectionsTimer[dataType];
+ }
+
@Override public long getMobileRadioActiveTime(long elapsedRealtimeUs, int which) {
return mMobileRadioActiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
}
@@ -5542,6 +5588,10 @@
return mWifiStateTimer[wifiState].getCountLocked(which);
}
+ @Override public Timer getWifiStateTimer(int wifiState) {
+ return mWifiStateTimer[wifiState];
+ }
+
@Override public long getWifiSupplStateTime(int state,
long elapsedRealtimeUs, int which) {
return mWifiSupplStateTimer[state].getTotalTimeLocked(
@@ -5552,6 +5602,10 @@
return mWifiSupplStateTimer[state].getCountLocked(which);
}
+ @Override public Timer getWifiSupplStateTimer(int state) {
+ return mWifiSupplStateTimer[state];
+ }
+
@Override public long getWifiSignalStrengthTime(int strengthBin,
long elapsedRealtimeUs, int which) {
return mWifiSignalStrengthsTimer[strengthBin].getTotalTimeLocked(
@@ -5562,6 +5616,10 @@
return mWifiSignalStrengthsTimer[strengthBin].getCountLocked(which);
}
+ @Override public Timer getWifiSignalStrengthTimer(int strengthBin) {
+ return mWifiSignalStrengthsTimer[strengthBin];
+ }
+
@Override
public ControllerActivityCounter getBluetoothControllerActivity() {
return mBluetoothActivity;
@@ -8829,6 +8887,7 @@
mHandler = new MyHandler(handler.getLooper());
mStartCount++;
mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase);
+ mScreenDozeTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i] = new StopwatchTimer(mClocks, null, -100-i, null,
mOnBatteryTimeBase);
@@ -8887,6 +8946,7 @@
mCameraOnTimer = new StopwatchTimer(mClocks, null, -13, null, mOnBatteryTimeBase);
mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase);
mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase);
+ mDischargeScreenDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase);
mOnBattery = mOnBatteryInternal = false;
long uptime = mClocks.uptimeMillis() * 1000;
@@ -9430,8 +9490,16 @@
return mCharging;
}
- public boolean isScreenOn() {
- return mScreenState == Display.STATE_ON;
+ public boolean isScreenOn(int state) {
+ return state == Display.STATE_ON || state == Display.STATE_VR;
+ }
+
+ public boolean isScreenOff(int state) {
+ return state == Display.STATE_OFF;
+ }
+
+ public boolean isScreenDoze(int state) {
+ return state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND;
}
void initTimes(long uptime, long realtime) {
@@ -9451,9 +9519,12 @@
mDischargeAmountScreenOnSinceCharge = 0;
mDischargeAmountScreenOff = 0;
mDischargeAmountScreenOffSinceCharge = 0;
+ mDischargeAmountScreenDoze = 0;
+ mDischargeAmountScreenDozeSinceCharge = 0;
mDischargeStepTracker.init();
mChargeStepTracker.init();
mDischargeScreenOffCounter.reset(false);
+ mDischargeScreenDozeCounter.reset(false);
mDischargeCounter.reset(false);
}
@@ -9471,15 +9542,22 @@
mOnBatteryTimeBase.reset(uptime, realtime);
mOnBatteryScreenOffTimeBase.reset(uptime, realtime);
if ((mHistoryCur.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) == 0) {
- if (mScreenState == Display.STATE_ON) {
+ if (isScreenOn(mScreenState)) {
mDischargeScreenOnUnplugLevel = mHistoryCur.batteryLevel;
+ mDischargeScreenDozeUnplugLevel = 0;
+ mDischargeScreenOffUnplugLevel = 0;
+ } else if (isScreenDoze(mScreenState)) {
+ mDischargeScreenOnUnplugLevel = 0;
+ mDischargeScreenDozeUnplugLevel = mHistoryCur.batteryLevel;
mDischargeScreenOffUnplugLevel = 0;
} else {
mDischargeScreenOnUnplugLevel = 0;
+ mDischargeScreenDozeUnplugLevel = 0;
mDischargeScreenOffUnplugLevel = mHistoryCur.batteryLevel;
}
mDischargeAmountScreenOn = 0;
mDischargeAmountScreenOff = 0;
+ mDischargeAmountScreenDoze = 0;
}
initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
}
@@ -9490,6 +9568,7 @@
mStartCount = 0;
initTimes(uptimeMillis * 1000, elapsedRealtimeMillis * 1000);
mScreenOnTimer.reset(false);
+ mScreenDozeTimer.reset(false);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].reset(false);
}
@@ -9626,33 +9705,52 @@
}
}
- void updateDischargeScreenLevelsLocked(boolean oldScreenOn, boolean newScreenOn) {
- if (oldScreenOn) {
+ void updateDischargeScreenLevelsLocked(int oldState, int newState) {
+ updateOldDischargeScreenLevelLocked(oldState);
+ updateNewDischargeScreenLevelLocked(newState);
+ }
+
+ private void updateOldDischargeScreenLevelLocked(int state) {
+ if (isScreenOn(state)) {
int diff = mDischargeScreenOnUnplugLevel - mDischargeCurrentLevel;
if (diff > 0) {
mDischargeAmountScreenOn += diff;
mDischargeAmountScreenOnSinceCharge += diff;
}
- } else {
+ } else if (isScreenDoze(state)) {
+ int diff = mDischargeScreenDozeUnplugLevel - mDischargeCurrentLevel;
+ if (diff > 0) {
+ mDischargeAmountScreenDoze += diff;
+ mDischargeAmountScreenDozeSinceCharge += diff;
+ }
+ } else if (isScreenOff(state)){
int diff = mDischargeScreenOffUnplugLevel - mDischargeCurrentLevel;
if (diff > 0) {
mDischargeAmountScreenOff += diff;
mDischargeAmountScreenOffSinceCharge += diff;
}
}
- if (newScreenOn) {
+ }
+
+ private void updateNewDischargeScreenLevelLocked(int state) {
+ if (isScreenOn(state)) {
mDischargeScreenOnUnplugLevel = mDischargeCurrentLevel;
mDischargeScreenOffUnplugLevel = 0;
- } else {
+ mDischargeScreenDozeUnplugLevel = 0;
+ } else if (isScreenDoze(state)){
mDischargeScreenOnUnplugLevel = 0;
+ mDischargeScreenDozeUnplugLevel = mDischargeCurrentLevel;
+ mDischargeScreenOffUnplugLevel = 0;
+ } else if (isScreenOff(state)) {
+ mDischargeScreenOnUnplugLevel = 0;
+ mDischargeScreenDozeUnplugLevel = 0;
mDischargeScreenOffUnplugLevel = mDischargeCurrentLevel;
}
}
public void pullPendingStateUpdatesLocked() {
if (mOnBatteryInternal) {
- final boolean screenOn = mScreenState == Display.STATE_ON;
- updateDischargeScreenLevelsLocked(screenOn, screenOn);
+ updateDischargeScreenLevelsLocked(mScreenState, mScreenState);
}
}
@@ -10785,8 +10883,8 @@
return false;
}
- void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery,
- final int oldStatus, final int level, final int chargeUAh) {
+ protected void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime,
+ final boolean onBattery, final int oldStatus, final int level, final int chargeUAh) {
boolean doWrite = false;
Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE);
m.arg1 = onBattery ? 1 : 0;
@@ -10794,7 +10892,7 @@
final long uptime = mSecUptime * 1000;
final long realtime = mSecRealtime * 1000;
- final boolean screenOn = mScreenState == Display.STATE_ON;
+ final int screenState = mScreenState;
if (onBattery) {
// We will reset our status if we are unplugging after the
// battery was last full, or the level is at 100, or
@@ -10870,16 +10968,23 @@
}
addHistoryRecordLocked(mSecRealtime, mSecUptime);
mDischargeCurrentLevel = mDischargeUnplugLevel = level;
- if (screenOn) {
+ if (isScreenOn(screenState)) {
mDischargeScreenOnUnplugLevel = level;
+ mDischargeScreenDozeUnplugLevel = 0;
+ mDischargeScreenOffUnplugLevel = 0;
+ } else if (isScreenDoze(screenState)) {
+ mDischargeScreenOnUnplugLevel = 0;
+ mDischargeScreenDozeUnplugLevel = level;
mDischargeScreenOffUnplugLevel = 0;
} else {
mDischargeScreenOnUnplugLevel = 0;
+ mDischargeScreenDozeUnplugLevel = 0;
mDischargeScreenOffUnplugLevel = level;
}
mDischargeAmountScreenOn = 0;
+ mDischargeAmountScreenDoze = 0;
mDischargeAmountScreenOff = 0;
- updateTimeBasesLocked(true, !screenOn, uptime, realtime);
+ updateTimeBasesLocked(true, screenState, uptime, realtime);
} else {
mLastChargingStateLevel = level;
mOnBattery = mOnBatteryInternal = false;
@@ -10894,8 +10999,8 @@
mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1;
mHighDischargeAmountSinceCharge += mDischargeUnplugLevel-level;
}
- updateDischargeScreenLevelsLocked(screenOn, screenOn);
- updateTimeBasesLocked(false, !screenOn, uptime, realtime);
+ updateDischargeScreenLevelsLocked(screenState, screenState);
+ updateTimeBasesLocked(false, screenState, uptime, realtime);
mChargeStepTracker.init();
mLastChargeStepLevel = level;
mMaxChargeStepLevel = level;
@@ -11012,6 +11117,9 @@
final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh;
mDischargeCounter.addCountLocked(chargeDiff);
mDischargeScreenOffCounter.addCountLocked(chargeDiff);
+ if (isScreenDoze(mScreenState)) {
+ mDischargeScreenDozeCounter.addCountLocked(chargeDiff);
+ }
}
mHistoryCur.batteryChargeUAh = chargeUAh;
setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level, chargeUAh);
@@ -11054,6 +11162,9 @@
final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh;
mDischargeCounter.addCountLocked(chargeDiff);
mDischargeScreenOffCounter.addCountLocked(chargeDiff);
+ if (isScreenDoze(mScreenState)) {
+ mDischargeScreenDozeCounter.addCountLocked(chargeDiff);
+ }
}
mHistoryCur.batteryChargeUAh = chargeUAh;
changed = true;
@@ -11362,10 +11473,11 @@
return dischargeAmount;
}
+ @Override
public int getDischargeAmountScreenOn() {
synchronized(this) {
int val = mDischargeAmountScreenOn;
- if (mOnBattery && mScreenState == Display.STATE_ON
+ if (mOnBattery && isScreenOn(mScreenState)
&& mDischargeCurrentLevel < mDischargeScreenOnUnplugLevel) {
val += mDischargeScreenOnUnplugLevel-mDischargeCurrentLevel;
}
@@ -11373,10 +11485,11 @@
}
}
+ @Override
public int getDischargeAmountScreenOnSinceCharge() {
synchronized(this) {
int val = mDischargeAmountScreenOnSinceCharge;
- if (mOnBattery && mScreenState == Display.STATE_ON
+ if (mOnBattery && isScreenOn(mScreenState)
&& mDischargeCurrentLevel < mDischargeScreenOnUnplugLevel) {
val += mDischargeScreenOnUnplugLevel-mDischargeCurrentLevel;
}
@@ -11384,23 +11497,51 @@
}
}
+ @Override
public int getDischargeAmountScreenOff() {
synchronized(this) {
int val = mDischargeAmountScreenOff;
- if (mOnBattery && mScreenState != Display.STATE_ON
+ if (mOnBattery && isScreenOff(mScreenState)
&& mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) {
val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel;
}
+ // For backward compatibility, doze discharge is counted into screen off.
+ return val + getDischargeAmountScreenDoze();
+ }
+ }
+
+ @Override
+ public int getDischargeAmountScreenOffSinceCharge() {
+ synchronized(this) {
+ int val = mDischargeAmountScreenOffSinceCharge;
+ if (mOnBattery && isScreenOff(mScreenState)
+ && mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) {
+ val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel;
+ }
+ // For backward compatibility, doze discharge is counted into screen off.
+ return val + getDischargeAmountScreenDozeSinceCharge();
+ }
+ }
+
+ @Override
+ public int getDischargeAmountScreenDoze() {
+ synchronized(this) {
+ int val = mDischargeAmountScreenDoze;
+ if (mOnBattery && isScreenDoze(mScreenState)
+ && mDischargeCurrentLevel < mDischargeScreenDozeUnplugLevel) {
+ val += mDischargeScreenDozeUnplugLevel-mDischargeCurrentLevel;
+ }
return val;
}
}
- public int getDischargeAmountScreenOffSinceCharge() {
+ @Override
+ public int getDischargeAmountScreenDozeSinceCharge() {
synchronized(this) {
- int val = mDischargeAmountScreenOffSinceCharge;
- if (mOnBattery && mScreenState != Display.STATE_ON
- && mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) {
- val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel;
+ int val = mDischargeAmountScreenDozeSinceCharge;
+ if (mOnBattery && isScreenDoze(mScreenState)
+ && mDischargeCurrentLevel < mDischargeScreenDozeUnplugLevel) {
+ val += mDischargeScreenDozeUnplugLevel-mDischargeCurrentLevel;
}
return val;
}
@@ -11759,12 +11900,14 @@
mHighDischargeAmountSinceCharge = in.readInt();
mDischargeAmountScreenOnSinceCharge = in.readInt();
mDischargeAmountScreenOffSinceCharge = in.readInt();
+ mDischargeAmountScreenDozeSinceCharge = in.readInt();
mDischargeStepTracker.readFromParcel(in);
mChargeStepTracker.readFromParcel(in);
mDailyDischargeStepTracker.readFromParcel(in);
mDailyChargeStepTracker.readFromParcel(in);
mDischargeCounter.readSummaryFromParcelLocked(in);
mDischargeScreenOffCounter.readSummaryFromParcelLocked(in);
+ mDischargeScreenDozeCounter.readSummaryFromParcelLocked(in);
int NPKG = in.readInt();
if (NPKG > 0) {
mDailyPackageChanges = new ArrayList<>(NPKG);
@@ -11787,6 +11930,7 @@
mScreenState = Display.STATE_UNKNOWN;
mScreenOnTimer.readSummaryFromParcelLocked(in);
+ mScreenDozeTimer.readSummaryFromParcelLocked(in);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].readSummaryFromParcelLocked(in);
}
@@ -12180,12 +12324,14 @@
out.writeInt(getHighDischargeAmountSinceCharge());
out.writeInt(getDischargeAmountScreenOnSinceCharge());
out.writeInt(getDischargeAmountScreenOffSinceCharge());
+ out.writeInt(getDischargeAmountScreenDozeSinceCharge());
mDischargeStepTracker.writeToParcel(out);
mChargeStepTracker.writeToParcel(out);
mDailyDischargeStepTracker.writeToParcel(out);
mDailyChargeStepTracker.writeToParcel(out);
mDischargeCounter.writeSummaryFromParcelLocked(out);
mDischargeScreenOffCounter.writeSummaryFromParcelLocked(out);
+ mDischargeScreenDozeCounter.writeSummaryFromParcelLocked(out);
if (mDailyPackageChanges != null) {
final int NPKG = mDailyPackageChanges.size();
out.writeInt(NPKG);
@@ -12203,6 +12349,7 @@
out.writeLong(mNextMaxDailyDeadline);
mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+ mScreenDozeTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
}
@@ -12635,6 +12782,7 @@
mScreenState = Display.STATE_UNKNOWN;
mScreenOnTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase, in);
+ mScreenDozeTimer = new StopwatchTimer(mClocks, null, -1, null, mOnBatteryTimeBase, in);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i] = new StopwatchTimer(mClocks, null, -100-i, null,
mOnBatteryTimeBase, in);
@@ -12671,7 +12819,7 @@
mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
mMobileRadioActiveTimer = new StopwatchTimer(mClocks, null, -400, null,
mOnBatteryTimeBase, in);
- mMobileRadioActivePerAppTimer = new StopwatchTimer(mClocks, null, -401, null,
+ mMobileRadioActivePerAppTimer = new StopwatchTimer(mClocks, null, -401, null,
mOnBatteryTimeBase, in);
mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
@@ -12728,10 +12876,13 @@
mDischargeAmountScreenOnSinceCharge = in.readInt();
mDischargeAmountScreenOff = in.readInt();
mDischargeAmountScreenOffSinceCharge = in.readInt();
+ mDischargeAmountScreenDoze = in.readInt();
+ mDischargeAmountScreenDozeSinceCharge = in.readInt();
mDischargeStepTracker.readFromParcel(in);
mChargeStepTracker.readFromParcel(in);
mDischargeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
- mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
+ mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase, in);
+ mDischargeScreenDozeCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
mLastWriteTime = in.readLong();
mRpmStats.clear();
@@ -12848,6 +12999,7 @@
mOnBatteryScreenOffTimeBase.writeToParcel(out, uSecUptime, uSecRealtime);
mScreenOnTimer.writeToParcel(out, uSecRealtime);
+ mScreenDozeTimer.writeToParcel(out, uSecRealtime);
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
mScreenBrightnessTimer[i].writeToParcel(out, uSecRealtime);
}
@@ -12910,10 +13062,13 @@
out.writeInt(mDischargeAmountScreenOnSinceCharge);
out.writeInt(mDischargeAmountScreenOff);
out.writeInt(mDischargeAmountScreenOffSinceCharge);
+ out.writeInt(mDischargeAmountScreenDoze);
+ out.writeInt(mDischargeAmountScreenDozeSinceCharge);
mDischargeStepTracker.writeToParcel(out);
mChargeStepTracker.writeToParcel(out);
mDischargeCounter.writeToParcel(out);
mDischargeScreenOffCounter.writeToParcel(out);
+ mDischargeScreenDozeCounter.writeToParcel(out);
out.writeLong(mLastWriteTime);
out.writeInt(mRpmStats.size());
@@ -13020,8 +13175,10 @@
pw.println("mOnBatteryScreenOffTimeBase:");
mOnBatteryScreenOffTimeBase.dump(pw, " ");
Printer pr = new PrintWriterPrinter(pw);
- pr.println("*** Screen timer:");
+ pr.println("*** Screen on timer:");
mScreenOnTimer.logState(pr, " ");
+ pr.println("*** Screen doze timer:");
+ mScreenDozeTimer.logState(pr, " ");
for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
pr.println("*** Screen brightness #" + i + ":");
mScreenBrightnessTimer[i].logState(pr, " ");
diff --git a/core/java/com/android/internal/os/LoggingPrintStream.java b/core/java/com/android/internal/os/LoggingPrintStream.java
index f14394a..d27874c 100644
--- a/core/java/com/android/internal/os/LoggingPrintStream.java
+++ b/core/java/com/android/internal/os/LoggingPrintStream.java
@@ -28,12 +28,15 @@
import java.util.Formatter;
import java.util.Locale;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* A print stream which logs output line by line.
*
* {@hide}
*/
-abstract class LoggingPrintStream extends PrintStream {
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+public abstract class LoggingPrintStream extends PrintStream {
private final StringBuilder builder = new StringBuilder();
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 82eb1ab..03603e4 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -58,10 +58,12 @@
void onNotificationError(String pkg, String tag, int id,
int uid, int initialPid, String message, int userId);
void onClearAllNotifications(int userId);
- void onNotificationClear(String pkg, String tag, int id, int userId);
+ void onNotificationClear(String pkg, String tag, int id, int userId, String key, int dismissalSurface);
void onNotificationVisibilityChanged( in NotificationVisibility[] newlyVisibleKeys,
in NotificationVisibility[] noLongerVisibleKeys);
void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded);
+ void onNotificationDirectReplied(String key);
+ void onNotificationSettingsViewed(String key);
void setSystemUiVisibility(int vis, int mask, String cause);
void onGlobalActionsShown();
diff --git a/core/java/com/android/internal/util/BitUtils.java b/core/java/com/android/internal/util/BitUtils.java
index 28f12eb..ba80aea 100644
--- a/core/java/com/android/internal/util/BitUtils.java
+++ b/core/java/com/android/internal/util/BitUtils.java
@@ -93,6 +93,10 @@
return s & 0xffff;
}
+ public static int uint16(byte hi, byte lo) {
+ return ((hi & 0xff) << 8) | (lo & 0xff);
+ }
+
public static long uint32(int i) {
return i & 0xffffffffL;
}
diff --git a/core/java/com/android/internal/widget/LinearLayoutManager.java b/core/java/com/android/internal/widget/LinearLayoutManager.java
index d82c746..0000a74 100644
--- a/core/java/com/android/internal/widget/LinearLayoutManager.java
+++ b/core/java/com/android/internal/widget/LinearLayoutManager.java
@@ -168,10 +168,6 @@
/**
* Constructor used when layout manager is set in XML by RecyclerView attribute
* "layoutManager". Defaults to vertical orientation.
- *
- * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation
- * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout
- * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd
*/
public LinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index ad05a51..635eed3 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1051,7 +1051,7 @@
}
// Read the bitmap blob.
- size_t size = bitmap->getSize();
+ size_t size = bitmap->computeByteSize();
android::Parcel::ReadableBlob blob;
android::status_t status = p->readBlob(size, &blob);
if (status) {
@@ -1188,7 +1188,7 @@
p->allowFds() ? "allowed" : "forbidden");
#endif
- size_t size = bitmap.getSize();
+ size_t size = bitmap.computeByteSize();
android::Parcel::WritableBlob blob;
status = p->writeBlob(size, mutableCopy, &blob);
if (status) {
@@ -1411,7 +1411,7 @@
android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);
// the java side has already checked that buffer is large enough
- memcpy(abp.pointer(), src, bitmap.getSize());
+ memcpy(abp.pointer(), src, bitmap.computeByteSize());
}
}
@@ -1424,7 +1424,7 @@
if (NULL != dst) {
android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);
// the java side has already checked that buffer is large enough
- memcpy(dst, abp.pointer(), bitmap.getSize());
+ memcpy(dst, abp.pointer(), bitmap.computeByteSize());
bitmap.notifyPixelsChanged();
}
}
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 64e12b4..5990d7b 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -174,13 +174,12 @@
return false;
}
- const int64_t size64 = info.getSafeSize64(bitmap->rowBytes());
- if (!sk_64_isS32(size64)) {
+ const size_t size = info.computeByteSize(bitmap->rowBytes());
+ if (size > SK_MaxS32) {
ALOGW("bitmap is too large");
return false;
}
- const size_t size = sk_64_asS32(size64);
if (size > mSize) {
ALOGW("bitmap marked for reuse (%u bytes) can't fit new bitmap "
"(%zu bytes)", mSize, size);
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 5ea501e..90cc7bb 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -645,7 +645,7 @@
const int maxHeight = SkTMax(bitmap->height(), mRecycledBitmap->info().height());
const SkImageInfo maxInfo = bitmap->info().makeWH(maxWidth, maxHeight);
const size_t rowBytes = maxInfo.minRowBytes();
- const size_t bytesNeeded = maxInfo.getSafeSize(rowBytes);
+ const size_t bytesNeeded = maxInfo.computeByteSize(rowBytes);
if (bytesNeeded <= mRecycledBytes) {
// Here we take advantage of reconfigure() to reset the rowBytes
// of mRecycledBitmap. It is very important that we pass in
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index e4540c0..d5f2a5c 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -30,14 +30,14 @@
static jlong Typeface_createFromTypeface(JNIEnv* env, jobject, jlong familyHandle, jint style) {
Typeface* family = reinterpret_cast<Typeface*>(familyHandle);
- Typeface* face = Typeface::createRelative(family, (SkTypeface::Style)style);
+ Typeface* face = Typeface::createRelative(family, (Typeface::Style)style);
// TODO: the following logic shouldn't be necessary, the above should always succeed.
// Try to find the closest matching font, using the standard heuristic
if (NULL == face) {
- face = Typeface::createRelative(family, (SkTypeface::Style)(style ^ SkTypeface::kItalic));
+ face = Typeface::createRelative(family, (Typeface::Style)(style ^ Typeface::kItalic));
}
for (int i = 0; NULL == face && i < 4; i++) {
- face = Typeface::createRelative(family, (SkTypeface::Style)i);
+ face = Typeface::createRelative(family, (Typeface::Style)i);
}
return reinterpret_cast<jlong>(face);
}
@@ -78,7 +78,7 @@
static jint Typeface_getStyle(JNIEnv* env, jobject obj, jlong faceHandle) {
Typeface* face = reinterpret_cast<Typeface*>(faceHandle);
- return face->fSkiaStyle;
+ return face->fAPIStyle;
}
static jint Typeface_getWeight(JNIEnv* env, jobject obj, jlong faceHandle) {
diff --git a/core/jni/android_app_admin_SecurityLog.cpp b/core/jni/android_app_admin_SecurityLog.cpp
index 5c45b4b..b3bcaa0 100644
--- a/core/jni/android_app_admin_SecurityLog.cpp
+++ b/core/jni/android_app_admin_SecurityLog.cpp
@@ -14,183 +14,26 @@
* limitations under the License.
*/
-#include <fcntl.h>
-
-#include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
-#include "jni.h"
+#include <log/log_id.h>
#include <private/android_logger.h>
-// The size of the tag number comes out of the payload size.
-#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+
+#include "core_jni_helpers.h"
+#include "eventlog_helper.h"
namespace android {
-static jclass gCollectionClass;
-static jmethodID gCollectionAddID;
-
-static jclass gEventClass;
-static jmethodID gEventInitID;
-
-static jclass gIntegerClass;
-static jfieldID gIntegerValueID;
-
-static jclass gLongClass;
-static jfieldID gLongValueID;
-
-static jclass gFloatClass;
-static jfieldID gFloatValueID;
-
-static jclass gStringClass;
-
+constexpr char kSecurityLogEventClass[] = "android/app/admin/SecurityLog$SecurityEvent";
+template class EventLogHelper<log_id_t::LOG_ID_SECURITY, kSecurityLogEventClass>;
+using SLog = EventLogHelper<log_id_t::LOG_ID_SECURITY, kSecurityLogEventClass>;
static jboolean android_app_admin_SecurityLog_isLoggingEnabled(JNIEnv* env,
jobject /* clazz */) {
return (bool)__android_log_security();
}
-static jint android_app_admin_SecurityLog_writeEvent_String(JNIEnv* env,
- jobject /* clazz */,
- jint tag, jstring value) {
- uint8_t buf[MAX_EVENT_PAYLOAD];
-
- // Don't throw NPE -- I feel like it's sort of mean for a logging function
- // to be all crashy if you pass in NULL -- but make the NULL value explicit.
- const char *str = value != NULL ? env->GetStringUTFChars(value, NULL) : "NULL";
- uint32_t len = strlen(str);
- size_t max = sizeof(buf) - sizeof(len) - 2; // Type byte, final newline
- if (len > max) len = max;
-
- buf[0] = EVENT_TYPE_STRING;
- memcpy(&buf[1], &len, sizeof(len));
- memcpy(&buf[1 + sizeof(len)], str, len);
- buf[1 + sizeof(len) + len] = '\n';
-
- if (value != NULL) env->ReleaseStringUTFChars(value, str);
- return __android_log_security_bwrite(tag, buf, 2 + sizeof(len) + len);
-}
-
-static jint android_app_admin_SecurityLog_writeEvent_Array(JNIEnv* env, jobject clazz,
- jint tag, jobjectArray value) {
- if (value == NULL) {
- return android_app_admin_SecurityLog_writeEvent_String(env, clazz, tag, NULL);
- }
-
- uint8_t buf[MAX_EVENT_PAYLOAD];
- const size_t max = sizeof(buf) - 1; // leave room for final newline
- size_t pos = 2; // Save room for type tag & array count
-
- jsize copied = 0, num = env->GetArrayLength(value);
- for (; copied < num && copied < 255; ++copied) {
- jobject item = env->GetObjectArrayElement(value, copied);
- if (item == NULL || env->IsInstanceOf(item, gStringClass)) {
- if (pos + 1 + sizeof(jint) > max) break;
- const char *str = item != NULL ? env->GetStringUTFChars((jstring) item, NULL) : "NULL";
- jint len = strlen(str);
- if (pos + 1 + sizeof(len) + len > max) len = max - pos - 1 - sizeof(len);
- buf[pos++] = EVENT_TYPE_STRING;
- memcpy(&buf[pos], &len, sizeof(len));
- memcpy(&buf[pos + sizeof(len)], str, len);
- pos += sizeof(len) + len;
- if (item != NULL) env->ReleaseStringUTFChars((jstring) item, str);
- } else if (env->IsInstanceOf(item, gIntegerClass)) {
- jint intVal = env->GetIntField(item, gIntegerValueID);
- if (pos + 1 + sizeof(intVal) > max) break;
- buf[pos++] = EVENT_TYPE_INT;
- memcpy(&buf[pos], &intVal, sizeof(intVal));
- pos += sizeof(intVal);
- } else if (env->IsInstanceOf(item, gLongClass)) {
- jlong longVal = env->GetLongField(item, gLongValueID);
- if (pos + 1 + sizeof(longVal) > max) break;
- buf[pos++] = EVENT_TYPE_LONG;
- memcpy(&buf[pos], &longVal, sizeof(longVal));
- pos += sizeof(longVal);
- } else if (env->IsInstanceOf(item, gFloatClass)) {
- jfloat floatVal = env->GetFloatField(item, gFloatValueID);
- if (pos + 1 + sizeof(floatVal) > max) break;
- buf[pos++] = EVENT_TYPE_FLOAT;
- memcpy(&buf[pos], &floatVal, sizeof(floatVal));
- pos += sizeof(floatVal);
- } else {
- jniThrowException(env,
- "java/lang/IllegalArgumentException",
- "Invalid payload item type");
- return -1;
- }
- env->DeleteLocalRef(item);
- }
-
- buf[0] = EVENT_TYPE_LIST;
- buf[1] = copied;
- buf[pos++] = '\n';
- return __android_log_security_bwrite(tag, buf, pos);
-}
-
-static void readEvents(JNIEnv* env, int loggerMode, jlong startTime, jobject out) {
- struct logger_list *logger_list;
- if (startTime) {
- logger_list = android_logger_list_alloc_time(loggerMode,
- log_time(startTime / NS_PER_SEC, startTime % NS_PER_SEC), 0);
- } else {
- logger_list = android_logger_list_alloc(loggerMode, 0, 0);
- }
- if (!logger_list) {
- jniThrowIOException(env, errno);
- return;
- }
-
- if (!android_logger_open(logger_list, LOG_ID_SECURITY)) {
- jniThrowIOException(env, errno);
- android_logger_list_free(logger_list);
- return;
- }
-
- while (1) {
- log_msg log_msg;
- int ret = android_logger_list_read(logger_list, &log_msg);
-
- if (ret == 0) {
- break;
- }
- if (ret < 0) {
- if (ret == -EINTR) {
- continue;
- }
- if (ret == -EINVAL) {
- jniThrowException(env, "java/io/IOException", "Event too short");
- } else if (ret != -EAGAIN) {
- jniThrowIOException(env, -ret); // Will throw on return
- }
- break;
- }
-
- if (log_msg.id() != LOG_ID_SECURITY) {
- continue;
- }
-
- jsize len = ret;
- jbyteArray array = env->NewByteArray(len);
- if (array == NULL) {
- break;
- }
-
- jbyte *bytes = env->GetByteArrayElements(array, NULL);
- memcpy(bytes, log_msg.buf, len);
- env->ReleaseByteArrayElements(array, bytes, 0);
-
- jobject event = env->NewObject(gEventClass, gEventInitID, array);
- if (event == NULL) {
- break;
- }
-
- env->CallBooleanMethod(out, gCollectionAddID, event);
- env->DeleteLocalRef(event);
- env->DeleteLocalRef(array);
- }
-
- android_logger_list_close(logger_list);
-}
-
static void android_app_admin_SecurityLog_readEvents(JNIEnv* env, jobject /* clazz */,
jobject out) {
@@ -198,7 +41,7 @@
jniThrowNullPointerException(env, NULL);
return;
}
- readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, out);
+ SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, out);
}
static void android_app_admin_SecurityLog_readEventsSince(JNIEnv* env, jobject /* clazz */,
@@ -209,7 +52,7 @@
jniThrowNullPointerException(env, NULL);
return;
}
- readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, timestamp, out);
+ SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, timestamp, out);
}
static void android_app_admin_SecurityLog_readPreviousEvents(JNIEnv* env, jobject /* clazz */,
@@ -219,7 +62,7 @@
jniThrowNullPointerException(env, NULL);
return;
}
- readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_PSTORE, 0, out);
+ SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_PSTORE, 0, out);
}
static void android_app_admin_SecurityLog_readEventsOnWrapping(JNIEnv* env, jobject /* clazz */,
@@ -229,7 +72,8 @@
jniThrowNullPointerException(env, NULL);
return;
}
- readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP, timestamp, out);
+ SLog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP, timestamp,
+ out);
}
/*
@@ -243,11 +87,11 @@
},
{ "writeEvent",
"(ILjava/lang/String;)I",
- (void*) android_app_admin_SecurityLog_writeEvent_String
+ (void*) SLog::writeEventString
},
{ "writeEvent",
"(I[Ljava/lang/Object;)I",
- (void*) android_app_admin_SecurityLog_writeEvent_Array
+ (void*) SLog::writeEventArray
},
{ "readEvents",
"(Ljava/util/Collection;)V",
@@ -267,41 +111,8 @@
},
};
-static struct { const char *name; jclass *clazz; } gClasses[] = {
- { "android/app/admin/SecurityLog$SecurityEvent", &gEventClass },
- { "java/lang/Integer", &gIntegerClass },
- { "java/lang/Long", &gLongClass },
- { "java/lang/Float", &gFloatClass },
- { "java/lang/String", &gStringClass },
- { "java/util/Collection", &gCollectionClass },
-};
-
-static struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = {
- { &gIntegerClass, "value", "I", &gIntegerValueID },
- { &gLongClass, "value", "J", &gLongValueID },
- { &gFloatClass, "value", "F", &gFloatValueID },
-};
-
-static struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = {
- { &gEventClass, "<init>", "([B)V", &gEventInitID },
- { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID },
-};
-
int register_android_app_admin_SecurityLog(JNIEnv* env) {
- for (int i = 0; i < NELEM(gClasses); ++i) {
- jclass clazz = FindClassOrDie(env, gClasses[i].name);
- *gClasses[i].clazz = MakeGlobalRefOrDie(env, clazz);
- }
-
- for (int i = 0; i < NELEM(gFields); ++i) {
- *gFields[i].id = GetFieldIDOrDie(env,
- *gFields[i].c, gFields[i].name, gFields[i].ft);
- }
-
- for (int i = 0; i < NELEM(gMethods); ++i) {
- *gMethods[i].id = GetMethodIDOrDie(env,
- *gMethods[i].c, gMethods[i].name, gMethods[i].mt);
- }
+ SLog::Init(env);
return RegisterMethodsOrDie(
env,
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp
index 83ffeff..1f7277a 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_StaticLayout.cpp
@@ -166,7 +166,7 @@
jobject recycle, jintArray recycleBreaks,
jfloatArray recycleWidths, jfloatArray recycleAscents,
jfloatArray recycleDescents, jintArray recycleFlags,
- jint recycleLength) {
+ jint recycleLength, jfloatArray charWidths) {
minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
size_t nBreaks = b->computeBreaks();
@@ -175,6 +175,8 @@
recycleFlags, recycleLength, nBreaks, b->getBreaks(), b->getWidths(), b->getAscents(),
b->getDescents(), b->getFlags());
+ env->SetFloatArrayRegion(charWidths, 0, b->size(), b->charWidths());
+
b->finish();
return static_cast<jint>(nBreaks);
@@ -256,11 +258,6 @@
b->addReplacement(start, end, width, langTagsString.get(), makeHyphenators(env, hyphenators));
}
-static void nGetWidths(JNIEnv* env, jclass, jlong nativePtr, jfloatArray widths) {
- minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
- env->SetFloatArrayRegion(widths, 0, b->size(), b->charWidths());
-}
-
static const JNINativeMethod gMethods[] = {
// TODO performance: many of these are candidates for fast jni, awaiting guidance
{"nNewBuilder", "()J", (void*) nNewBuilder},
@@ -269,8 +266,7 @@
{"nSetupParagraph", "(J[CIFIF[IIIIZ[I[I[II)V", (void*) nSetupParagraph},
{"nAddStyleRun", "(JJIIZLjava/lang/String;[J)V", (void*) nAddStyleRun},
{"nAddReplacementRun", "(JIIFLjava/lang/String;[J)V", (void*) nAddReplacementRun},
- {"nGetWidths", "(J[F)V", (void*) nGetWidths},
- {"nComputeLineBreaks", "(JLandroid/text/StaticLayout$LineBreaks;[I[F[F[F[II)I",
+ {"nComputeLineBreaks", "(JLandroid/text/StaticLayout$LineBreaks;[I[F[F[F[II[F)I",
(void*) nComputeLineBreaks}
};
diff --git a/core/jni/android_util_EventLog.cpp b/core/jni/android_util_EventLog.cpp
index 9fd7c40..3b5a144 100644
--- a/core/jni/android_util_EventLog.cpp
+++ b/core/jni/android_util_EventLog.cpp
@@ -14,214 +14,20 @@
* limitations under the License.
*/
-#include <fcntl.h>
-
-#include <log/log_event_list.h>
-
-#include <log/log.h>
+#include <android-base/macros.h>
+#include <log/log_id.h>
#include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
#include "jni.h"
-#define UNUSED __attribute__((__unused__))
+#include "core_jni_helpers.h"
+#include "eventlog_helper.h"
namespace android {
-static jclass gCollectionClass;
-static jmethodID gCollectionAddID;
-
-static jclass gEventClass;
-static jmethodID gEventInitID;
-
-static jclass gIntegerClass;
-static jfieldID gIntegerValueID;
-
-static jclass gLongClass;
-static jfieldID gLongValueID;
-
-static jclass gFloatClass;
-static jfieldID gFloatValueID;
-
-static jclass gStringClass;
-
-/*
- * In class android.util.EventLog:
- * static native int writeEvent(int tag, int value)
- */
-static jint android_util_EventLog_writeEvent_Integer(JNIEnv* env UNUSED,
- jobject clazz UNUSED,
- jint tag, jint value)
-{
- android_log_event_list ctx(tag);
- ctx << (int32_t)value;
- return ctx.write();
-}
-
-/*
- * In class android.util.EventLog:
- * static native int writeEvent(long tag, long value)
- */
-static jint android_util_EventLog_writeEvent_Long(JNIEnv* env UNUSED,
- jobject clazz UNUSED,
- jint tag, jlong value)
-{
- android_log_event_list ctx(tag);
- ctx << (int64_t)value;
- return ctx.write();
-}
-
-/*
- * In class android.util.EventLog:
- * static native int writeEvent(long tag, float value)
- */
-static jint android_util_EventLog_writeEvent_Float(JNIEnv* env UNUSED,
- jobject clazz UNUSED,
- jint tag, jfloat value)
-{
- android_log_event_list ctx(tag);
- ctx << (float)value;
- return ctx.write();
-}
-
-/*
- * In class android.util.EventLog:
- * static native int writeEvent(int tag, String value)
- */
-static jint android_util_EventLog_writeEvent_String(JNIEnv* env,
- jobject clazz UNUSED,
- jint tag, jstring value) {
- android_log_event_list ctx(tag);
- // Don't throw NPE -- I feel like it's sort of mean for a logging function
- // to be all crashy if you pass in NULL -- but make the NULL value explicit.
- if (value != NULL) {
- const char *str = env->GetStringUTFChars(value, NULL);
- ctx << str;
- env->ReleaseStringUTFChars(value, str);
- } else {
- ctx << "NULL";
- }
- return ctx.write();
-}
-
-/*
- * In class android.util.EventLog:
- * static native int writeEvent(long tag, Object... value)
- */
-static jint android_util_EventLog_writeEvent_Array(JNIEnv* env, jobject clazz,
- jint tag, jobjectArray value) {
- android_log_event_list ctx(tag);
-
- if (value == NULL) {
- ctx << "[NULL]";
- return ctx.write();
- }
-
- jsize copied = 0, num = env->GetArrayLength(value);
- for (; copied < num && copied < 255; ++copied) {
- if (ctx.status()) break;
- jobject item = env->GetObjectArrayElement(value, copied);
- if (item == NULL) {
- ctx << "NULL";
- } else if (env->IsInstanceOf(item, gStringClass)) {
- const char *str = env->GetStringUTFChars((jstring) item, NULL);
- ctx << str;
- env->ReleaseStringUTFChars((jstring) item, str);
- } else if (env->IsInstanceOf(item, gIntegerClass)) {
- ctx << (int32_t)env->GetIntField(item, gIntegerValueID);
- } else if (env->IsInstanceOf(item, gLongClass)) {
- ctx << (int64_t)env->GetLongField(item, gLongValueID);
- } else if (env->IsInstanceOf(item, gFloatClass)) {
- ctx << (float)env->GetFloatField(item, gFloatValueID);
- } else {
- jniThrowException(env,
- "java/lang/IllegalArgumentException",
- "Invalid payload item type");
- return -1;
- }
- env->DeleteLocalRef(item);
- }
- return ctx.write();
-}
-
-static void readEvents(JNIEnv* env, int loggerMode, jintArray tags, jlong startTime, jobject out) {
- struct logger_list *logger_list;
- if (startTime) {
- logger_list = android_logger_list_alloc_time(loggerMode,
- log_time(startTime / NS_PER_SEC, startTime % NS_PER_SEC), 0);
- } else {
- logger_list = android_logger_list_alloc(loggerMode, 0, 0);
- }
- if (!logger_list) {
- jniThrowIOException(env, errno);
- return;
- }
-
- if (!android_logger_open(logger_list, LOG_ID_EVENTS)) {
- jniThrowIOException(env, errno);
- android_logger_list_free(logger_list);
- return;
- }
-
- jsize tagLength = env->GetArrayLength(tags);
- jint *tagValues = env->GetIntArrayElements(tags, NULL);
-
- while (1) {
- log_msg log_msg;
- int ret = android_logger_list_read(logger_list, &log_msg);
-
- if (ret == 0) {
- break;
- }
- if (ret < 0) {
- if (ret == -EINTR) {
- continue;
- }
- if (ret == -EINVAL) {
- jniThrowException(env, "java/io/IOException", "Event too short");
- } else if (ret != -EAGAIN) {
- jniThrowIOException(env, -ret); // Will throw on return
- }
- break;
- }
-
- if (log_msg.id() != LOG_ID_EVENTS) {
- continue;
- }
-
- int32_t tag = * (int32_t *) log_msg.msg();
-
- int found = 0;
- for (int i = 0; !found && i < tagLength; ++i) {
- found = (tag == tagValues[i]);
- }
-
- if (found) {
- jsize len = ret;
- jbyteArray array = env->NewByteArray(len);
- if (array == NULL) {
- break;
- }
-
- jbyte *bytes = env->GetByteArrayElements(array, NULL);
- memcpy(bytes, log_msg.buf, len);
- env->ReleaseByteArrayElements(array, bytes, 0);
-
- jobject event = env->NewObject(gEventClass, gEventInitID, array);
- if (event == NULL) {
- break;
- }
-
- env->CallBooleanMethod(out, gCollectionAddID, event);
- env->DeleteLocalRef(event);
- env->DeleteLocalRef(array);
- }
- }
-
- android_logger_list_close(logger_list);
-
- env->ReleaseIntArrayElements(tags, tagValues, 0);
-}
+constexpr char kEventLogEventClass[] = "android/util/EventLog$Event";
+template class EventLogHelper<log_id_t::LOG_ID_EVENTS, kEventLogEventClass>;
+using ELog = EventLogHelper<log_id_t::LOG_ID_EVENTS, kEventLogEventClass>;
/*
* In class android.util.EventLog:
@@ -229,7 +35,7 @@
*
* Reads events from the event log
*/
-static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz UNUSED,
+static void android_util_EventLog_readEvents(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED,
jintArray tags,
jobject out) {
@@ -238,7 +44,7 @@
return;
}
- readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, tags, 0, out);
+ ELog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, tags, 0, out);
}
/*
* In class android.util.EventLog:
@@ -246,7 +52,7 @@
*
* Reads events from the event log, blocking until events after timestamp are to be overwritten.
*/
-static void android_util_EventLog_readEventsOnWrapping(JNIEnv* env, jobject clazz UNUSED,
+static void android_util_EventLog_readEventsOnWrapping(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED,
jintArray tags,
jlong timestamp,
jobject out) {
@@ -254,8 +60,8 @@
jniThrowNullPointerException(env, NULL);
return;
}
- readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP,
- tags, timestamp, out);
+ ELog::readEvents(env, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK | ANDROID_LOG_WRAP, tags,
+ timestamp, out);
}
/*
@@ -263,17 +69,11 @@
*/
static const JNINativeMethod gRegisterMethods[] = {
/* name, signature, funcPtr */
- { "writeEvent", "(II)I", (void*) android_util_EventLog_writeEvent_Integer },
- { "writeEvent", "(IJ)I", (void*) android_util_EventLog_writeEvent_Long },
- { "writeEvent", "(IF)I", (void*) android_util_EventLog_writeEvent_Float },
- { "writeEvent",
- "(ILjava/lang/String;)I",
- (void*) android_util_EventLog_writeEvent_String
- },
- { "writeEvent",
- "(I[Ljava/lang/Object;)I",
- (void*) android_util_EventLog_writeEvent_Array
- },
+ { "writeEvent", "(II)I", (void*) ELog::writeEventInteger },
+ { "writeEvent", "(IJ)I", (void*) ELog::writeEventLong },
+ { "writeEvent", "(IF)I", (void*) ELog::writeEventFloat },
+ { "writeEvent", "(ILjava/lang/String;)I", (void*) ELog::writeEventString },
+ { "writeEvent", "(I[Ljava/lang/Object;)I", (void*) ELog::writeEventArray },
{ "readEvents",
"([ILjava/util/Collection;)V",
(void*) android_util_EventLog_readEvents
@@ -284,41 +84,8 @@
},
};
-static struct { const char *name; jclass *clazz; } gClasses[] = {
- { "android/util/EventLog$Event", &gEventClass },
- { "java/lang/Integer", &gIntegerClass },
- { "java/lang/Long", &gLongClass },
- { "java/lang/Float", &gFloatClass },
- { "java/lang/String", &gStringClass },
- { "java/util/Collection", &gCollectionClass },
-};
-
-static struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = {
- { &gIntegerClass, "value", "I", &gIntegerValueID },
- { &gLongClass, "value", "J", &gLongValueID },
- { &gFloatClass, "value", "F", &gFloatValueID },
-};
-
-static struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = {
- { &gEventClass, "<init>", "([B)V", &gEventInitID },
- { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID },
-};
-
int register_android_util_EventLog(JNIEnv* env) {
- for (int i = 0; i < NELEM(gClasses); ++i) {
- jclass clazz = FindClassOrDie(env, gClasses[i].name);
- *gClasses[i].clazz = MakeGlobalRefOrDie(env, clazz);
- }
-
- for (int i = 0; i < NELEM(gFields); ++i) {
- *gFields[i].id = GetFieldIDOrDie(env,
- *gFields[i].c, gFields[i].name, gFields[i].ft);
- }
-
- for (int i = 0; i < NELEM(gMethods); ++i) {
- *gMethods[i].id = GetMethodIDOrDie(env,
- *gMethods[i].c, gMethods[i].name, gMethods[i].mt);
- }
+ ELog::Init(env);
return RegisterMethodsOrDie(
env,
diff --git a/core/jni/eventlog_helper.h b/core/jni/eventlog_helper.h
new file mode 100644
index 0000000..3a05195
--- /dev/null
+++ b/core/jni/eventlog_helper.h
@@ -0,0 +1,272 @@
+/*
+ * 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.
+ */
+
+#ifndef FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_
+#define FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_
+
+#include <memory>
+
+#include <fcntl.h>
+
+#include <android-base/macros.h>
+#include <log/log_event_list.h>
+
+#include <log/log.h>
+
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include "core_jni_helpers.h"
+#include "jni.h"
+
+namespace android {
+
+template <log_id_t LogID, const char* EventClassDescriptor>
+class EventLogHelper {
+public:
+ static void Init(JNIEnv* env) {
+ struct { const char *name; jclass *clazz; } gClasses[] = {
+ { EventClassDescriptor, &gEventClass },
+ { "java/lang/Integer", &gIntegerClass },
+ { "java/lang/Long", &gLongClass },
+ { "java/lang/Float", &gFloatClass },
+ { "java/lang/String", &gStringClass },
+ { "java/util/Collection", &gCollectionClass },
+ };
+ struct { jclass *c; const char *name, *ft; jfieldID *id; } gFields[] = {
+ { &gIntegerClass, "value", "I", &gIntegerValueID },
+ { &gLongClass, "value", "J", &gLongValueID },
+ { &gFloatClass, "value", "F", &gFloatValueID },
+ };
+ struct { jclass *c; const char *name, *mt; jmethodID *id; } gMethods[] = {
+ { &gEventClass, "<init>", "([B)V", &gEventInitID },
+ { &gCollectionClass, "add", "(Ljava/lang/Object;)Z", &gCollectionAddID },
+ };
+
+ for (size_t i = 0; i < NELEM(gClasses); ++i) {
+ ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, gClasses[i].name));
+ *gClasses[i].clazz = MakeGlobalRefOrDie(env, clazz.get());
+ }
+ for (size_t i = 0; i < NELEM(gFields); ++i) {
+ *gFields[i].id = GetFieldIDOrDie(env,
+ *gFields[i].c, gFields[i].name, gFields[i].ft);
+ }
+
+ for (size_t i = 0; i < NELEM(gMethods); ++i) {
+ *gMethods[i].id = GetMethodIDOrDie(env,
+ *gMethods[i].c, gMethods[i].name, gMethods[i].mt);
+ }
+ }
+
+ static jint writeEventInteger(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED,
+ jint tag, jint value) {
+ android_log_event_list ctx(tag);
+ ctx << (int32_t)value;
+ return ctx.write(LogID);
+ }
+ static jint writeEventLong(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED,
+ jint tag, jlong value) {
+ android_log_event_list ctx(tag);
+ ctx << (int64_t)value;
+ return ctx.write(LogID);
+ }
+ static jint writeEventFloat(JNIEnv* env ATTRIBUTE_UNUSED, jobject clazz ATTRIBUTE_UNUSED,
+ jint tag, jfloat value) {
+ android_log_event_list ctx(tag);
+ ctx << (float)value;
+ return ctx.write(LogID);
+ }
+ static jint writeEventString(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jint tag,
+ jstring value) {
+ android_log_event_list ctx(tag);
+ // Don't throw NPE -- I feel like it's sort of mean for a logging function
+ // to be all crashy if you pass in NULL -- but make the NULL value explicit.
+ ctx << (value != nullptr ? ScopedUtfChars(env, value).c_str() : "NULL");
+ return ctx.write(LogID);
+ }
+ static jint writeEventArray(JNIEnv* env, jobject clazz ATTRIBUTE_UNUSED, jint tag,
+ jobjectArray value) {
+ android_log_event_list ctx(tag);
+
+ if (value == nullptr) {
+ ctx << "[NULL]";
+ return ctx.write(LogID);
+ }
+
+ jsize copied = 0, num = env->GetArrayLength(value);
+ for (; copied < num && copied < 255; ++copied) {
+ if (ctx.status()) break;
+ ScopedLocalRef<jobject> item(env, env->GetObjectArrayElement(value, copied));
+ if (item == nullptr) {
+ ctx << "NULL";
+ } else if (env->IsInstanceOf(item.get(), gStringClass)) {
+ ctx << ScopedUtfChars(env, (jstring) item.get()).c_str();
+ } else if (env->IsInstanceOf(item.get(), gIntegerClass)) {
+ ctx << (int32_t)env->GetIntField(item.get(), gIntegerValueID);
+ } else if (env->IsInstanceOf(item.get(), gLongClass)) {
+ ctx << (int64_t)env->GetLongField(item.get(), gLongValueID);
+ } else if (env->IsInstanceOf(item.get(), gFloatClass)) {
+ ctx << (float)env->GetFloatField(item.get(), gFloatValueID);
+ } else {
+ jniThrowException(env,
+ "java/lang/IllegalArgumentException",
+ "Invalid payload item type");
+ return -1;
+ }
+ }
+ return ctx.write(LogID);
+ }
+
+ static void readEvents(JNIEnv* env, int loggerMode, jlong startTime, jobject out) {
+ readEvents(env, loggerMode, nullptr, startTime, out);
+ }
+
+ static void readEvents(JNIEnv* env, int loggerMode, jintArray jTags, jlong startTime,
+ jobject out) {
+ std::unique_ptr<struct logger_list, decltype(&android_logger_list_close)> logger_list(
+ nullptr, android_logger_list_close);
+ if (startTime) {
+ logger_list.reset(android_logger_list_alloc_time(loggerMode,
+ log_time(startTime / NS_PER_SEC, startTime % NS_PER_SEC), 0));
+ } else {
+ logger_list.reset(android_logger_list_alloc(loggerMode, 0, 0));
+ }
+ if (!logger_list) {
+ jniThrowIOException(env, errno);
+ return;
+ }
+
+ if (!android_logger_open(logger_list.get(), LogID)) {
+ jniThrowIOException(env, errno);
+ return;
+ }
+
+ ScopedIntArrayRO tags(env);
+ if (jTags != nullptr) {
+ tags.reset(jTags);
+ }
+
+ while (1) {
+ log_msg log_msg;
+ int ret = android_logger_list_read(logger_list.get(), &log_msg);
+
+ if (ret == 0) {
+ return;
+ }
+ if (ret < 0) {
+ if (ret == -EINTR) {
+ continue;
+ }
+ if (ret == -EINVAL) {
+ jniThrowException(env, "java/io/IOException", "Event too short");
+ } else if (ret != -EAGAIN) {
+ jniThrowIOException(env, -ret); // Will throw on return
+ }
+ return;
+ }
+
+ if (log_msg.id() != LogID) {
+ continue;
+ }
+
+ int32_t tag = * (int32_t *) log_msg.msg();
+
+ if (jTags != nullptr) {
+ bool found = false;
+ for (size_t i = 0; !found && i < tags.size(); ++i) {
+ found = (tag == tags[i]);
+ }
+ if (!found) {
+ continue;
+ }
+ }
+
+ jsize len = ret;
+ ScopedLocalRef<jbyteArray> array(env, env->NewByteArray(len));
+ if (array == nullptr) {
+ return;
+ }
+
+ {
+ ScopedByteArrayRW bytes(env, array.get());
+ memcpy(bytes.get(), log_msg.buf, len);
+ }
+
+ ScopedLocalRef<jobject> event(env,
+ env->NewObject(gEventClass, gEventInitID, array.get()));
+ if (event == nullptr) {
+ return;
+ }
+
+ env->CallBooleanMethod(out, gCollectionAddID, event.get());
+ if (env->ExceptionCheck() == JNI_TRUE) {
+ return;
+ }
+ }
+ }
+
+private:
+ static jclass gCollectionClass;
+ static jmethodID gCollectionAddID;
+
+ static jclass gEventClass;
+ static jmethodID gEventInitID;
+
+ static jclass gIntegerClass;
+ static jfieldID gIntegerValueID;
+
+ static jclass gLongClass;
+ static jfieldID gLongValueID;
+
+ static jclass gFloatClass;
+ static jfieldID gFloatValueID;
+
+ static jclass gStringClass;
+};
+
+// Explicit instantiation declarations.
+template <log_id_t LogID, const char* EventClassDescriptor>
+jclass EventLogHelper<LogID, EventClassDescriptor>::gCollectionClass;
+template <log_id_t LogID, const char* EventClassDescriptor>
+jmethodID EventLogHelper<LogID, EventClassDescriptor>::gCollectionAddID;
+
+template <log_id_t LogID, const char* EventClassDescriptor>
+jclass EventLogHelper<LogID, EventClassDescriptor>::gEventClass;
+template <log_id_t LogID, const char* EventClassDescriptor>
+jmethodID EventLogHelper<LogID, EventClassDescriptor>::gEventInitID;
+
+template <log_id_t LogID, const char* EventClassDescriptor>
+jclass EventLogHelper<LogID, EventClassDescriptor>::gIntegerClass;
+template <log_id_t LogID, const char* EventClassDescriptor>
+jfieldID EventLogHelper<LogID, EventClassDescriptor>::gIntegerValueID;
+
+template <log_id_t LogID, const char* EventClassDescriptor>
+jclass EventLogHelper<LogID, EventClassDescriptor>::gLongClass;
+template <log_id_t LogID, const char* EventClassDescriptor>
+jfieldID EventLogHelper<LogID, EventClassDescriptor>::gLongValueID;
+
+template <log_id_t LogID, const char* EventClassDescriptor>
+jclass EventLogHelper<LogID, EventClassDescriptor>::gFloatClass;
+template <log_id_t LogID, const char* EventClassDescriptor>
+jfieldID EventLogHelper<LogID, EventClassDescriptor>::gFloatValueID;
+
+template <log_id_t LogID, const char* EventClassDescriptor>
+jclass EventLogHelper<LogID, EventClassDescriptor>::gStringClass;
+
+} // namespace android
+
+#endif // FRAMEWORKS_BASE_CORE_JNI_EVENTLOG_HELPER_H_
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
new file mode 100644
index 0000000..8d85038
--- /dev/null
+++ b/core/proto/android/os/batterystats.proto
@@ -0,0 +1,448 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+option java_multiple_files = true;
+
+package android.os;
+
+import "frameworks/base/core/proto/android/telephony/signalstrength.proto";
+
+message BatteryStatsProto {
+ int32 report_version = 1;
+ int64 parcel_version = 2;
+ string start_platform_version = 3;
+ string end_platform_version = 4;
+ BatteryHistoryProto history = 5;
+ repeated UidProto uids = 6;
+ SystemProto system = 7;
+}
+
+message BatteryHistoryProto {
+}
+
+message ControllerActivityProto {
+ // Time (milliseconds) spent in the idle state.
+ int64 idle_duration_ms = 1;
+ // Time (milliseconds) spent in the receive state.
+ int64 rx_duration_ms = 2;
+ // Total power (mAh) consumed by the controller in all states. The value may
+ // always be 0 if the device doesn't support power calculations.
+ int64 power_mah = 3;
+
+ // Represents a transmit level, where each level may draw a different amount
+ // of power. The levels themselves are controller-specific (and may possibly
+ // be device specific...yet to be confirmed).
+ message TxLevel {
+ // Transmit level. Higher levels draw more power.
+ int32 level = 1;
+ // Time spent in this specific transmit level state.
+ int64 duration_ms = 2;
+ }
+ repeated TxLevel tx = 4;
+}
+
+message SystemProto {
+ message Battery {
+ // Wall clock time when the data collection started.
+ // In case of device time manually reset by users:
+ // start_clock_time_ms keeps the same value in the current collection
+ // period and changes for later collection periods.
+ int64 start_clock_time_ms = 1;
+ // #times the device has been started since start_clock_time_millis.
+ int64 start_count = 2;
+ // Total realtime duration (= SINCE_UNPLUGGED battery_realtime_millis.)
+ int64 total_realtime_ms = 3;
+ int64 total_uptime_ms = 4;
+ // Realtime duration on battery.
+ int64 battery_realtime_ms = 5;
+ // Uptime duration (i.e., not suspend).
+ // Uptime is anytime the CPUs were on. The radio and Wifi chip
+ // can be running while the CPUs are off.
+ int64 battery_uptime_ms = 6;
+ // Total realtime duration measured with screen off or dozing.
+ int64 screen_off_realtime_ms = 7;
+ // Total uptime duration measured with screen off or dozing.
+ int64 screen_off_uptime_ms = 8;
+ // Total time the screen was dozing while the device was running on battery.
+ // For historical reasons, screen_doze_duration_msec is a subset of
+ // screen_off_realtime_msec.
+ int64 screen_doze_duration_ms = 9;
+ // The estimated real battery capacity, which may be less than the declared
+ // battery capacity (for example, because of battery aging). This field is
+ // less reliable than min(max)_learned_battery_capacity_uah, use those two
+ // fields whenever possible.
+ int64 estimated_battery_capacity_mah = 10;
+ // The minimum learned battery capacity in uAh.
+ int64 min_learned_battery_capacity_uah = 11;
+ // The maximum learned battery capacity in uAh.
+ int64 max_learned_battery_capacity_uah = 12;
+ };
+ Battery battery = 1;
+
+ message BatteryDischarge {
+ // Discharged battery percentage points since the stats were last reset
+ // after charging (lower bound approximation).
+ int32 lower_bound_since_charge = 1;
+ // Upper bound approximation.
+ int32 upper_bound_since_charge = 2;
+ // Discharged points while screen is on.
+ int32 screen_on_since_charge = 3;
+ // Discharged points while screen is off.
+ int32 screen_off_since_charge = 4;
+ // Discharged points while screen was dozing. For historical reasons,
+ // screen_doze_since_charge is a subset of screen_off_since_charge.
+ int32 screen_doze_since_charge = 5;
+ // Total amount of battery discharged in mAh. This will only be non-zero for
+ // devices that report battery discharge via a coulomb counter.
+ int64 total_mah = 6;
+ // Total amount of battery discharged while the screen was off in mAh.
+ // This will only be non-zero for devices that report battery discharge
+ // via a coulomb counter.
+ int64 total_mah_screen_off = 7;
+ // Total amount of battery discharged while the screen was dozing in mAh.
+ // This will only be non-zero for devices that report battery discharge
+ // via a coulomb counter. For historical reasons, total_mah_screen_doze is
+ // a subset of total_mah_screen_off.
+ int64 total_mah_screen_doze = 8;
+ };
+ BatteryDischarge battery_discharge = 2;
+
+ oneof time_remaining {
+ // Approximation for how much time remains until the battery is fully
+ // charged. The device will print -1 if there wasn't enough data to
+ // calculate an estimate, or if the battery is currently discharging.
+ int64 charge_time_remaining_ms = 3;
+ // Approximation for how much time remains until the battery is fully
+ // discharged. The device will print -1 if there wasn't enough data to
+ // calculate an estimate, or if the battery is currently charging.
+ int64 discharge_time_remaining_ms = 4;
+ }
+
+ // BatteryLevelStep tracks data for which conditions were continuously held for
+ // the entire duration. Field for which the conditions were not consistent
+ // for the entire duration should be marked MIXED.
+ message BatteryLevelStep {
+ // How long the battery was at the current level.
+ int64 duration_ms = 1;
+ // Battery level
+ int32 level = 2;
+
+ // State of the display. A special enum is used rather than
+ // DisplayProto.State because a MIXED value needs to be in the enum, and
+ // batterystats doesn't care about all of the different display states.
+ enum DisplayState {
+ DS_MIXED = 0;
+ DS_ON = 1;
+ DS_OFF = 2;
+ DS_DOZE = 3;
+ DS_DOZE_SUSPEND = 4;
+ // Any display state error that comes through should be sent to hackbod@.
+ DS_ERROR = 5;
+ }
+ // The state of the display for the entire battery level step. MIXED is used
+ // if there were multiple states for this step.
+ DisplayState display_state = 3;
+
+ // Indicates status in power save mode.
+ enum PowerSaveMode {
+ PSM_MIXED = 0;
+ PSM_ON = 1;
+ PSM_OFF = 2;
+ }
+ // Battery Saver mode for the entire battery level step. MIXED is used
+ // if there were multiple states for this step.
+ PowerSaveMode power_save_mode = 4;
+
+ // Indicates status in idle mode.
+ enum IdleMode {
+ IM_MIXED = 0;
+ IM_ON = 2;
+ IM_OFF = 3;
+ }
+ // Doze mode for the entire battery level step. MIXED is used if there were
+ // multiple states for this step.
+ IdleMode idle_mode = 5;
+ };
+ // Battery level steps when the device was charging.
+ repeated BatteryLevelStep charge_step = 5;
+ // Battery level steps when the device was discharging.
+ repeated BatteryLevelStep discharge_step = 6;
+
+ // All CPU frequencies of the device.
+ repeated int64 cpu_frequency = 7;
+
+ message DataConnection {
+ enum Name {
+ NONE = 0;
+ GPRS = 1;
+ EDGE = 2;
+ UMTS = 3;
+ CDMA = 4;
+ EVDO_0 = 5;
+ EVDO_A = 6;
+ ONE_X_RTT = 7; // 1xRTT.
+ HSDPA = 8;
+ HSUPA = 9;
+ HSPA = 10;
+ IDEN = 11;
+ EVDO_B = 12;
+ LTE = 13;
+ EHRPD = 14;
+ HSPAP = 15;
+ OTHER = 16;
+ };
+ Name name = 1;
+ TimerProto total = 2;
+ };
+ repeated DataConnection data_connection = 8;
+
+ ControllerActivityProto global_bluetooth_controller = 9;
+ ControllerActivityProto global_modem_controller = 10;
+ ControllerActivityProto global_wifi_controller = 11;
+
+ message GlobalNetwork {
+ // Total Bytes received on mobile connections.
+ int64 mobile_bytes_rx = 1;
+ // Total Bytes transmitted on mobile connections.
+ int64 mobile_bytes_tx = 2;
+ // Total Bytes received on wifi connections.
+ int64 wifi_bytes_rx = 3;
+ // Total Bytes transmitted on wifi connections.
+ int64 wifi_bytes_tx = 4;
+ // Total Packets received on mobile connections.
+ int64 mobile_packets_rx = 5;
+ // Total Packets transmitted on mobile connections.
+ int64 mobile_packets_tx = 6;
+ // Total Packets received on wifi connections.
+ int64 wifi_packets_rx = 7;
+ // Total Packets transmitted on wifi connections.
+ int64 wifi_packets_tx = 8;
+ // Total Bytes received on bluetooth connections.
+ int64 bt_bytes_rx = 9;
+ // Total Bytes transmitted on bluetooth connections.
+ int64 bt_bytes_tx = 10;
+ };
+ GlobalNetwork global_network = 12;
+
+ message GlobalWifi {
+ // The amount of time that wifi has been on while the device was running on
+ // battery.
+ int64 on_duration_ms = 1;
+ // The amount of time that wifi has been on and the driver has been in the
+ // running state while the device was running on battery.
+ int64 running_duration_ms = 2;
+ }
+ GlobalWifi global_wifi = 13;
+
+ // Kernel wakelock metrics are only recorded when the device is unplugged
+ // *and* the screen is off.
+ message KernelWakelock {
+ string name = 1;
+ // Kernel wakelock stats aren't apportioned across all kernel wakelocks (as
+ // app wakelocks stats are).
+ TimerProto total = 2;
+ // The kernel doesn't have the data to enable printing out current and max
+ // durations.
+ };
+ repeated KernelWakelock kernel_wakelock = 14;
+
+ message Misc {
+ int64 screen_on_duration_ms = 1;
+ int64 phone_on_duration_ms = 2;
+ int64 full_wakelock_total_duration_ms = 3;
+ // The total elapsed time that a partial wakelock was held. This duration
+ // does not double count wakelocks held at the same time.
+ int64 partial_wakelock_total_duration_ms = 4;
+ int64 mobile_radio_active_duration_ms = 5;
+ // The time that is the difference between the mobile radio time we saw
+ // based on the elapsed timestamp when going down vs. the given time stamp
+ // from the radio.
+ int64 mobile_radio_active_adjusted_time_ms = 6;
+ int32 mobile_radio_active_count = 7;
+ // The amount of time that the mobile network has been active (in a high
+ // power state) but not being able to blame on an app.
+ int32 mobile_radio_active_unknown_duration_ms = 8;
+ // Total amount of time the device was in the interactive state.
+ int64 interactive_duration_ms = 9;
+ int64 battery_saver_mode_enabled_duration_ms = 10;
+ int32 num_connectivity_changes = 11;
+ // Amount of time the device was in deep Doze.
+ int64 deep_doze_enabled_duration_ms = 12;
+ // How many times the device went into deep Doze mode.
+ int32 deep_doze_count = 13;
+ // Amount of time the device was idling in deep Doze. Idling time
+ // encompasses "doze" time and the maintenance windows that allow apps to
+ // operate.
+ int64 deep_doze_idling_duration_ms = 14;
+ // How many times the device idling for deep Doze mode.
+ int32 deep_doze_idling_count = 15;
+ int64 longest_deep_doze_duration_ms = 16;
+ // Amount of time the device was in Doze Light.
+ int64 light_doze_enabled_duration_ms = 17;
+ // How many times the device went into Doze Light mode.
+ int32 light_doze_count = 18;
+ // Amount of time the device was idling in Doze Light. Idling time
+ // encompasses "doze" time and the maintenance windows that allow apps to
+ // operate.
+ int64 light_doze_idling_duration_ms = 19;
+ // How many times the device idling for Doze Light mode.
+ int32 light_doze_idling_count = 20;
+ int64 longest_light_doze_duration_ms = 21;
+ }
+ Misc misc = 15;
+
+ message PhoneSignalStrength {
+ android.telephony.SignalStrengthProto.StrengthName name = 1;
+ TimerProto total = 2;
+ };
+ repeated PhoneSignalStrength phone_signal_strength = 16;
+
+ message PowerUseItem {
+ enum Sipper {
+ UNKNOWN_SIPPER = 0;
+ IDLE = 1;
+ CELL = 2;
+ PHONE = 3;
+ WIFI = 4;
+ BLUETOOTH = 5;
+ FLASHLIGHT = 6;
+ SCREEN = 7;
+ USER = 8;
+ UNACCOUNTED = 9;
+ OVERCOUNTED = 10;
+ CAMERA = 11;
+ MEMORY = 12;
+ };
+ Sipper name = 1;
+ // UID, only valid for the USER sipper.
+ int32 uid = 2;
+ // Estimated power use in mAh.
+ double computed_power_mah = 3;
+ // Starting in Oreo, Battery Settings has two modes to display the battery
+ // info. The first is "app usage list". In this mode, items with should_hide
+ // enabled are hidden.
+ bool should_hide = 4;
+ // Smeared power from screen usage. Screen usage power is split and smeared
+ // among apps, based on activity time.
+ double screen_power_mah = 5;
+ // Smeared power using proportional method. Power usage from hidden sippers
+ // is smeared to all apps proportionally (except for screen usage).
+ double proportional_smear_mah = 6;
+ };
+ repeated PowerUseItem power_use_item = 17;
+
+ message PowerUseSummary {
+ double battery_capacity_mah = 1;
+ double computed_power_mah = 2;
+ // Lower bound of actual power drained.
+ double min_drained_power_mah = 3;
+ // Upper bound of actual power drained.
+ double max_drained_power_mah = 4;
+ };
+ PowerUseSummary power_use_summary = 18;
+
+ message ResourcePowerManager {
+ string name = 1;
+ TimerProto total = 2;
+ TimerProto screen_off = 3;
+ }
+ ResourcePowerManager resource_power_manager = 19;
+
+ message ScreenBrightness {
+ enum Name {
+ DARK = 0; // Not screen-off.
+ DIM = 1;
+ MEDIUM = 2;
+ LIGHT = 3;
+ BRIGHT = 4;
+ };
+ Name name = 1;
+ TimerProto total = 2;
+ };
+ repeated ScreenBrightness screen_brightness = 20;
+
+ // Duration and number of times trying to acquire a signal
+ TimerProto signal_scanning = 21;
+
+ message WakeupReason {
+ string name = 1;
+ TimerProto total = 2;
+ };
+ repeated WakeupReason wakeup_reason = 22;
+
+ message WifiSignalStrength {
+ enum Name {
+ NONE = 0;
+ POOR = 1;
+ MODERATE = 2;
+ GOOD = 3;
+ GREAT = 4;
+ };
+ Name name = 1;
+ TimerProto total = 2;
+ };
+ repeated WifiSignalStrength wifi_signal_strength = 23;
+
+ message WifiState {
+ enum Name {
+ OFF = 0;
+ OFF_SCANNING = 1;
+ ON_NO_NETWORKS = 2;
+ ON_DISCONNECTED = 3;
+ ON_CONNECTED_STA = 4;
+ ON_CONNECTED_P2P = 5;
+ ON_CONNECTED_STA_P2P = 6;
+ SOFT_AP = 7;
+ };
+ Name name = 1;
+ TimerProto total = 2;
+ };
+ repeated WifiState wifi_state = 24;
+
+ message WifiSupplicantState {
+ enum Name {
+ INVALID = 0;
+ DISCONNECTED = 1;
+ INTERFACE_DISABLED = 2;
+ INACTIVE = 3;
+ SCANNING = 4;
+ AUTHENTICATING = 5;
+ ASSOCIATING = 6;
+ ASSOCIATED = 7;
+ FOUR_WAY_HANDSHAKE = 8;
+ GROUP_HANDSHAKE = 9;
+ COMPLETED = 10;
+ DORMANT = 11;
+ UNINITIALIZED = 12;
+ };
+ Name name = 1;
+ TimerProto total = 2;
+ };
+ repeated WifiSupplicantState wifi_supplicant_state = 25;
+}
+
+message TimerProto {
+ int64 duration_ms = 1;
+ int64 count = 2;
+}
+
+message UidProto {
+ // Combination of app ID and user ID.
+ int32 uid = 1;
+ repeated string package_names = 2;
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index c3ceb1c..b3a606f 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -23,6 +23,7 @@
import "frameworks/base/libs/incident/proto/android/section.proto";
import "frameworks/base/core/proto/android/service/appwidget.proto";
import "frameworks/base/core/proto/android/service/battery.proto";
+import "frameworks/base/core/proto/android/service/batterystats.proto";
import "frameworks/base/core/proto/android/service/fingerprint.proto";
import "frameworks/base/core/proto/android/service/diskstats.proto";
import "frameworks/base/core/proto/android/service/netstats.proto";
@@ -84,6 +85,11 @@
(section).args = "notification --proto"
];
+ android.service.batterystats.BatteryStatsServiceDumpProto batterystats = 3005 [
+ (section).type = SECTION_DUMPSYS,
+ (section).args = "batterystats --proto"
+ ];
+
android.service.battery.BatteryServiceDumpProto battery = 3006 [
(section).type = SECTION_DUMPSYS,
(section).args = "battery --proto"
diff --git a/core/proto/android/service/batterystats.proto b/core/proto/android/service/batterystats.proto
new file mode 100644
index 0000000..4e989b7
--- /dev/null
+++ b/core/proto/android/service/batterystats.proto
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package android.service.batterystats;
+
+option java_multiple_files = true;
+option java_outer_classname = "BatteryStatsServiceProto";
+
+import "frameworks/base/core/proto/android/os/batterystats.proto";
+
+message BatteryStatsServiceDumpProto {
+ android.os.BatteryStatsProto batterystats = 1;
+}
diff --git a/cmds/statsd/src/parse_util.h b/core/proto/android/telephony/signalstrength.proto
similarity index 60%
copy from cmds/statsd/src/parse_util.h
copy to core/proto/android/telephony/signalstrength.proto
index 8b82e7b..ff230cb 100644
--- a/cmds/statsd/src/parse_util.h
+++ b/core/proto/android/telephony/signalstrength.proto
@@ -13,24 +13,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef PARSE_UTIL_H
-#define PARSE_UTIL_H
-#include "DropboxWriter.h"
-#include "LogReader.h"
+syntax = "proto3";
-#include <log/logprint.h>
+option java_package = "android.telephony";
+option java_multiple_files = true;
-namespace android {
-namespace os {
-namespace statsd {
+package android.telephony;
-EventMetricData parse(log_msg msg);
-
-int getTagId(log_msg msg);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#endif // PARSE_UTIL_H
+/**
+ * An android.telephony.SignalStrength object.
+ */
+message SignalStrengthProto {
+ enum StrengthName {
+ SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
+ SIGNAL_STRENGTH_POOR = 1;
+ SIGNAL_STRENGTH_MODERATE = 2;
+ SIGNAL_STRENGTH_GOOD = 3;
+ SIGNAL_STRENGTH_GREAT = 4;
+ }
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 6a9afbc..3d5ae3d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -551,6 +551,9 @@
<protected-broadcast android:name="android.media.tv.action.CHANNEL_BROWSABLE_REQUESTED" />
<protected-broadcast android:name="com.android.server.InputMethodManagerService.SHOW_INPUT_METHOD_PICKER" />
+ <!-- Made protected in P (was introduced in JB-MR2) -->
+ <protected-broadcast android:name="android.intent.action.GET_RESTRICTION_ENTRIES" />
+
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -963,7 +966,7 @@
android:permissionGroup="android.permission-group.MICROPHONE"
android:label="@string/permlab_recordAudio"
android:description="@string/permdesc_recordAudio"
- android:protectionLevel="dangerous"/>
+ android:protectionLevel="dangerous|instant"/>
<!-- ====================================================================== -->
<!-- Permissions for accessing the UCE Service -->
diff --git a/core/res/res/drawable/ic_slice_send.xml b/core/res/res/drawable/ic_slice_send.xml
new file mode 100644
index 0000000..b8b6954a7
--- /dev/null
+++ b/core/res/res/drawable/ic_slice_send.xml
@@ -0,0 +1,24 @@
+<!-- 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4.02,42.0L46.0,24.0 4.02,6.0 4.0,20.0l30.0,4.0 -30.0,4.0z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/slice_remote_input_bg.xml b/core/res/res/drawable/slice_remote_input_bg.xml
new file mode 100644
index 0000000..3120679
--- /dev/null
+++ b/core/res/res/drawable/slice_remote_input_bg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="#ff6c6c6c" />
+ <corners
+ android:bottomRightRadius="16dp"
+ android:bottomLeftRadius="16dp"/>
+</shape>
diff --git a/core/res/res/drawable/slice_ripple_drawable.xml b/core/res/res/drawable/slice_ripple_drawable.xml
new file mode 100644
index 0000000..5ba1f07
--- /dev/null
+++ b/core/res/res/drawable/slice_ripple_drawable.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 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
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="?android:attr/colorControlHighlight" />
\ No newline at end of file
diff --git a/core/res/res/layout/slice_grid.xml b/core/res/res/layout/slice_grid.xml
new file mode 100644
index 0000000..70df76b
--- /dev/null
+++ b/core/res/res/layout/slice_grid.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<android.slice.views.GridView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:background="?android:attr/activatedBackgroundIndicator"
+ android:clipToPadding="false">
+</android.slice.views.GridView>
diff --git a/core/res/res/layout/slice_message.xml b/core/res/res/layout/slice_message.xml
new file mode 100644
index 0000000..a3279b6
--- /dev/null
+++ b/core/res/res/layout/slice_message.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<android.slice.views.MessageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="12dp"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/activatedBackgroundIndicator"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@android:id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="-4dp"
+ android:gravity="start|center_vertical"
+ android:orientation="horizontal"
+ android:paddingEnd="12dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <!-- TODO: Support text source -->
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxWidth="48dp"
+ android:maxHeight="48dp" />
+ </LinearLayout>
+
+ <TextView android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:maxLines="10" />
+</android.slice.views.MessageView>
diff --git a/core/res/res/layout/slice_message_local.xml b/core/res/res/layout/slice_message_local.xml
new file mode 100644
index 0000000..d4180f3
--- /dev/null
+++ b/core/res/res/layout/slice_message_local.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<android.slice.views.MessageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical|end"
+ android:paddingTop="12dp"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/activatedBackgroundIndicator"
+ android:clipToPadding="false">
+
+ <TextView android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignStart="@android:id/title"
+ android:layout_gravity="end"
+ android:gravity="end"
+ android:padding="8dp"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:background="#ffeeeeee"
+ android:maxLines="10" />
+
+</android.slice.views.MessageView>
diff --git a/core/res/res/layout/slice_remote_input.xml b/core/res/res/layout/slice_remote_input.xml
new file mode 100644
index 0000000..dc570c4
--- /dev/null
+++ b/core/res/res/layout/slice_remote_input.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- 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.
+-->
+<!-- LinearLayout -->
+<android.slice.views.RemoteInputView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/remote_input"
+ android:background="@drawable/slice_remote_input_bg"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+
+ <view class="com.android.internal.slice.view.RemoteInputView$RemoteEditText"
+ android:id="@+id/remote_input_text"
+ android:layout_height="match_parent"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:paddingTop="2dp"
+ android:paddingBottom="4dp"
+ android:paddingStart="16dp"
+ android:paddingEnd="12dp"
+ android:gravity="start|center_vertical"
+ android:textAppearance="?android:attr/textAppearance"
+ android:textColor="#FFFFFFFF"
+ android:textColorHint="#99ffffff"
+ android:textSize="16sp"
+ android:background="@null"
+ android:singleLine="true"
+ android:ellipsize="start"
+ android:inputType="textShortMessage|textAutoCorrect|textCapSentences"
+ android:imeOptions="actionSend|flagNoExtractUi|flagNoFullscreen" />
+
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_vertical">
+
+ <ImageButton
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:paddingStart="12dp"
+ android:paddingEnd="24dp"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:id="@+id/remote_input_send"
+ android:src="@drawable/ic_slice_send"
+ android:tint="#FFFFFF"
+ android:tintMode="src_in"
+ android:background="@drawable/slice_ripple_drawable" />
+
+ <ProgressBar
+ android:id="@+id/remote_input_progress"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_marginEnd="6dp"
+ android:layout_gravity="center"
+ android:visibility="invisible"
+ android:indeterminate="true"
+ style="?android:attr/progressBarStyleSmall" />
+
+ </FrameLayout>
+
+</android.slice.views.RemoteInputView>
\ No newline at end of file
diff --git a/core/res/res/layout/slice_secondary_text.xml b/core/res/res/layout/slice_secondary_text.xml
new file mode 100644
index 0000000..80a1574
--- /dev/null
+++ b/core/res/res/layout/slice_secondary_text.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- 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.
+-->
+<!-- LinearLayout -->
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:gravity="center"
+ android:layout_height="wrap_content"
+ android:padding="4dp"
+ android:layout_width="match_parent" />
diff --git a/core/res/res/layout/slice_small_template.xml b/core/res/res/layout/slice_small_template.xml
new file mode 100644
index 0000000..cced42b
--- /dev/null
+++ b/core/res/res/layout/slice_small_template.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/activatedBackgroundIndicator"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@android:id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="-4dp"
+ android:gravity="start|center_vertical"
+ android:orientation="horizontal"
+ android:paddingEnd="12dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"/>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="center_vertical"
+ android:orientation="vertical">
+
+ <TextView android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:textAppearance="?android:attr/textAppearanceListItem" />
+
+ <TextView android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="10" />
+
+ </LinearLayout>
+
+ <LinearLayout android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="end|center_vertical"
+ android:orientation="horizontal" />
+
+</LinearLayout>
diff --git a/core/res/res/layout/slice_title.xml b/core/res/res/layout/slice_title.xml
new file mode 100644
index 0000000..455d59f
--- /dev/null
+++ b/core/res/res/layout/slice_title.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- 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.
+-->
+
+<!-- LinearLayout -->
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorPrimary"
+ android:gravity="center"
+ android:layout_height="wrap_content"
+ android:padding="4dp"
+ android:layout_width="match_parent" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 9e4184b..8bc50b5 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Werkprofiel is weens ontbrekende administrasieprogram uitgevee"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Die werkprofiel se administrasieprogram ontbreek of is korrup. Gevolglik is jou werkprofiel en verwante data uitgevee. Kontak jou administrateur vir bystand."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Jou werkprofiel is nie meer op hierdie toestel beskikbaar nie"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Te veel wagwoordpogings"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Toestel word bestuur"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Jou organisasie bestuur hierdie toestel en kan netwerkverkeer monitor. Tik vir besonderhede."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Jou toestel sal uitgevee word"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Foon"</string>
<string name="map" msgid="6068210738233985748">"Kaarte"</string>
<string name="browse" msgid="6993590095938149861">"Blaaier"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kontak"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Bergingspasie word min"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Sommige stelselfunksies werk moontlik nie"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nie genoeg berging vir die stelsel nie. Maak seker jy het 250 MB spasie beskikbaar en herbegin."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM word nie toegelaat nie"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Foon word nie toegelaat nie"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Opspringvenster"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 00257ee..77a0ed7 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"በጎደለ የአስተዳዳሪ መተግበሪያ ምክንያት የሥራ መገለጫ ተሰርዟል።"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"የሥራ መገለጫ አስተዳዳሪ መተግበሪያው ወይም ይጎድላል ወይም ተበላሽቷል። በዚህ ምክንያት የሥራ መገለጫዎ እና ተዛማጅ ውሂብ ተሰርዘዋል። እርዳታን ለማግኘት አስተዳዳሪዎን ያነጋግሩ።"</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"የሥራ መገለጫዎ ከዚህ በኋላ በዚህ መሣሪያ ላይ አይገኝም"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"በጣም ብዙ የይለፍ ቃል ሙከራዎች"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"መሣሪያው የሚተዳደር ነው"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"የእርስዎ ድርጅት ይህን መሣሪያ ያስተዳድራል፣ እና የአውታረ መረብ ትራፊክን ሊከታተል ይችላል። ዝርዝሮችን ለማግኘት መታ ያድርጉ።"</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"የእርስዎ መሣሪያ ይደመሰሳል"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"ስልክ"</string>
<string name="map" msgid="6068210738233985748">"ካርታዎች"</string>
<string name="browse" msgid="6993590095938149861">"አሳሽ"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"ኤስኤምኤስ"</string>
+ <string name="add_contact" msgid="7990645816259405444">"ዕውቂያ"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"የማከማቻ ቦታ እያለቀ ነው"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"አንዳንድ የስርዓት ተግባራት ላይሰሩ ይችላሉ"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ለስርዓቱ የሚሆን በቂ ቦታ የለም። 250 ሜባ ነጻ ቦታ እንዳለዎት ያረጋግጡና ዳግም ያስጀምሩ።"</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"ሲም አይፈቀድም"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"ስልክ አይፈቀድም"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"ብቅ-ባይ መስኮት"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 8f78cfa..7b516c5 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -180,6 +180,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"تم حذف الملف الشخصي للعمل نتيجة فقد تطبيق المشرف"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"تطبيق المشرف للملف الشخصي للعمل مفقود أو تالف لذا تم حذف الملف الشخصي للعمل والبيانات ذات الصلة. اتصل بالمشرف للحصول على المساعدة."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"لم يعد ملفك الشخصي للعمل متاحًا على هذا الجهاز"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"تم إجراء محاولات كثيرة جدًا لإدخال كلمة المرور"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"تتم إدارة الجهاز"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"تدير مؤسستك هذا الجهاز ويمكنها مراقبة حركة بيانات الشبكة. يمكنك النقر للحصول على تفاصيل."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"سيتم محو بيانات جهازك."</string>
@@ -1060,10 +1061,8 @@
<string name="dial" msgid="4204975095406423102">"الهاتف"</string>
<string name="map" msgid="6068210738233985748">"الخرائط"</string>
<string name="browse" msgid="6993590095938149861">"المتصفح"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"رسالة SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"جهة اتصال"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"مساحة التخزين منخفضة"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"قد لا تعمل بعض وظائف النظام"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ليست هناك سعة تخزينية كافية للنظام. تأكد من أنه لديك مساحة خالية تبلغ ٢٥٠ ميغابايت وأعد التشغيل."</string>
@@ -1930,4 +1929,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"غير مسموح باستخدام SIM"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"غير مسموح باستخدام الهاتف"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"نافذة منبثقة"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 97a67e0..8ff588b 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Admin tətbiqi olmadığından iş profili silindi"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"İş profili admin tətbiqi ya yoxdur, ya da korlanıb. Nəticədə iş profili və onunla bağlı data silinib. Kömək üçün admin ilə əlaqə saxlayın."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"İş profili artıq bu cihazda əlçatan deyil"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Həddindən çox parol cəhdi"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Cihaz idarə olunur"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Təşkilat bu cihazı idarə edir və şəbəkənin ötürülməsinə nəzarət edə bilər. Detallar üçün klikləyin."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Cihazınız təmizlənəcəkdir"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Xəritə"</string>
<string name="browse" msgid="6993590095938149861">"Brauzer"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Yaddaş yeri bitir"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bəzi sistem funksiyaları işləməyə bilər"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sistem üçün yetərincə yaddaş ehtiyatı yoxdur. 250 MB yaddaş ehtiyatının olmasına əmin olun və yenidən başladın."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-ə icazə verilmir"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefona icazə verilmir"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Popap Pəncərəsi"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 2daf942..b1e1117 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -174,6 +174,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Profil za Work je izbrisan jer nedostaje aplikacija za administratore"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Aplikacija za administratore na profilu za Work nedostaje ili je oštećena. Zbog toga su profil za Work i povezani podaci izbrisani. Obratite se administratoru za pomoć."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Profil za Work više nije dostupan na ovom uređaju"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Previše pokušaja unosa lozinke"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Uređajem se upravlja"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Organizacija upravlja ovim uređajem i može da nadgleda mrežni saobraćaj. Dodirnite za detalje."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Uređaj će biti obrisan"</string>
@@ -1000,10 +1001,8 @@
<string name="dial" msgid="4204975095406423102">"Pozovi"</string>
<string name="map" msgid="6068210738233985748">"Mape"</string>
<string name="browse" msgid="6993590095938149861">"Pregledač"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Memorijski prostor je na izmaku"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Neke sistemske funkcije možda ne funkcionišu"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nema dovoljno memorijskog prostora za sistem. Uverite se da imate 250 MB slobodnog prostora i ponovo pokrenite."</string>
@@ -1825,4 +1824,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM kartica nije dozvoljena"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefon nije dozvoljen"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Iskačući prozor"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index c89987b..42937df 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -176,6 +176,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Працоўны профіль выдалены з-за адсутнасці праграмы адміністратара"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Праграма адміністратара для працоўнага профілю адсутнічае або пашкоджана. У выніку гэтага ваш працоўны профіль і звязаныя з ім даныя былі выдалены. Звярніцеся па дапамогу да адміністратара."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Ваш працоўны профіль больш не даступны на гэтай прыладзе"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Занадта шмат спроб уводу пароля"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Прылада знаходзіцца пад кіраваннем"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Ваша арганізацыя кіруе гэтай прыладай і можа сачыць за сеткавым трафікам. Дакраніцеся для атрымання дадатковай інфармацыі."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Даныя вашай прылады будуць сцерты"</string>
@@ -1020,10 +1021,8 @@
<string name="dial" msgid="4204975095406423102">"Тэлефон"</string>
<string name="map" msgid="6068210738233985748">"Карты"</string>
<string name="browse" msgid="6993590095938149861">"Браўзер"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Кантакт"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Месца для захавання на зыходзе"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некаторыя сістэмныя функцыі могуць не працаваць"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Не хапае сховішча для сістэмы. Пераканайцеся, што ў вас ёсць 250 МБ свабоднага месца, і перазапусціце."</string>
@@ -1860,4 +1859,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-карта не дапускаецца"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Тэлефон не дапускаецца"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Выплыўное акно"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 07bdc3d..5643b7d 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Служебният потребителски профил е изтрит поради липса на приложение за администриране"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Приложението за администриране на служебния потребителски профил липсва или е повредено. В резултат на това той и свързаните с него данни са изтрити. За съдействие се свържете с администратора си."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Служебният ви потребителски профил вече не е налице на това устройство"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Опитите за паролата са твърде много"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Устройството се управлява"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Организацията ви управлява това устройство и може да наблюдава мрежовия трафик. Докоснете за подробности."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Данните на устройството ви ще бъдат изтрити"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Телефон"</string>
<string name="map" msgid="6068210738233985748">"Карти"</string>
<string name="browse" msgid="6993590095938149861">"Браузър"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Контакт"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Мястото в хранилището е на изчерпване"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Възможно е някои функции на системата да не работят"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"За системата няма достатъчно място в хранилището. Уверете се, че имате свободни 250 МБ, и рестартирайте."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM картата не е разрешена"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Телефонът не е разрешен"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Изскачащ прозорец"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 5d19486..48b8e64 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"প্রশাসক অ্যাপ না থাকায় কর্মস্থলের প্রোফাইল মুছে ফেলা হয়েছে"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"কর্মস্থলের প্রোফাইলের প্রশাসক অ্যাপটি হয় নেই, অথবা সেটি ক্ষতিগ্রস্ত হয়েছে৷ এর ফলে আপনার কর্মস্থলের প্রোফাইল এবং সম্পর্কিত ডেটা মুছে ফেলা হয়েছে৷ সহায়তার জন্য আপনার প্রশাসকের সাথে যোগাযোগ করুন৷"</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"আপনার কর্মস্থলের প্রোফাইলটি আর এই ডিভাইসে নেই"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"বহুবার ভুল পাসওয়ার্ড দিয়েছেন"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"ডিভাইসটি পরিচালনা করা হচ্ছে"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"আপনার প্রতিষ্ঠান এই ডিভাইসটি পরিচালনা করে এবং এটির নেটওয়ার্ক ট্রাফিকের উপরে নজর রাখতে পারে। বিশদ বিবরণের জন্য ট্যাপ করুন।,"</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"আপনার ডিভাইসটি মুছে ফেলা হবে"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"ফোন করুন"</string>
<string name="map" msgid="6068210738233985748">"মানচিত্র"</string>
<string name="browse" msgid="6993590095938149861">"ব্রাউজার"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"এসএমএস পাঠান"</string>
+ <string name="add_contact" msgid="7990645816259405444">"পরিচিতি যোগ করুন"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"স্টোরেজ পূর্ণ হতে চলেছে"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"কিছু কিছু সিস্টেম ক্রিয়াকলাপ কাজ নাও করতে পারে"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"সিস্টেমের জন্য যথেষ্ট স্টোরেজ নেই৷ আপনার কাছে ২৫০এমবি ফাঁকা স্থান রয়েছে কিনা সে বিষয়ে নিশ্চিত হন এবং সিস্টেম চালু করুন৷"</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"সিম অনুমোদিত নয়"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"ফোন অনুমোদিত নয়"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"পপ-আপ উইন্ডো"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 31ecfc5..c4a6d69 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -128,7 +128,7 @@
<item msgid="4397097370387921767">"Wi-Fi pozivanje preko operatera %s"</item>
</string-array>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Isključeno"</string>
- <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Prednost ima Wi-Fi"</string>
+ <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Preferira se Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferira se mobilna mreža"</string>
<string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Samo Wi-Fi"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: Nije proslijeđen"</string>
@@ -174,6 +174,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Radni profil je izbrisan jer nedostaje aplikacija administratora"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Nedostaje aplikacija administratora za radni profil ili je neispravna. Zbog toga su vaš radni profil i povezani podaci izbrisani. Obratite administratoru za pomoć."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Radni profil više nije dostupan na ovom uređaju"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Previše puta ste pokušali otključati uređaj"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Uređajem se upravlja."</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Vaša organizacija upravlja ovim uređajem i može pratiti mrežni saobraćaj. Dodirnite za detalje."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Uređaj će biti izbrisan"</string>
@@ -350,7 +351,7 @@
<string name="permdesc_persistentActivity" product="tv" msgid="5086862529499103587">"Dozvoljava aplikaciji da jednim dijelom trajno ostaje u memoriji. Time se ostalim aplikacijama dostupna memorija može ograničiti te usporiti rad TV-a."</string>
<string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"Omogućava aplikaciji da neke svoje dijelove pohrani trajno u memoriji. Ovo može ograničiti veličinu raspoložive memorije za druge aplikacije i tako usporiti telefon."</string>
<string name="permlab_getPackageSize" msgid="7472921768357981986">"mjerenje prostora kojeg aplikacije zauzimaju u pohrani"</string>
- <string name="permdesc_getPackageSize" msgid="3921068154420738296">"Dozvoljava aplikaciji preuzimanje svog kôda, podataka i veličine keš memorije"</string>
+ <string name="permdesc_getPackageSize" msgid="3921068154420738296">"Dozvoljava aplikaciji preuzimanje svog koda, podataka i veličine keš memorije"</string>
<string name="permlab_writeSettings" msgid="2226195290955224730">"izmjena postavki sistema"</string>
<string name="permdesc_writeSettings" msgid="7775723441558907181">"Dozvoljava aplikaciji izmijenu postavki sistema. Zlonamjerne aplikacije mogu oštetiti konfiguraciju sistema."</string>
<string name="permlab_receiveBootCompleted" msgid="5312965565987800025">"pokrenuti pri pokretanju"</string>
@@ -552,7 +553,7 @@
<string name="permlab_access_notification_policy" msgid="4247510821662059671">"pristup opciji Ne ometaj"</string>
<string name="permdesc_access_notification_policy" msgid="3296832375218749580">"Omogućava aplikaciji da čita i upisuje konfiguraciju opcije Ne ometaj."</string>
<string name="policylab_limitPassword" msgid="4497420728857585791">"Postavljanje pravila za lozinke"</string>
- <string name="policydesc_limitPassword" msgid="2502021457917874968">"Kontrolira dužinu i znakove koji su dozvoljeni u lozinkama za zaključavanje ekrana i PIN kodovima."</string>
+ <string name="policydesc_limitPassword" msgid="2502021457917874968">"Kontrolira dužinu i znakove koji su dozvoljeni u lozinkama za zaključavanje ekrana i PIN-ovima."</string>
<string name="policylab_watchLogin" msgid="5091404125971980158">"Prati pokušaje otključavanja ekrana"</string>
<string name="policydesc_watchLogin" product="tablet" msgid="3215729294215070072">"Prati broj pogrešno unijetih lozinki prilikom otključavanja ekrana i zaključava tablet ili briše sve podatke s njega ukoliko se previše puta unese pogrešna lozinka."</string>
<string name="policydesc_watchLogin" product="TV" msgid="2707817988309890256">"Prati koliko puta je lozinka neispravno otkucana prilikom otključavanja ekrana i zaključaj TV ili izbriši sve podatke s TV-a ako se lozinka neispravno ukuca previše puta."</string>
@@ -1000,10 +1001,8 @@
<string name="dial" msgid="4204975095406423102">"Pozovi"</string>
<string name="map" msgid="6068210738233985748">"Mape"</string>
<string name="browse" msgid="6993590095938149861">"Preglednik"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ponestaje prostora za pohranu"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Neke funkcije sistema možda neće raditi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nema dovoljno prostora za sistem. Obezbijedite 250MB slobodnog prostora i ponovo pokrenite uređaj."</string>
@@ -1489,14 +1488,14 @@
<string name="kg_pin_instructions" msgid="2377242233495111557">"Unesite PIN"</string>
<string name="kg_password_instructions" msgid="5753646556186936819">"Unesite lozinku"</string>
<string name="kg_puk_enter_puk_hint" msgid="453227143861735537">"SIM je sada onemogućen. Unesite PUK kôd da nastavite. Za više informacija obratite se operateru."</string>
- <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Unesite željeni PIN kôd"</string>
- <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Potvrdi željeni PIN kôd"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="7871604527429602024">"Unesite željeni PIN"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="325676184762529976">"Potvrdi željeni PIN"</string>
<string name="kg_sim_unlock_progress_dialog_message" msgid="8950398016976865762">"Otključavanje SIM kartice…"</string>
- <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Pogrešan PIN kôd."</string>
+ <string name="kg_password_wrong_pin_code" msgid="1139324887413846912">"Pogrešan PIN."</string>
<string name="kg_invalid_sim_pin_hint" msgid="8795159358110620001">"Unesite PIN koji sadrži od 4 do 8 brojeva."</string>
<string name="kg_invalid_sim_puk_hint" msgid="6025069204539532000">"PUK kôd bi trebao imati 8 brojeva."</string>
<string name="kg_invalid_puk" msgid="3638289409676051243">"Ponovo unesite ispravan PUK kôd. Ponovljeni pokušaji će trajno onemogućiti SIM."</string>
- <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN kodovi se ne poklapaju"</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="7003469261464593516">"PIN-ovi se ne poklapaju"</string>
<string name="kg_login_too_many_attempts" msgid="6486842094005698475">"Previše pokušaja otključavanja pomoću uzorka"</string>
<string name="kg_login_instructions" msgid="1100551261265506448">"Da otključate, prijavite se sa svojim Google računom."</string>
<string name="kg_login_username_hint" msgid="5718534272070920364">"Korisničko ime (adresa e-pošte)"</string>
@@ -1626,7 +1625,7 @@
<string name="reason_service_unavailable" msgid="7824008732243903268">"Usluga štampanja nije omogućena."</string>
<string name="print_service_installed_title" msgid="2246317169444081628">"Usluga <xliff:g id="NAME">%s</xliff:g> je instalirana"</string>
<string name="print_service_installed_message" msgid="5897362931070459152">"Dodirnite da omogućite"</string>
- <string name="restr_pin_enter_admin_pin" msgid="8641662909467236832">"Upišite PIN kôd administratora"</string>
+ <string name="restr_pin_enter_admin_pin" msgid="8641662909467236832">"Upišite PIN administratora"</string>
<string name="restr_pin_enter_pin" msgid="3395953421368476103">"Unesite PIN"</string>
<string name="restr_pin_incorrect" msgid="8571512003955077924">"Netačno"</string>
<string name="restr_pin_enter_old_pin" msgid="1462206225512910757">"Trenutni PIN"</string>
@@ -1827,4 +1826,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM kartica nije dozvoljena"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefon nije dozvoljen"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Iskočni prozor"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 082513d..af48cf7 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -127,7 +127,7 @@
<item msgid="4397097370387921767">"Trucada de Wi-Fi de: %s"</item>
</string-array>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Desactivat"</string>
- <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Preferència per la Wi-Fi"</string>
+ <string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Preferència per a la Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferència per a dades mòbils"</string>
<string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Només Wi-Fi"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: no s\'ha desviat"</string>
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"S\'ha suprimit el perfil professional perquè falta l\'aplicació d\'administració"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Falta l\'aplicació d\'administració del perfil professional o està malmesa. Com a conseqüència, s\'han suprimit el teu perfil professional i les dades relacionades. Contacta amb l\'administrador per obtenir ajuda."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"El teu perfil professional ja no està disponible en aquest dispositiu"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Has intentat introduir la contrasenya massa vegades"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"El dispositiu està gestionat"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"La teva organització gestiona aquest dispositiu i és possible que supervisi el trànsit de xarxa. Toca per obtenir més informació."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"El contingut del dispositiu s\'esborrarà"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Truca"</string>
<string name="map" msgid="6068210738233985748">"Mapes"</string>
<string name="browse" msgid="6993590095938149861">"Navegador"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Contacte"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"L\'espai d\'emmagatzematge s\'està esgotant"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"És possible que algunes funcions del sistema no funcionin"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"No hi ha prou espai d\'emmagatzematge per al sistema. Comprova que tinguis 250 MB d\'espai lliure i reinicia."</string>
@@ -1086,13 +1085,13 @@
<string name="volume_call" msgid="3941680041282788711">"Volum en trucada"</string>
<string name="volume_bluetooth_call" msgid="2002891926351151534">"Volum en trucada Bluetooth"</string>
<string name="volume_alarm" msgid="1985191616042689100">"Volum de l\'alarma"</string>
- <string name="volume_notification" msgid="2422265656744276715">"Volum de notificació"</string>
+ <string name="volume_notification" msgid="2422265656744276715">"Volum de notificacions"</string>
<string name="volume_unknown" msgid="1400219669770445902">"Volum"</string>
<string name="volume_icon_description_bluetooth" msgid="6538894177255964340">"Volum de Bluetooth"</string>
<string name="volume_icon_description_ringer" msgid="3326003847006162496">"Volum del so"</string>
<string name="volume_icon_description_incall" msgid="8890073218154543397">"Volum de trucada"</string>
<string name="volume_icon_description_media" msgid="4217311719665194215">"Volum de multimèdia"</string>
- <string name="volume_icon_description_notification" msgid="7044986546477282274">"Volum de notificació"</string>
+ <string name="volume_icon_description_notification" msgid="7044986546477282274">"Volum de notificacions"</string>
<string name="ringtone_default" msgid="3789758980357696936">"So predeterminat"</string>
<string name="ringtone_default_with_actual" msgid="1767304850491060581">"Predeterminat (<xliff:g id="ACTUAL_RINGTONE">%1$s</xliff:g>)"</string>
<string name="ringtone_silent" msgid="7937634392408977062">"Cap"</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM no compatible"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Telèfon no no compatible"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Finestra emergent"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 23f3cb9..d1029e6 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -176,6 +176,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Pracovní profil byl smazán, protože není k dispozici aplikace pro správu"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Aplikace pro správu pracovního profilu chybí nebo je poškozena. Váš pracovní profil a související data proto byla smazána. Požádejte o pomoc administrátora."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Váš pracovní profil v tomto zařízení již není k dispozici"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Příliš mnoho pokusů o zadání hesla"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Zařízení je spravováno"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Toto zařízení je spravováno vaší organizací, která může sledovat síťový provoz. Podrobnosti zobrazíte klepnutím."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Zařízení bude vymazáno"</string>
@@ -1020,10 +1021,8 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Mapy"</string>
<string name="browse" msgid="6993590095938149861">"Prohlížeč"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"V úložišti je málo místa"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Některé systémové funkce nemusí fungovat"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Pro systém není dostatek místa v úložišti. Uvolněte alespoň 250 MB místa a restartujte zařízení."</string>
@@ -1112,7 +1111,7 @@
<string name="heavy_weight_switcher_text" msgid="7022631924534406403">"Než spustíte novou aplikaci, je třeba zastavit jinou spuštěnou aplikaci."</string>
<string name="old_app_action" msgid="493129172238566282">"Návrat do aplikace <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
<string name="old_app_description" msgid="2082094275580358049">"Nespouštět novou aplikaci."</string>
- <string name="new_app_action" msgid="5472756926945440706">"Spustit aplikaci <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
+ <string name="new_app_action" msgid="5472756926945440706">"Do aplikace <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
<string name="new_app_description" msgid="1932143598371537340">"Zastavit starou aplikaci bez uložení."</string>
<string name="dump_heap_notification" msgid="2618183274836056542">"Proces <xliff:g id="PROC">%1$s</xliff:g> překročil limit paměti"</string>
<string name="dump_heap_notification_detail" msgid="6901391084243999274">"Byl shromážděn výpis haldy, klepnutím jej můžete sdílet"</string>
@@ -1860,4 +1859,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM karta není povolena"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefon není povolen"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Vyskakovací okno"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 9360cbe..66af879 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Arbejdsprofilen blev slettet, fordi der mangler en administrationsapp"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Administrationsappen til arbejdsprofilen mangler eller er beskadiget. Derfor er din arbejdsprofil og dine relaterede data blevet slettet. Kontakt din administrator for at få hjælp."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Din arbejdsprofil er ikke længere tilgængelig på denne enhed"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"For mange mislykkede adgangskodeforsøg"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Dette er en administreret enhed"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Din organisation administrerer denne enhed og kan overvåge netværkstrafik. Tryk for at se oplysninger."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Enheden slettes"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Opkald"</string>
<string name="map" msgid="6068210738233985748">"Kort"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"Sms"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kontaktperson"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Der er snart ikke mere lagerplads"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Nogle systemfunktioner virker måske ikke"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Der er ikke nok ledig lagerplads til systemet. Sørg for, at du har 250 MB ledig plads, og genstart."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-kortet har ikke adgangstilladelse"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefonen har ikke adgangstilladelse"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Pop op-vindue"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 8b31d96..f76408a 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Arbeitsprofil aufgrund fehlender Admin-App gelöscht"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Die Admin-App für das Arbeitsprofil fehlt oder ist beschädigt. Daher wurden dein Arbeitsprofil und alle zugehörigen Daten gelöscht. Bitte wende dich für weitere Hilfe an deinen Administrator."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Dein Arbeitsprofil ist auf diesem Gerät nicht mehr verfügbar"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Zu viele falsche Passworteingaben"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Dies ist ein verwaltetes Gerät"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Deine Organisation verwaltet dieses Gerät und überprüft unter Umständen den Netzwerkverkehr. Tippe hier, um weitere Informationen zu erhalten."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Die Daten auf deinem Gerät werden gelöscht."</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Karten"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Der Speicherplatz wird knapp"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Einige Systemfunktionen funktionieren möglicherweise nicht."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Der Speicherplatz reicht nicht für das System aus. Stelle sicher, dass 250 MB freier Speicherplatz vorhanden sind, und starte das Gerät dann neu."</string>
@@ -993,8 +992,7 @@
<string name="cancel" msgid="6442560571259935130">"Abbrechen"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"Abbrechen"</string>
- <!-- no translation found for close (2318214661230355730) -->
- <skip />
+ <string name="close" msgid="2318214661230355730">"SCHLIEẞEN"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Achtung"</string>
<string name="loading" msgid="7933681260296021180">"Wird geladen…"</string>
<string name="capital_on" msgid="1544682755514494298">"AN"</string>
@@ -1051,10 +1049,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Skalieren"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Immer anzeigen"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Eine erneute Aktivierung ist in den Systemeinstellungen unter \"Apps > Heruntergeladen\" möglich."</string>
- <!-- no translation found for top_app_killed_title (6814231368167994497) -->
- <skip />
- <!-- no translation found for top_app_killed_message (3487519022191609844) -->
- <skip />
+ <string name="top_app_killed_title" msgid="6814231368167994497">"App reagiert nicht"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> belegt möglicherweise zu viel Speicherplatz."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> unterstützt nicht die aktuelle Einstellung für die Anzeigegröße, sodass ein unerwartetes Verhalten auftreten kann."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Immer anzeigen"</string>
<string name="smv_application" msgid="3307209192155442829">"Die App <xliff:g id="APPLICATION">%1$s</xliff:g> (Prozess <xliff:g id="PROCESS">%2$s</xliff:g>) hat gegen deine selbsterzwungene StrictMode-Richtlinie verstoßen."</string>
@@ -1793,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-Karte nicht zulässig"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Smartphone nicht zulässig"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Pop-up-Fenster"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 19aafc8..d2cc930 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Το προφίλ εργασίας διαγράφηκε λόγω απουσίας της εφαρμογής διαχείρισης"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Η εφαρμογή διαχείρισης προφίλ εργασίας είτε λείπει είτε είναι κατεστραμμένη. Ως αποτέλεσμα, διαγράφηκε το προφίλ εργασίας και τα σχετικά δεδομένα. Επικοινωνήστε με τον διαχειριστή σας για βοήθεια."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Το προφίλ εργασίας σας δεν είναι πια διαθέσιμο σε αυτήν τη συσκευή"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Πάρα πολλές προσπάθειες εισαγωγής κωδικού πρόσβασης"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Η συσκευή είναι διαχειριζόμενη"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Ο οργανισμός σας διαχειρίζεται αυτήν τη συσκευή και ενδέχεται να παρακολουθεί την επισκεψιμότητα δικτύου. Πατήστε για λεπτομέρειες."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Η συσκευή σας θα διαγραφεί"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Τηλέφωνο"</string>
<string name="map" msgid="6068210738233985748">"Χάρτες"</string>
<string name="browse" msgid="6993590095938149861">"Πρόγραμμα περιήγησης"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Επαφή"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ο αποθηκευτικός χώρος εξαντλείται"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Ορισμένες λειτουργίες συστήματος ενδέχεται να μην λειτουργούν"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Δεν υπάρχει αρκετός αποθηκευτικός χώρος για το σύστημα. Βεβαιωθείτε ότι διαθέτετε 250 MB ελεύθερου χώρου και κάντε επανεκκίνηση."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"Η κάρτα SIM δεν επιτρέπεται"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Το τηλέφωνο δεν επιτρέπεται"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Αναδυόμενο παράθυρο"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 7fff878..ad03374 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Work profile deleted due to missing admin app"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"The work profile admin app is either missing or corrupted. As a result, your work profile and related data have been deleted. Contact your admin for assistance."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Your work profile is no longer available on this device"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Too many password attempts"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Device is managed"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Your organisation manages this device and may monitor network traffic. Tap for details."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Your device will be erased"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Phone"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Contact"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM not allowed"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Phone not allowed"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Pop-Up Window"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 7fff878..ad03374 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Work profile deleted due to missing admin app"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"The work profile admin app is either missing or corrupted. As a result, your work profile and related data have been deleted. Contact your admin for assistance."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Your work profile is no longer available on this device"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Too many password attempts"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Device is managed"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Your organisation manages this device and may monitor network traffic. Tap for details."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Your device will be erased"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Phone"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Contact"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM not allowed"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Phone not allowed"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Pop-Up Window"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 7fff878..ad03374 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Work profile deleted due to missing admin app"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"The work profile admin app is either missing or corrupted. As a result, your work profile and related data have been deleted. Contact your admin for assistance."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Your work profile is no longer available on this device"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Too many password attempts"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Device is managed"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Your organisation manages this device and may monitor network traffic. Tap for details."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Your device will be erased"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Phone"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Contact"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM not allowed"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Phone not allowed"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Pop-Up Window"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 7fff878..ad03374 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Work profile deleted due to missing admin app"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"The work profile admin app is either missing or corrupted. As a result, your work profile and related data have been deleted. Contact your admin for assistance."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Your work profile is no longer available on this device"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Too many password attempts"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Device is managed"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Your organisation manages this device and may monitor network traffic. Tap for details."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Your device will be erased"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Phone"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Contact"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure that you have 250 MB of free space and restart."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM not allowed"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Phone not allowed"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Pop-Up Window"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index b93db1c..d6da98d 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Work profile deleted due to missing admin app"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"The work profile admin app is either missing or corrupted. As a result, your work profile and related data have been deleted. Contact your admin for assistance."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Your work profile is no longer available on this device"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Too many password attempts"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Device is managed"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Your organization manages this device and may monitor network traffic. Tap for details."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Your device will be erased"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Phone"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Contact"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Not enough storage for the system. Make sure you have 250MB of free space and restart."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM not allowed"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Phone not allowed"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Popup Window"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 3946e7d..ea8b54e 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Se borró el perfil de trabajo debido a la falta de una app de administración"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"La app de administración de perfil de trabajo no se encuentra o está dañada. Por lo tanto, se borraron tu perfil de trabajo y los datos relacionados. Para obtener asistencia, comunícate con el administrador."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Tu perfil de trabajo ya no está disponible en este dispositivo"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Demasiados intentos para ingresar la contraseña"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Dispositivo administrado"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Tu organización administra este dispositivo y es posible que controle el tráfico de red. Presiona para obtener más información."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Se borrarán los datos del dispositivo"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Teléfono"</string>
<string name="map" msgid="6068210738233985748">"Mapas"</string>
<string name="browse" msgid="6993590095938149861">"Navegador"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Contacto"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Queda poco espacio de almacenamiento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Es posible que algunas funciones del sistema no estén disponibles."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"No hay espacio suficiente para el sistema. Asegúrate de que haya 250 MB libres y reinicia el dispositivo."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM no permitida"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Teléfono no permitido"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Ventana emergente"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 7085fda..6164bac 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -128,7 +128,7 @@
</string-array>
<string name="wifi_calling_off_summary" msgid="8720659586041656098">"Desactivado"</string>
<string name="wfc_mode_wifi_preferred_summary" msgid="1994113411286935263">"Preferir Wi-Fi"</string>
- <string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferencia a datos móviles"</string>
+ <string name="wfc_mode_cellular_preferred_summary" msgid="1988279625335345908">"Preferir datos móviles"</string>
<string name="wfc_mode_wifi_only_summary" msgid="2379919155237869320">"Solo conexión Wi-Fi"</string>
<string name="cfTemplateNotForwarded" msgid="1683685883841272560">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: No desviada"</string>
<string name="cfTemplateForwarded" msgid="1302922117498590521">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g>: <xliff:g id="DIALING_NUMBER">{1}</xliff:g>"</string>
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Se ha eliminado el perfil de trabajo porque falta la aplicación de administración"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Falta la aplicación de administración del perfil de trabajo o está dañada. Por ello, se han eliminado tu perfil de trabajo y los datos relacionados. Ponte en contacto con el administrador para obtener ayuda."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Tu perfil de trabajo ya no está disponible en este dispositivo"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Has fallado demasiadas veces al introducir la contraseña"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"El dispositivo está administrado"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Tu organización administra este dispositivo y puede supervisar el tráfico de red. Toca la notificación para obtener más información."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Tu dispositivo se borrará"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Teléfono"</string>
<string name="map" msgid="6068210738233985748">"Mapas"</string>
<string name="browse" msgid="6993590095938149861">"Navegador"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Contacto"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Queda poco espacio"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Es posible que algunas funciones del sistema no funcionen."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"No hay espacio suficiente para el sistema. Comprueba que haya 250 MB libres y reinicia el dispositivo."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM no compatible"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Teléfono no compatible"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Ventana emergente"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 2565e62..b48afe0 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Tööprofiil on kustutatud puuduva administraatori rakenduse tõttu"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Tööprofiili administraatori rakendus puudub või on rikutud. Seetõttu on teie tööprofiil ja seotud andmed kustutatud. Abi saamiseks võtke ühendust administraatoriga."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Teie tööprofiil pole selles seadmes enam saadaval"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Liiga palju paroolikatseid"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Seade on hallatud"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Teie organisatsioon haldab seda seadet ja võib jälgida võrguliiklust. Puudutage üksikasjade vaatamiseks."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Seade kustutatakse"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"Brauser"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Talletusruum saab täis"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Mõned süsteemifunktsioonid ei pruugi töötada"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Süsteemis pole piisavalt talletusruumi. Veenduge, et seadmes oleks 250 MB vaba ruumi, ja käivitage seade uuesti."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-kaart pole lubatud"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefon pole lubatud"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Hüpikaken"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index f88d4c4..98b5f5a 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Laneko profila ezabatu egin da hura administratzeko aplikazioa falta delako"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Laneko profila administratzeko aplikazioa falta da edo hondatuta dago. Ondorioz, ezabatu egin dira laneko profila bera eta harekin erlazionatutako datuak. Laguntza lortzeko, jarri administratzailearekin harremanetan."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Laneko profila ez dago erabilgarri gailu honetan"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Gehiegitan saiatu zara pasahitza idazten"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Jabeak kudeatzen du gailua"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Erakundeak kudeatzen du gailua eta baliteke sareko trafikoa gainbegiratzea. Sakatu hau xehetasunak ikusteko."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Gailuko datuak ezabatu egingo dira"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Telefonoa"</string>
<string name="map" msgid="6068210738233985748">"Mapak"</string>
<string name="browse" msgid="6993590095938149861">"Arakatzailea"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMSa"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kontaktua"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Memoria betetzen ari da"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Sistemaren funtzio batzuek ez dute agian funtzionatuko"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sisteman ez dago behar adina memoria. Ziurtatu gutxienez 250 MB erabilgarri dituzula eta, ondoren, berrabiarazi gailua."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"Ez da onartzen SIM txartela"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Ez da onartzen telefonoa"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Leiho gainerakorra"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 2bd7696..9385f58 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"به دلیل نداشتن برنامه سرپرست، نمایه کاری حذف شد"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"برنامه سرپرست نمایه کاری یا وجود ندارد یا خراب است. در نتیجه، نمایه کاری شما و دادههای مرتبط با آن حذف شده است. برای دریافت راهنمایی با سرپرست سیستم تماس بگیرید."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"نمایه کاری شما دیگر در این دستگاه دردسترس نیست"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"تلاشهای بسیار زیادی برای وارد کردن گذرواژه انجام شده است"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"دستگاه مدیریت میشود"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"سازمانتان این دستگاه را مدیریت میکند و ممکن است ترافیک شبکه را پایش کند. برای اطلاع از جزئیات، ضربه بزنید."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"دستگاهتان پاک خواهد شد"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"تلفن"</string>
<string name="map" msgid="6068210738233985748">"نقشهها"</string>
<string name="browse" msgid="6993590095938149861">"مرورگر"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"پیامک"</string>
+ <string name="add_contact" msgid="7990645816259405444">"مخاطب"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"حافظه درحال پر شدن است"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"برخی از عملکردهای سیستم ممکن است کار نکنند"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"فضای ذخیرهسازی سیستم کافی نیست. اطمینان حاصل کنید که دارای ۲۵۰ مگابایت فضای خالی هستید و سیستم را راهاندازی مجدد کنید."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"سیمکارت مجاز نیست"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"تلفن مجاز نیست"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"پنجره بازشو"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index c2a2ccb63..c523452 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Työprofiili poistettiin, koska laitteesta puuttuu hallintasovellus."</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Työprofiilin hallintasovellus puuttuu tai se on vioittunut. Tästä syystä työprofiilisi ja siihen liittyvät tiedot on poistettu. Pyydä ohjeita järjestelmänvalvojaltasi."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Työprofiilisi ei ole enää käytettävissä tällä laitteella."</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Liikaa salasanayrityksiä"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Hallinnoitu laite"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Organisaatiosi hallinnoi tätä laitetta ja voi tarkkailla verkkoliikennettä. Katso lisätietoja napauttamalla."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Laitteen tiedot poistetaan"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Puhelin"</string>
<string name="map" msgid="6068210738233985748">"Kartat"</string>
<string name="browse" msgid="6993590095938149861">"Selain"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"Tekstiviesti"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Yhteystieto"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Tallennustila loppumassa"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Kaikki järjestelmätoiminnot eivät välttämättä toimi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Tallennustila ei riitä. Varmista, että vapaata tilaa on 250 Mt, ja käynnistä uudelleen."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-kortti estetty"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Puhelin estetty"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Ponnahdusikkuna"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index fbed666..4f6b3aa 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Profil professionnel supprimé en raison de l\'application d\'administration manquante"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Le profil professionnel de l\'application d\'administration est manquant ou corrompu. Votre profil professionnel et ses données connexes ont donc été supprimés. Communiquez avec votre administrateur pour obtenir de l\'assistance."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Votre profil professionnel n\'est plus accessible sur cet appareil"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Trop de tentatives d\'entrée du mot de passe"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"L\'appareil est géré"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Votre organisation gère cet appareil et peut surveiller le trafic réseau. Touchez ici pour obtenir plus d\'information."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Le contenu de votre appareil sera effacé"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Téléphone"</string>
<string name="map" msgid="6068210738233985748">"Cartes"</string>
<string name="browse" msgid="6993590095938149861">"Navigateur"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"Messagerie texte"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Contact"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Espace de stockage bientôt saturé"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Espace de stockage insuffisant pour le système. Assurez-vous de disposer de 250 Mo d\'espace libre, puis redémarrez."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"Carte SIM non autorisée"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Téléphone non autorisé"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Fenêtre contextuelle"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index fcdccad..92838e4 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Profil professionnel supprimé, car une application d\'administration est manquante"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"L\'application d\'administration du profil professionnel est manquante ou endommagée. Par conséquent, votre profil professionnel et toutes les données associées ont été supprimés. Pour obtenir de l\'aide, contactez l\'administrateur."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Votre profil professionnel n\'est plus disponible sur cet appareil"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Trop de tentatives de saisie du mot de passe"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"L\'appareil est géré"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Votre organisation gère cet appareil et peut surveiller le trafic réseau. Appuyez ici pour obtenir plus d\'informations."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Les données de votre appareil vont être effacées"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Téléphone"</string>
<string name="map" msgid="6068210738233985748">"Cartes"</string>
<string name="browse" msgid="6993590095938149861">"Navigateur"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Contact"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Espace de stockage bientôt saturé"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Espace de stockage insuffisant pour le système. Assurez-vous de disposer de 250 Mo d\'espace libre, puis redémarrez."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"Carte SIM non autorisée"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Téléphone non autorisé"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Fenêtre pop-up"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 0c0e12a..6d56700 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -52,8 +52,8 @@
</plurals>
<string name="imei" msgid="2625429890869005782">"IMEI"</string>
<string name="meid" msgid="4841221237681254195">"MEID"</string>
- <string name="ClipMmi" msgid="6952821216480289285">"ID de chamada entrante"</string>
- <string name="ClirMmi" msgid="7784673673446833091">"ID de chamada saínte"</string>
+ <string name="ClipMmi" msgid="6952821216480289285">"Identificador de chamada entrante"</string>
+ <string name="ClirMmi" msgid="7784673673446833091">"Identificador de chamada saínte"</string>
<string name="ColpMmi" msgid="3065121483740183974">"ID de liña conectada"</string>
<string name="ColrMmi" msgid="4996540314421889589">"Restrición de ID de liña conectada"</string>
<string name="CfMmi" msgid="5123218989141573515">"Desvío de chamadas"</string>
@@ -67,24 +67,24 @@
<string name="RuacMmi" msgid="7827887459138308886">"Rexeitamento de chamadas molestas non desexadas"</string>
<string name="CndMmi" msgid="3116446237081575808">"Entrega de número de chamada entrante"</string>
<string name="DndMmi" msgid="1265478932418334331">"Non molestar"</string>
- <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"De forma predeterminada, restrínxese o ID de chamada. Próxima chamada: restrinxido."</string>
- <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"De forma predeterminada, restrínxese o ID de chamada. Próxima chamada: non restrinxido."</string>
- <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"De forma predeterminada, non se restrinxe o ID de chamada. Próxima chamada: restrinxido."</string>
- <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"De forma predeterminada, non se restrinxe o ID de chamada. Próxima chamada: non restrinxido."</string>
+ <string name="CLIRDefaultOnNextCallOn" msgid="429415409145781923">"O valor predeterminado do identificador de chamada é restrinxido. Próxima chamada: restrinxido"</string>
+ <string name="CLIRDefaultOnNextCallOff" msgid="3092918006077864624">"O valor predeterminado do identificador de chamada é restrinxido. Próxima chamada: non restrinxido"</string>
+ <string name="CLIRDefaultOffNextCallOn" msgid="6179425182856418465">"O valor predeterminado do identificador de chamada é non restrinxido. Próxima chamada: restrinxido"</string>
+ <string name="CLIRDefaultOffNextCallOff" msgid="2567998633124408552">"O valor predeterminado do identificador de chamada é restrinxido. Próxima chamada: non restrinxido"</string>
<string name="serviceNotProvisioned" msgid="8614830180508686666">"Servizo non ofrecido."</string>
- <string name="CLIRPermanent" msgid="3377371145926835671">"Non podes cambiar a configuración do ID de chamada."</string>
+ <string name="CLIRPermanent" msgid="3377371145926835671">"Non podes cambiar a configuración do identificador de chamada."</string>
<string name="RestrictedOnDataTitle" msgid="1322504692764166532">"Non hai servizo de datos"</string>
- <string name="RestrictedOnEmergencyTitle" msgid="3646729271176394091">"Non se poden realizar chamadas de emerxencia"</string>
+ <string name="RestrictedOnEmergencyTitle" msgid="3646729271176394091">"Non se poden realizar chamadas de urxencia"</string>
<string name="RestrictedOnNormalTitle" msgid="3179574012752700984">"Non hai servizo de chamadas de voz"</string>
- <string name="RestrictedOnAllVoiceTitle" msgid="158800171499150681">"Non hai servizo de chamadas de emerxencia nin de voz"</string>
+ <string name="RestrictedOnAllVoiceTitle" msgid="158800171499150681">"Non hai servizo de chamadas de urxencia nin de voz"</string>
<string name="RestrictedStateContent" msgid="4278821484643362350">"A rede de telefonía móbil non ofrece o servizo na túa localización temporalmente"</string>
<string name="NetworkPreferenceSwitchTitle" msgid="4008877505368566980">"Non se pode conectar coa rede"</string>
<string name="NetworkPreferenceSwitchSummary" msgid="1203771446683319957">"Para mellorar a recepción, proba a cambiar o tipo seleccionado en Configuración > Rede e Internet > Redes de telefonía móbil > Tipo de rede preferido."</string>
<string name="EmergencyCallWarningTitle" msgid="4790413876281901612">"As chamadas por wifi están activadas"</string>
- <string name="EmergencyCallWarningSummary" msgid="8973232888021643293">"As chamadas de emerxencia precisan unha rede de telefonía móbil."</string>
+ <string name="EmergencyCallWarningSummary" msgid="8973232888021643293">"As chamadas de urxencia precisan unha rede de telefonía móbil."</string>
<string name="notification_channel_network_alert" msgid="4427736684338074967">"Alertas"</string>
<string name="notification_channel_call_forward" msgid="2419697808481833249">"Desvío de chamadas"</string>
- <string name="notification_channel_emergency_callback" msgid="6686166232265733921">"Modo de devolución de chamadas de emerxencia"</string>
+ <string name="notification_channel_emergency_callback" msgid="6686166232265733921">"Modo de devolución de chamadas de urxencia"</string>
<string name="notification_channel_mobile_data_status" msgid="4575131690860945836">"Estado dos datos móbiles"</string>
<string name="notification_channel_sms" msgid="3441746047346135073">"Mensaxes SMS"</string>
<string name="notification_channel_voice_mail" msgid="3954099424160511919">"Mensaxes de correo de voz"</string>
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Eliminouse o perfil de traballo porque falta a aplicación de administración"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Falta a aplicación de administración do perfil de traballo ou ben está danada. Como resultado, eliminouse o teu perfil de traballo e os datos relacionados. Para obter asistencia, contacta co administrador."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"O teu perfil de traballo xa non está dispoñible neste dispositivo"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Demasiados intentos de introdución do contrasinal"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"O dispositivo está xestionado"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"A túa organización xestiona este dispositivo e pode controlar o tráfico de rede. Toca para obter máis detalles."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Borrarase o teu dispositivo"</string>
@@ -188,7 +189,7 @@
<string name="silent_mode_silent" msgid="319298163018473078">"Timbre desactivado"</string>
<string name="silent_mode_vibrate" msgid="7072043388581551395">"Timbre en vibración"</string>
<string name="silent_mode_ring" msgid="8592241816194074353">"Timbre activado"</string>
- <string name="reboot_to_update_title" msgid="6212636802536823850">"Actualización do sistema de Android"</string>
+ <string name="reboot_to_update_title" msgid="6212636802536823850">"Actualización do sistema Android"</string>
<string name="reboot_to_update_prepare" msgid="6305853831955310890">"Preparando para actualizar…"</string>
<string name="reboot_to_update_package" msgid="3871302324500927291">"Procesando paquete de actualización…"</string>
<string name="reboot_to_update_reboot" msgid="6428441000951565185">"Reiniciando..."</string>
@@ -209,7 +210,7 @@
<string name="global_actions" product="default" msgid="2406416831541615258">"Opcións de teléfono"</string>
<string name="global_action_lock" msgid="2844945191792119712">"Bloqueo de pantalla"</string>
<string name="global_action_power_off" msgid="4471879440839879722">"Apagar"</string>
- <string name="global_action_emergency" msgid="7112311161137421166">"Emerxencias"</string>
+ <string name="global_action_emergency" msgid="7112311161137421166">"Urxencias"</string>
<string name="global_action_bug_report" msgid="7934010578922304799">"Informe de erros"</string>
<string name="bugreport_title" msgid="2667494803742548533">"Crear informe de erros"</string>
<string name="bugreport_message" msgid="398447048750350456">"Este informe recompilará información acerca do estado actual do teu dispositivo para enviala en forma de mensaxe de correo electrónico. O informe de erros tardará un pouco en completarse desde o seu inicio ata que estea preparado para enviarse, polo que che recomendamos que teñas paciencia."</string>
@@ -315,7 +316,7 @@
<string name="permlab_receiveMms" msgid="1821317344668257098">"recibir mensaxes de texto (MMS)"</string>
<string name="permdesc_receiveMms" msgid="533019437263212260">"Permite á aplicación recibir e procesar mensaxes MMS. Isto significa que a aplicación pode supervisar ou eliminar mensaxes enviadas ao teu dispositivo sen mostrarchas."</string>
<string name="permlab_readCellBroadcasts" msgid="1598328843619646166">"ler mensaxes de difusión móbil"</string>
- <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite á aplicación ler mensaxes de difusión móbil recibidas polo teu dispositivo. As alertas de difusión móbil envíanse nalgunhas localizacións para avisar de situacións de emerxencia. É posible que aplicacións maliciosas afecten ao rendemento ou funcionamento do teu dispositivo cando se recibe unha difusión móbil de emerxencia."</string>
+ <string name="permdesc_readCellBroadcasts" msgid="6361972776080458979">"Permite á aplicación ler mensaxes de difusión móbil recibidas polo teu dispositivo. As alertas de difusión móbil envíanse nalgunhas localizacións para avisar de situacións de urxencia. É posible que aplicacións maliciosas afecten ao rendemento ou funcionamento do teu dispositivo cando se recibe unha difusión móbil de urxencia."</string>
<string name="permlab_subscribedFeedsRead" msgid="4756609637053353318">"ler feeds subscritos"</string>
<string name="permdesc_subscribedFeedsRead" msgid="5557058907906144505">"Permite á aplicación obter detalles acerca dos feeds sincronizados actualmente."</string>
<string name="permlab_sendSms" msgid="7544599214260982981">"enviar e consultar mensaxes de SMS"</string>
@@ -401,7 +402,7 @@
<string name="permlab_vibrate" msgid="7696427026057705834">"controlar a vibración"</string>
<string name="permdesc_vibrate" msgid="6284989245902300945">"Permite á aplicación controlar o vibrador."</string>
<string name="permlab_callPhone" msgid="3925836347681847954">"chamar directamente aos números de teléfono"</string>
- <string name="permdesc_callPhone" msgid="3740797576113760827">"Permite á aplicación chamar a números de teléfono sen a túa intervención. Esta acción pode implicar chamadas ou custos inesperados. Ten en conta que isto non permite á aplicación chamar a números de emerxencia. É posible que aplicacións maliciosas che custen diñeiro debido á realización de chamadas sen a túa confirmación."</string>
+ <string name="permdesc_callPhone" msgid="3740797576113760827">"Permite á aplicación chamar a números de teléfono sen a túa intervención. Esta acción pode implicar chamadas ou custos inesperados. Ten en conta que isto non permite á aplicación chamar a números de urxencia. É posible que aplicacións maliciosas che custen diñeiro debido á realización de chamadas sen a túa confirmación."</string>
<string name="permlab_accessImsCallService" msgid="3574943847181793918">"acceso ao servizo de chamadas de IMS"</string>
<string name="permdesc_accessImsCallService" msgid="8992884015198298775">"Permite que a aplicación use o servizo de IMS para facer chamadas sen a túa intervención."</string>
<string name="permlab_readPhoneState" msgid="9178228524507610486">"ler o estado e a identidade do teléfono"</string>
@@ -702,13 +703,13 @@
<string name="keyguard_password_enter_pin_password_code" msgid="6391755146112503443">"Escribe o PIN para desbloquear"</string>
<string name="keyguard_password_wrong_pin_code" msgid="2422225591006134936">"Código PIN incorrecto"</string>
<string name="keyguard_label_text" msgid="861796461028298424">"Para desbloquear, preme Menú e, a continuación, 0."</string>
- <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de emerxencia"</string>
+ <string name="emergency_call_dialog_number_for_display" msgid="696192103195090970">"Número de urxencia"</string>
<string name="lockscreen_carrier_default" msgid="6169005837238288522">"Sen servizo"</string>
<string name="lockscreen_screen_locked" msgid="7288443074806832904">"Pantalla bloqueada"</string>
- <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Preme Menú para desbloquear ou realizar unha chamada de emerxencia."</string>
+ <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Preme Menú para desbloquear ou realizar unha chamada de urxencia."</string>
<string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Preme Menú para desbloquear."</string>
<string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Crea o padrón de desbloqueo"</string>
- <string name="lockscreen_emergency_call" msgid="5298642613417801888">"Emerxencia"</string>
+ <string name="lockscreen_emergency_call" msgid="5298642613417801888">"Urxencia"</string>
<string name="lockscreen_return_to_call" msgid="5244259785500040021">"Volver á chamada"</string>
<string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Correcto!"</string>
<string name="lockscreen_pattern_wrong" msgid="4317955014948108794">"Téntao de novo"</string>
@@ -730,7 +731,7 @@
<string name="lockscreen_transport_stop_description" msgid="5907083260651210034">"Deter"</string>
<string name="lockscreen_transport_rew_description" msgid="6944412838651990410">"Rebobinar"</string>
<string name="lockscreen_transport_ffw_description" msgid="42987149870928985">"Avance rápido"</string>
- <string name="emergency_calls_only" msgid="6733978304386365407">"Só chamadas de emerxencia"</string>
+ <string name="emergency_calls_only" msgid="6733978304386365407">"Só chamadas de urxencia"</string>
<string name="lockscreen_network_locked_message" msgid="143389224986028501">"Bloqueada pola rede"</string>
<string name="lockscreen_sim_puk_locked_message" msgid="7441797339976230">"A tarxeta SIM está bloqueada con código PUK."</string>
<string name="lockscreen_sim_puk_locked_instructions" msgid="8127916255245181063">"Consulta a guía do usuario ou ponte en contacto co servizo de asistencia ao cliente."</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Teléfono"</string>
<string name="map" msgid="6068210738233985748">"Mapas"</string>
<string name="browse" msgid="6993590095938149861">"Navegador"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Contacto"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Estase esgotando o espazo de almacenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"É posible que algunhas funcións do sistema non funcionen"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Non hai almacenamento suficiente para o sistema. Asegúrate de ter un espazo libre de 250 MB e reinicia o dispositivo."</string>
@@ -1782,7 +1781,7 @@
<string name="etws_primary_default_message_earthquake" msgid="5541962250262769193">"Mantén a calma e busca refuxio cerca."</string>
<string name="etws_primary_default_message_tsunami" msgid="1887685943498368548">"Abandona de inmediato rexións costeiras e situadas na beira de ríos para dirixirte a un lugar máis seguro, como un terreo elevado."</string>
<string name="etws_primary_default_message_earthquake_and_tsunami" msgid="998797956848445862">"Mantén a calma e busca refuxio cerca."</string>
- <string name="etws_primary_default_message_test" msgid="2709597093560037455">"Proba de mensaxes de emerxencia"</string>
+ <string name="etws_primary_default_message_test" msgid="2709597093560037455">"Proba de mensaxes de urxencia"</string>
<string name="notification_reply_button_accessibility" msgid="3621714652387814344">"Responder"</string>
<string name="etws_primary_default_message_others" msgid="6293148756130398971"></string>
<string name="mmcc_authentication_reject" msgid="7729819349669603406">"Non se admite a tarxeta SIM"</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"Non se admite a tarxeta SIM"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Non se admite o teléfono"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Ventá emerxente"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index d985e28..afc082b 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"ખૂટતી વ્યવસ્થાપક ઍપ્લિકેશનને કારણે કાર્યાલયની પ્રોફાઇલ કાઢી નાખી"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"કાર્ય પ્રોફાઇલ વ્યવસ્થાપક ઍપ્લિકેશન ખૂટે છે અથવા તો દૂષિત છે. પરિણામે, તમારી કાર્યાલયની પ્રોફાઇલ અને તે સંબંધિત ડેટા કાઢી નાખવામાં આવ્યો છે. સહાયતા માટે તમારા વ્યવસ્થાપકનો સંપર્ક કરો."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"આ ઉપકરણ પર તમારી કાર્યાલયની પ્રોફાઇલ હવે ઉપલબ્ધ નથી"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"પાસવર્ડના ઘણા વધુ પ્રયત્નો"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"ઉપકરણ સંચાલિત છે"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"તમારી સંસ્થા આ ઉપકરણનું સંચાલન કરે છે અને નેટવર્ક ટ્રાફિફનું નિયમન કરી શકે છે. વિગતો માટે ટૅપ કરો."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"તમારું ઉપકરણ કાઢી નાખવામાં આવશે"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"ફોન"</string>
<string name="map" msgid="6068210738233985748">"નકશા"</string>
<string name="browse" msgid="6993590095938149861">"બ્રાઉઝર"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"સંપર્ક"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"સ્ટોરેજ સ્થાન સમાપ્ત થયું"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"કેટલાક સિસ્ટમ કાર્યો કામ કરી શકશે નહીં"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"સિસ્ટમ માટે પર્યાપ્ત સ્ટોરેજ નથી. ખાતરી કરો કે તમારી પાસે 250MB ખાલી સ્થાન છે અને ફરીથી પ્રારંભ કરો."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"સિમ મંજૂર નથી"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"ફોન મંજૂર નથી"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"પૉપઅપ વિંડો"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index e29deab..925565a 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"अनुपलब्ध व्यवस्थापक ऐप्लिकेशन के कारण कार्य प्रोफ़ाइल हटा दी गई"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"कार्य प्रोफ़ाइल व्यवस्थापक ऐप्लिकेशन या तो मौजूद नहीं है या वह खराब हो गया है. परिणामस्वरूप, आपकी कार्य प्रोफ़ाइल और उससे जुड़े डेटा को हटा दिया गया है. सहायता के लिए अपने व्यवस्थापक से संपर्क करें."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"आपकी कार्य प्रोफ़ाइल अब इस डिवाइस पर उपलब्ध नहीं है"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"कई बार गलत पासवर्ड डाला गया"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"डिवाइस प्रबंधित है"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"आपका संगठन इस डिवाइस का प्रबंधन करता है और वह नेटवर्क ट्रैफ़िक की निगरानी भी कर सकता है. विवरण के लिए टैप करें."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"आपके डिवाइस को मिटा दिया जाएगा"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"फ़ोन"</string>
<string name="map" msgid="6068210738233985748">"मानचित्र"</string>
<string name="browse" msgid="6993590095938149861">"ब्राउज़र"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"मैसेज (एसएमएस)"</string>
+ <string name="add_contact" msgid="7990645816259405444">"संपर्क"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"मेमोरी में जगह नहीं बची है"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"हो सकता है कुछ सिस्टम फ़ंक्शन कार्य न करें"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"सिस्टम के लिए ज़रूरी मेमोरी नहीं है. पक्का करें कि आपके पास 250एमबी की खाली जगह है और फिर से शुरू करें."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM की अनुमति नहीं है"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"फ़ोन की अनुमति नहीं है"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"पॉपअप विंडो"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index bcf789b..a7fa3ce 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -174,6 +174,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Radni je profil izbrisan jer nedostaje administratorska aplikacija"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Administratorska aplikacija radnog profila nedostaje ili je oštećena. Zbog toga su radni profil i povezani podaci izbrisani. Za pomoć se obratite svom administratoru."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Vaš radni profil više nije dostupan na ovom uređaju"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Previše pokušaja unosa zaporke"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Uređaj je upravljan"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Vaša organizacija upravlja ovim uređajem i može nadzirati mrežni promet. Dodirnite za pojedinosti."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Uređaj će se izbrisati"</string>
@@ -1000,10 +1001,8 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Karte"</string>
<string name="browse" msgid="6993590095938149861">"Preglednik"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ponestaje prostora za pohranu"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Neke sistemske funkcije možda neće raditi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nema dovoljno pohrane za sustav. Oslobodite 250 MB prostora i pokrenite uređaj ponovo."</string>
@@ -1825,4 +1824,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM nije dopušten"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefon nije dopušten"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Skočni prozor"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 93eb9ac..b1ef463 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"A munkaprofilt a rendszer hiányzó rendszergazdai alkalmazás miatt törölte"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"A munkaprofil rendszergazdai alkalmazása hiányzik vagy sérült. A rendszer ezért törölte a munkaprofilt, és az ahhoz kapcsolódó adatokat. Ha segítségre van szüksége, vegye fel a kapcsolatot rendszergazdájával."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Munkaprofilja már nem hozzáférhető ezen az eszközön."</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Túl sok jelszómegadási kísérlet"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Felügyelt eszköz"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Ezt az eszközt szervezete kezeli, és lehetséges, hogy a hálózati forgalmat is figyelik. További részletekért koppintson."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"A rendszer törölni fogja eszközét"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Térkép"</string>
<string name="browse" msgid="6993590095938149861">"Böngésző"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Névjegy"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Kevés a szabad terület"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Előfordulhat, hogy néhány rendszerfunkció nem működik."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nincs elegendő tárhely a rendszerhez. Győződjön meg arról, hogy rendelkezik 250 MB szabad területtel, majd kezdje elölről."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"A SIM-kártya nem engedélyezett"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"A telefon nem engedélyezett"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Előugró ablak"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index e5d92c6..b367c67 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Աշխատանքային պրոֆիլը ջնջվել է ադմինիստրատորի հավելվածի բացակայության պատճառով"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Աշխատանքային պրոֆիլի ադմինիստրատորի հավելվածը բացակայում է կամ վնասված է: Արդյունքում ձեր աշխատանքային պրոֆիլը և առնչվող տվյալները ջնջվել են: Օգնության համար դիմեք ձեր ադմինիստրատորին:"</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Ձեր աշխատանքային պրոֆիլն այս սարքում այլևս հասանելի չէ"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Գաղտնաբառը մուտքագրելու չափից շատ փորձեր են կատարվել"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Սարքը կառավարվում է"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Ձեր կազմակերպությունը կառավարում է այս սարքը և կարող է վերահսկել ցանցի թրաֆիկը: Հպեք՝ մանրամասները դիտելու համար:"</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Ձեր սարքը ջնջվելու է"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Հեռախոս"</string>
<string name="map" msgid="6068210738233985748">"Քարտեզներ"</string>
<string name="browse" msgid="6993590095938149861">"Դիտարկիչ"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Կոնտակտ"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Հիշողությունը սպառվում է"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Որոշ գործառույթներ կարող են չաշխատել"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Համակարգի համար բավարար հիշողություն չկա: Համոզվեք, որ ունեք 250ՄԲ ազատ տարածություն և վերագործարկեք:"</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM քարտի օգտագործումն արգելված է"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Հեռախոսի օգտագործումն արգելված է"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Հայտնվող պատուհան"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 89e96f9..a9d4316 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -149,7 +149,7 @@
<string name="httpErrorRedirectLoop" msgid="8679596090392779516">"Laman ini berisi terlalu banyak pengalihan server."</string>
<string name="httpErrorUnsupportedScheme" msgid="5015730812906192208">"Protokol tidak didukung."</string>
<string name="httpErrorFailedSslHandshake" msgid="96549606000658641">"Tidak dapat membuat sambungan aman."</string>
- <string name="httpErrorBadUrl" msgid="3636929722728881972">"Tidak dapat membuka laman karena URL tidak valid."</string>
+ <string name="httpErrorBadUrl" msgid="3636929722728881972">"Tidak dapat membuka halaman karena URL tidak valid."</string>
<string name="httpErrorFile" msgid="2170788515052558676">"Tidak dapat mengakses file."</string>
<string name="httpErrorFileNotFound" msgid="6203856612042655084">"Tidak dapat menemukan file yang diminta."</string>
<string name="httpErrorTooManyRequests" msgid="1235396927087188253">"Terlalu banyak permintaan yang diproses. Coba lagi nanti."</string>
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Profil kerja dihapus karena tidak ada aplikasi admin"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Aplikasi admin profil kerja tidak ada atau rusak. Akibatnya, profil kerja dan data terkait telah dihapus. Hubungi admin untuk meminta bantuan."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Profil kerja tidak tersedia lagi di perangkat ini"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Terlalu banyak percobaan memasukkan sandi"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Perangkat ini ada yang mengelola"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Organisasi mengelola perangkat ini dan mungkin memantau traffic jaringan. Tap untuk melihat detailnya."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Perangkat akan dihapus"</string>
@@ -805,7 +806,7 @@
<string name="js_dialog_before_unload_title" msgid="2619376555525116593">"Konfirmasi Navigasi"</string>
<string name="js_dialog_before_unload_positive_button" msgid="3112752010600484130">"Keluar dari Laman ini"</string>
<string name="js_dialog_before_unload_negative_button" msgid="5614861293026099715">"Tetap di Laman ini"</string>
- <string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nYakin ingin beranjak dari laman ini?"</string>
+ <string name="js_dialog_before_unload" msgid="3468816357095378590">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nYakin ingin beranjak dari halaman ini?"</string>
<string name="save_password_label" msgid="6860261758665825069">"Konfirmasi"</string>
<string name="double_tap_toast" msgid="4595046515400268881">"Kiat: Ketuk dua kali untuk memperbesar dan memperkecil."</string>
<string name="autofill_this_form" msgid="4616758841157816676">"IsiOtomatis"</string>
@@ -842,7 +843,7 @@
<string name="save_password_notnow" msgid="6389675316706699758">"Tidak sekarang"</string>
<string name="save_password_remember" msgid="6491879678996749466">"Ingat"</string>
<string name="save_password_never" msgid="8274330296785855105">"Jangan"</string>
- <string name="open_permission_deny" msgid="7374036708316629800">"Anda tidak memiliki izin untuk membuka laman ini."</string>
+ <string name="open_permission_deny" msgid="7374036708316629800">"Anda tidak memiliki izin untuk membuka halaman ini."</string>
<string name="text_copied" msgid="4985729524670131385">"Teks disalin ke papan klip."</string>
<string name="more_item_label" msgid="4650918923083320495">"Lainnya"</string>
<string name="prepend_shortcut_label" msgid="2572214461676015642">"Menu+"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Telepon"</string>
<string name="map" msgid="6068210738233985748">"Peta"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kontak"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ruang penyimpanan hampir habis"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Beberapa fungsi sistem mungkin tidak dapat bekerja"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Penyimpanan tidak cukup untuk sistem. Pastikan Anda memiliki 250 MB ruang kosong, lalu mulai ulang."</string>
@@ -1318,7 +1317,7 @@
<string name="next_button_label" msgid="1080555104677992408">"Selanjutnya"</string>
<string name="skip_button_label" msgid="1275362299471631819">"Lewati"</string>
<string name="no_matches" msgid="8129421908915840737">"Tidak ada kecocokan"</string>
- <string name="find_on_page" msgid="1946799233822820384">"Temukan pada laman"</string>
+ <string name="find_on_page" msgid="1946799233822820384">"Temukan pada halaman"</string>
<plurals name="matches_found" formatted="false" msgid="1210884353962081884">
<item quantity="other"><xliff:g id="INDEX">%d</xliff:g> dari <xliff:g id="TOTAL">%d</xliff:g></item>
<item quantity="one">1 kecocokan</item>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM tidak diizinkan"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Ponsel tidak diizinkan"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Jendela Pop-up"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 2850fec..3cdee4c 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Vinnusniði eytt vegna þess að stjórnunarforrit vantar"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Stjórnunarforrit vinnusniðsins vantar eða er skemmt. Vinnusniðinu og gögnum því tengdu hefur því verið eytt. Hafðu samband við kerfisstjórann til að fá frekari aðstoð."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Vinnusniðið þitt er ekki lengur í boði á þessu tæki"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Of margar tilraunir til að slá inn aðgangsorð"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Tækinu er stjórnað"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Fyrirtækið þitt stjórnar þessu tæki og kann að fylgjast með netnotkun. Ýttu hér til að fá upplýsingar."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Tækið verður hreinsað"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Sími"</string>
<string name="map" msgid="6068210738233985748">"Kort"</string>
<string name="browse" msgid="6993590095938149861">"Vafri"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS-skilaboð"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Tengiliður"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Geymslurýmið er senn á þrotum"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Sumir kerfiseiginleikar kunna að vera óvirkir"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Ekki nægt geymslurými fyrir kerfið. Gakktu úr skugga um að 250 MB séu laus og endurræstu."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-kort er ekki leyft"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Sími er ekki leyfður"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Sprettigluggi"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 0274602..f08ed1b 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Profilo di lavoro eliminato per app di amministrazione mancante"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"L\'app di amministrazione dei profili di lavoro manca o è danneggiata. Di conseguenza, il tuo profilo di lavoro e i relativi dati sono stati eliminati. Contatta l\'amministratore per ricevere assistenza."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Il tuo profilo di lavoro non è più disponibile sul dispositivo"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Troppi tentativi di inserimento della password"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Il dispositivo è gestito"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Questo dispositivo è gestito dalla tua organizzazione, che potrebbe monitorare il traffico di rete. Tocca per i dettagli."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Il dispositivo verrà resettato"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Telefono"</string>
<string name="map" msgid="6068210738233985748">"Mappe"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Contatto"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spazio di archiviazione in esaurimento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Alcune funzioni di sistema potrebbero non funzionare"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Memoria insufficiente per il sistema. Assicurati di avere 250 MB di spazio libero e riavvia."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"Scheda SIM non consentita"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefono non consentito"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Finestra popup"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 5fa6c52..98b17dc 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -176,6 +176,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"פרופיל העבודה נמחק מפני שחסרה אפליקציית ניהול"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"אפליקציית הניהול של פרופיל העבודה חסרה או פגומה. כתוצאה מכך, פרופיל העבודה שלך נמחק, כולל כל הנתונים הקשורים אליו. לקבלת עזרה, פנה למנהל המערכת."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"פרופיל העבודה שלך אינו זמין עוד במכשיר הזה"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"בוצעו ניסיונות רבים מדי להזנת סיסמה"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"המכשיר מנוהל"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"הארגון שלך מנהל מכשיר זה ועשוי לנטר את התנועה ברשת. הקש לקבלת פרטים."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"תתבצע מחיקה של המכשיר"</string>
@@ -1020,10 +1021,8 @@
<string name="dial" msgid="4204975095406423102">"טלפון"</string>
<string name="map" msgid="6068210738233985748">"מפות"</string>
<string name="browse" msgid="6993590095938149861">"דפדפן"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"איש קשר"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"שטח האחסון אוזל"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ייתכן שפונקציות מערכת מסוימות לא יפעלו"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"אין מספיק שטח אחסון עבור המערכת. ודא שיש לך שטח פנוי בגודל 250MB התחל שוב."</string>
@@ -1860,4 +1859,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"כרטיס ה-SIM לא מורשה"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"הטלפון לא מורשה"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"חלון קופץ"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 78e59a3..66ce56c 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"管理アプリがないため仕事用プロファイルが削除されました"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"仕事用プロファイルの管理アプリがないか、破損しています。そのため仕事用プロファイルと関連データが削除されました。管理者にサポートをご依頼ください。"</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"お使いの仕事用プロファイルはこの端末で使用できなくなりました"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"パスワード入力回数が上限を超えました"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"管理対象の端末"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"この端末は組織によって管理され、ネットワーク トラフィックが監視される場合があります。詳しくはタップしてください。"</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"端末のデータが消去されます"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"電話"</string>
<string name="map" msgid="6068210738233985748">"マップ"</string>
<string name="browse" msgid="6993590095938149861">"ブラウザ"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"連絡先"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"空き容量わずか"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"一部のシステム機能が動作しない可能性があります"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"システムに十分な容量がありません。250MBの空き容量を確保して再起動してください。"</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM は許可されていません"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"電話は許可されていません"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"ポップアップ ウィンドウ"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index d9947b0..879270b 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"სამსახურის პროფილი წაიშალა ადმინისტრატორის აპის არქონის გამო"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"სამსახურის პროფილის ადმინისტრატორის აპი მიუწვდომელია ან დაზიანებულია. ამის გამო, თქვენი სამსახურის პროფილი და დაკავშირებული მონაცემები წაიშალა. დახმარებისთვის დაუკავშირდით თქვენს ადმინისტრატორს."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"თქვენი სამსახურის პროფილი აღარ არის ხელმისაწვდომი ამ მოწყობილობაზე"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"დაფიქსირდა პაროლის შეყვანის ზედმეტად ბევრი მცდელობა"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"მოწყობილობა მართულია"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"ამ მოწყობილობას თქვენი ორგანიზაცია მართავს და მას ქსელის ტრაფიკის მონიტორინგი შეუძლია. შეეხეთ დამატებითი დეტალებისთვის."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"თქვენი მოწყობილობა წაიშლება"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"ტელეფონი"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"ბრაუზერი"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"კონტაქტი"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"თავისუფალი ადგილი იწურება"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"სისტემის ზოგიერთმა ფუნქციამ შესაძლოა არ იმუშავოს"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"სისტემისათვის საკმარისი საცავი არ არის. დარწმუნდით, რომ იქონიოთ სულ მცირე 250 მბაიტი თავისუფალი სივრცე და დაიწყეთ ხელახლა."</string>
@@ -993,8 +992,7 @@
<string name="cancel" msgid="6442560571259935130">"გაუქმება"</string>
<string name="yes" msgid="5362982303337969312">"OK"</string>
<string name="no" msgid="5141531044935541497">"გაუქმება"</string>
- <!-- no translation found for close (2318214661230355730) -->
- <skip />
+ <string name="close" msgid="2318214661230355730">"დახურვა"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"ყურადღება"</string>
<string name="loading" msgid="7933681260296021180">"ჩატვირთვა…"</string>
<string name="capital_on" msgid="1544682755514494298">"ჩართ."</string>
@@ -1051,10 +1049,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"მასშტაბი"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"ყოველთვის ჩვენება"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"ხელახალი გააქტიურება განყოფილებაში: სისტემის პარამეტრები > აპები > ჩამოტვირთულები."</string>
- <!-- no translation found for top_app_killed_title (6814231368167994497) -->
- <skip />
- <!-- no translation found for top_app_killed_message (3487519022191609844) -->
- <skip />
+ <string name="top_app_killed_title" msgid="6814231368167994497">"აპი არ რეაგირებს"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> შესაძლოა მეხსიერებას გადამეტებით იყენებდეს."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ის მიერ ეკრანის ამჟამინდელი პარამეტრები მხარდაუჭერელია და შეიძლება არასათანადოდ იმუშაოს."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"ყოველთვის ჩვენება"</string>
<string name="smv_application" msgid="3307209192155442829">"აპმა <xliff:g id="APPLICATION">%1$s</xliff:g> (პროცესი <xliff:g id="PROCESS">%2$s</xliff:g>) დაარღვია საკუთარი StrictMode დებულება."</string>
@@ -1793,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM ბარათი დაუშვებელია"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"ტელეფონი დაუშვებელია"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"ამომხტარი ფანჯარა"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 95e1459..36dc3a6 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Әкімші қолданбасы болмағандықтан жұмыс профилі жойылды"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Жұмыс профилінің әкімші қолданбасы жоқ немесе бүлінген. Нәтижесінде жұмыс профиліңіз және қатысты деректер жойылды. Көмек алу үшін әкімшіге хабарласыңыз."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Жұмыс профиліңіз осы құрылғыда енді қолжетімді емес"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Тым көп құпия сөз енгізу әрекеті жасалды"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Құрылғы басқарылады"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Ұйымыңыз осы құрылғыны басқарады және желі трафигін бақылауы мүмкін. Мәліметтер алу үшін түртіңіз."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Құрылғыңыздағы деректер өшіріледі"</string>
@@ -968,7 +969,7 @@
<string name="delete" msgid="6098684844021697789">"Жою"</string>
<string name="copyUrl" msgid="2538211579596067402">"URL мекенжайын көшіру"</string>
<string name="selectTextMode" msgid="1018691815143165326">"Мәтінді бөлектеу"</string>
- <string name="undo" msgid="7905788502491742328">"Кері қайтару"</string>
+ <string name="undo" msgid="7905788502491742328">"Қайтару"</string>
<string name="redo" msgid="7759464876566803888">"Қайтару"</string>
<string name="autofill" msgid="3035779615680565188">"Aвтотолтыру"</string>
<string name="textSelectionCABTitle" msgid="5236850394370820357">"Мәтін таңдау"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Телефон"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"Браузер"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Контакт"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Жадта орын азайып барады"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Жүйенің кейбір функциялары жұмыс істемеуі мүмкін"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Жүйе үшін жад жеткіліксіз. 250 МБ бос орын бар екенін тексеріп, қайта іске қосыңыз."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM картасына рұқсат етілмеген"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Телефонға рұқсат етілмеген"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Қалқымалы терезе"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index ae11db4..b8f9f8b 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"កម្រងព័ត៌មានការងារត្រូវបានលុបដោយសារបាត់កម្មវិធីអ្នកគ្រប់គ្រង"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"កម្មវិធីអ្នកគ្រប់គ្រងកម្រងព័ត៌មានការងារនេះអាចបាត់ ឬមានបញ្ហា។ ដូច្នេះហើយទើបកម្រងព័ត៌មានការងាររបស់អ្នក និងទិន្នន័យដែលពាក់ព័ន្ធត្រូវបានលុប។ សូមទាក់ទងទៅអ្នកគ្រប់គ្រងរបស់អ្នក ដើម្បីទទួលបានជំនួយ។"</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"កម្រងព័ត៌មានការងាររបស់អ្នកលែងមាននៅលើឧបករណ៍នេះទៀតហើយ"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"ការព្យាយាមបញ្ចូលពាក្យសម្ងាត់ច្រើនដងពេកហើយ"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"ឧបករណ៍ស្ថិតក្រោមការគ្រប់គ្រង"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"ស្ថាប័នរបស់អ្នកគ្រប់គ្រងឧបករណ៍នេះ ហើយអាចនឹងតាមដានចរាចរណ៍បណ្តាញ។ ចុចដើម្បីទទួលបានព័ត៌មានលម្អិត។"</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"ឧបករណ៍របស់អ្នកនឹងត្រូវបានលុប"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"ទូរសព្ទ"</string>
<string name="map" msgid="6068210738233985748">"ផែនទី"</string>
<string name="browse" msgid="6993590095938149861">"កម្មវិធីរុករកតាមអ៊ីនធឺណិត"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"ទំនាក់ទំនង"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"អស់ទំហំផ្ទុក"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារប្រព័ន្ធមួយចំនួនអាចមិនដំណើរការ"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"មិនមានទំហំផ្ទុកគ្រប់គ្រាន់សម្រាប់ប្រព័ន្ធ។ សូមប្រាកដថាអ្នកមានទំហំទំនេរ 250MB ហើយចាប់ផ្ដើមឡើងវិញ។"</string>
@@ -1792,4 +1791,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"មិនអនុញ្ញាតចំពោះសីុមទេ"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"មិនអនុញ្ញាតចំពោះទូរសព្ទទេ"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"វិនដូលេចឡើង"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 125ccbb..c362ce1 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"ನಿರ್ವಾಹಕ ಅಪ್ಲಿಕೇಶನ್ ತಪ್ಪಿಹೋಗಿರುವುದರಿಂದ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ ಅನ್ನು ಅಳಿಸಲಾಗಿದೆ"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ ನಿರ್ವಾಹಕ ಅಪ್ಲಿಕೇಶನ್ ಕಳೆದು ಹೋಗಿದೆ ಅಥವಾ ಹಾಳಾಗಿದೆ. ಇದರ ಪರಿಣಾಮವಾಗಿ ನಿಮ್ಮ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ ಮತ್ತು ಅದಕ್ಕೆ ಸಂಬಂಧಿಸಿದ ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗಿದೆ. ಸಹಾಯಕ್ಕಾಗಿ ನಿಮ್ಮ ನಿರ್ವಾಹಕರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"ನಿಮ್ಮ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್ ಈ ಸಾಧನದಲ್ಲಿ ಈಗ ಲಭ್ಯವಿಲ್ಲ"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"ಹಲವಾರು ಪಾಸ್ವರ್ಡ್ ಪ್ರಯತ್ನಗಳು"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"ಸಾಧನವನ್ನು ನಿರ್ವಹಿಸಲಾಗುತ್ತಿದೆ"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ಈ ಸಾಧನವನ್ನು ನಿರ್ವಹಿಸುತ್ತದೆ ಮತ್ತು ಅದು ನೆಟ್ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಮೇಲೆ ಗಮನವಿರಿಸಬಹುದು. ವಿವರಗಳಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"ನಿಮ್ಮ ಸಾಧನವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"ಫೋನ್"</string>
<string name="map" msgid="6068210738233985748">"ನಕ್ಷೆಗಳು"</string>
<string name="browse" msgid="6993590095938149861">"ಬ್ರೌಸರ್"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"ಎಸ್ಎಂಎಸ್"</string>
+ <string name="add_contact" msgid="7990645816259405444">"ಸಂಪರ್ಕ"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"ಸಂಗ್ರಹಣೆ ಸ್ಥಳವು ತುಂಬಿದೆ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ಕೆಲವು ಸಿಸ್ಟಂ ಕಾರ್ಯವಿಧಾನಗಳು ಕಾರ್ಯನಿರ್ವಹಿಸದೇ ಇರಬಹುದು"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ಸಿಸ್ಟಂನಲ್ಲಿ ಸಾಕಷ್ಟು ಸಂಗ್ರಹಣೆಯಿಲ್ಲ. ನೀವು 250MB ನಷ್ಟು ಖಾಲಿ ಸ್ಥಳವನ್ನು ಹೊಂದಿರುವಿರಾ ಎಂಬುದನ್ನು ಖಚಿತಪಡಿಸಿಕೊಳ್ಳಿ ಹಾಗೂ ಮರುಪ್ರಾರಂಭಿಸಿ."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"ಸಿಮ್ಗೆ ಅನುಮತಿಯಿಲ್ಲ"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"ಫೋನ್ಗೆ ಅನುಮತಿಯಿಲ್ಲ"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"ಪಾಪ್ಅಪ್ ವಿಂಡೋ"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index c253742..991a9833 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"관리 앱이 없어서 직장 프로필이 삭제되었습니다."</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"직장 프로필 관리 앱이 없거나 손상되어 직장 프로필 및 관련 데이터가 삭제되었습니다. 도움이 필요한 경우 관리자에게 문의하세요."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"직장 프로필을 이 기기에서 더 이상 사용할 수 없습니다."</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"비밀번호 입력을 너무 많이 시도함"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"관리되는 기기"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"조직에서 이 기기를 관리하며 네트워크 트래픽을 모니터링할 수도 있습니다. 자세한 내용을 보려면 탭하세요."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"기기가 삭제됩니다."</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"전화"</string>
<string name="map" msgid="6068210738233985748">"지도"</string>
<string name="browse" msgid="6993590095938149861">"브라우저"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"연락처"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"저장 공간이 부족함"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"일부 시스템 기능이 작동하지 않을 수 있습니다."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"시스템의 저장 공간이 부족합니다. 250MB의 여유 공간이 확보한 후 다시 시작하세요."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM이 허용되지 않음"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"전화가 허용되지 않음"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"팝업 창"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 77f3b0c..06bbabe 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Башкаруучу колдонмосу болбогондуктан, жумуш профили жок кылынды"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Жумуш профилинин башкаруучу колдонмосу жок же бузулгандыктан, жумуш профилиңиз жана ага байланыштуу дайындар жок кылынды. Жардам алуу үчүн администраторуңузга кайрылыңыз."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Жумуш профилиңиз бул түзмөктөн жок кылынды"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Өтө көп жолу сырсөздү киргизүү аракети жасалды"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Түзмөктү ишкана башкарат"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Ишканаңыз бул түзмөктү башкарат жана тармак трафигин көзөмөлдөшү мүмкүн. Чоо-жайын көрүү үчүн таптап коюңуз."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Түзмөгүңүз тазаланат"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Телефон"</string>
<string name="map" msgid="6068210738233985748">"Карталар"</string>
<string name="browse" msgid="6993590095938149861">"Серепчи"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Байланыш"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Сактагычта орун калбай баратат"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Системанын кээ бир функциялары иштебеши мүмкүн"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Тутумда сактагыч жетишсиз. 250МБ бош орун бар экенин текшерип туруп, өчүрүп күйгүзүңүз."</string>
@@ -993,8 +992,7 @@
<string name="cancel" msgid="6442560571259935130">"Жокко чыгаруу"</string>
<string name="yes" msgid="5362982303337969312">"Жарайт"</string>
<string name="no" msgid="5141531044935541497">"Жокко чыгаруу"</string>
- <!-- no translation found for close (2318214661230355730) -->
- <skip />
+ <string name="close" msgid="2318214661230355730">"ЖАБУУ"</string>
<string name="dialog_alert_title" msgid="2049658708609043103">"Көңүл буруңуз"</string>
<string name="loading" msgid="7933681260296021180">"Жүктөлүүдө…"</string>
<string name="capital_on" msgid="1544682755514494298">"ЖАНДЫРЫЛГАН"</string>
@@ -1051,10 +1049,8 @@
<string name="screen_compat_mode_scale" msgid="3202955667675944499">"Шкала"</string>
<string name="screen_compat_mode_show" msgid="4013878876486655892">"Ар дайым көрсөтүлсүн"</string>
<string name="screen_compat_mode_hint" msgid="1064524084543304459">"Муну тутум жөндөөлөрүнөн кайра иштетүү > Колдонмолор > Жүктөлүп алынган."</string>
- <!-- no translation found for top_app_killed_title (6814231368167994497) -->
- <skip />
- <!-- no translation found for top_app_killed_message (3487519022191609844) -->
- <skip />
+ <string name="top_app_killed_title" msgid="6814231368167994497">"Колдонмо жооп бербей жатат"</string>
+ <string name="top_app_killed_message" msgid="3487519022191609844">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу эстутумду өтө көп колдонуп жатышы мүмкүн."</string>
<string name="unsupported_display_size_message" msgid="6545327290756295232">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу көрүнүштүн тандалган өлчөмүн экранда көрсөтө албайт жана туура эмес иштеши мүмкүн."</string>
<string name="unsupported_display_size_show" msgid="7969129195360353041">"Ар дайым көрсөтүлсүн"</string>
<string name="smv_application" msgid="3307209192155442829">"<xliff:g id="APPLICATION">%1$s</xliff:g> колдонмосу (<xliff:g id="PROCESS">%2$s</xliff:g> процесси) өз алдынча иштеткен StrictMode саясатын бузду."</string>
@@ -1794,4 +1790,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM картаны колдонууга тыюу салынган"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Телефонду колдонууга тыюу салынган"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Калкып чыкма терезе"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index d53c702..374188f 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"ລຶບໂປຣໄຟລ໌ບ່ອນເຮັດວຽກແລ້ວເນື່ອງຈາກບໍ່ມີແອັບຜູ້ເບິ່ງແຍງລະບົບ"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"ບໍ່ມີແອັບຜູ້ເບິ່ງແຍງລະບົບໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ ຫຼື ເສຍຫາຍ. ຜົນກໍຄື, ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ ແລະ ຂໍ້ມູນທີ່ກ່ຽວຂ້ອງຂອງທ່ານຖືກລຶບອອກແລ້ວ. ໃຫ້ຕິດຕໍ່ຜູ້ເບິ່ງແຍງລະບົບສຳລັບການຊ່ວຍເຫຼືອ."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານບໍ່ສາມາດໃຊ້ໄດ້ໃນອຸປະກອນນີ້ອີກຕໍ່ໄປ"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"ລອງໃສ່ລະຫັດຜ່ານຫຼາຍເທື່ອເກີນໄປ"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"ອຸປະກອນມີການຈັດການ"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"ອົງກອນຂອງທ່ານຈັດການອຸປະກອນນີ້ ແລະ ອາດກວດສອບທຣາບຟິກເຄືອຂ່າຍນຳ. ແຕະເພື່ອເບິ່ງລາຍລະອຽດ."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"ອຸປະກອນຂອງທ່ານຈະຖືກລຶບ"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"ໂທລະສັບ"</string>
<string name="map" msgid="6068210738233985748">"ແຜນທີ່"</string>
<string name="browse" msgid="6993590095938149861">"ໂປຣແກຣມທ່ອງເວັບ"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"ຕິດຕໍ່"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"ພື້ນທີ່ຈັດເກັບຂໍ້ມູນກຳລັງຈະເຕັມ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ການເຮັດວຽກບາງຢ່າງຂອງລະບົບບາງອາດຈະໃຊ້ບໍ່ໄດ້"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ບໍ່ມີບ່ອນເກັບຂໍ້ມູນພຽງພໍສຳລັບລະບົບ. ກວດສອບໃຫ້ແນ່ໃຈວ່າທ່ານມີພື້ນທີ່ຫວ່າງຢ່າງໜ້ອຍ 250MB ແລ້ວລອງໃໝ່."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"ບໍ່ອະນຸຍາດໃຫ້ໃຊ້ SIM"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"ບໍ່ອະນຸຍາດໃຫ້ໃຊ້ໂທລະສັບ"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"ໜ້າຈໍປັອບອັບ"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index a3deaa7..ab60a62 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -176,6 +176,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Darbo profilis ištrintas dėl trūkstamos administratoriaus programos"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Trūksta darbo profilio administratoriaus programos arba ji sugadinta. Todėl darbo profilis ir susiję duomenys buvo ištrinti. Jei reikia pagalbos, susisiekite su administratoriumi."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Darbo profilis nebepasiekiamas šiame įrenginyje"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Per daug slaptažodžio bandymų"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Įrenginys yra tvarkomas"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Šį įrenginį tvarko organizacija ir gali stebėti tinklo srautą. Palieskite, kad gautumėte daugiau informacijos."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Įrenginys bus ištrintas"</string>
@@ -1020,10 +1021,8 @@
<string name="dial" msgid="4204975095406423102">"Telefonas"</string>
<string name="map" msgid="6068210738233985748">"Žemėlapiai"</string>
<string name="browse" msgid="6993590095938149861">"Naršyklė"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kontaktas"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Mažėja laisvos saugyklos vietos"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Kai kurios sistemos funkcijos gali neveikti"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sistemos saugykloje nepakanka vietos. Įsitikinkite, kad yra 250 MB laisvos vietos, ir paleiskite iš naujo."</string>
@@ -1860,4 +1859,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM kortelė neleidžiama"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefonas neleidžiamas"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Iššokantysis langas"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index c323fcc..f64fe62 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -174,6 +174,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Darba profils tika dzēsts, jo trūkst administratora lietotnes."</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Trūkst darba profila administratora lietotnes, vai šī lietotne ir bojāta. Šī iemesla dēļ jūsu darba profils un saistītie dati tika dzēsti. Lai saņemtu palīdzību, sazinieties ar administratoru."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Jūsu darba profils šai ierīcē vairs nav pieejams."</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Veikts pārāk daudz paroles ievadīšanas mēģinājumu."</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Ierīce tiek pārvaldīta"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Jūsu organizācija pārvalda šo ierīci un var uzraudzīt tīkla datplūsmu. Pieskarieties, lai saņemtu detalizētu informāciju."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Jūsu ierīces dati tiks dzēsti"</string>
@@ -1000,10 +1001,8 @@
<string name="dial" msgid="4204975095406423102">"Tālrunis"</string>
<string name="map" msgid="6068210738233985748">"Kartes"</string>
<string name="browse" msgid="6993590095938149861">"Pārlūkprogramma"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"Īsziņas"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kontaktpersona"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Paliek maz brīvas vietas"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Dažas sistēmas funkcijas var nedarboties."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sistēmai pietrūkst vietas. Atbrīvojiet vismaz 250 MB vietas un restartējiet ierīci."</string>
@@ -1825,4 +1824,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM karti nav atļauts izmantot"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Tālruni nav atļauts izmantot"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Uznirstošais logs"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-mcc001-mnc01/strings.xml b/core/res/res/values-mcc001-mnc01/strings.xml
new file mode 100644
index 0000000..96af975
--- /dev/null
+++ b/core/res/res/values-mcc001-mnc01/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc030/strings.xml b/core/res/res/values-mcc310-mnc030/strings.xml
index a3fea29..6a404d5 100644
--- a/core/res/res/values-mcc310-mnc030/strings.xml
+++ b/core/res/res/values-mcc310-mnc030/strings.xml
@@ -20,4 +20,5 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string>
<string name="mmcc_illegal_ms">SIM not allowed MM#3</string>
+ <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc150/strings.xml b/core/res/res/values-mcc310-mnc150/strings.xml
new file mode 100644
index 0000000..96af975
--- /dev/null
+++ b/core/res/res/values-mcc310-mnc150/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
+</resources>
diff --git a/core/res/res/values-mcc310-mnc170/strings.xml b/core/res/res/values-mcc310-mnc170/strings.xml
index a3fea29..6a404d5 100644
--- a/core/res/res/values-mcc310-mnc170/strings.xml
+++ b/core/res/res/values-mcc310-mnc170/strings.xml
@@ -20,4 +20,5 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string>
<string name="mmcc_illegal_ms">SIM not allowed MM#3</string>
+ <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc280/strings.xml b/core/res/res/values-mcc310-mnc280/strings.xml
index a3fea29..6a404d5 100644
--- a/core/res/res/values-mcc310-mnc280/strings.xml
+++ b/core/res/res/values-mcc310-mnc280/strings.xml
@@ -20,4 +20,5 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string>
<string name="mmcc_illegal_ms">SIM not allowed MM#3</string>
+ <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc410/strings.xml b/core/res/res/values-mcc310-mnc410/strings.xml
index a3fea29..6a404d5 100644
--- a/core/res/res/values-mcc310-mnc410/strings.xml
+++ b/core/res/res/values-mcc310-mnc410/strings.xml
@@ -20,4 +20,5 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string>
<string name="mmcc_illegal_ms">SIM not allowed MM#3</string>
+ <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc560/strings.xml b/core/res/res/values-mcc310-mnc560/strings.xml
index a3fea29..6a404d5 100644
--- a/core/res/res/values-mcc310-mnc560/strings.xml
+++ b/core/res/res/values-mcc310-mnc560/strings.xml
@@ -20,4 +20,5 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string>
<string name="mmcc_illegal_ms">SIM not allowed MM#3</string>
+ <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
</resources>
diff --git a/core/res/res/values-mcc310-mnc950/strings.xml b/core/res/res/values-mcc310-mnc950/strings.xml
index a3fea29..6a404d5 100644
--- a/core/res/res/values-mcc310-mnc950/strings.xml
+++ b/core/res/res/values-mcc310-mnc950/strings.xml
@@ -20,4 +20,5 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string>
<string name="mmcc_illegal_ms">SIM not allowed MM#3</string>
+ <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
</resources>
diff --git a/core/res/res/values-mcc311-mnc180/strings.xml b/core/res/res/values-mcc311-mnc180/strings.xml
index a3fea29..6a404d5 100644
--- a/core/res/res/values-mcc311-mnc180/strings.xml
+++ b/core/res/res/values-mcc311-mnc180/strings.xml
@@ -20,4 +20,5 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="mmcc_imsi_unknown_in_hlr">SIM not provisioned MM#2</string>
<string name="mmcc_illegal_ms">SIM not allowed MM#3</string>
+ <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
</resources>
diff --git a/core/res/res/values-mcc312-mnc670/strings.xml b/core/res/res/values-mcc312-mnc670/strings.xml
new file mode 100644
index 0000000..96af975
--- /dev/null
+++ b/core/res/res/values-mcc312-mnc670/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
+</resources>
diff --git a/core/res/res/values-mcc313-mnc100/strings.xml b/core/res/res/values-mcc313-mnc100/strings.xml
new file mode 100644
index 0000000..96af975
--- /dev/null
+++ b/core/res/res/values-mcc313-mnc100/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="mmcc_illegal_me">Phone not allowed MM#6</string>
+</resources>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index c3de4a9..aef21c3 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Работниот профил е избришан поради исчезнувањето на апликацијата на администратор"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Апликацијата на администраторот за работниот профил или исчезна или е оштетена. Како резултат на тоа, вашиот работен профил и поврзаните податоци ќе се избришат. За помош, контактирајте со администраторот."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Вашиот работен профил веќе не е достапен на уредов"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Премногу обиди за внесување лозинка"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Некој управува со уредот"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Вашата организација управува со уредов и можно е да го следи сообраќајот на мрежата. Допрете за детали."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Уредот ќе се избрише"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Телефон"</string>
<string name="map" msgid="6068210738233985748">"„Карти“"</string>
<string name="browse" msgid="6993590095938149861">"Прелистувач"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Контакт"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Меморијата е речиси полна"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некои системски функции може да не работат"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Нема доволно меморија во системот. Проверете дали има слободен простор од 250 МБ и рестартирајте."</string>
@@ -1792,4 +1791,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"Не е дозволена SIM-картичка"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Не е дозволен телефон"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Појавен прозорец"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 2623167..89333cc 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"അഡ്മിൻ ആപ്പ് വിട്ടുപോയിരിക്കുന്നതിനാൽ ഔദ്യോഗിക പ്രൊഫൈൽ ഇല്ലാതാക്കി"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"ഔദ്യോഗിക പ്രൊഫൈൽ അഡ്മിൻ ആപ്പ് വിട്ടുപോയിരിക്കുന്നു അല്ലെങ്കിൽ കേടായിരിക്കുന്നു. ഫലമായി, നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈലും ബന്ധപ്പെട്ട വിവരങ്ങളും ഇല്ലാതാക്കിയിരിക്കുന്നു. സഹായത്തിന് അഡ്മിനെ ബന്ധപ്പെടുക."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"ഈ ഉപകരണത്തിൽ തുടർന്നങ്ങോട്ട് നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈൽ ലഭ്യമല്ല"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"വളരെയധികം പാസ്വേഡ് ശ്രമങ്ങൾ"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"ഉപകരണം മാനേജുചെയ്യുന്നുണ്ട്"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"നിങ്ങളുടെ സ്ഥാപനമാണ് ഈ ഉപകരണം മാനേജുചെയ്യുന്നത്, നെറ്റ്വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കുകയും ചെയ്തേക്കാം, വിശദാംശങ്ങൾ അറിയാൻ ടാപ്പുചെയ്യുക."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"നിങ്ങളുടെ ഉപകരണം മായ്ക്കും"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"ഫോണ്"</string>
<string name="map" msgid="6068210738233985748">"മാപ്സ്"</string>
<string name="browse" msgid="6993590095938149861">"ബ്രൗസർ"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"കോണ്ടാക്റ്റ്"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"സംഭരണയിടം കഴിഞ്ഞു"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ചില സിസ്റ്റം പ്രവർത്തനങ്ങൾ പ്രവർത്തിക്കണമെന്നില്ല."</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"സിസ്റ്റത്തിനായി മതിയായ സംഭരണമില്ല. 250MB സൗജന്യ സംഭരണമുണ്ടെന്ന് ഉറപ്പുവരുത്തി പുനരാരംഭിക്കുക."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM അനുവദനീയമല്ല"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"ഫോൺ അനുവദനീയമല്ല"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"പോപ്പ് അപ്പ് വിൻഡോ"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index ce09140..9173f2c 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Админ апп байхгүй байгаа тул ажлын профайлыг устгасан байна"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Ажлын профайлын админ апп байхгүй эсвэл эвдэрсэн байна. Үүний улмаас таны ажлын профайл болон холбогдох мэдээллийг устгасан болно. Тусламж хэрэгтэй бол админтай холбогдоно уу."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Таны ажлын профайл энэ төхөөрөмжид боломжгүй байна"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Нууц үгийг хэт олон удаа буруу оруулсан байна"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Төхөөрөмжийг удирдсан"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Таны байгууллага энэ төхөөрөмжийг удирдаж, сүлжээний ачааллыг хянадаг. Дэлгэрэнгүй мэдээлэл авах бол товшино уу."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Таны төхөөрөмж устах болно."</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Утас"</string>
<string name="map" msgid="6068210738233985748">"Газрын зураг"</string>
<string name="browse" msgid="6993590095938149861">"Хөтөч"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Харилцагч"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Сангийн хэмжээ дутагдаж байна"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Зарим систем функц ажиллахгүй байна"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Системд хангалттай сан байхгүй байна. 250MБ чөлөөтэй зай байгаа эсэхийг шалгаад дахин эхлүүлнэ үү."</string>
@@ -1788,4 +1787,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM боломжгүй"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Утас боломжгүй"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"гэнэт гарч ирэх цонх"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 2d0cad7..eac23b9 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"प्रशासक अॅप गहाळ असल्यामुळे कार्य प्रोफाइल हटवले गेले"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"कार्य प्रोफाइल प्रशासक अॅप गहाळ आहे किंवा करप्ट आहे. परिणामी, आपले कार्य प्रोफाइल आणि संबंधित डेटा हटवले गेले आहेत. सहाय्यासाठी आपल्या प्रशासकाशी संपर्क साधा."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"आपले कार्य प्रोफाइल आता या डिव्हाइसवर उपलब्ध नाही"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"बर्याचदा पासवर्ड टाकण्याचा प्रयत्न केला"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"डिव्हाइस व्यवस्थापित केले आहे"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"आपली संस्था हे डिव्हाइस व्यवस्थापित करते आणि नेटवर्क रहदारीचे निरीक्षण करू शकते. तपशीलांसाठी टॅप करा."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"तुमचे डिव्हाइस मिटविले जाईल"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"फोन"</string>
<string name="map" msgid="6068210738233985748">"नकाशे"</string>
<string name="browse" msgid="6993590095938149861">"ब्राउझर"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"एसएमएस"</string>
+ <string name="add_contact" msgid="7990645816259405444">"संपर्क"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"संचयन स्थान संपत आहे"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"काही सिस्टम कार्ये कार्य करू शकत नाहीत"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"सिस्टीमसाठी पुरेसे संचयन नाही. आपल्याकडे 250MB मोकळे स्थान असल्याचे सुनिश्चित करा आणि रीस्टार्ट करा."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"सिमला अनुमती नाही"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"फोनला अनुमती नाही"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"पॉपअप विंडो"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 536f2c2..a627a05 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Profil kerja dipadamkan kerana ketiadaan apl pentadbir"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Apl pentadbir profil kerja tiada atau rosak. Akibatnya, profil kerja anda dan data yang berkaitan telah dipadamkan. Hubungi pentadbir anda untuk mendapatkan bantuan."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Profil kerja anda tidak lagi tersedia pada peranti ini"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Terlalu banyak percubaan kata laluan"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Peranti ini diurus"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Organisasi anda mengurus peranti ini dan mungkin memantau trafik rangkaian. Ketik untuk mendapatkan butiran."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Peranti anda akan dipadam"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Peta"</string>
<string name="browse" msgid="6993590095938149861">"Penyemak imbas"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kenalan"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ruang storan semakin berkurangan"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Beberapa fungsi sistem mungkin tidak berfungsi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Tidak cukup storan untuk sistem. Pastikan anda mempunyai 250MB ruang kosong dan mulakan semula."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM tidak dibenarkan"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefon tidak dibenarkan"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Tetingkap Timbul"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index c0539a5..01f7b76 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"စီမံခန့်ခွဲရန် အက်ပ်မရှိသောကြောင့် အလုပ်ပရိုဖိုင်ကို ဖျက်လိုက်ခြင်းဖြစ်သည်"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"အလုပ်ပရိုဖိုင် စီမံခန့်ခွဲရန်အက်ပ် မရှိပါ သို့မဟုတ် ပျက်စီးနေပါသည်။ ထို့ကြောင့် သင်၏ အလုပ်ပရိုဖိုင်နှင့် ဆက်စပ်နေသော ဒေတာများကို ဖျက်လိုက်ပါပြီ။ အကူအညီရယူရန် သင်၏စီမံခန့်ခွဲသူကို ဆက်သွယ်ပါ။"</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"ဤစက်ပစ္စည်းတွင် သင်၏ အလုပ်ပရိုဖိုင်မရှိတော့ပါ"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"စကားဝှက်ထည့်သွင်းရန် ကြိုးစားသည့် အကြိမ်အရေအတွက် အလွန်များသွား၍ ဖြစ်ပါသည်"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"စက်ပစ္စည်းကို စီမံခန့်ခွဲထားပါသည်"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"ဤစက်ပစ္စည်းကို သင်၏ အဖွဲ့အစည်းက စီမံပြီး ကွန်ရက်အသွားအလာကို စောင့်ကြည့်နိုင်ပါသည်။ ထပ်မံလေ့လာရန် တို့ပါ။"</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"သင့်ကိရိယာအား ပယ်ဖျက်လိမ့်မည်"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"ဖုန်း"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"ဘရောင်ဇာ"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS စာတိုစနစ်"</string>
+ <string name="add_contact" msgid="7990645816259405444">"အဆက်အသွယ်"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"သိမ်းဆည်သော နေရာ နည်းနေပါသည်"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"တချို့ စနစ်လုပ်ငန်းများ အလုပ် မလုပ်ခြင်း ဖြစ်နိုင်ပါသည်"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"စနစ်အတွက် သိုလှောင်ခန်း မလုံလောက်ပါ။ သင့်ဆီမှာ နေရာလွတ် ၂၅၀ MB ရှိတာ စစ်ကြည့်ပြီး စတင်ပါ။"</string>
@@ -1187,7 +1186,7 @@
<string name="no_permissions" msgid="7283357728219338112">"ခွင့်ပြုချက်မလိုအပ်ပါ"</string>
<string name="perm_costs_money" msgid="4902470324142151116">"သင့်အတွက် ပိုက်ဆံကုန်ကျနိုင်ပါသည်"</string>
<string name="dlg_ok" msgid="7376953167039865701">"အိုကေ"</string>
- <string name="usb_charging_notification_title" msgid="6895185153353640787">"USB ဖြင့်ဤစက်ပစ္စည်းကို အားသွင်းနေသည်"</string>
+ <string name="usb_charging_notification_title" msgid="6895185153353640787">"ဤစက်ကို USB ဖြင့် အားသွင်းနေသည်"</string>
<string name="usb_supplying_notification_title" msgid="5310642257296510271">"ချိတ်ဆက်ထားသည့် စက်ပစ္စည်းကို USB မှတစ်ဆင့် အားသွင်းနေသည်"</string>
<string name="usb_mtp_notification_title" msgid="8396264943589760855">"ဖိုင်လွှဲပြောင်းရန်အတွက် USB"</string>
<string name="usb_ptp_notification_title" msgid="1347328437083192112">"ဓာတ်ပုံလွှဲပြောင်းရန်အတွက် USB"</string>
@@ -1196,8 +1195,8 @@
<string name="usb_notification_message" msgid="3370903770828407960">"နောက်ထပ်ရွေးချယ်စရာများအတွက် တို့ပါ။"</string>
<string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"အန်နာလော့ အသံကိရိယာကို တွေ့ထားပါသည်"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"တပ်ဆင်ထားသော ကိရိယာကို ဤဖုန်းနှင့် တွဲသုံး၍မရပါ။ ပိုမိုလေ့လာရန် တို့ပါ။"</string>
- <string name="adb_active_notification_title" msgid="6729044778949189918">"USB အမှားစစ်ခြင်းအား ချိတ်ဆက်ထားသည်"</string>
- <string name="adb_active_notification_message" msgid="4948470599328424059">"USB ဆက်သွယ်ရေးစနစ်ကို ပိတ်ရန် တို့ပါ။"</string>
+ <string name="adb_active_notification_title" msgid="6729044778949189918">"USB အမှားရှာပြင်စနစ် ချိတ်ဆက်ထားသည်"</string>
+ <string name="adb_active_notification_message" msgid="4948470599328424059">"USB အမှားရှာပြင်စနစ် ပိတ်ရန် တို့ပါ။"</string>
<string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ဖြင့် အမှားရှာပြင်ခြင်းကို ပိတ်ရန် ရွေးပါ။"</string>
<string name="taking_remote_bugreport_notification_title" msgid="6742483073875060934">"ချွတ်ယွင်းချက် အစီရင်ခံစာပြုစုနေသည်..."</string>
<string name="share_remote_bugreport_notification_title" msgid="4987095013583691873">"ချွတ်ယွင်းချက် အစီရင်ခံစာကို မျှဝေမလား။"</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"ဆင်းမ်ကို ခွင့်မပြုပါ"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"ဖုန်းကို ခွင့်မပြုပါ"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"ပေါ့ပ်အပ် ဝင်းဒိုး"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 4eb0f1d..8e7b683 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Jobbprofilen er slettet på grunn av manglende administratorapp"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Administratorappen for jobbprofilen mangler eller er skadet. Dette har ført til at jobbprofilen og alle data knyttet til den, har blitt slettet. Ta kontakt med administratoren for å få hjelp."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Jobbprofilen din er ikke lenger tilgjengelig på denne enheten"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"For mange passordforsøk"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Enheten administreres"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Organisasjonen din kontrollerer denne enheten og kan overvåke nettverkstrafikk. Trykk for å få mer informasjon."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Enheten blir slettet"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Kart"</string>
<string name="browse" msgid="6993590095938149861">"Nettleser"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Lite ledig lagringsplass"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Enkelte systemfunksjoner fungerer muligens ikke slik de skal"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Det er ikke nok lagringsplass for systemet. Kontrollér at du har 250 MB ledig plass, og start på nytt."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-kortet er ikke tillatt"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefonen er ikke tillatt"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Forgrunnsvindu"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 16743d5..41bb71b 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"प्रशासकीय अनुप्रयोग नभएकाले कार्य प्रोफाइल मेटाइयो"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"उक्त कार्य प्रोफाइलको प्रशासकीय अनुप्रयोग छैन वा बिग्रेको छ। त्यसले गर्दा, तपाईंको कार्य प्रोफाइल र सम्बन्धित डेटालाई मेटिएको छ। सहायताका लागि आफ्ना प्रशासकलाई सम्पर्क गर्नुहोस्।"</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"तपाईंको कार्य प्रोफाइल अब उप्रान्त यस यन्त्रमा उपलब्ध छैन"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"पासवर्ड प्रविष्ट गर्ने अत्यधिक गलत प्रयासहरू भए"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"यन्त्र व्यवस्थित गरिएको छ"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"तपाईंको संगठनले यस यन्त्रको व्यवस्थापन गर्दछ र नेटवर्क ट्राफिकको अनुगमन गर्न सक्छ। विवरणहरूका लागि ट्याप गर्नुहोस्।"</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"तपाईंको यन्त्र मेटिनेछ"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"फोन गर्नुहोस्"</string>
<string name="map" msgid="6068210738233985748">"नक्सा"</string>
<string name="browse" msgid="6993590095938149861">"ब्राउजर"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"सम्पर्क"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"भण्डारण ठाउँ सकिँदै छ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"सायद केही प्रणाली कार्यक्रमहरूले काम गर्दैनन्"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"प्रणालीको लागि पर्याप्त भण्डारण छैन। तपाईँसँग २५० मेगा बाइट ठाउँ खाली भएको निश्चित गर्नुहोस् र फेरि सुरु गर्नुहोस्।"</string>
@@ -1796,4 +1795,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM लाई अनुमति छैन"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"फोनलाई अनुमति छैन"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"पपअप विन्डो"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index e914caf..1355ed3 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Werkprofiel verwijderd vanwege ontbrekende beheer-app"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"De beheer-app van het werkprofiel ontbreekt of is beschadigd. Als gevolg hiervan zijn je werkprofiel en alle gerelateerde gegevens verwijderd. Neem contact op met je beheerder voor hulp."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Je werkprofiel is niet meer beschikbaar op dit apparaat"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Te veel wachtwoordpogingen"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Apparaat wordt beheerd"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Dit apparaat wordt beheerd door je organisatie. Het netwerkverkeer kan worden bijgehouden. Tik voor meer informatie."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Je apparaat wordt gewist"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Telefoon"</string>
<string name="map" msgid="6068210738233985748">"Kaarten"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"Sms"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Contact"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Opslagruimte is bijna vol"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bepaalde systeemfuncties werken mogelijk niet"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Onvoldoende opslagruimte voor het systeem. Zorg ervoor dat je 250 MB vrije ruimte hebt en start opnieuw."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"Simkaart niet toegestaan"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefoon niet toegestaan"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Pop-upvenster"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 7917b23..d6ecf91 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"ਗੁੰਮਸ਼ੁਦਾ ਪ੍ਰਸ਼ਾਸਕ ਐਪ ਦੇ ਕਾਰਨ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਮਿਟਾਇਆ ਗਿਆ"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਪ੍ਰਸ਼ਾਸਕ ਐਪ ਜਾਂ ਤਾਂ ਗੁੰਮਸ਼ੁਦਾ ਹੈ ਜਾਂ ਖਰਾਬ ਹੈ। ਨਤੀਜੇ ਵਜੋਂ, ਤੁਹਾਡੀ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਅਤੇ ਸਬੰਧਿਤ ਡਾਟਾ ਮਿਟਾਇਆ ਗਿਆ ਹੈ। ਸਹਾਇਤਾ ਲਈ ਆਪਣੇ ਪ੍ਰਸ਼ਾਸਕ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।"</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"ਤੁਹਾਡਾ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਹੁਣ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"ਕਈ ਵਾਰ ਗਲਤ ਪਾਸਵਰਡ ਦਾਖਲ ਕੀਤਾ ਗਿਆ"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"ਡੀਵਾਈਸ ਪ੍ਰਬੰਧਨ ਅਧੀਨ ਹੈ"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"ਤੁਹਾਡਾ ਸੰਗਠਨ ਇਸ ਡੀਵਾਈਸ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਦਾ ਹੈ ਅਤੇ ਨੈੱਟਵਰਕ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦਾ ਹੈ। ਵੇਰਵਿਆਂ ਲਈ ਟੈਪ ਕਰੋ।"</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"ਤੁਹਾਡਾ ਡੀਵਾਈਸ ਮਿਟਾਇਆ ਜਾਏਗਾ"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"ਫ਼ੋਨ ਕਰੋ"</string>
<string name="map" msgid="6068210738233985748">"ਨਕਸ਼ੇ"</string>
<string name="browse" msgid="6993590095938149861">"ਬ੍ਰਾਊਜ਼ਰ"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"ਸੰਪਰਕ"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"ਸਟੋਰੇਜ ਦੀ ਜਗ੍ਹਾ ਖਤਮ ਹੋ ਰਹੀ ਹੈ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ਕੁਝ ਸਿਸਟਮ ਫੰਕਸ਼ਨ ਕੰਮ ਨਹੀਂ ਵੀ ਕਰ ਸਕਦੇ"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"ਸਿਸਟਮ ਲਈ ਲੋੜੀਂਦੀ ਸਟੋਰੇਜ ਨਹੀਂ ਹੈ। ਯਕੀਨੀ ਬਣਾਓ ਕਿ ਤੁਹਾਡੇ ਕੋਲ 250MB ਖਾਲੀ ਜਗ੍ਹਾ ਹੈ ਅਤੇ ਮੁੜ-ਚਾਲੂ ਕਰੋ।"</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"ਫ਼ੋਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"ਪੌਪਅੱਪ ਵਿੰਡੋ"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 0989576..5904abd 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -176,6 +176,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Profil do pracy został usunięty z powodu braku aplikacji administratora"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Brakuje aplikacji administratora profilu do pracy lub jest ona uszkodzona. Dlatego Twój profil do pracy i związane z nim dane zostały usunięte. Skontaktuj się ze swoim administratorem, by uzyskać pomoc."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Twój profil do pracy nie jest już dostępny na tym urządzeniu"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Zbyt wiele prób podania hasła"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Urządzenie jest zarządzane"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Twoja organizacja zarządza tym urządzeniem i może monitorować ruch w sieci. Kliknij, by dowiedzieć się więcej."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Twoje urządzenie zostanie wyczyszczone"</string>
@@ -1020,10 +1021,8 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Mapy"</string>
<string name="browse" msgid="6993590095938149861">"Internet"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Kończy się miejsce"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Niektóre funkcje systemu mogą nie działać"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Za mało pamięci w systemie. Upewnij się, że masz 250 MB wolnego miejsca i uruchom urządzenie ponownie."</string>
@@ -1860,4 +1859,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"Niedozwolona karta SIM"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Niedozwolony telefon"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Wyskakujące okienko"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index a9e0ac9..23b0eef 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Perfil de trabalho excluído devido à ausência de um app para administrador"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"O app para administrador do perfil de trabalho não foi encontrado ou está corrompido. Consequentemente, seu perfil de trabalho e os dados relacionados foram excluídos. Entre em contato com seu administrador para receber assistência."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Seu perfil de trabalho não está mais disponível neste dispositivo"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Muitas tentativas de senha"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"O dispositivo é gerenciado"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Sua organização gerencia este dispositivo e pode monitorar o tráfego de rede. Toque para ver detalhes."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Seu dispositivo será limpo"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Telefone"</string>
<string name="map" msgid="6068210738233985748">"Mapas"</string>
<string name="browse" msgid="6993590095938149861">"Navegador"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Contato"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Pouco espaço de armazenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema podem não funcionar"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM não permitido"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Smartphone não permitido"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Janela pop-up"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index cd26bd5..141c8f090 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Perfil de trabalho eliminado devido a aplicação de administração em falta"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"A aplicação de administração do perfil de trabalho está em falta ou danificada. Consequentemente, o seu perfil de trabalho e os dados relacionados foram eliminados. Contacte o administrador para obter assistência."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"O seu perfil de trabalho já não está disponível neste dispositivo"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Demasiadas tentativas de introdução da palavra-passe"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"O dispositivo é gerido"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"A sua entidade gere este dispositivo e pode monitorizar o tráfego de rede. Toque para obter mais detalhes."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"O seu dispositivo será apagado"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Telemóvel"</string>
<string name="map" msgid="6068210738233985748">"Mapas"</string>
<string name="browse" msgid="6993590095938149861">"Navegador"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Contacto"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Está quase sem espaço de armazenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema poderão não funcionar"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Não existe armazenamento suficiente para o sistema. Certifique-se de que tem 250 MB de espaço livre e reinicie."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM não permitido"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Telemóvel não permitido"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Janela pop-up"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index a9e0ac9..23b0eef 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Perfil de trabalho excluído devido à ausência de um app para administrador"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"O app para administrador do perfil de trabalho não foi encontrado ou está corrompido. Consequentemente, seu perfil de trabalho e os dados relacionados foram excluídos. Entre em contato com seu administrador para receber assistência."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Seu perfil de trabalho não está mais disponível neste dispositivo"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Muitas tentativas de senha"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"O dispositivo é gerenciado"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Sua organização gerencia este dispositivo e pode monitorar o tráfego de rede. Toque para ver detalhes."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Seu dispositivo será limpo"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Telefone"</string>
<string name="map" msgid="6068210738233985748">"Mapas"</string>
<string name="browse" msgid="6993590095938149861">"Navegador"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Contato"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Pouco espaço de armazenamento"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema podem não funcionar"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Não há armazenamento suficiente para o sistema. Certifique-se de ter 250 MB de espaço livre e reinicie."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM não permitido"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Smartphone não permitido"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Janela pop-up"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 67f17b25..7cf8b1a 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -174,6 +174,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Profilul de serviciu a fost șters, deoarece aplicația de administrare lipsește"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Aplicația de administrare a profilului de serviciu lipsește sau este deteriorată. Prin urmare, profilul de serviciu și datele asociate au fost șterse. Pentru asistență, contactați administratorul."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Profilul de serviciu nu mai este disponibil pe acest dispozitiv"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Prea multe încercări de introducere a parolei"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Dispozitivul este gestionat"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Organizația dvs. gestionează acest dispozitiv și poate monitoriza traficul în rețea. Atingeți pentru mai multe detalii."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Datele de pe dispozitiv vor fi șterse"</string>
@@ -1000,10 +1001,8 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Hărți"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Persoană de contact"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spațiul de stocare aproape ocupat"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Este posibil ca unele funcții de sistem să nu funcționeze"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Spațiu de stocare insuficient pentru sistem. Asigurați-vă că aveți 250 MB de spațiu liber și reporniți."</string>
@@ -1825,4 +1824,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"Cardul SIM nu este permis"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefonul nu este permis"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Fereastră pop-up"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 70631d0..b8267c3 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -176,6 +176,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Рабочий профиль удален, поскольку отсутствует приложение для администрирования"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Приложение для администрирования рабочего профиля отсутствует или повреждено. Из-за этого рабочий профиль и связанные с ним данные были удалены. Если у вас возникли вопросы, обратитесь к администратору."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Ваш рабочий профиль больше не доступен на этом устройстве"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Слишком много попыток ввести пароль."</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Это управляемое устройство"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Ваша организация управляет этим устройством и может отслеживать сетевой трафик. Подробнее…"</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Все данные с устройства будут удалены"</string>
@@ -1020,10 +1021,8 @@
<string name="dial" msgid="4204975095406423102">"Телефон"</string>
<string name="map" msgid="6068210738233985748">"Карты"</string>
<string name="browse" msgid="6993590095938149861">"Браузер"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Контакт"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Недостаточно памяти"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некоторые функции могут не работать"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Недостаточно свободного места для системы. Освободите не менее 250 МБ дискового пространства и перезапустите устройство."</string>
@@ -1860,4 +1859,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"Использование SIM-карты запрещено"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Звонки запрещены"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Всплывающее окно"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 11c42dc..bb9e6ec 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"පරිපාලක යෙදුමක් නොමැති වීමෙන් කාර්යාල පැතිකඩ මකා දමන ලදි"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"කාර්යාල පැතිකඩ පාලක යෙදුම නොමැති හෝ දූෂණය වී ඇත. ප්රතිඵලයක් ලෙස ඔබගේ කාර්යාල පැතිකඩ සහ අදාළ දත්ත මකා දමා ඇත. සහය සඳහා ඔබගේ පරිපාලකයා සම්බන්ධ කර ගන්න."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"ඔබේ කාර්යාල පැතිකඩ මෙම උපාංගය මත තවදුරටත් ලබා ගැනීමට නොහැකිය"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"මුරපද උත්සාහ කිරීම් ඉතා වැඩි ගණනකි"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"උපාංගය කළමනාකරණය කෙරේ"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"ඔබගේ ආයතනය මෙම උපාංගය කළමනාකරණය කරන අතර එය ජාල තදබදය නිරීක්ෂණය කළ හැක. විස්තර සඳහා තට්ටු කරන්න."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"ඔබගේ උපාංගය මකා දැමෙනු ඇත"</string>
@@ -982,10 +983,8 @@
<string name="dial" msgid="4204975095406423102">"දුරකථනය"</string>
<string name="map" msgid="6068210738233985748">"සිතියම්"</string>
<string name="browse" msgid="6993590095938149861">"බ්රවුසරය"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"සම්බන්ධතා"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"ආචයනය ඉඩ ප්රමාණය අඩු වී ඇත"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"සමහර පද්ධති කාර්යයන් ක්රියා නොකරනු ඇත"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"පද්ධතිය සඳහා ප්රමාණවත් ඉඩ නොමැත. ඔබට 250MB නිදහස් ඉඩක් තිබෙන ඔබට තිබෙන බව සහතික කරගෙන නැවත උත්සාහ කරන්න."</string>
@@ -1792,4 +1791,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM එක සඳහා ඉඩ නොදේ"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"දුරකථනය සඳහා ඉඩ නොදේ"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"උත්පතන කවුළුව"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 1eb24ac..8646e95 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -176,6 +176,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Pracovný profil bol odstránený z dôvodu chýbajúcej aplikácie na správu"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Aplikácia na správu pracovného profilu buď chýba, alebo je poškodená. Z toho dôvodu bol odstránený pracovný profil aj k nemu priradené dáta. Ak potrebujete pomoc, kontaktujte svojho správcu."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Váš pracovný profil už v tomto zariadení nie je k dispozícii"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Príliš veľa pokusov o zadanie hesla"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Zariadenie je spravované"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Vaša organizácia spravuje toto zariadenie a môže sledovať sieťovú premávku. Klepnutím zobrazíte podrobnosti."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Vaše zariadenie bude vymazané"</string>
@@ -1020,10 +1021,8 @@
<string name="dial" msgid="4204975095406423102">"Telefón"</string>
<string name="map" msgid="6068210738233985748">"Mapy"</string>
<string name="browse" msgid="6993590095938149861">"Prehliadač"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nedostatok ukladacieho priestoru"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Niektoré systémové funkcie nemusia fungovať"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"V úložisku nie je dostatok voľného miesta pre systém. Zaistite, aby ste mali 250 MB voľného miesta a zariadenie reštartujte."</string>
@@ -1860,4 +1859,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM karta je zakázaná"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefón je zakázaný"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Automaticky otvárané okno"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 4396cd3..da2a261 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -176,6 +176,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Delovni profil izbrisan zaradi manjkajoče skrbniške aplikacije"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Skrbniška aplikacija delovnega profila manjka ali pa je poškodovana, zaradi česar je bil delovni profil s povezanimi podatki izbrisan. Za pomoč se obrnite na skrbnika."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Vaš delovni profil ni več na voljo v tej napravi"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Preveč poskusov vnosa gesla"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Naprava je upravljana"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Vaša organizacija upravlja to napravo in lahko nadzira omrežni promet. Dotaknite se za podrobnosti."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Podatki v napravi bodo izbrisani"</string>
@@ -1020,10 +1021,8 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Zemljevidi"</string>
<string name="browse" msgid="6993590095938149861">"Brskalnik"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Stik"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Prostor za shranjevanje bo pošel"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Nekatere sistemske funkcije morda ne delujejo"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"V shrambi ni dovolj prostora za sistem. Sprostite 250 MB prostora in znova zaženite napravo."</string>
@@ -1860,4 +1859,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"Kartica SIM ni dovoljena"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefon ni dovoljen"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Pojavno okno"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index d62f95f..0669d4c 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Profili i punës u fshi për shkak të mungesës së aplikacionit të administratorit"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Aplikacioni i administratorit të profilit të punës mungon ose është dëmtuar. Si rezultat i kësaj, profili yt i punës dhe të dhënat përkatëse janë fshirë. Kontakto me administratorin për ndihmë."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Profili yt i punës nuk është më i disponueshëm në këtë pajisje"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Shumë përpjekje për fjalëkalimin"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Pajisja është e menaxhuar"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Organizata jote e menaxhon këtë pajisje dhe mund të monitorojë trafikun e rrjetit. Trokit për detaje."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Pajisja do të spastrohet"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Telefoni"</string>
<string name="map" msgid="6068210738233985748">"Hartat"</string>
<string name="browse" msgid="6993590095938149861">"Shfletuesi"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kontakti"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Hapësira ruajtëse po mbaron"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Disa funksione të sistemit mund të mos punojnë"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Nuk ka hapësirë të mjaftueshme ruajtjeje për sistemin. Sigurohu që të kesh 250 MB hapësirë të lirë dhe pastaj të rifillosh."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"Karta SIM nuk lejohet"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefoni nuk lejohet"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Dritare kërcyese"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 37552af..26d7a298 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -174,6 +174,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Профил за Work је избрисан јер недостаје апликација за администраторе"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Апликација за администраторе на профилу за Work недостаје или је оштећена. Због тога су профил за Work и повезани подаци избрисани. Обратите се администратору за помоћ."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Профил за Work више није доступан на овом уређају"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Превише покушаја уноса лозинке"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Уређајем се управља"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Организација управља овим уређајем и може да надгледа мрежни саобраћај. Додирните за детаље."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Уређај ће бити обрисан"</string>
@@ -1000,10 +1001,8 @@
<string name="dial" msgid="4204975095406423102">"Позови"</string>
<string name="map" msgid="6068210738233985748">"Мапе"</string>
<string name="browse" msgid="6993590095938149861">"Прегледач"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Контакт"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Меморијски простор је на измаку"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Неке системске функције можда не функционишу"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Нема довољно меморијског простора за систем. Уверите се да имате 250 MB слободног простора и поново покрените."</string>
@@ -1825,4 +1824,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM картица није дозвољена"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Телефон није дозвољен"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Искачући прозор"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 64d8cba..5b3fdf6 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Jobbprofilen har raderats eftersom det saknas en administratörsapp"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Administratörsappen för jobbprofilen saknas eller är skadad. Det innebär att jobbprofilen och all relaterad data har raderats. Kontakta administratören om du vill ha hjälp."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Jobbprofilen är inte längre tillgänglig på enheten"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"För många försök med lösenord"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Enheten hanteras"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Organisationen hanterar den här enheten och kan övervaka nätverkstrafiken. Tryck om du vill veta mer."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Enheten kommer att rensas"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Kartor"</string>
<string name="browse" msgid="6993590095938149861">"Webbläsare"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"Sms"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Lagringsutrymmet börjar ta slut"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Det kan hända att vissa systemfunktioner inte fungerar"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Det finns inte tillräckligt med utrymme för systemet. Kontrollera att du har ett lagringsutrymme på minst 250 MB och starta om."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-kort tillåts inte"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Mobil tillåts inte"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"popup-fönster"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index c3ee444..78fc186 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -170,6 +170,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Wasifu wa kazini umefutwa kutokana na kupotea kwa programu ya msimamizi"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Programu ya msimamizi wa wasifu wa kazini imepotea au ina hitilafu. Kwa sababu hiyo, wasifu wako wa kazini na data husika imefutwa. Wasiliana na msimamizi wako kwa usaidizi."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Wasifu wako wa kazini haupatikani tena kwenye kifaa hiki"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Umejaribu kuweka nenosiri mara nyingi mno"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Kifaa kinadhibitiwa"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Shirika lako linadhibiti kifaa hiki na huenda likafuatilia shughuli kwenye mtandao. Gonga ili upate maelezo zaidi."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Data iliyomo kwenye kifaa chako itafutwa"</string>
@@ -978,10 +979,8 @@
<string name="dial" msgid="4204975095406423102">"Simu"</string>
<string name="map" msgid="6068210738233985748">"Ramani"</string>
<string name="browse" msgid="6993590095938149861">"Kivinjari"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Anwani"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nafasi ya kuhifadhi inakaribia kujaa"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Baadhi ya vipengee vya mfumo huenda visifanye kazi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Hifadhi haitoshi kwa ajili ya mfumo. Hakikisha una MB 250 za nafasi ya hifadhi isiyotumika na uanzishe upya."</string>
@@ -1788,4 +1787,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM imekataliwa"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Simu imekataliwa"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Dirisha Ibukizi"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index cd2a566..544d109 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"நிர்வாகிப் பயன்பாடு இல்லாததால், பணி விவரம் நீக்கப்பட்டது"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"பணி விவர நிர்வாகிப் பயன்பாடு இல்லை அல்லது அது சிதைந்துள்ளது. இதன் விளைவாக, உங்கள் பணி விவரமும் அதனுடன் தொடர்புடைய தரவும் நீக்கப்பட்டன. உதவிக்கு, நிர்வாகியைத் தொடர்புகொள்ளவும்."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"இந்தச் சாதனத்தில் இனி பணி விவரம் கிடைக்காது"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"கடவுச்சொல்லை அதிக முறை தவறாக முயற்சித்துவிட்டீர்கள்"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"சாதனம் நிர்வகிக்கப்படுகிறது"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"உங்கள் நிறுவனம் இந்தச் சாதனத்தை நிர்வகிக்கும், அத்துடன் அது நெட்வொர்க் ட்ராஃபிக்கைக் கண்காணிக்கலாம். விவரங்களுக்கு, தட்டவும்."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"சாதனத் தரவு அழிக்கப்படும்"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"ஃபோன்"</string>
<string name="map" msgid="6068210738233985748">"வரைபடம்"</string>
<string name="browse" msgid="6993590095938149861">"உலாவி"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"தொடர்பு"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"சேமிப்பிடம் குறைகிறது"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"சில அமைப்பு செயல்பாடுகள் வேலை செய்யாமல் போகலாம்"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"முறைமையில் போதுமான சேமிப்பகம் இல்லை. 250மெ.பை. அளவு காலி இடவசதி இருப்பதை உறுதிசெய்து மீண்டும் தொடங்கவும்."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"சிம் அனுமதிக்கப்படவில்லை"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"ஃபோன் அனுமதிக்கப்படவில்லை"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"பாப்அப் சாளரம்"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 90981e7..75c37249 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"నిర్వాహక యాప్ లేనందున కార్యాలయ ప్రొఫైల్ తొలగించబడింది"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"కార్యాలయ ప్రొఫైల్ నిర్వాహక యాప్ లేదు లేదా పాడైంది. తత్ఫలితంగా, మీ కార్యాలయ ప్రొఫైల్ మరియు సంబంధిత డేటా తొలగించబడ్డాయి. సహాయం కోసం మీ నిర్వాహకులను సంప్రదించండి."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"ఈ పరికరంలో మీ కార్యాలయ ప్రొఫైల్ ఇప్పుడు అందుబాటులో లేదు"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"చాలా ఎక్కువ పాస్వర్డ్ ప్రయత్నాలు చేసారు"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"పరికరం నిర్వహించబడింది"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"మీ సంస్థ ఈ పరికరాన్ని నిర్వహిస్తుంది మరియు నెట్వర్క్ ట్రాఫిక్ని పర్యవేక్షించవచ్చు. వివరాల కోసం నొక్కండి."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"మీ పరికరంలోని డేటా తొలగించబడుతుంది"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"ఫోన్"</string>
<string name="map" msgid="6068210738233985748">"మ్యాప్స్"</string>
<string name="browse" msgid="6993590095938149861">"బ్రౌజర్"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"పరిచయం"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"నిల్వ ఖాళీ అయిపోతోంది"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"కొన్ని సిస్టమ్ కార్యాచరణలు పని చేయకపోవచ్చు"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"సిస్టమ్ కోసం తగినంత నిల్వ లేదు. మీకు 250MB ఖాళీ స్థలం ఉందని నిర్ధారించుకుని, పునఃప్రారంభించండి."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM అనుమతించబడదు"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"ఫోన్ అనుమతించబడదు"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"పాప్అప్ విండో"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 310bd95..66a7280 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"ลบโปรไฟล์งานแล้วเนื่องจากไม่มีแอปผู้ดูแลระบบ"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"แอปผู้ดูแลระบบโปรไฟล์งานไม่มีอยู่หรือเสียหาย ระบบจึงทำการลบโปรไฟล์งานและข้อมูลที่เกี่ยวข้องของคุณออก โปรดติดต่อผู้ดูแลระบบเพื่อรับความช่วยเหลือ"</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"โปรไฟล์งานของคุณไม่สามารถใช้ในอุปกรณ์นี้อีกต่อไป"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"ลองป้อนรหัสผ่านหลายครั้งเกินไป"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"อุปกรณ์มีการจัดการ"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"องค์กรของคุณจัดการอุปกรณ์นี้และอาจตรวจสอบการจราจรของข้อมูลในเครือข่าย แตะเพื่อดูรายละเอียด"</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"ระบบจะลบข้อมูลในอุปกรณ์ของคุณ"</string>
@@ -666,7 +667,7 @@
<string name="imProtocolYahoo" msgid="8271439408469021273">"Yahoo"</string>
<string name="imProtocolSkype" msgid="9019296744622832951">"Skype"</string>
<string name="imProtocolQq" msgid="8887484379494111884">"QQ"</string>
- <string name="imProtocolGoogleTalk" msgid="493902321140277304">"แฮงเอาท์"</string>
+ <string name="imProtocolGoogleTalk" msgid="493902321140277304">" Hangouts"</string>
<string name="imProtocolIcq" msgid="1574870433606517315">"ICQ"</string>
<string name="imProtocolJabber" msgid="2279917630875771722">"Jabber"</string>
<string name="imProtocolNetMeeting" msgid="8287625655986827971">"NetMeeting"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"โทรศัพท์"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"เบราว์เซอร์"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"ติดต่อ"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"พื้นที่จัดเก็บเหลือน้อย"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"บางฟังก์ชันระบบอาจไม่ทำงาน"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"พื้นที่เก็บข้อมูลไม่เพียงพอสำหรับระบบ โปรดตรวจสอบว่าคุณมีพื้นที่ว่าง 250 MB แล้วรีสตาร์ท"</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"ไม่อนุญาตให้ใช้ซิม"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"ไม่อนุญาตให้ใช้โทรศัพท์"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"หน้าต่างป๊อปอัป"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 50d09a0..bafcef3 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Na-delete ang profile sa trabaho dahil wala itong admin app"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Nawawala o nasira ang admin app ng profile sa trabaho. Dahil dito, na-delete ang profile mo sa trabaho at nauugnay na data. Makipag-ugnayan sa iyong admin para sa tulong."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Hindi na available sa device na ito ang iyong profile sa trabaho"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Masyadong maraming pagsubok sa password"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Pinamamahalaan ang device"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Pinamamahalaan ng iyong organisasyon ang device na ito, at maaari nitong subaybayan ang trapiko sa network. I-tap para sa mga detalye."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Buburahin ang iyong device"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Telepono"</string>
<string name="map" msgid="6068210738233985748">"Mga Mapa"</string>
<string name="browse" msgid="6993590095938149861">"Browser"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Contact"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nauubusan na ang puwang ng storage"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Maaaring hindi gumana nang tama ang ilang paggana ng system"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Walang sapat na storage para sa system. Tiyaking mayroon kang 250MB na libreng espasyo at i-restart."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"Hindi pinahihintulutan ang SIM"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Hindi pinahihintulutan ang telepono"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Window ng Popup"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 9bead0c..7e2d2bfe 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Eksik yönetici uygulaması nedeniyle iş profili silindi"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"İş profili yönetici uygulaması eksik ya da bozuk. Bunun sonucunda iş profiliniz ve ilgili veriler silindi. Yardım almak için yöneticiniz ile iletişim kurun."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"İş profiliniz arık bu cihazda kullanılamıyor"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Çok fazla şifre denemesi yapıldı"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Cihaz yönetiliyor"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Kuruluşunuz bu cihazı yönetmekte olup ağ trafiğini izleyebilir. Ayrıntılar için dokunun."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Cihazınız silinecek"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Haritalar"</string>
<string name="browse" msgid="6993590095938149861">"Tarayıcı"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kişi"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Depolama alanı bitiyor"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bazı sistem işlevleri çalışmayabilir"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Sistem için yeterli depolama alanı yok. 250 MB boş alanınızın bulunduğundan emin olun ve yeniden başlatın."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM\'e izin verilmiyor"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Telefona izin verilmiyor"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Pop-up Pencere"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index cb2f5b4..050b26b 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -176,6 +176,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Робочий профіль видалено через відсутність додатка адміністратора"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Додаток адміністратора в робочому профілі відсутній або пошкоджений. У результаті ваш робочий профіль і пов’язані з ним дані видалено. Зверніться до свого адміністратора по допомогу."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Робочий профіль більше не доступний на цьому пристрої"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Забагато спроб ввести пароль"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Пристрій контролюється"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Адміністратор вашої організації контролює цей пристрій і відстежує мережевий трафік. Торкніться, щоб дізнатися більше."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"З вашого пристрою буде стерто всі дані"</string>
@@ -1020,10 +1021,8 @@
<string name="dial" msgid="4204975095406423102">"Телефонувати"</string>
<string name="map" msgid="6068210738233985748">"Карти"</string>
<string name="browse" msgid="6993590095938149861">"Веб-переглядач"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Контакт"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Закінчується пам’ять"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Деякі системні функції можуть не працювати"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Недостатньо місця для системи. Переконайтесь, що на пристрої є 250 МБ вільного місця, і повторіть спробу."</string>
@@ -1860,4 +1859,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM-карта заборонена"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Телефон заборонено"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Спливаюче вікно"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index ab69fc3..44e7350 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"گمشدہ منتظم ایپ کی وجہ سے دفتری پروفائل حذف کر دیا گیا"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"دفتری پروفائل کی منتظم ایپ یا تو غائب ہے یا خراب ہے۔ اس کی وجہ سے، آپ کا دفتری پروفائل اور متعلقہ ڈیٹا حذف کر دیے گئے ہیں۔ مدد کیلئے اپنے منتظم سے رابطہ کریں۔"</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"آپ کا دفتری پروفائل اس آلہ پر مزید دستیاب نہیں ہے"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"پاس ورڈ کی بہت ساری کوششیں"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"آلہ زیر انتظام ہے"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"آپ کی تنظیم اس آلے کا نظم کرتی ہے اور وہ نیٹ ورک ٹریفک کی نگرانی کر سکتی ہے۔ تفاصیل کیلئے تھپتھپائیں۔"</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"آپ کا آلہ صاف کر دیا جائے گا"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"فون کریں"</string>
<string name="map" msgid="6068210738233985748">"Maps"</string>
<string name="browse" msgid="6993590095938149861">"براؤزر"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"رابطہ"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"اسٹوریج کی جگہ ختم ہو رہی ہے"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"ممکن ہے سسٹم کے کچھ فنکشنز کام نہ کریں"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"سسٹم کیلئے کافی اسٹوریج نہیں ہے۔ اس بات کو یقینی بنائیں کہ آپ کے پاس 250MB خالی جگہ ہے اور دوبارہ شروع کریں۔"</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM کی اجازت نہیں ہے"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"فون کی اجازت نہیں ہے"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"پاپ اپ ونڈو"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index f402e26..b62d5b9 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Administrator ilovasi yo‘qligi sababli ishchi profil o‘chirib tashlandi"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Ishchi profilning administrator ilovasi yo‘q yoki buzilgan. Shuning uchun, ishchi profilingiz va unga aloqador ma’lumotlar o‘chirib tashlandi. Yordam olish uchun administratoringizga murojaat qiling."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Bu qurilmada endi ishchi profilingiz mavjud emas"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Parol ko‘p marta xato kiritildi"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Bu – boshqariladigan qurilma"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Tashkilotingiz bu qurilmani boshqaradi va tarmoq trafigini nazorat qilishi mumkin. Tafsilotlar uchun bosing."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Qurilmangizdagi ma’lumotlar o‘chirib tashlanadi"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Telefon"</string>
<string name="map" msgid="6068210738233985748">"Xaritalar"</string>
<string name="browse" msgid="6993590095938149861">"Brauzer"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Kontakt"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Xotirada bo‘sh joy tugamoqda"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Ba‘zi tizim funksiyalari ishlamasligi mumkin"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Tizim uchun xotirada joy yetarli emas. Avval 250 megabayt joy bo‘shatib, keyin qurilmani o‘chirib yoqing."</string>
@@ -1007,9 +1006,9 @@
<string name="whichEditApplication" msgid="144727838241402655">"Tahrirlash…"</string>
<string name="whichEditApplicationNamed" msgid="1775815530156447790">"“%1$s” yordamida tahrirlash"</string>
<string name="whichEditApplicationLabel" msgid="7183524181625290300">"Tahrirlash"</string>
- <string name="whichSendApplication" msgid="6902512414057341668">"Baham ko‘rish"</string>
+ <string name="whichSendApplication" msgid="6902512414057341668">"Ulashish"</string>
<string name="whichSendApplicationNamed" msgid="2799370240005424391">"“%1$s” orqali ulashish"</string>
- <string name="whichSendApplicationLabel" msgid="4579076294675975354">"Baham ko‘rish"</string>
+ <string name="whichSendApplicationLabel" msgid="4579076294675975354">"Ulashish"</string>
<string name="whichSendToApplication" msgid="8272422260066642057">"Ilovani tanlang"</string>
<string name="whichSendToApplicationNamed" msgid="7768387871529295325">"%1$s orqali yuborish"</string>
<string name="whichSendToApplicationLabel" msgid="8878962419005813500">"Yuborish"</string>
@@ -1417,7 +1416,7 @@
<string name="sha1_fingerprint" msgid="7930330235269404581">"SHA-1 barmoq izi:"</string>
<string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Barchasini ko‘rish"</string>
<string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Harakat turini tanlang"</string>
- <string name="share_action_provider_share_with" msgid="5247684435979149216">"Baham ko‘rish"</string>
+ <string name="share_action_provider_share_with" msgid="5247684435979149216">"Ulashish"</string>
<string name="sending" msgid="3245653681008218030">"Jo‘natilmoqda…"</string>
<string name="launchBrowserDefault" msgid="2057951947297614725">"Brauzer ishga tushirilsinmi?"</string>
<string name="SetupCallDefault" msgid="5834948469253758575">"Qo‘ng‘iroqni qabul qilasizmi?"</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM karta ishlatish taqiqlangan"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Chaqiruvlar taqiqlangan"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Qalqib chiquvchi oyna"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 9a057cf..921f7c6 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Đã xóa hồ sơ công việc do thiếu ứng dụng quản trị"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Ứng dụng quản trị hồ sơ công việc bị thiếu hoặc hỏng. Do vậy, hồ sơ công việc của bạn và dữ liệu liên quan đã bị xóa. Hãy liên hệ với quản trị viên của bạn để được trợ giúp."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Hồ sơ công việc của bạn không có sẵn trên thiết bị này nữa"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Quá nhiều lần nhập mật khẩu"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Thiết bị được quản lý"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Tổ chức của bạn sẽ quản lý thiết bị này và có thể theo dõi lưu lượng truy cập mạng. Nhấn để biết chi tiết."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Thiết bị của bạn sẽ bị xóa"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Điện thoại"</string>
<string name="map" msgid="6068210738233985748">"Bản đồ"</string>
<string name="browse" msgid="6993590095938149861">"Trình duyệt"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Liên hệ"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Sắp hết dung lượng lưu trữ"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Một số chức năng hệ thống có thể không hoạt động"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Không đủ bộ nhớ cho hệ thống. Đảm bảo bạn có 250 MB dung lượng trống và khởi động lại."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"SIM không được cho phép"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Điện thoại không được cho phép"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Cửa sổ bật lên"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 962b05f..06b781d 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"由于缺少管理应用,工作资料已被删除"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"工作资料管理应用缺失或损坏,因此系统已删除您的工作资料及相关数据。如需帮助,请与您的管理员联系。"</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"您的工作资料已不在此设备上"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"密码尝试次数过多"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"设备为受管理设备"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"贵单位会管理该设备,且可能会监控网络流量。点按即可了解详情。"</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"系统将清空您的设备"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"电话"</string>
<string name="map" msgid="6068210738233985748">"地图"</string>
<string name="browse" msgid="6993590095938149861">"浏览器"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"短信"</string>
+ <string name="add_contact" msgid="7990645816259405444">"联系人"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"存储空间不足"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"某些系统功能可能无法正常使用"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"系统存储空间不足。请确保您有250MB的可用空间,然后重新启动。"</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"不受允许的 SIM 卡"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"不受允许的手机"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"弹出式窗口"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 4b5a4ec..aac5fcc 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"由於沒有管理員應用程式,工作設定檔已刪除"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"工作設定檔管理員應用程式已遺失或損毀。因此,您的工作設定檔和相關資料已刪除。請聯絡您的管理員以取得協助。"</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"您的工作設定檔無法再在此裝置上使用"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"密碼輸入錯誤的次數過多"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"裝置已受管理"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"您的機構會管理此裝置,並可能會監控網絡流量。輕按即可瞭解詳情。"</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"您的裝置將被清除"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"撥打電話"</string>
<string name="map" msgid="6068210738233985748">"地圖"</string>
<string name="browse" msgid="6993590095938149861">"瀏覽器"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"短訊"</string>
+ <string name="add_contact" msgid="7990645816259405444">"聯絡人"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"儲存空間即將用盡"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"部分系統功能可能無法運作"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"系統儲存空間不足。請確認裝置有 250 MB 的可用空間,然後重新啟動。"</string>
@@ -1615,7 +1614,7 @@
</plurals>
<string name="restr_pin_try_later" msgid="973144472490532377">"稍後再試"</string>
<string name="immersive_cling_title" msgid="8394201622932303336">"開啟全螢幕"</string>
- <string name="immersive_cling_description" msgid="3482371193207536040">"由上往下刷退出。"</string>
+ <string name="immersive_cling_description" msgid="3482371193207536040">"由頂部向下快速滑動即可退出。"</string>
<string name="immersive_cling_positive" msgid="5016839404568297683">"知道了"</string>
<string name="done_label" msgid="2093726099505892398">"完成"</string>
<string name="hour_picker_description" msgid="6698199186859736512">"小時環形滑桿"</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"不允許使用 SIM 卡"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"不允許使用手機"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"彈出式視窗"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 5166b87..6a35994 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Work 設定檔因管理員應用程式遺失而遭到刪除"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Work 設定檔管理員應用程式遺失或已毀損,因此系統刪除了你的 Work 設定檔和相關資料。如需協助,請與你的管理員聯絡。"</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"你的 Work 設定檔已不在這個裝置上"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"密碼輸入錯誤的次數過多"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"裝置受到管理"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"貴機構會管理這個裝置,且可能監控網路流量。輕觸即可瞭解詳情。"</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"你的裝置資料將遭到清除"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"電話"</string>
<string name="map" msgid="6068210738233985748">"地圖"</string>
<string name="browse" msgid="6993590095938149861">"瀏覽器"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"簡訊"</string>
+ <string name="add_contact" msgid="7990645816259405444">"聯絡人"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"儲存空間即將用盡"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"部分系統功能可能無法運作"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"系統儲存空間不足。請確定你已釋出 250MB 的可用空間,然後重新啟動。"</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"不受允許的 SIM 卡"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"不受允許的手機"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"彈出式視窗"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index b7a6ae4..6f5e4cb 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -172,6 +172,7 @@
<string name="work_profile_deleted_description" msgid="1100529432509639864">"Iphrofayela yomsebenzi isuswe ngenxa yohlelo lokusebenza olungekho lomlawuli"</string>
<string name="work_profile_deleted_details" msgid="6307630639269092360">"Uhlelo lokusebenza lokulawula lephrofayela yomsebenzi kungenzeka alukho noma lonakele. Njengomphumela, iphrofayela yakho yomsebenzi nedatha ehlobene isusiwe. Xhumana nomlawuli wakho ukuze uthole usizo."</string>
<string name="work_profile_deleted_description_dpm_wipe" msgid="8823792115612348820">"Iphrofayela yakho yomsebenzi ayisatholakali kule divayisi"</string>
+ <string name="work_profile_deleted_reason_maximum_password_failure" msgid="8986903510053359694">"Imizamo yamaphasiwedi eminingi kakhulu"</string>
<string name="network_logging_notification_title" msgid="6399790108123704477">"Idivayisi iphethwe"</string>
<string name="network_logging_notification_text" msgid="7930089249949354026">"Inhlangano yakho iphethe le divayisi futhi kungenzeka ingaqaphi ithrafikhi yenethiwekhi. Thephela imininingwane."</string>
<string name="factory_reset_warning" msgid="5423253125642394387">"Idivayisi yakho izosulwa"</string>
@@ -980,10 +981,8 @@
<string name="dial" msgid="4204975095406423102">"Ifoni"</string>
<string name="map" msgid="6068210738233985748">"Amamephu"</string>
<string name="browse" msgid="6993590095938149861">"Isiphequluli"</string>
- <!-- no translation found for sms (8250353543787396737) -->
- <skip />
- <!-- no translation found for add_contact (7990645816259405444) -->
- <skip />
+ <string name="sms" msgid="8250353543787396737">"I-SMS"</string>
+ <string name="add_contact" msgid="7990645816259405444">"Oxhumana naye"</string>
<string name="low_internal_storage_view_title" msgid="5576272496365684834">"Isikhala sokulondoloza siyaphela"</string>
<string name="low_internal_storage_view_text" msgid="6640505817617414371">"Eminye imisebenzi yohlelo ingahle ingasebenzi"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="6935190099204693424">"Akusona isitoreji esanele sesistimu. Qiniseka ukuthi unesikhala esikhululekile esingu-250MB uphinde uqalise kabusha."</string>
@@ -1790,4 +1789,6 @@
<string name="mmcc_illegal_ms" msgid="2769452751852211112">"I-SIM ayivunyelwe"</string>
<string name="mmcc_illegal_me" msgid="4438696681169345015">"Ifoni ayivunyelwe"</string>
<string name="popup_window_default_title" msgid="4874318849712115433">"Iwindi lesigelekeqe"</string>
+ <!-- no translation found for slice_more_content (8504342889413274608) -->
+ <skip />
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 11cdb76..4b9839f 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2546,16 +2546,24 @@
<attr name="filterTouchesWhenObscured" format="boolean" />
<!-- Defines the quality of translucent drawing caches. This property is used
- only when the drawing cache is enabled and translucent. The default value is auto. -->
+ only when the drawing cache is enabled and translucent. The default value is auto.
+ Deprecated: The view drawing cache was largely made obsolete with the introduction of
+ hardware-accelerated rendering in API 11. -->
<attr name="drawingCacheQuality">
<!-- Lets the framework decide what quality level should be used
- for the drawing cache. -->
+ for the drawing cache.
+ Deprecated: The view drawing cache was largely made obsolete with the introduction
+ of hardware-accelerated rendering in API 11. -->
<enum name="auto" value="0" />
<!-- Low quality. When set to low quality, the drawing cache uses a lower color
- depth, thus losing precision in rendering gradients, but uses less memory. -->
+ depth, thus losing precision in rendering gradients, but uses less memory.
+ Deprecated: The view drawing cache was largely made obsolete with the introduction
+ of hardware-accelerated rendering in API 11. -->
<enum name="low" value="1" />
<!-- High quality. When set to high quality, the drawing cache uses a higher
- color depth but uses more memory. -->
+ color depth but uses more memory.
+ Deprecated: The view drawing cache was largely made obsolete with the introduction
+ of hardware-accelerated rendering in API 11. -->
<enum name="high" value="2" />
</attr>
@@ -3060,7 +3068,9 @@
instance during a scrolling.) This property lets you persist the cache
in memory after its initial usage. Persisting the cache consumes more
memory but may prevent frequent garbage collection is the cache is created
- over and over again. By default the persistence is set to scrolling. -->
+ over and over again. By default the persistence is set to scrolling.
+ Deprecated: The view drawing cache was largely made obsolete with the introduction of
+ hardware-accelerated rendering in API 11. -->
<attr name="persistentDrawingCache">
<!-- The drawing cache is not persisted after use. -->
<flag name="none" value="0x0" />
@@ -3072,7 +3082,9 @@
<flag name="all" value="0x3" />
</attr>
<!-- Defines whether the ViewGroup should always draw its children using their
- drawing cache or not. The default value is true. -->
+ drawing cache or not. The default value is true.
+ Deprecated: The view drawing cache was largely made obsolete with the introduction of
+ hardware-accelerated rendering in API 11. -->
<attr name="alwaysDrawnWithCache" format="boolean" />
<!-- Sets whether this ViewGroup's drawable states also include
its children's drawable states. This is used, for example, to
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index e6c829f..0e90287 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1449,8 +1449,7 @@
quit it. Only one such app can be running at a time; if the user
tries to launch a second such app, they will be prompted
to quit the first before doing so. While the
- application is running, the user will be informed of this.
- @hide -->
+ application is running, the user will be informed of this. -->
<attr name="cantSaveState" format="boolean" />
<attr name="uiOptions" />
<!-- Declare that your application will be able to deal with RTL (right to left) layouts.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6bf5c63..dcb56a2 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3092,4 +3092,8 @@
booted. -->
<integer name="config_stableDeviceDisplayWidth">-1</integer>
<integer name="config_stableDeviceDisplayHeight">-1</integer>
+
+ <!-- Decide whether to display 'No service' on status bar instead of 'Emergency calls only'
+ when SIM is unready. -->
+ <bool name="config_display_no_service_when_sim_unready">false</bool>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index b3d0053..14069e7 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -617,4 +617,10 @@
-->
<dimen name="autofill_save_icon_max_size">300dp</dimen>
+ <!-- Size of a slice shortcut view -->
+ <dimen name="slice_shortcut_size">56dp</dimen>
+ <!-- Size of action icons in a slice -->
+ <dimen name="slice_icon_size">24dp</dimen>
+ <!-- Standard padding used in a slice view -->
+ <dimen name="slice_padding">16dp</dimen>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 108ac1c..0364b81 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2745,18 +2745,6 @@
<!-- ===============================================================
Resources added in version O of the platform
-
- NOTE: add <public> elements within a <public-group> like so:
-
- <public-group type="attr" first-id="0x01010531">
- <public name="exampleAttr1" />
- <public name="exampleAttr2" />
- </public-group>
-
- To add a new public-group block, choose an id value that is 1 greater
- than the last of that item above. For example, the last "attr" id
- value above is 0x01010530, so the public-group of attrs below has
- the id value of 0x01010531.
=============================================================== -->
<eat-comment />
@@ -2827,18 +2815,6 @@
<!-- ===============================================================
Resources added in version O MR1 of the platform
-
- NOTE: add <public> elements within a <public-group> like so:
-
- <public-group type="attr" first-id="0x01010531">
- <public name="exampleAttr1" />
- <public name="exampleAttr2" />
- </public-group>
-
- To add a new public-group block, choose an id value that is 1 greater
- than the last of that item above. For example, the last "attr" id
- value above is 0x01010530, so the public-group of attrs below has
- the id value of 0x01010531.
=============================================================== -->
<eat-comment />
@@ -2868,6 +2844,7 @@
<eat-comment />
<public-group type="attr" first-id="0x0101056e">
+ <public name="cantSaveState" />
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 085f8dd..085241a 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -591,6 +591,11 @@
<!-- Text shown when viewing channel settings for notifications related to a usb connection -->
<string name="notification_channel_usb">USB connection</string>
+ <!-- Text shown when viewing channel settings for notification about a heavy-weight app
+ currently running.
+ [CHAR_LIMIT=NONE] -->
+ <string name="notification_channel_heavy_weight_app">App running</string>
+
<!-- This is the label for the notification channel settings that controls the behavior
of the notification about applications that are running in the background (that is,
perhaps confusingly, running foreground services but not the foreground UI on the screen).
@@ -4727,4 +4732,7 @@
<!-- Popup window default title to be read by a screen reader-->
<string name="popup_window_default_title">Popup Window</string>
+
+ <!-- Format string for indicating there is more content in a slice view -->
+ <string name="slice_more_content">+ <xliff:g id="number" example="5">%1$d</xliff:g></string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a5dbe26..44a10e8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3033,6 +3033,7 @@
<java-symbol type="string" name="notification_channel_alerts" />
<java-symbol type="string" name="notification_channel_retail_mode" />
<java-symbol type="string" name="notification_channel_usb" />
+ <java-symbol type="string" name="notification_channel_heavy_weight_app" />
<java-symbol type="string" name="config_defaultAutofillService" />
<java-symbol type="string" name="notification_channel_foreground_service" />
@@ -3099,4 +3100,20 @@
<java-symbol type="integer" name="config_stableDeviceDisplayWidth" />
<java-symbol type="integer" name="config_stableDeviceDisplayHeight" />
+ <java-symbol type="bool" name="config_display_no_service_when_sim_unready" />
+
+ <java-symbol type="layout" name="slice_grid" />
+ <java-symbol type="layout" name="slice_message_local" />
+ <java-symbol type="layout" name="slice_message" />
+ <java-symbol type="layout" name="slice_title" />
+ <java-symbol type="layout" name="slice_secondary_text" />
+ <java-symbol type="layout" name="slice_remote_input" />
+ <java-symbol type="layout" name="slice_small_template" />
+ <java-symbol type="id" name="remote_input_progress" />
+ <java-symbol type="id" name="remote_input_send" />
+ <java-symbol type="id" name="remote_input" />
+ <java-symbol type="dimen" name="slice_shortcut_size" />
+ <java-symbol type="dimen" name="slice_icon_size" />
+ <java-symbol type="dimen" name="slice_padding" />
+ <java-symbol type="string" name="slice_more_content" />
</resources>
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index dbc9e5d..15eab1f 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -36,7 +36,8 @@
ub-uiautomator \
platform-test-annotations \
compatibility-device-util \
- truth-prebuilt
+ truth-prebuilt \
+ print-test-util-lib
LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt telephony-common org.apache.http.legacy
LOCAL_PACKAGE_NAME := FrameworksCoreTests
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index ac5d224..9c0543b 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -102,6 +102,7 @@
<!-- os storage test permissions -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.ASEC_ACCESS" />
+ <uses-permission android:name="android.permission.ASEC_ACCESS" />
<uses-permission android:name="android.permission.ASEC_CREATE" />
<uses-permission android:name="android.permission.ASEC_DESTROY" />
<uses-permission android:name="android.permission.ASEC_MOUNT_UNMOUNT" />
@@ -1345,10 +1346,12 @@
</intent-filter>
</activity>
- <activity android:name="android.print.PrintTestActivity"/>
+ <activity
+ android:name="android.print.test.PrintDocumentActivity"
+ android:theme="@style/Theme" />
<service
- android:name="android.print.mockservice.MockPrintService"
+ android:name="android.print.test.services.FirstPrintService"
android:permission="android.permission.BIND_PRINT_SERVICE">
<intent-filter>
<action android:name="android.printservice.PrintService" />
@@ -1360,9 +1363,10 @@
</service>
<activity
- android:name="android.print.mockservice.SettingsActivity"
+ android:name="android.print.test.services.SettingsActivity"
android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"
- android:exported="true">
+ android:exported="true"
+ android:theme="@style/Theme">
</activity>
<activity
diff --git a/core/tests/coretests/src/android/print/BasePrintTest.java b/core/tests/coretests/src/android/print/BasePrintTest.java
deleted file mode 100644
index a70c604..0000000
--- a/core/tests/coretests/src/android/print/BasePrintTest.java
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.print;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doCallRealMethod;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.annotation.NonNull;
-import android.app.Instrumentation;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.CancellationSignal;
-import android.os.ParcelFileDescriptor;
-import android.os.SystemClock;
-import android.print.mockservice.PrintServiceCallbacks;
-import android.print.mockservice.PrinterDiscoverySessionCallbacks;
-import android.print.mockservice.StubbablePrinterDiscoverySession;
-import android.printservice.CustomPrinterIconCallback;
-import android.printservice.PrintJob;
-import android.printservice.PrintService;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.uiautomator.UiDevice;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Rule;
-import org.mockito.stubbing.Answer;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This is the base class for print tests.
- */
-abstract class BasePrintTest {
- protected static final long OPERATION_TIMEOUT = 30000;
- private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success";
- private static final int CURRENT_USER_ID = -2; // Mirrors UserHandle.USER_CURRENT
-
- private android.print.PrintJob mPrintJob;
-
- private CallCounter mStartCallCounter;
- private CallCounter mStartSessionCallCounter;
-
- private static Instrumentation sInstrumentation;
- private static UiDevice sUiDevice;
-
- @Rule
- public ActivityTestRule<PrintTestActivity> mActivityRule =
- new ActivityTestRule<>(PrintTestActivity.class, false, true);
-
- /**
- * {@link Runnable} that can throw and {@link Exception}
- */
- interface Invokable {
- /**
- * Execute the invokable
- *
- * @throws Exception
- */
- void run() throws Exception;
- }
-
- /**
- * Assert that the invokable throws an expectedException
- *
- * @param invokable The {@link Invokable} to run
- * @param expectedClass The {@link Exception} that is supposed to be thrown
- */
- void assertException(Invokable invokable, Class<? extends Exception> expectedClass)
- throws Exception {
- try {
- invokable.run();
- } catch (Exception e) {
- if (e.getClass().isAssignableFrom(expectedClass)) {
- return;
- } else {
- throw e;
- }
- }
-
- throw new AssertionError("No exception thrown");
- }
-
- /**
- * Return the UI device
- *
- * @return the UI device
- */
- public UiDevice getUiDevice() {
- return sUiDevice;
- }
-
- protected static Instrumentation getInstrumentation() {
- return sInstrumentation;
- }
-
- @BeforeClass
- public static void setUpClass() throws Exception {
- sInstrumentation = InstrumentationRegistry.getInstrumentation();
- assumeTrue(sInstrumentation.getContext().getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_PRINTING));
-
- sUiDevice = UiDevice.getInstance(sInstrumentation);
-
- // Make sure we start with a clean slate.
- clearPrintSpoolerData();
-
- // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2
- // Dexmaker is used by mockito.
- System.setProperty("dexmaker.dexcache", getInstrumentation()
- .getTargetContext().getCacheDir().getPath());
- }
-
- @Before
- public void initCounters() throws Exception {
- // Initialize the latches.
- mStartCallCounter = new CallCounter();
- mStartSessionCallCounter = new CallCounter();
- }
-
- @Before
- public void unlockScreen() throws Exception {
- // Unlock screen.
- runShellCommand(getInstrumentation(), "input keyevent KEYCODE_WAKEUP");
- runShellCommand(getInstrumentation(), "wm dismiss-keyguard");
- }
-
- @After
- public void exitActivities() throws Exception {
- // Exit print spooler
- getUiDevice().pressBack();
- getUiDevice().pressBack();
- }
-
- protected android.print.PrintJob print(@NonNull final PrintDocumentAdapter adapter,
- final PrintAttributes attributes) {
- // Initiate printing as if coming from the app.
- getInstrumentation().runOnMainSync(() -> {
- PrintManager printManager = (PrintManager) getActivity()
- .getSystemService(Context.PRINT_SERVICE);
- mPrintJob = printManager.print("Print job", adapter, attributes);
- });
-
- return mPrintJob;
- }
-
- protected void onStartCalled() {
- mStartCallCounter.call();
- }
-
- protected void onPrinterDiscoverySessionStartCalled() {
- mStartSessionCallCounter.call();
- }
-
- protected void waitForPrinterDiscoverySessionStartCallbackCalled() {
- waitForCallbackCallCount(mStartSessionCallCounter, 1,
- "Did not get expected call to onStartPrinterDiscoverySession.");
- }
-
- protected void waitForStartAdapterCallbackCalled() {
- waitForCallbackCallCount(mStartCallCounter, 1, "Did not get expected call to start.");
- }
-
- private static void waitForCallbackCallCount(CallCounter counter, int count, String message) {
- try {
- counter.waitForCount(count, OPERATION_TIMEOUT);
- } catch (TimeoutException te) {
- fail(message);
- }
- }
-
- protected PrintTestActivity getActivity() {
- return mActivityRule.getActivity();
- }
-
- public static String runShellCommand(Instrumentation instrumentation, String cmd)
- throws IOException {
- ParcelFileDescriptor pfd = instrumentation.getUiAutomation().executeShellCommand(cmd);
- byte[] buf = new byte[512];
- int bytesRead;
- FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
- StringBuilder stdout = new StringBuilder();
- while ((bytesRead = fis.read(buf)) != -1) {
- stdout.append(new String(buf, 0, bytesRead));
- }
- fis.close();
- return stdout.toString();
- }
-
- protected static void clearPrintSpoolerData() throws Exception {
- assertTrue("failed to clear print spooler data",
- runShellCommand(getInstrumentation(), String.format(
- "pm clear --user %d %s", CURRENT_USER_ID,
- PrintManager.PRINT_SPOOLER_PACKAGE_NAME))
- .contains(PM_CLEAR_SUCCESS_OUTPUT));
- }
-
- @SuppressWarnings("unchecked")
- protected PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks(
- Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery,
- Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking,
- Answer<Void> onRequestCustomPrinterIcon, Answer<Void> onStopPrinterStateTracking,
- Answer<Void> onDestroy) {
- PrinterDiscoverySessionCallbacks callbacks = mock(PrinterDiscoverySessionCallbacks.class);
-
- doCallRealMethod().when(callbacks).setSession(any(StubbablePrinterDiscoverySession.class));
- when(callbacks.getSession()).thenCallRealMethod();
-
- if (onStartPrinterDiscovery != null) {
- doAnswer(onStartPrinterDiscovery).when(callbacks).onStartPrinterDiscovery(
- any(List.class));
- }
- if (onStopPrinterDiscovery != null) {
- doAnswer(onStopPrinterDiscovery).when(callbacks).onStopPrinterDiscovery();
- }
- if (onValidatePrinters != null) {
- doAnswer(onValidatePrinters).when(callbacks).onValidatePrinters(
- any(List.class));
- }
- if (onStartPrinterStateTracking != null) {
- doAnswer(onStartPrinterStateTracking).when(callbacks).onStartPrinterStateTracking(
- any(PrinterId.class));
- }
- if (onRequestCustomPrinterIcon != null) {
- doAnswer(onRequestCustomPrinterIcon).when(callbacks).onRequestCustomPrinterIcon(
- any(PrinterId.class), any(CancellationSignal.class),
- any(CustomPrinterIconCallback.class));
- }
- if (onStopPrinterStateTracking != null) {
- doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking(
- any(PrinterId.class));
- }
- if (onDestroy != null) {
- doAnswer(onDestroy).when(callbacks).onDestroy();
- }
-
- return callbacks;
- }
-
- protected PrintServiceCallbacks createMockPrintServiceCallbacks(
- Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks,
- Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) {
- final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class);
-
- doCallRealMethod().when(service).setService(any(PrintService.class));
- when(service.getService()).thenCallRealMethod();
-
- if (onCreatePrinterDiscoverySessionCallbacks != null) {
- doAnswer(onCreatePrinterDiscoverySessionCallbacks).when(service)
- .onCreatePrinterDiscoverySessionCallbacks();
- }
- if (onPrintJobQueued != null) {
- doAnswer(onPrintJobQueued).when(service).onPrintJobQueued(any(PrintJob.class));
- }
- if (onRequestCancelPrintJob != null) {
- doAnswer(onRequestCancelPrintJob).when(service).onRequestCancelPrintJob(
- any(PrintJob.class));
- }
-
- return service;
- }
-
- private static final class CallCounter {
- private final Object mLock = new Object();
-
- private int mCallCount;
-
- public void call() {
- synchronized (mLock) {
- mCallCount++;
- mLock.notifyAll();
- }
- }
-
- int getCallCount() {
- synchronized (mLock) {
- return mCallCount;
- }
- }
-
- public void waitForCount(int count, long timeoutMillis) throws TimeoutException {
- synchronized (mLock) {
- final long startTimeMillis = SystemClock.uptimeMillis();
- while (mCallCount < count) {
- try {
- final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
- final long remainingTimeMillis = timeoutMillis - elapsedTimeMillis;
- if (remainingTimeMillis <= 0) {
- throw new TimeoutException();
- }
- mLock.wait(timeoutMillis);
- } catch (InterruptedException ie) {
- /* ignore */
- }
- }
- }
- }
- }
-}
diff --git a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
index 45e3f67..5d12f7e 100644
--- a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
+++ b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
@@ -16,6 +16,8 @@
package android.print;
+import static android.print.test.Utils.assertException;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -33,10 +35,11 @@
import android.print.PrintAttributes.Margins;
import android.print.PrintAttributes.MediaSize;
import android.print.PrintAttributes.Resolution;
-import android.print.mockservice.MockPrintService;
-import android.print.mockservice.PrintServiceCallbacks;
-import android.print.mockservice.PrinterDiscoverySessionCallbacks;
-import android.print.mockservice.StubbablePrinterDiscoverySession;
+import android.print.test.BasePrintTest;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrintServiceCallbacks;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.StubbablePrinterDiscoverySession;
import android.printservice.recommendation.IRecommendationsChangeListener;
import android.support.test.filters.LargeTest;
import android.support.test.filters.MediumTest;
@@ -149,7 +152,7 @@
session.addPrinters(printers);
}
- onPrinterDiscoverySessionStartCalled();
+ onPrinterDiscoverySessionCreateCalled();
return null;
}, null, null, null, null, null, null),
null, null);
@@ -200,13 +203,18 @@
}
private void startPrinting() {
- mGoodPrintJob = print(createMockAdapter(), null);
+ mGoodPrintJob = print(createMockAdapter(), (PrintAttributes) null);
// Wait for PrintActivity to be ready
- waitForStartAdapterCallbackCalled();
+ waitForAdapterStartCallbackCalled();
// Wait for printer discovery session to be ready
- waitForPrinterDiscoverySessionStartCallbackCalled();
+ waitForPrinterDiscoverySessionCreateCallbackCalled();
+ }
+
+ private void endPrinting() {
+ getUiDevice().pressBack();
+ getUiDevice().pressBack();
}
/**
@@ -220,7 +228,7 @@
@Before
public void setUpMockService() throws Exception {
- MockPrintService.setCallbacks(createMockCallbacks());
+ FirstPrintService.setCallbacks(createMockCallbacks());
mIPrintManager = IPrintManager.Stub
.asInterface(ServiceManager.getService(Context.PRINT_SERVICE));
@@ -231,7 +239,7 @@
*/
@LargeTest
@Test
- public void testGetPrintJobInfo() throws Exception {
+ public void testGetPrintJobInfo() throws Throwable {
startPrinting();
assertEquals(mGoodPrintJob.getId(), mIPrintManager.getPrintJobInfo(mGoodPrintJob.getId(),
@@ -244,6 +252,8 @@
SecurityException.class);
// Cannot test bad user Id as these tests are allowed to call across users
+
+ endPrinting();
}
/**
@@ -251,7 +261,7 @@
*/
@LargeTest
@Test
- public void testGetPrintJobInfos() throws Exception {
+ public void testGetPrintJobInfos() throws Throwable {
startPrinting();
List<PrintJobInfo> infos = mIPrintManager.getPrintJobInfos(mAppId, mUserId);
@@ -269,6 +279,8 @@
SecurityException.class);
// Cannot test bad user Id as these tests are allowed to call across users
+
+ endPrinting();
}
/**
@@ -276,7 +288,7 @@
*/
@LargeTest
@Test
- public void testPrint() throws Exception {
+ public void testPrint() throws Throwable {
final String name = "dummy print job";
final IPrintDocumentAdapter adapter = new PrintManager
@@ -303,6 +315,8 @@
getActivity().getPackageName(), BAD_APP_ID, mUserId), SecurityException.class);
// Cannot test bad user Id as these tests are allowed to call across users
+
+ endPrinting();
}
/**
@@ -310,7 +324,7 @@
*/
@LargeTest
@Test
- public void testCancelPrintJob() throws Exception {
+ public void testCancelPrintJob() throws Throwable {
startPrinting();
// Invalid print jobs IDs do not produce an exception
@@ -325,6 +339,8 @@
// Must be last as otherwise mGoodPrintJob will not be good anymore
mIPrintManager.cancelPrintJob(mGoodPrintJob.getId(), mAppId, mUserId);
+
+ endPrinting();
}
/**
@@ -332,7 +348,7 @@
*/
@LargeTest
@Test
- public void testRestartPrintJob() throws Exception {
+ public void testRestartPrintJob() throws Throwable {
startPrinting();
mIPrintManager.restartPrintJob(mGoodPrintJob.getId(), mAppId, mUserId);
@@ -346,6 +362,8 @@
SecurityException.class);
// Cannot test bad user Id as these tests are allowed to call across users
+
+ endPrinting();
}
/**
@@ -353,7 +371,8 @@
*/
@MediumTest
@Test
- public void testAddPrintJobStateChangeListener() throws Exception {
+ @NoActivity
+ public void testAddPrintJobStateChangeListener() throws Throwable {
final IPrintJobStateChangeListener listener = createMockIPrintJobStateChangeListener();
mIPrintManager.addPrintJobStateChangeListener(listener, mAppId, mUserId);
@@ -373,7 +392,8 @@
*/
@MediumTest
@Test
- public void testRemovePrintJobStateChangeListener() throws Exception {
+ @NoActivity
+ public void testRemovePrintJobStateChangeListener() throws Throwable {
final IPrintJobStateChangeListener listener = createMockIPrintJobStateChangeListener();
mIPrintManager.addPrintJobStateChangeListener(listener, mAppId, mUserId);
@@ -394,7 +414,8 @@
*/
@MediumTest
@Test
- public void testAddPrintServicesChangeListener() throws Exception {
+ @NoActivity
+ public void testAddPrintServicesChangeListener() throws Throwable {
final IPrintServicesChangeListener listener = createMockIPrintServicesChangeListener();
assertException(() -> mIPrintManager.addPrintServicesChangeListener(listener, mUserId),
@@ -411,7 +432,8 @@
*/
@MediumTest
@Test
- public void testRemovePrintServicesChangeListener() throws Exception {
+ @NoActivity
+ public void testRemovePrintServicesChangeListener() throws Throwable {
final IPrintServicesChangeListener listener = createMockIPrintServicesChangeListener();
assertException(() -> mIPrintManager.removePrintServicesChangeListener(listener, mUserId),
@@ -426,7 +448,8 @@
*/
@MediumTest
@Test
- public void testGetPrintServices() throws Exception {
+ @NoActivity
+ public void testGetPrintServices() throws Throwable {
assertException(() -> mIPrintManager.getPrintServices(PrintManager.ALL_SERVICES, mUserId),
SecurityException.class);
@@ -444,7 +467,8 @@
*/
@MediumTest
@Test
- public void testSetPrintServiceEnabled() throws Exception {
+ @NoActivity
+ public void testSetPrintServiceEnabled() throws Throwable {
assertException(
() -> mIPrintManager.setPrintServiceEnabled(new ComponentName("bad", "name"), true,
mUserId), SecurityException.class);
@@ -460,7 +484,8 @@
*/
@MediumTest
@Test
- public void testAddPrintServiceRecommendationsChangeListener() throws Exception {
+ @NoActivity
+ public void testAddPrintServiceRecommendationsChangeListener() throws Throwable {
final IRecommendationsChangeListener listener =
createMockIPrintServiceRecommendationsChangeListener();
@@ -479,7 +504,8 @@
*/
@MediumTest
@Test
- public void testRemovePrintServiceRecommendationsChangeListener() throws Exception {
+ @NoActivity
+ public void testRemovePrintServiceRecommendationsChangeListener() throws Throwable {
final IRecommendationsChangeListener listener =
createMockIPrintServiceRecommendationsChangeListener();
@@ -498,7 +524,8 @@
*/
@MediumTest
@Test
- public void testGetPrintServiceRecommendations() throws Exception {
+ @NoActivity
+ public void testGetPrintServiceRecommendations() throws Throwable {
assertException(() -> mIPrintManager.getPrintServiceRecommendations(mUserId),
SecurityException.class);
@@ -510,7 +537,8 @@
*/
@MediumTest
@Test
- public void testCreatePrinterDiscoverySession() throws Exception {
+ @NoActivity
+ public void testCreatePrinterDiscoverySession() throws Throwable {
final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
mIPrintManager.createPrinterDiscoverySession(listener, mUserId);
@@ -533,7 +561,7 @@
*/
@LargeTest
@Test
- public void testStartPrinterDiscovery() throws Exception {
+ public void testStartPrinterDiscovery() throws Throwable {
startPrinting();
final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
@@ -562,6 +590,8 @@
NullPointerException.class);
// Cannot test bad user Id as these tests are allowed to call across users
+
+ endPrinting();
}
/**
@@ -569,7 +599,8 @@
*/
@MediumTest
@Test
- public void testStopPrinterDiscovery() throws Exception {
+ @NoActivity
+ public void testStopPrinterDiscovery() throws Throwable {
final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
mIPrintManager.startPrinterDiscovery(listener, null, mUserId);
@@ -590,7 +621,7 @@
*/
@LargeTest
@Test
- public void testValidatePrinters() throws Exception {
+ public void testValidatePrinters() throws Throwable {
startPrinting();
final List<PrinterId> goodPrinters = new ArrayList<>();
@@ -617,6 +648,8 @@
NullPointerException.class);
// Cannot test bad user Id as these tests are allowed to call across users
+
+ endPrinting();
}
/**
@@ -624,7 +657,7 @@
*/
@LargeTest
@Test
- public void testStartPrinterStateTracking() throws Exception {
+ public void testStartPrinterStateTracking() throws Throwable {
startPrinting();
mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId);
@@ -636,6 +669,8 @@
NullPointerException.class);
// Cannot test bad user Id as these tests are allowed to call across users
+
+ endPrinting();
}
/**
@@ -643,7 +678,7 @@
*/
@LargeTest
@Test
- public void testGetCustomPrinterIcon() throws Exception {
+ public void testGetCustomPrinterIcon() throws Throwable {
startPrinting();
mIPrintManager.getCustomPrinterIcon(mGoodPrinterId, mUserId);
@@ -655,6 +690,8 @@
NullPointerException.class);
// Cannot test bad user Id as these tests are allowed to call across users
+
+ endPrinting();
}
/**
@@ -662,7 +699,7 @@
*/
@LargeTest
@Test
- public void testStopPrinterStateTracking() throws Exception {
+ public void testStopPrinterStateTracking() throws Throwable {
startPrinting();
mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId);
@@ -679,6 +716,8 @@
NullPointerException.class);
// Cannot test bad user Id as these tests are allowed to call across users
+
+ endPrinting();
}
/**
@@ -686,7 +725,8 @@
*/
@MediumTest
@Test
- public void testDestroyPrinterDiscoverySession() throws Exception {
+ @NoActivity
+ public void testDestroyPrinterDiscoverySession() throws Throwable {
final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
mIPrintManager.createPrinterDiscoverySession(listener, mUserId);
diff --git a/core/tests/coretests/src/android/print/PrintTestActivity.java b/core/tests/coretests/src/android/print/PrintTestActivity.java
deleted file mode 100644
index e9b001f..0000000
--- a/core/tests/coretests/src/android/print/PrintTestActivity.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.print;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-public class PrintTestActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
- | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
- | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
- }
-}
diff --git a/core/tests/coretests/src/android/print/mockservice/MockPrintService.java b/core/tests/coretests/src/android/print/mockservice/MockPrintService.java
deleted file mode 100644
index 9c11c22..0000000
--- a/core/tests/coretests/src/android/print/mockservice/MockPrintService.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.print.mockservice;
-
-public class MockPrintService extends StubbablePrintService {
-
- private static final Object sLock = new Object();
-
- private static PrintServiceCallbacks sCallbacks;
-
- public static void setCallbacks(PrintServiceCallbacks callbacks) {
- synchronized (sLock) {
- sCallbacks = callbacks;
- }
- }
-
- @Override
- protected PrintServiceCallbacks getCallbacks() {
- synchronized (sLock) {
- if (sCallbacks != null) {
- sCallbacks.setService(this);
- }
- return sCallbacks;
- }
- }
-}
diff --git a/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java b/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java
deleted file mode 100644
index 4e89207..0000000
--- a/core/tests/coretests/src/android/print/mockservice/PrintServiceCallbacks.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.print.mockservice;
-
-import android.printservice.PrintJob;
-import android.printservice.PrintService;
-
-public abstract class PrintServiceCallbacks {
-
- private PrintService mService;
-
- public PrintService getService() {
- return mService;
- }
-
- public void setService(PrintService service) {
- mService = service;
- }
-
- public abstract PrinterDiscoverySessionCallbacks onCreatePrinterDiscoverySessionCallbacks();
-
- public abstract void onRequestCancelPrintJob(PrintJob printJob);
-
- public abstract void onPrintJobQueued(PrintJob printJob);
-}
diff --git a/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java b/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java
deleted file mode 100644
index be002e2..0000000
--- a/core/tests/coretests/src/android/print/mockservice/PrinterDiscoverySessionCallbacks.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.print.mockservice;
-
-import android.os.CancellationSignal;
-import android.print.PrinterId;
-import android.printservice.CustomPrinterIconCallback;
-
-import java.util.List;
-
-public abstract class PrinterDiscoverySessionCallbacks {
-
- private StubbablePrinterDiscoverySession mSession;
-
- public void setSession(StubbablePrinterDiscoverySession session) {
- mSession = session;
- }
-
- public StubbablePrinterDiscoverySession getSession() {
- return mSession;
- }
-
- public abstract void onStartPrinterDiscovery(List<PrinterId> priorityList);
-
- public abstract void onStopPrinterDiscovery();
-
- public abstract void onValidatePrinters(List<PrinterId> printerIds);
-
- public abstract void onStartPrinterStateTracking(PrinterId printerId);
-
- public abstract void onRequestCustomPrinterIcon(PrinterId printerId,
- CancellationSignal cancellationSignal, CustomPrinterIconCallback callback);
-
- public abstract void onStopPrinterStateTracking(PrinterId printerId);
-
- public abstract void onDestroy();
-}
diff --git a/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java b/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java
deleted file mode 100644
index b58b2735..0000000
--- a/core/tests/coretests/src/android/print/mockservice/StubbablePrintService.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.print.mockservice;
-
-import android.printservice.PrintJob;
-import android.printservice.PrintService;
-import android.printservice.PrinterDiscoverySession;
-
-public abstract class StubbablePrintService extends PrintService {
-
- @Override
- public PrinterDiscoverySession onCreatePrinterDiscoverySession() {
- PrintServiceCallbacks callbacks = getCallbacks();
- if (callbacks != null) {
- return new StubbablePrinterDiscoverySession(this,
- getCallbacks().onCreatePrinterDiscoverySessionCallbacks());
- }
- return null;
- }
-
- @Override
- public void onRequestCancelPrintJob(PrintJob printJob) {
- PrintServiceCallbacks callbacks = getCallbacks();
- if (callbacks != null) {
- callbacks.onRequestCancelPrintJob(printJob);
- }
- }
-
- @Override
- public void onPrintJobQueued(PrintJob printJob) {
- PrintServiceCallbacks callbacks = getCallbacks();
- if (callbacks != null) {
- callbacks.onPrintJobQueued(printJob);
- }
- }
-
- protected abstract PrintServiceCallbacks getCallbacks();
-}
diff --git a/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java b/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java
deleted file mode 100644
index f3a5373..0000000
--- a/core/tests/coretests/src/android/print/mockservice/StubbablePrinterDiscoverySession.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.print.mockservice;
-
-import android.support.annotation.NonNull;
-import android.os.CancellationSignal;
-import android.print.PrinterId;
-import android.printservice.CustomPrinterIconCallback;
-import android.printservice.PrintService;
-import android.printservice.PrinterDiscoverySession;
-
-import java.util.List;
-
-public class StubbablePrinterDiscoverySession extends PrinterDiscoverySession {
- private final PrintService mService;
- private final PrinterDiscoverySessionCallbacks mCallbacks;
-
- public StubbablePrinterDiscoverySession(PrintService service,
- PrinterDiscoverySessionCallbacks callbacks) {
- mService = service;
- mCallbacks = callbacks;
- if (mCallbacks != null) {
- mCallbacks.setSession(this);
- }
- }
-
- public PrintService getService() {
- return mService;
- }
-
- @Override
- public void onStartPrinterDiscovery(@NonNull List<PrinterId> priorityList) {
- if (mCallbacks != null) {
- mCallbacks.onStartPrinterDiscovery(priorityList);
- }
- }
-
- @Override
- public void onStopPrinterDiscovery() {
- if (mCallbacks != null) {
- mCallbacks.onStopPrinterDiscovery();
- }
- }
-
- @Override
- public void onValidatePrinters(@NonNull List<PrinterId> printerIds) {
- if (mCallbacks != null) {
- mCallbacks.onValidatePrinters(printerIds);
- }
- }
-
- @Override
- public void onStartPrinterStateTracking(@NonNull PrinterId printerId) {
- if (mCallbacks != null) {
- mCallbacks.onStartPrinterStateTracking(printerId);
- }
- }
-
- @Override
- public void onRequestCustomPrinterIcon(@NonNull PrinterId printerId,
- @NonNull CancellationSignal cancellationSignal,
- @NonNull CustomPrinterIconCallback callback) {
- if (mCallbacks != null) {
- mCallbacks.onRequestCustomPrinterIcon(printerId, cancellationSignal, callback);
- }
- }
-
- @Override
- public void onStopPrinterStateTracking(@NonNull PrinterId printerId) {
- if (mCallbacks != null) {
- mCallbacks.onStopPrinterStateTracking(printerId);
- }
- }
-
- @Override
- public void onDestroy() {
- if (mCallbacks != null) {
- mCallbacks.onDestroy();
- }
- }
-}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index d9bcc57d..6731536 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -168,7 +168,6 @@
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,
Settings.Global.DEVICE_DEMO_MODE,
Settings.Global.DEVICE_IDLE_CONSTANTS,
- Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH,
Settings.Global.BATTERY_SAVER_CONSTANTS,
Settings.Global.DEFAULT_SM_DP_PLUS,
Settings.Global.DEVICE_NAME,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
index eff6ad9..c611a01 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
@@ -22,6 +22,7 @@
import android.os.WorkSource;
import android.support.test.filters.SmallTest;
import android.util.ArrayMap;
+import android.view.Display;
import junit.framework.TestCase;
@@ -44,7 +45,7 @@
// Off-battery, non-existent
clocks.realtime = clocks.uptime = 10;
cur = clocks.realtime * 1000;
- bi.updateTimeBasesLocked(false, false, cur, cur); // off battery
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, cur, cur); // off battery
assertFalse(bgtb.isRunning());
assertEquals(0, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED));
@@ -65,7 +66,7 @@
// On-battery, background
clocks.realtime = clocks.uptime = 303;
cur = clocks.realtime * 1000;
- bi.updateTimeBasesLocked(true, false, cur, cur); // on battery
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, cur, cur); // on battery
// still in ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
assertTrue(bgtb.isRunning());
assertEquals(0, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED));
@@ -73,7 +74,7 @@
// On-battery, background - but change screen state
clocks.realtime = clocks.uptime = 409;
cur = clocks.realtime * 1000;
- bi.updateTimeBasesLocked(true, true, cur, cur); // on battery (again)
+ bi.updateTimeBasesLocked(true, Display.STATE_OFF, cur, cur); // on battery (again)
assertTrue(bgtb.isRunning());
assertEquals(106_000, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED));
@@ -87,7 +88,7 @@
// Off-battery, foreground
clocks.realtime = clocks.uptime = 530;
cur = clocks.realtime * 1000;
- bi.updateTimeBasesLocked(false, false, cur, cur); // off battery
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, cur, cur); // off battery
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
assertFalse(bgtb.isRunning());
assertEquals(227_000, bgtb.computeRealtime(cur, STATS_SINCE_CHARGED));
@@ -112,17 +113,17 @@
// battery=off, screen=off, background=off
cur = (clocks.realtime = clocks.uptime = 100) * 1000;
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
- bi.updateTimeBasesLocked(false, false, cur, cur);
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, cur, cur);
assertFalse(bgtb.isRunning());
// battery=on, screen=off, background=off
cur = (clocks.realtime = clocks.uptime = 200) * 1000;
- bi.updateTimeBasesLocked(true, false, cur, cur);
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, cur, cur);
assertFalse(bgtb.isRunning());
// battery=on, screen=on, background=off
cur = (clocks.realtime = clocks.uptime = 300) * 1000;
- bi.updateTimeBasesLocked(true, true, cur, cur);
+ bi.updateTimeBasesLocked(true, Display.STATE_OFF, cur, cur);
assertFalse(bgtb.isRunning());
// battery=on, screen=on, background=on
@@ -133,12 +134,12 @@
// battery=on, screen=off, background=on
cur = (clocks.realtime = clocks.uptime = 550) * 1000;
- bi.updateTimeBasesLocked(true, false, cur, cur);
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, cur, cur);
assertFalse(bgtb.isRunning());
// battery=off, screen=off, background=on
cur = (clocks.realtime = clocks.uptime = 660) * 1000;
- bi.updateTimeBasesLocked(false, false, cur, cur);
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, cur, cur);
assertFalse(bgtb.isRunning());
// battery=off, screen=off, background=off
@@ -157,7 +158,7 @@
// On battery
curr = 1000 * (clocks.realtime = clocks.uptime = 100);
- bi.updateTimeBasesLocked(true, false, curr, curr); // on battery
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr); // on battery
// App in foreground
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
@@ -171,7 +172,7 @@
// Off battery
curr = 1000 * (clocks.realtime = clocks.uptime = 305);
- bi.updateTimeBasesLocked(false, false, curr, curr); // off battery
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr); // off battery
// Stop timer
curr = 1000 * (clocks.realtime = clocks.uptime = 409);
@@ -200,7 +201,7 @@
// On battery
curr = 1000 * (clocks.realtime = clocks.uptime = 100);
- bi.updateTimeBasesLocked(true, false, curr, curr); // on battery
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr); // on battery
// App in foreground
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
@@ -215,7 +216,7 @@
// Off battery
curr = 1000 * (clocks.realtime = clocks.uptime = 305);
- bi.updateTimeBasesLocked(false, false, curr, curr); // off battery
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr); // off battery
// Start timer (unoptimized)
curr = 1000 * (clocks.realtime = clocks.uptime = 1000);
@@ -223,7 +224,7 @@
// On battery
curr = 1000 * (clocks.realtime = clocks.uptime = 2001);
- bi.updateTimeBasesLocked(true, false, curr, curr); // on battery
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr); // on battery
// Move to foreground
curr = 1000 * (clocks.realtime = clocks.uptime = 3004);
@@ -270,7 +271,7 @@
// On battery
curr = 1000 * (clocks.realtime = clocks.uptime = 100);
- bi.updateTimeBasesLocked(true, false, curr, curr); // on battery
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr); // on battery
// App in foreground
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
@@ -292,7 +293,7 @@
// Off battery
curr = 1000 * (clocks.realtime = clocks.uptime = 305);
- bi.updateTimeBasesLocked(false, false, curr, curr); // off battery
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr); // off battery
// Stop timer
curr = 1000 * (clocks.realtime = clocks.uptime = 409);
@@ -331,7 +332,7 @@
// On battery
curr = 1000 * (clocks.realtime = clocks.uptime = 100);
- bi.updateTimeBasesLocked(true, false, curr, curr); // on battery
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr); // on battery
// App in foreground
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
@@ -353,7 +354,7 @@
// Off battery
curr = 1000 * (clocks.realtime = clocks.uptime = 305);
- bi.updateTimeBasesLocked(false, false, curr, curr); // off battery
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr); // off battery
// Stop timer
curr = 1000 * (clocks.realtime = clocks.uptime = 409);
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
index 97b54b1..6ff0ab2 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
@@ -39,6 +39,7 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.util.SparseLongArray;
+import android.view.Display;
import com.android.internal.util.ArrayUtils;
@@ -179,7 +180,7 @@
@Test
public void testUpdateClusterSpeedTimes() {
// PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final long[][] clusterSpeedTimesMs = {{20, 30}, {40, 50, 60}};
initKernelCpuSpeedReaders(clusterSpeedTimesMs.length);
for (int i = 0; i < clusterSpeedTimesMs.length; ++i) {
@@ -224,7 +225,7 @@
@Test
public void testReadKernelUidCpuTimesLocked() {
//PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
final int[] testUids = getUids(testUserId, new int[] {
@@ -295,7 +296,7 @@
@Test
public void testReadKernelUidCpuTimesLocked_isolatedUid() {
//PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
final int isolatedAppId = FIRST_ISOLATED_UID + 27;
@@ -382,7 +383,7 @@
@Test
public void testReadKernelUidCpuTimesLocked_invalidUid() {
//PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
final int invalidUserId = 15;
final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99);
@@ -427,7 +428,7 @@
@Test
public void testReadKernelUidCpuTimesLocked_withPartialTimers() {
//PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
final int[] testUids = getUids(testUserId, new int[] {
@@ -509,7 +510,7 @@
@Test
public void testReadKernelUidCpuFreqTimesLocked() {
// PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
@@ -550,7 +551,7 @@
// Repeat the test when the screen is off.
// PRECONDITIONS
- updateTimeBasesLocked(true, true, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
final long[][] deltasMs = {
{3, 12, 55, 100, 32},
{3248327490475l, 232349349845043l, 123, 2398, 0},
@@ -584,7 +585,7 @@
@Test
public void testReadKernelUidCpuFreqTimesLocked_perClusterTimesAvailable() {
// PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
@@ -644,7 +645,7 @@
// Repeat the test when the screen is off.
// PRECONDITIONS
- updateTimeBasesLocked(true, true, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
final long[][] deltasMs = {
{3, 12, 55, 100, 32},
{3248327490475l, 232349349845043l, 123, 2398, 0},
@@ -688,7 +689,7 @@
@Test
public void testReadKernelUidCpuFreqTimesLocked_partialTimers() {
// PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
@@ -792,7 +793,7 @@
@Test
public void testReadKernelUidCpuFreqTimesLocked_freqsChanged() {
// PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
@@ -833,7 +834,7 @@
// Repeat the test with the freqs from proc file changed.
// PRECONDITIONS
- updateTimeBasesLocked(true, true, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
final long[][] deltasMs = {
{3, 12, 55, 100, 32, 34984, 27983},
{3248327490475l, 232349349845043l, 123, 2398, 0, 398, 0},
@@ -867,7 +868,7 @@
@Test
public void testReadKernelUidCpuFreqTimesLocked_isolatedUid() {
// PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
@@ -961,7 +962,7 @@
@Test
public void testReadKernelUiidCpuFreqTimesLocked_invalidUid() {
// PRECONDITIONS
- updateTimeBasesLocked(true, false, 0, 0);
+ updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
final int testUserId = 11;
final int invalidUserId = 15;
@@ -1008,12 +1009,12 @@
verify(mKernelUidCpuFreqTimeReader).removeUid(invalidUid);
}
- private void updateTimeBasesLocked(boolean unplugged, boolean screenOff,
+ private void updateTimeBasesLocked(boolean unplugged, int screenState,
long upTime, long realTime) {
// Set PowerProfile=null before calling updateTimeBasesLocked to avoid execution of
// BatteryStatsImpl.updateCpuTimeLocked
mBatteryStatsImpl.setPowerProfile(null);
- mBatteryStatsImpl.updateTimeBasesLocked(unplugged, screenOff, upTime, realTime);
+ mBatteryStatsImpl.updateTimeBasesLocked(unplugged, screenState, upTime, realTime);
mBatteryStatsImpl.setPowerProfile(mPowerProfile);
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index 7769e69..4e83221 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -19,9 +19,11 @@
import static android.os.BatteryStats.WAKE_TYPE_PARTIAL;
import android.app.ActivityManager;
+import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.WorkSource;
import android.support.test.filters.SmallTest;
+import android.view.Display;
import junit.framework.TestCase;
@@ -50,7 +52,7 @@
@SmallTest
public void testNoteBluetoothScanResultLocked() throws Exception {
MockBatteryStatsImpl bi = new MockBatteryStatsImpl(new MockClocks());
- bi.updateTimeBasesLocked(true, true, 0, 0);
+ bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
bi.noteBluetoothScanResultsFromSourceLocked(WS, 1);
@@ -82,7 +84,7 @@
int pid = 10;
String name = "name";
- bi.updateTimeBasesLocked(true, true, 0, 0);
+ bi.updateTimeBasesLocked(true, Display.STATE_OFF, 0, 0);
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_TOP);
bi.getUidStatsLocked(UID).noteStartWakeLocked(pid, name, WAKE_TYPE_PARTIAL, clocks.realtime);
@@ -124,7 +126,7 @@
stateRuntimeMap.put(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT, 5309);
stateRuntimeMap.put(ActivityManager.PROCESS_STATE_CACHED_EMPTY, 42);
- bi.updateTimeBasesLocked(true, false, 0, 0);
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
for (Map.Entry<Integer, Integer> entry : stateRuntimeMap.entrySet()) {
bi.noteUidProcessStateLocked(UID, entry.getKey());
@@ -189,4 +191,94 @@
expectedRunTimeMs = stateRuntimeMap.get(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
assertEquals(expectedRunTimeMs * 1000, actualRunTimeUs);
}
+
+ /** Test BatteryStatsImpl.updateTimeBasesLocked. */
+ @SmallTest
+ public void testUpdateTimeBasesLocked() throws Exception {
+ final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+ bi.updateTimeBasesLocked(false, Display.STATE_OFF, 0, 0);
+ assertFalse(bi.getOnBatteryTimeBase().isRunning());
+ bi.updateTimeBasesLocked(false, Display.STATE_DOZE, 10, 10);
+ assertFalse(bi.getOnBatteryTimeBase().isRunning());
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, 20, 20);
+ assertFalse(bi.getOnBatteryTimeBase().isRunning());
+
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, 30, 30);
+ assertTrue(bi.getOnBatteryTimeBase().isRunning());
+ assertFalse(bi.getOnBatteryScreenOffTimeBase().isRunning());
+ bi.updateTimeBasesLocked(true, Display.STATE_DOZE, 40, 40);
+ assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning());
+ bi.updateTimeBasesLocked(true, Display.STATE_OFF, 40, 40);
+ assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning());
+ }
+
+ /** Test BatteryStatsImpl.noteScreenStateLocked sets timebases and screen states correctly. */
+ @SmallTest
+ public void testNoteScreenStateLocked() throws Exception {
+ final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
+ bi.noteScreenStateLocked(Display.STATE_ON);
+ bi.noteScreenStateLocked(Display.STATE_DOZE);
+ assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning());
+ assertEquals(bi.getScreenState(), Display.STATE_DOZE);
+ bi.noteScreenStateLocked(Display.STATE_ON);
+ assertFalse(bi.getOnBatteryScreenOffTimeBase().isRunning());
+ assertEquals(bi.getScreenState(), Display.STATE_ON);
+ bi.noteScreenStateLocked(Display.STATE_OFF);
+ assertTrue(bi.getOnBatteryScreenOffTimeBase().isRunning());
+ assertEquals(bi.getScreenState(), Display.STATE_OFF);
+ }
+
+ /** Test BatteryStatsImpl.noteScreenStateLocked updates timers correctly.
+ *
+ * Unknown and doze should both be subset of off state
+ *
+ * Timeline 0----100----200----310----400------------1000
+ * Unknown -------
+ * On -------
+ * Off ------- ----------------------
+ * Doze ----------------
+ */
+ @SmallTest
+ public void testNoteScreenStateTimersLocked() throws Exception {
+ final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms
+ MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
+
+ clocks.realtime = clocks.uptime = 100;
+ // Device startup, setOnBatteryLocked calls updateTimebases
+ bi.updateTimeBasesLocked(true, Display.STATE_UNKNOWN, 100_000, 100_000);
+ // Turn on display at 200us
+ clocks.realtime = clocks.uptime = 200;
+ bi.noteScreenStateLocked(Display.STATE_ON);
+ assertEquals(150_000, bi.computeBatteryRealtime(250_000, STATS_SINCE_CHARGED));
+ assertEquals(100_000, bi.computeBatteryScreenOffRealtime(250_000, STATS_SINCE_CHARGED));
+ assertEquals(50_000, bi.getScreenOnTime(250_000, STATS_SINCE_CHARGED));
+ assertEquals(0, bi.getScreenDozeTime(250_000, STATS_SINCE_CHARGED));
+
+ clocks.realtime = clocks.uptime = 310;
+ bi.noteScreenStateLocked(Display.STATE_OFF);
+ assertEquals(250_000, bi.computeBatteryRealtime(350_000, STATS_SINCE_CHARGED));
+ assertEquals(140_000, bi.computeBatteryScreenOffRealtime(350_000, STATS_SINCE_CHARGED));
+ assertEquals(110_000, bi.getScreenOnTime(350_000, STATS_SINCE_CHARGED));
+ assertEquals(0, bi.getScreenDozeTime(350_000, STATS_SINCE_CHARGED));
+
+ clocks.realtime = clocks.uptime = 400;
+ bi.noteScreenStateLocked(Display.STATE_DOZE);
+ assertEquals(400_000, bi.computeBatteryRealtime(500_000, STATS_SINCE_CHARGED));
+ assertEquals(290_000, bi.computeBatteryScreenOffRealtime(500_000, STATS_SINCE_CHARGED));
+ assertEquals(110_000, bi.getScreenOnTime(500_000, STATS_SINCE_CHARGED));
+ assertEquals(100_000, bi.getScreenDozeTime(500_000, STATS_SINCE_CHARGED));
+
+ clocks.realtime = clocks.uptime = 1000;
+ bi.noteScreenStateLocked(Display.STATE_OFF);
+ assertEquals(1400_000, bi.computeBatteryRealtime(1500_000, STATS_SINCE_CHARGED));
+ assertEquals(1290_000, bi.computeBatteryScreenOffRealtime(1500_000, STATS_SINCE_CHARGED));
+ assertEquals(110_000, bi.getScreenOnTime(1500_000, STATS_SINCE_CHARGED));
+ assertEquals(600_000, bi.getScreenDozeTime(1500_000, STATS_SINCE_CHARGED));
+ }
+
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
index 0294095..a751f90 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
@@ -18,6 +18,7 @@
import android.app.ActivityManager;
import android.os.BatteryStats;
import android.support.test.filters.SmallTest;
+import android.view.Display;
import junit.framework.TestCase;
@@ -74,7 +75,7 @@
// Plugged-in (battery=off, sensor=off)
curr = 1000 * (clocks.realtime = clocks.uptime = 100);
- bi.updateTimeBasesLocked(false, false, curr, curr);
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr);
// Start sensor (battery=off, sensor=on)
@@ -110,7 +111,7 @@
// Unplugged (battery=on, sensor=off)
curr = 1000 * (clocks.realtime = clocks.uptime = 100);
- bi.updateTimeBasesLocked(true, false, curr, curr);
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr);
// Start sensor (battery=on, sensor=on)
curr = 1000 * (clocks.realtime = clocks.uptime = 200);
@@ -145,7 +146,7 @@
// On battery (battery=on, sensor=off)
curr = 1000 * (clocks.realtime = clocks.uptime = 100);
- bi.updateTimeBasesLocked(true, false, curr, curr);
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr);
bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
// Start sensor (battery=on, sensor=on)
@@ -154,7 +155,7 @@
// Off battery (battery=off, sensor=on)
curr = 1000 * (clocks.realtime = clocks.uptime = 305);
- bi.updateTimeBasesLocked(false, false, curr, curr);
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr);
// Stop sensor while off battery (battery=off, sensor=off)
curr = 1000 * (clocks.realtime = clocks.uptime = 409);
@@ -191,7 +192,7 @@
// Plugged-in (battery=off, sensor=off)
curr = 1000 * (clocks.realtime = clocks.uptime = 100);
- bi.updateTimeBasesLocked(false, false, curr, curr);
+ bi.updateTimeBasesLocked(false, Display.STATE_ON, curr, curr);
// Start sensor (battery=off, sensor=on)
curr = 1000 * (clocks.realtime = clocks.uptime = 200);
@@ -209,7 +210,7 @@
// Unplug (battery=on, sensor=on)
curr = 1000 * (clocks.realtime = clocks.uptime = 305);
- bi.updateTimeBasesLocked(true, false, curr, curr);
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr);
//Test situation
curr = 1000 * (clocks.realtime = clocks.uptime = 410);
@@ -243,7 +244,7 @@
long curr = 0; // realtime in us
// Entire test is on-battery
curr = 1000 * (clocks.realtime = clocks.uptime = 1000);
- bi.updateTimeBasesLocked(true, false, curr, curr);
+ bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr);
// See below for a diagram of events.
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 73ee3e5..63d1e5a 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -28,6 +28,10 @@
MockBatteryStatsImpl(Clocks clocks) {
super(clocks);
this.clocks = mClocks;
+ mScreenOnTimer = new BatteryStatsImpl.StopwatchTimer(clocks, null, -1, null,
+ mOnBatteryTimeBase);
+ mScreenDozeTimer = new BatteryStatsImpl.StopwatchTimer(clocks, null, -1, null,
+ mOnBatteryTimeBase);
mBluetoothScanTimer = new StopwatchTimer(mClocks, null, -14, null, mOnBatteryTimeBase);
}
@@ -39,6 +43,14 @@
return mOnBatteryTimeBase;
}
+ public TimeBase getOnBatteryScreenOffTimeBase() {
+ return mOnBatteryScreenOffTimeBase;
+ }
+
+ public int getScreenState() {
+ return mScreenState;
+ }
+
public boolean isOnBattery() {
return mForceOnBattery ? true : super.isOnBattery();
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 9948f7e..902b872 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -253,12 +253,14 @@
</privapp-permissions>
<privapp-permissions package="com.android.shell">
+ <permission name="android.permission.ACCESS_LOWPAN_STATE"/>
<permission name="android.permission.BACKUP"/>
<permission name="android.permission.BATTERY_STATS"/>
<permission name="android.permission.BIND_APPWIDGET"/>
<permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/>
<permission name="android.permission.CHANGE_CONFIGURATION"/>
<permission name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" />
+ <permission name="android.permission.CHANGE_LOWPAN_STATE"/>
<permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/>
<permission name="android.permission.CLEAR_APP_CACHE"/>
<permission name="android.permission.CONNECTIVITY_INTERNAL"/>
@@ -282,6 +284,7 @@
<permission name="android.permission.MOVE_PACKAGE"/>
<permission name="android.permission.PACKAGE_USAGE_STATS" />
<permission name="android.permission.READ_FRAME_BUFFER"/>
+ <permission name="android.permission.READ_LOWPAN_CREDENTIAL"/>
<permission name="android.permission.REAL_GET_TASKS"/>
<permission name="android.permission.REGISTER_CALL_PROVIDER"/>
<permission name="android.permission.REGISTER_CONNECTION_MANAGER"/>
@@ -349,11 +352,14 @@
</privapp-permissions>
<privapp-permissions package="com.android.tv">
- <permission name="android.permission.DVB_DEVICE" />
- <permission name="android.permission.GLOBAL_SEARCH" />
- <permission name="android.permission.MODIFY_PARENTAL_CONTROLS" />
- <permission name="com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA" />
- <permission name="com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS" />
+ <permission name="android.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE"/>
+ <permission name="android.permission.DVB_DEVICE"/>
+ <permission name="android.permission.GLOBAL_SEARCH"/>
+ <permission name="android.permission.HDMI_CEC"/>
+ <permission name="android.permission.MODIFY_PARENTAL_CONTROLS"/>
+ <permission name="android.permission.READ_CONTENT_RATING_SYSTEMS"/>
+ <permission name="com.android.providers.tv.permission.ACCESS_ALL_EPG_DATA"/>
+ <permission name="com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS"/>
</privapp-permissions>
<privapp-permissions package="com.android.vpndialogs">
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 57c7549..0072012 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
+import android.annotation.WorkerThread;
import android.content.res.ResourcesImpl;
import android.os.Parcel;
import android.os.Parcelable;
@@ -1233,6 +1234,7 @@
* @param stream The outputstream to write the compressed data.
* @return true if successfully compressed to the specified stream.
*/
+ @WorkerThread
public boolean compress(CompressFormat format, int quality, OutputStream stream) {
checkRecycled("Can't compress a recycled bitmap");
// do explicit check before calling the native method
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index ffb39e3..f5bf754 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -354,6 +354,7 @@
* decode, in the case of which a more accurate, but slightly slower,
* IDCT method will be used instead.
*/
+ @Deprecated
public boolean inPreferQualityOverSpeed;
/**
@@ -412,6 +413,7 @@
* can check, inbetween the bounds decode and the image decode, to see
* if the operation is canceled.
*/
+ @Deprecated
public boolean mCancel;
/**
@@ -426,6 +428,7 @@
* or if inJustDecodeBounds is true, will set outWidth/outHeight
* to -1
*/
+ @Deprecated
public void requestCancelDecode() {
mCancel = true;
}
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index b090681..1727eca 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -269,7 +269,6 @@
setRippleActive(focused || (enabled && pressed));
setBackgroundActive(hovered, hovered);
-
return changed;
}
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index 0de3f11..a675eaf 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -41,7 +41,6 @@
// Pixel-based accelerations and velocities.
private static final float WAVE_TOUCH_DOWN_ACCELERATION = 1024;
- private static final float WAVE_TOUCH_UP_ACCELERATION = 3400;
private static final float WAVE_OPACITY_DECAY_VELOCITY = 3;
// Bounded ripple animation properties.
@@ -83,6 +82,11 @@
/** Whether this ripple has finished its exit animation. */
private boolean mHasFinishedExit;
+ /**
+ * If we have a bound, don't start from 0. Start from 60% of the max out of width and height.
+ */
+ private float mStartRadius = 0;
+
public RippleForeground(RippleDrawable owner, Rect bounds, float startingX, float startingY,
boolean isBounded, boolean forceSoftware) {
super(owner, bounds, forceSoftware);
@@ -96,6 +100,8 @@
} else {
mBoundedRadius = 0;
}
+ // Take 60% of the maximum of the width and height, then divided half to get the radius.
+ mStartRadius = Math.max(bounds.width(), bounds.height()) * 0.3f;
}
@Override
@@ -158,19 +164,18 @@
@Override
protected Animator createSoftwareEnter(boolean fast) {
- final int duration = (int)
- (1000 * Math.sqrt(mTargetRadius / WAVE_TOUCH_DOWN_ACCELERATION * mDensityScale) + 0.5);
+ final int duration = getRadiusDuration();
final ObjectAnimator tweenRadius = ObjectAnimator.ofFloat(this, TWEEN_RADIUS, 1);
tweenRadius.setAutoCancel(true);
tweenRadius.setDuration(duration);
- tweenRadius.setInterpolator(LINEAR_INTERPOLATOR);
+ tweenRadius.setInterpolator(DECELERATE_INTERPOLATOR);
tweenRadius.setStartDelay(RIPPLE_ENTER_DELAY);
final ObjectAnimator tweenOrigin = ObjectAnimator.ofFloat(this, TWEEN_ORIGIN, 1);
tweenOrigin.setAutoCancel(true);
tweenOrigin.setDuration(duration);
- tweenOrigin.setInterpolator(LINEAR_INTERPOLATOR);
+ tweenOrigin.setInterpolator(DECELERATE_INTERPOLATOR);
tweenOrigin.setStartDelay(RIPPLE_ENTER_DELAY);
final ObjectAnimator opacity = ObjectAnimator.ofFloat(this, OPACITY, 1);
@@ -192,14 +197,14 @@
return MathUtils.lerp(mClampedStartingY - mBounds.exactCenterY(), mTargetY, mTweenY);
}
- private int getRadiusExitDuration() {
+ private int getRadiusDuration() {
final float remainingRadius = mTargetRadius - getCurrentRadius();
- return (int) (1000 * Math.sqrt(remainingRadius / (WAVE_TOUCH_UP_ACCELERATION
- + WAVE_TOUCH_DOWN_ACCELERATION) * mDensityScale) + 0.5);
+ return (int) (1000 * Math.sqrt(remainingRadius / WAVE_TOUCH_DOWN_ACCELERATION *
+ mDensityScale) + 0.5);
}
private float getCurrentRadius() {
- return MathUtils.lerp(0, mTargetRadius, mTweenRadius);
+ return MathUtils.lerp(mStartRadius, mTargetRadius, mTweenRadius);
}
private int getOpacityExitDuration() {
@@ -212,7 +217,7 @@
final int originDuration;
final int opacityDuration;
- radiusDuration = getRadiusExitDuration();
+ radiusDuration = getRadiusDuration();
originDuration = radiusDuration;
opacityDuration = getOpacityExitDuration();
@@ -244,7 +249,7 @@
final int originDuration;
final int opacityDuration;
- radiusDuration = getRadiusExitDuration();
+ radiusDuration = getRadiusDuration();
originDuration = radiusDuration;
opacityDuration = getOpacityExitDuration();
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index 0a38874..7cc0871 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -34,12 +34,12 @@
namespace android {
-static SkTypeface::Style computeSkiaStyle(int weight, bool italic) {
+static Typeface::Style computeAPIStyle(int weight, bool italic) {
// This bold detection comes from SkTypeface.h
if (weight >= SkFontStyle::kSemiBold_Weight) {
- return italic ? SkTypeface::kBoldItalic : SkTypeface::kBold;
+ return italic ? Typeface::kBoldItalic : Typeface::kBold;
} else {
- return italic ? SkTypeface::kItalic : SkTypeface::kNormal;
+ return italic ? Typeface::kItalic : Typeface::kNormal;
}
}
@@ -50,12 +50,12 @@
}
// Resolve the relative weight from the baseWeight and target style.
-static minikin::FontStyle computeRelativeStyle(int baseWeight, SkTypeface::Style relativeStyle) {
+static minikin::FontStyle computeRelativeStyle(int baseWeight, Typeface::Style relativeStyle) {
int weight = baseWeight;
- if ((relativeStyle & SkTypeface::kBold) != 0) {
+ if ((relativeStyle & Typeface::kBold) != 0) {
weight += 300;
}
- bool italic = (relativeStyle & SkTypeface::kItalic) != 0;
+ bool italic = (relativeStyle & Typeface::kItalic) != 0;
return computeMinikinStyle(weight, italic);
}
@@ -66,13 +66,13 @@
return src == nullptr ? gDefaultTypeface : src;
}
-Typeface* Typeface::createRelative(Typeface* src, SkTypeface::Style style) {
+Typeface* Typeface::createRelative(Typeface* src, Typeface::Style style) {
const Typeface* resolvedFace = Typeface::resolveDefault(src);
Typeface* result = new Typeface;
if (result != nullptr) {
result->fFontCollection = resolvedFace->fFontCollection;
result->fBaseWeight = resolvedFace->fBaseWeight;
- result->fSkiaStyle = style;
+ result->fAPIStyle = style;
result->fStyle = computeRelativeStyle(result->fBaseWeight, style);
}
return result;
@@ -84,7 +84,7 @@
if (result != nullptr) {
result->fFontCollection = resolvedFace->fFontCollection;
result->fBaseWeight = resolvedFace->fBaseWeight;
- result->fSkiaStyle = computeSkiaStyle(weight, italic);
+ result->fAPIStyle = computeAPIStyle(weight, italic);
result->fStyle = computeMinikinStyle(weight, italic);
}
return result;
@@ -105,7 +105,7 @@
// Do not update styles.
// TODO: We may want to update base weight if the 'wght' is specified.
result->fBaseWeight = resolvedFace->fBaseWeight;
- result->fSkiaStyle = resolvedFace->fSkiaStyle;
+ result->fAPIStyle = resolvedFace->fAPIStyle;
result->fStyle = resolvedFace->fStyle;
}
return result;
@@ -117,8 +117,8 @@
if (result != nullptr) {
result->fFontCollection = resolvedFace->fFontCollection;
result->fBaseWeight = weight;
- result->fSkiaStyle = resolvedFace->fSkiaStyle;
- result->fStyle = computeRelativeStyle(weight, result->fSkiaStyle);
+ result->fAPIStyle = resolvedFace->fAPIStyle;
+ result->fStyle = computeRelativeStyle(weight, result->fAPIStyle);
}
return result;
}
@@ -161,7 +161,7 @@
}
result->fBaseWeight = weight;
- result->fSkiaStyle = computeSkiaStyle(weight, italic);
+ result->fAPIStyle = computeAPIStyle(weight, italic);
result->fStyle = computeMinikinStyle(weight, italic);
return result;
}
@@ -191,7 +191,7 @@
Typeface* hwTypeface = new Typeface();
hwTypeface->fFontCollection = collection;
- hwTypeface->fSkiaStyle = SkTypeface::kNormal;
+ hwTypeface->fAPIStyle = Typeface::kNormal;
hwTypeface->fBaseWeight = SkFontStyle::kNormal_Weight;
hwTypeface->fStyle = minikin::FontStyle(4 /* weight */, false /* italic */);
diff --git a/libs/hwui/hwui/Typeface.h b/libs/hwui/hwui/Typeface.h
index 38c6234..d90114d 100644
--- a/libs/hwui/hwui/Typeface.h
+++ b/libs/hwui/hwui/Typeface.h
@@ -38,8 +38,14 @@
// resolved style actually used for rendering
minikin::FontStyle fStyle;
- // style used for constructing and querying Typeface objects
- SkTypeface::Style fSkiaStyle;
+ // style used in the API
+ enum Style : uint8_t {
+ kNormal = 0,
+ kBold = 0x01,
+ kItalic = 0x02,
+ kBoldItalic = 0x03
+ };
+ Style fAPIStyle;
static const Typeface* resolveDefault(const Typeface* src);
@@ -68,7 +74,7 @@
//
// Typeface* black = createAbsolute(base, 900, false); // Rendered with a weight of 900.
static Typeface* createWithDifferentBaseWeight(Typeface* src, int baseweight);
- static Typeface* createRelative(Typeface* src, SkTypeface::Style desiredStyle);
+ static Typeface* createRelative(Typeface* src, Style desiredStyle);
static Typeface* createAbsolute(Typeface* base, int weight, bool italic);
static Typeface* createFromTypefaceWithVariation(Typeface* src,
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index 5c6078d..3b72a8b 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -72,15 +72,23 @@
textureMatrixInv = textureMatrix;
}
- SkMatrix matrix = SkMatrix::Concat(textureMatrixInv, layerTransform);
+ SkMatrix matrix = SkMatrix::Concat(layerTransform, textureMatrixInv);
SkPaint paint;
paint.setAlpha(layer->getAlpha());
paint.setBlendMode(layer->getMode());
paint.setColorFilter(sk_ref_sp(layer->getColorFilter()));
- // draw image with a shader to avoid save/restore of the matrix
- paint.setShader(layerImage->makeShader(&matrix));
- canvas->drawRect(SkRect::MakeWH(layerWidth, layerHeight), paint);
+
+ const bool nonIdentityMatrix = !matrix.isIdentity();
+ if (nonIdentityMatrix) {
+ canvas->save();
+ canvas->concat(matrix);
+ }
+ canvas->drawImage(layerImage.get(), 0, 0, &paint);
+ // restore the original matrix
+ if (nonIdentityMatrix) {
+ canvas->restore();
+ }
}
return layerImage;
diff --git a/libs/hwui/service/GraphicsStatsService.cpp b/libs/hwui/service/GraphicsStatsService.cpp
index afb1193..f7a90b0 100644
--- a/libs/hwui/service/GraphicsStatsService.cpp
+++ b/libs/hwui/service/GraphicsStatsService.cpp
@@ -22,10 +22,11 @@
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include <log/log.h>
-#include <inttypes.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <errno.h>
#include <fcntl.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
#include <sys/mman.h>
@@ -366,4 +367,4 @@
}
} /* namespace uirenderer */
-} /* namespace android */
\ No newline at end of file
+} /* namespace android */
diff --git a/libs/hwui/tests/unit/TypefaceTests.cpp b/libs/hwui/tests/unit/TypefaceTests.cpp
index 439d690..1f3eaef 100644
--- a/libs/hwui/tests/unit/TypefaceTests.cpp
+++ b/libs/hwui/tests/unit/TypefaceTests.cpp
@@ -84,159 +84,159 @@
std::unique_ptr<Typeface> bold(Typeface::createWithDifferentBaseWeight(nullptr, 700));
EXPECT_EQ(7, bold->fStyle.getWeight());
EXPECT_FALSE(bold->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kNormal, bold->fSkiaStyle);
+ EXPECT_EQ(Typeface::kNormal, bold->fAPIStyle);
std::unique_ptr<Typeface> light(Typeface::createWithDifferentBaseWeight(nullptr, 300));
EXPECT_EQ(3, light->fStyle.getWeight());
EXPECT_FALSE(light->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kNormal, light->fSkiaStyle);
+ EXPECT_EQ(Typeface::kNormal, light->fAPIStyle);
}
TEST(TypefaceTest, createRelativeTest_fromRegular) {
// In Java, Typeface.create(Typeface.DEFAULT, Typeface.NORMAL);
- std::unique_ptr<Typeface> normal(Typeface::createRelative(nullptr, SkTypeface::kNormal));
+ std::unique_ptr<Typeface> normal(Typeface::createRelative(nullptr, Typeface::kNormal));
EXPECT_EQ(4, normal->fStyle.getWeight());
EXPECT_FALSE(normal->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle);
+ EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
// In Java, Typeface.create(Typeface.DEFAULT, Typeface.BOLD);
- std::unique_ptr<Typeface> bold(Typeface::createRelative(nullptr, SkTypeface::kBold));
+ std::unique_ptr<Typeface> bold(Typeface::createRelative(nullptr, Typeface::kBold));
EXPECT_EQ(7, bold->fStyle.getWeight());
EXPECT_FALSE(bold->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+ EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, Typeface.create(Typeface.DEFAULT, Typeface.ITALIC);
- std::unique_ptr<Typeface> italic(Typeface::createRelative(nullptr, SkTypeface::kItalic));
+ std::unique_ptr<Typeface> italic(Typeface::createRelative(nullptr, Typeface::kItalic));
EXPECT_EQ(4, italic->fStyle.getWeight());
EXPECT_TRUE(italic->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+ EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java, Typeface.create(Typeface.DEFAULT, Typeface.BOLD_ITALIC);
std::unique_ptr<Typeface> boldItalic(
- Typeface::createRelative(nullptr, SkTypeface::kBoldItalic));
+ Typeface::createRelative(nullptr, Typeface::kBoldItalic));
EXPECT_EQ(7, boldItalic->fStyle.getWeight());
EXPECT_TRUE(boldItalic->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+ EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
}
TEST(TypefaceTest, createRelativeTest_BoldBase) {
std::unique_ptr<Typeface> base(Typeface::createWithDifferentBaseWeight(nullptr, 700));
// In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.NORMAL);
- std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal));
+ std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
EXPECT_EQ(7, normal->fStyle.getWeight());
EXPECT_FALSE(normal->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle);
+ EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
// In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.BOLD);
- std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold));
+ std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
EXPECT_EQ(10, bold->fStyle.getWeight());
EXPECT_FALSE(bold->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+ EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.ITALIC);
- std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic));
+ std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
EXPECT_EQ(7, italic->fStyle.getWeight());
EXPECT_TRUE(italic->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+ EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java, Typeface.create(Typeface.create("sans-serif-bold"), Typeface.BOLD_ITALIC);
std::unique_ptr<Typeface>
- boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic));
+ boldItalic(Typeface::createRelative(base.get(), Typeface::kBoldItalic));
EXPECT_EQ(10, boldItalic->fStyle.getWeight());
EXPECT_TRUE(boldItalic->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+ EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
}
TEST(TypefaceTest, createRelativeTest_LightBase) {
std::unique_ptr<Typeface> base(Typeface::createWithDifferentBaseWeight(nullptr, 300));
// In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.NORMAL);
- std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal));
+ std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
EXPECT_EQ(3, normal->fStyle.getWeight());
EXPECT_FALSE(normal->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle);
+ EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
// In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.BOLD);
- std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold));
+ std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
EXPECT_EQ(6, bold->fStyle.getWeight());
EXPECT_FALSE(bold->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+ EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.ITLIC);
- std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic));
+ std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
EXPECT_EQ(3, italic->fStyle.getWeight());
EXPECT_TRUE(italic->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+ EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java, Typeface.create(Typeface.create("sans-serif-light"), Typeface.BOLD_ITALIC);
std::unique_ptr<Typeface>
- boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic));
+ boldItalic(Typeface::createRelative(base.get(), Typeface::kBoldItalic));
EXPECT_EQ(6, boldItalic->fStyle.getWeight());
EXPECT_TRUE(boldItalic->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+ EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
}
TEST(TypefaceTest, createRelativeTest_fromBoldStyled) {
- std::unique_ptr<Typeface> base(Typeface::createRelative(nullptr, SkTypeface::kBold));
+ std::unique_ptr<Typeface> base(Typeface::createRelative(nullptr, Typeface::kBold));
// In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.NORMAL);
- std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal));
+ std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
EXPECT_EQ(4, normal->fStyle.getWeight());
EXPECT_FALSE(normal->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle);
+ EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
// In Java Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.BOLD);
- std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold));
+ std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
EXPECT_EQ(7, bold->fStyle.getWeight());
EXPECT_FALSE(bold->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+ EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.ITALIC);
- std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic));
+ std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
EXPECT_EQ(4, normal->fStyle.getWeight());
EXPECT_TRUE(italic->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+ EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
// Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.BOLD), Typeface.BOLD_ITALIC);
std::unique_ptr<Typeface>
- boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic));
+ boldItalic(Typeface::createRelative(base.get(), Typeface::kBoldItalic));
EXPECT_EQ(7, boldItalic->fStyle.getWeight());
EXPECT_TRUE(boldItalic->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+ EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
}
TEST(TypefaceTest, createRelativeTest_fromItalicStyled) {
- std::unique_ptr<Typeface> base(Typeface::createRelative(nullptr, SkTypeface::kItalic));
+ std::unique_ptr<Typeface> base(Typeface::createRelative(nullptr, Typeface::kItalic));
// In Java,
// Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.NORMAL);
- std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal));
+ std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
EXPECT_EQ(4, normal->fStyle.getWeight());
EXPECT_FALSE(normal->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle);
+ EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
// In Java, Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.BOLD);
- std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold));
+ std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
EXPECT_EQ(7, bold->fStyle.getWeight());
EXPECT_FALSE(bold->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+ EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java,
// Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.ITALIC);
- std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic));
+ std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
EXPECT_EQ(4, italic->fStyle.getWeight());
EXPECT_TRUE(italic->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+ EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
// Typeface.create(Typeface.create(Typeface.DEFAULT, Typeface.ITALIC), Typeface.BOLD_ITALIC);
std::unique_ptr<Typeface>
- boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic));
+ boldItalic(Typeface::createRelative(base.get(), Typeface::kBoldItalic));
EXPECT_EQ(7, boldItalic->fStyle.getWeight());
EXPECT_TRUE(boldItalic->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+ EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
}
TEST(TypefaceTest, createRelativeTest_fromSpecifiedStyled) {
@@ -246,38 +246,38 @@
// Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif")
// .setWeight(700).setItalic(false).build();
// Typeface.create(typeface, Typeface.NORMAL);
- std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), SkTypeface::kNormal));
+ std::unique_ptr<Typeface> normal(Typeface::createRelative(base.get(), Typeface::kNormal));
EXPECT_EQ(4, normal->fStyle.getWeight());
EXPECT_FALSE(normal->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kNormal, normal->fSkiaStyle);
+ EXPECT_EQ(Typeface::kNormal, normal->fAPIStyle);
// In Java,
// Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif")
// .setWeight(700).setItalic(false).build();
// Typeface.create(typeface, Typeface.BOLD);
- std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), SkTypeface::kBold));
+ std::unique_ptr<Typeface> bold(Typeface::createRelative(base.get(), Typeface::kBold));
EXPECT_EQ(7, bold->fStyle.getWeight());
EXPECT_FALSE(bold->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+ EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java,
// Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif")
// .setWeight(700).setItalic(false).build();
// Typeface.create(typeface, Typeface.ITALIC);
- std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), SkTypeface::kItalic));
+ std::unique_ptr<Typeface> italic(Typeface::createRelative(base.get(), Typeface::kItalic));
EXPECT_EQ(4, italic->fStyle.getWeight());
EXPECT_TRUE(italic->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+ EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
// Typeface typeface = new Typeface.Builder(invalid).setFallback("sans-serif")
// .setWeight(700).setItalic(false).build();
// Typeface.create(typeface, Typeface.BOLD_ITALIC);
std::unique_ptr<Typeface>
- boldItalic(Typeface::createRelative(base.get(), SkTypeface::kBoldItalic));
+ boldItalic(Typeface::createRelative(base.get(), Typeface::kBoldItalic));
EXPECT_EQ(7, boldItalic->fStyle.getWeight());
EXPECT_TRUE(boldItalic->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+ EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
}
TEST(TypefaceTest, createAbsolute) {
@@ -287,7 +287,7 @@
std::unique_ptr<Typeface> regular(Typeface::createAbsolute(nullptr, 400, false));
EXPECT_EQ(4, regular->fStyle.getWeight());
EXPECT_FALSE(regular->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kNormal, regular->fSkiaStyle);
+ EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
// In Java,
// new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(false)
@@ -295,7 +295,7 @@
std::unique_ptr<Typeface> bold(Typeface::createAbsolute(nullptr, 700, false));
EXPECT_EQ(7, bold->fStyle.getWeight());
EXPECT_FALSE(bold->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+ EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java,
// new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(400).setItalic(true)
@@ -303,7 +303,7 @@
std::unique_ptr<Typeface> italic(Typeface::createAbsolute(nullptr, 400, true));
EXPECT_EQ(4, italic->fStyle.getWeight());
EXPECT_TRUE(italic->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+ EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
// new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(700).setItalic(true)
@@ -311,7 +311,7 @@
std::unique_ptr<Typeface> boldItalic(Typeface::createAbsolute(nullptr, 700, true));
EXPECT_EQ(7, boldItalic->fStyle.getWeight());
EXPECT_TRUE(boldItalic->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kBoldItalic, boldItalic->fSkiaStyle);
+ EXPECT_EQ(Typeface::kBoldItalic, boldItalic->fAPIStyle);
// In Java,
// new Typeface.Builder(invalid).setFallback("sans-serif").setWeight(1100).setItalic(true)
@@ -319,7 +319,7 @@
std::unique_ptr<Typeface> over1000(Typeface::createAbsolute(nullptr, 1100, false));
EXPECT_EQ(10, over1000->fStyle.getWeight());
EXPECT_FALSE(over1000->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kBold, over1000->fSkiaStyle);
+ EXPECT_EQ(Typeface::kBold, over1000->fAPIStyle);
}
TEST(TypefaceTest, createFromFamilies_Single) {
@@ -328,21 +328,21 @@
Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoRegular), 400, false));
EXPECT_EQ(4, regular->fStyle.getWeight());
EXPECT_FALSE(regular->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kNormal, regular->fSkiaStyle);
+ EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
// In Java, new Typeface.Builder("Roboto-Bold.ttf").setWeight(700).setItalic(false).build();
std::unique_ptr<Typeface> bold(
Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 700, false));
EXPECT_EQ(7, bold->fStyle.getWeight());
EXPECT_FALSE(bold->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+ EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, new Typeface.Builder("Roboto-Italic.ttf").setWeight(400).setItalic(true).build();
std::unique_ptr<Typeface> italic(
Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoItalic), 400, true));
EXPECT_EQ(4, italic->fStyle.getWeight());
EXPECT_TRUE(italic->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+ EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
// new Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(700).setItalic(true).build();
@@ -350,7 +350,7 @@
Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBoldItalic), 700, true));
EXPECT_EQ(7, boldItalic->fStyle.getWeight());
EXPECT_TRUE(boldItalic->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+ EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java,
// new Typeface.Builder("Roboto-BoldItalic.ttf").setWeight(1100).setItalic(false).build();
@@ -358,7 +358,7 @@
Typeface::createFromFamilies(makeSingleFamlyVector(kRobotoBold), 1100, false));
EXPECT_EQ(10, over1000->fStyle.getWeight());
EXPECT_FALSE(over1000->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kBold, over1000->fSkiaStyle);
+ EXPECT_EQ(Typeface::kBold, over1000->fAPIStyle);
}
TEST(TypefaceTest, createFromFamilies_Single_resolveByTable) {
@@ -368,7 +368,7 @@
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
EXPECT_EQ(4, regular->fStyle.getWeight());
EXPECT_FALSE(regular->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kNormal, regular->fSkiaStyle);
+ EXPECT_EQ(Typeface::kNormal, regular->fAPIStyle);
// In Java, new Typeface.Builder("Roboto-Bold.ttf").build();
std::unique_ptr<Typeface> bold(
@@ -376,7 +376,7 @@
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
EXPECT_EQ(7, bold->fStyle.getWeight());
EXPECT_FALSE(bold->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kBold, bold->fSkiaStyle);
+ EXPECT_EQ(Typeface::kBold, bold->fAPIStyle);
// In Java, new Typeface.Builder("Roboto-Italic.ttf").build();
std::unique_ptr<Typeface> italic(
@@ -384,7 +384,7 @@
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
EXPECT_EQ(4, italic->fStyle.getWeight());
EXPECT_TRUE(italic->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+ EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
// In Java, new Typeface.Builder("Roboto-BoldItalic.ttf").build();
std::unique_ptr<Typeface> boldItalic(
@@ -392,7 +392,7 @@
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
EXPECT_EQ(7, boldItalic->fStyle.getWeight());
EXPECT_TRUE(boldItalic->fStyle.getItalic());
- EXPECT_EQ(SkTypeface::kItalic, italic->fSkiaStyle);
+ EXPECT_EQ(Typeface::kItalic, italic->fAPIStyle);
}
TEST(TypefaceTest, createFromFamilies_Family) {
diff --git a/libs/protoutil/Android.mk b/libs/protoutil/Android.mk
index a534816..2a2b087 100644
--- a/libs/protoutil/Android.mk
+++ b/libs/protoutil/Android.mk
@@ -22,15 +22,15 @@
-Wall -Werror -Wno-missing-field-initializers -Wno-unused-variable -Wunused-parameter
LOCAL_SHARED_LIBRARIES := \
- libbinder \
+ libcutils \
liblog \
- libutils
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/include
LOCAL_SRC_FILES := \
src/EncodedBuffer.cpp \
+ src/ProtoOutputStream.cpp \
src/protobuf.cpp \
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
diff --git a/libs/protoutil/include/android/util/EncodedBuffer.h b/libs/protoutil/include/android/util/EncodedBuffer.h
index cf09609..e568e4c 100644
--- a/libs/protoutil/include/android/util/EncodedBuffer.h
+++ b/libs/protoutil/include/android/util/EncodedBuffer.h
@@ -52,10 +52,10 @@
size_t index() const;
size_t offset() const;
- void move(size_t amt);
- inline void move() { move(1); };
+ Pointer* move(size_t amt);
+ inline Pointer* move() { return move(1); };
+ Pointer* rewind();
- void rewind();
Pointer copy() const;
private:
@@ -88,15 +88,71 @@
size_t currentToWrite();
/**
- * Write a varint into a vector. Return the size of the varint.
+ * Write a single byte to the buffer.
*/
- size_t writeRawVarint(uint32_t val);
+ void writeRawByte(uint8_t val);
+
+ /**
+ * Write a varint32 into the buffer. Return the size of the varint.
+ */
+ size_t writeRawVarint32(uint32_t val);
+
+ /**
+ * Write a varint64 into the buffer. Return the size of the varint.
+ */
+ size_t writeRawVarint64(uint64_t val);
+
+ /**
+ * Write Fixed32 into the buffer.
+ */
+ void writeRawFixed32(uint32_t val);
+
+ /**
+ * Write Fixed64 into the buffer.
+ */
+ void writeRawFixed64(uint64_t val);
/**
* Write a protobuf header. Return the size of the header.
*/
size_t writeHeader(uint32_t fieldId, uint8_t wireType);
+ /********************************* Edit APIs ************************************************/
+ /**
+ * Returns the edit pointer.
+ */
+ Pointer* ep();
+
+ /**
+ * Read a single byte at ep, and move ep to next byte;
+ */
+ uint8_t readRawByte();
+
+ /**
+ * Read varint starting at ep, ep will move to pos of next byte.
+ */
+ uint64_t readRawVarint();
+
+ /**
+ * Read 4 bytes starting at ep, ep will move to pos of next byte.
+ */
+ uint32_t readRawFixed32();
+
+ /**
+ * Read 8 bytes starting at ep, ep will move to pos of next byte.
+ */
+ uint64_t readRawFixed64();
+
+ /**
+ * Edit 4 bytes starting at pos.
+ */
+ void editRawFixed32(size_t pos, uint32_t val);
+
+ /**
+ * Copy _size_ bytes of data starting at __srcPos__ to wp.
+ */
+ void copy(size_t srcPos, size_t size);
+
/********************************* Read APIs ************************************************/
class iterator;
friend class iterator;
@@ -141,9 +197,8 @@
/**
* Read varint from iterator, the iterator will point to next available byte.
- * Return the number of bytes of the varint.
*/
- uint32_t readRawVarint();
+ uint64_t readRawVarint();
private:
const EncodedBuffer& mData;
@@ -160,6 +215,7 @@
vector<uint8_t*> mBuffers;
Pointer mWp;
+ Pointer mEp;
inline uint8_t* at(const Pointer& p) const; // helper function to get value
};
@@ -167,4 +223,5 @@
} // util
} // android
-#endif // ANDROID_UTIL_ENCODED_BUFFER_H
\ No newline at end of file
+#endif // ANDROID_UTIL_ENCODED_BUFFER_H
+
diff --git a/libs/protoutil/include/android/util/ProtoOutputStream.h b/libs/protoutil/include/android/util/ProtoOutputStream.h
new file mode 100644
index 0000000..49ec169
--- /dev/null
+++ b/libs/protoutil/include/android/util/ProtoOutputStream.h
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UTIL_PROTOOUTPUT_STREAM_H
+#define ANDROID_UTIL_PROTOOUTPUT_STREAM_H
+
+#include <android/util/EncodedBuffer.h>
+
+#include <stdint.h>
+#include <string>
+
+namespace android {
+namespace util {
+
+/**
+ * Class to write to a protobuf stream.
+ *
+ * Each write method takes an ID code from the protoc generated classes
+ * and the value to write. To make a nested object, call start
+ * and then end when you are done.
+ *
+ * See the java version implementation (ProtoOutputStream.java) for more infos.
+ */
+class ProtoOutputStream
+{
+public:
+ ProtoOutputStream(int fd);
+ ~ProtoOutputStream();
+
+ /**
+ * Write APIs for dumping protobuf data. Returns true if the write succeeds.
+ */
+ bool write(uint64_t fieldId, double val);
+ bool write(uint64_t fieldId, float val);
+ bool write(uint64_t fieldId, int val);
+ bool write(uint64_t fieldId, long long val);
+ bool write(uint64_t fieldId, bool val);
+ bool write(uint64_t fieldId, std::string val);
+ bool write(uint64_t fieldId, const char* val);
+
+ /**
+ * Starts a sub-message write session.
+ * Returns a token of this write session.
+ * Must call end(token) when finish write this sub-message.
+ */
+ long long start(uint64_t fieldId);
+ void end(long long token);
+
+ /**
+ * Flushes the protobuf data out.
+ */
+ bool flush();
+
+private:
+ EncodedBuffer mBuffer;
+ int mFd;
+ size_t mCopyBegin;
+ bool mCompact;
+ int mDepth;
+ int mObjectId;
+ long long mExpectedObjectToken;
+
+ inline void writeDoubleImpl(uint32_t id, double val);
+ inline void writeFloatImpl(uint32_t id, float val);
+ inline void writeInt64Impl(uint32_t id, long long val);
+ inline void writeInt32Impl(uint32_t id, int val);
+ inline void writeUint64Impl(uint32_t id, uint64_t val);
+ inline void writeUint32Impl(uint32_t id, uint32_t val);
+ inline void writeFixed64Impl(uint32_t id, uint64_t val);
+ inline void writeFixed32Impl(uint32_t id, uint32_t val);
+ inline void writeSFixed64Impl(uint32_t id, long long val);
+ inline void writeSFixed32Impl(uint32_t id, int val);
+ inline void writeZigzagInt64Impl(uint32_t id, long long val);
+ inline void writeZigzagInt32Impl(uint32_t id, int val);
+ inline void writeEnumImpl(uint32_t id, int val);
+ inline void writeBoolImpl(uint32_t id, bool val);
+ inline void writeUtf8StringImpl(uint32_t id, const char* val, size_t size);
+
+ bool compact();
+ size_t editEncodedSize(size_t rawSize);
+ bool compactSize(size_t rawSize);
+};
+
+}
+}
+
+#endif // ANDROID_UTIL_PROTOOUTPUT_STREAM_H
\ No newline at end of file
diff --git a/libs/protoutil/include/android/util/protobuf.h b/libs/protoutil/include/android/util/protobuf.h
index f4e8d09..ca45e26 100644
--- a/libs/protoutil/include/android/util/protobuf.h
+++ b/libs/protoutil/include/android/util/protobuf.h
@@ -24,6 +24,9 @@
using namespace std;
+const int FIELD_ID_SHIFT = 3;
+const uint8_t WIRE_TYPE_MASK = (1 << FIELD_ID_SHIFT) - 1;
+
const uint8_t WIRE_TYPE_VARINT = 0;
const uint8_t WIRE_TYPE_FIXED64 = 1;
const uint8_t WIRE_TYPE_LENGTH_DELIMITED = 2;
@@ -35,16 +38,20 @@
uint8_t read_wire_type(uint32_t varint);
/**
- * read field id from varint, it is varint >> 3;
+ * Read field id from varint, it is varint >> 3;
*/
uint32_t read_field_id(uint32_t varint);
/**
- * Write a varint into the buffer. Return the next position to write at.
- * There must be 10 bytes in the buffer. The same as
- * EncodedBuffer.writeRawVarint32
+ * Get the size of a varint.
*/
-uint8_t* write_raw_varint(uint8_t* buf, uint32_t val);
+size_t get_varint_size(uint64_t varint);
+
+/**
+ * Write a varint into the buffer. Return the next position to write at.
+ * There must be 10 bytes in the buffer.
+ */
+uint8_t* write_raw_varint(uint8_t* buf, uint64_t val);
/**
* Write a protobuf WIRE_TYPE_LENGTH_DELIMITED header. Return the next position
diff --git a/libs/protoutil/src/EncodedBuffer.cpp b/libs/protoutil/src/EncodedBuffer.cpp
index 84dc5b6..435ae88 100644
--- a/libs/protoutil/src/EncodedBuffer.cpp
+++ b/libs/protoutil/src/EncodedBuffer.cpp
@@ -15,6 +15,7 @@
*/
#include <android/util/EncodedBuffer.h>
+#include <android/util/protobuf.h>
#include <stdlib.h>
@@ -52,19 +53,21 @@
return mOffset;
}
-void
+EncodedBuffer::Pointer*
EncodedBuffer::Pointer::move(size_t amt)
{
size_t newOffset = mOffset + amt;
mIndex += newOffset / mChunkSize;
mOffset = newOffset % mChunkSize;
+ return this;
}
-void
+EncodedBuffer::Pointer*
EncodedBuffer::Pointer::rewind()
{
mIndex = 0;
mOffset = 0;
+ return this;
}
EncodedBuffer::Pointer
@@ -86,6 +89,7 @@
{
mChunkSize = chunkSize == 0 ? BUFFER_SIZE : chunkSize;
mWp = Pointer(mChunkSize);
+ mEp = Pointer(mChunkSize);
}
EncodedBuffer::~EncodedBuffer()
@@ -137,28 +141,136 @@
return mChunkSize - mWp.offset();
}
+void
+EncodedBuffer::writeRawByte(uint8_t val)
+{
+ *writeBuffer() = val;
+ mWp.move();
+}
+
size_t
-EncodedBuffer::writeRawVarint(uint32_t val)
+EncodedBuffer::writeRawVarint64(uint64_t val)
{
size_t size = 0;
while (true) {
size++;
if ((val & ~0x7F) == 0) {
- *writeBuffer() = (uint8_t) val;
- mWp.move();
+ writeRawByte((uint8_t) val);
return size;
} else {
- *writeBuffer() = (uint8_t)((val & 0x7F) | 0x80);
- mWp.move();
+ writeRawByte((uint8_t)((val & 0x7F) | 0x80));
val >>= 7;
}
}
}
size_t
+EncodedBuffer::writeRawVarint32(uint32_t val)
+{
+ uint64_t v =(uint64_t)val;
+ return writeRawVarint64(v);
+}
+
+void
+EncodedBuffer::writeRawFixed32(uint32_t val)
+{
+ writeRawByte((uint8_t) val);
+ writeRawByte((uint8_t) (val>>8));
+ writeRawByte((uint8_t) (val>>16));
+ writeRawByte((uint8_t) (val>>24));
+}
+
+void
+EncodedBuffer::writeRawFixed64(uint64_t val)
+{
+ writeRawByte((uint8_t) val);
+ writeRawByte((uint8_t) (val>>8));
+ writeRawByte((uint8_t) (val>>16));
+ writeRawByte((uint8_t) (val>>24));
+ writeRawByte((uint8_t) (val>>32));
+ writeRawByte((uint8_t) (val>>40));
+ writeRawByte((uint8_t) (val>>48));
+ writeRawByte((uint8_t) (val>>56));
+}
+
+size_t
EncodedBuffer::writeHeader(uint32_t fieldId, uint8_t wireType)
{
- return writeRawVarint((fieldId << 3) | wireType);
+ return writeRawVarint32((fieldId << FIELD_ID_SHIFT) | wireType);
+}
+
+/******************************** Edit APIs ************************************************/
+EncodedBuffer::Pointer*
+EncodedBuffer::ep()
+{
+ return &mEp;
+}
+
+uint8_t
+EncodedBuffer::readRawByte()
+{
+ uint8_t val = *at(mEp);
+ mEp.move();
+ return val;
+}
+
+uint64_t
+EncodedBuffer::readRawVarint()
+{
+ uint64_t val = 0, shift = 0;
+ size_t start = mEp.pos();
+ while (true) {
+ uint8_t byte = readRawByte();
+ val += (byte & 0x7F) << shift;
+ if ((byte & 0x80) == 0) break;
+ shift += 7;
+ }
+ return val;
+}
+
+uint32_t
+EncodedBuffer::readRawFixed32()
+{
+ uint32_t val = 0;
+ for (auto i=0; i<32; i+=8) {
+ val += (uint32_t)readRawByte() << i;
+ }
+ return val;
+}
+
+uint64_t
+EncodedBuffer::readRawFixed64()
+{
+ uint64_t val = 0;
+ for (auto i=0; i<64; i+=8) {
+ val += (uint64_t)readRawByte() << i;
+ }
+ return val;
+}
+
+void
+EncodedBuffer::editRawFixed32(size_t pos, uint32_t val)
+{
+ size_t oldPos = mEp.pos();
+ mEp.rewind()->move(pos);
+ for (auto i=0; i<32; i+=8) {
+ *at(mEp) = (uint8_t) (val >> i);
+ mEp.move();
+ }
+ mEp.rewind()->move(oldPos);
+}
+
+void
+EncodedBuffer::copy(size_t srcPos, size_t size)
+{
+ if (size == 0) return;
+ Pointer cp(mChunkSize);
+ cp.move(srcPos);
+
+ while (cp.pos() < srcPos + size) {
+ writeRawByte(*at(cp));
+ cp.move();
+ }
}
/********************************* Read APIs ************************************************/
@@ -220,10 +332,10 @@
return res;
}
-uint32_t
+uint64_t
EncodedBuffer::iterator::readRawVarint()
{
- uint32_t val = 0, shift = 0;
+ uint64_t val = 0, shift = 0;
while (true) {
uint8_t byte = next();
val += (byte & 0x7F) << shift;
diff --git a/libs/protoutil/src/ProtoOutputStream.cpp b/libs/protoutil/src/ProtoOutputStream.cpp
new file mode 100644
index 0000000..e9ca0dc
--- /dev/null
+++ b/libs/protoutil/src/ProtoOutputStream.cpp
@@ -0,0 +1,652 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "libprotoutil"
+
+#include <android/util/protobuf.h>
+#include <android/util/ProtoOutputStream.h>
+#include <cutils/log.h>
+#include <cstring>
+
+namespace android {
+namespace util {
+
+/**
+ * Position of the field type in a (long long) fieldId.
+ */
+const uint64_t FIELD_TYPE_SHIFT = 32;
+
+/**
+ * Mask for the field types stored in a fieldId. Leaves a whole
+ * byte for future expansion, even though there are currently only 17 types.
+ */
+const uint64_t FIELD_TYPE_MASK = 0x0ffULL << FIELD_TYPE_SHIFT;
+
+const uint64_t FIELD_TYPE_UNKNOWN = 0;
+const uint64_t TYPE_DOUBLE = 1ULL << FIELD_TYPE_SHIFT; // double, exactly eight bytes on the wire.
+const uint64_t TYPE_FLOAT = 2ULL << FIELD_TYPE_SHIFT; // float, exactly four bytes on the wire.
+const uint64_t TYPE_INT64 = 3ULL << FIELD_TYPE_SHIFT; // int64, varint on the wire. Negative numbers
+ // take 10 bytes. Use TYPE_SINT64 if negative
+ // values are likely.
+const uint64_t TYPE_UINT64 = 4ULL << FIELD_TYPE_SHIFT; // uint64, varint on the wire.
+const uint64_t TYPE_INT32 = 5ULL << FIELD_TYPE_SHIFT; // int32, varint on the wire. Negative numbers
+ // take 10 bytes. Use TYPE_SINT32 if negative
+ // values are likely.
+const uint64_t TYPE_FIXED64 = 6ULL << FIELD_TYPE_SHIFT; // uint64, exactly eight bytes on the wire.
+const uint64_t TYPE_FIXED32 = 7ULL << FIELD_TYPE_SHIFT; // uint32, exactly four bytes on the wire.
+const uint64_t TYPE_BOOL = 8ULL << FIELD_TYPE_SHIFT; // bool, varint on the wire.
+const uint64_t TYPE_STRING = 9ULL << FIELD_TYPE_SHIFT; // UTF-8 text.
+const uint64_t TYPE_GROUP = 10ULL << FIELD_TYPE_SHIFT; // Tag-delimited message. Deprecated.
+const uint64_t TYPE_MESSAGE = 11ULL << FIELD_TYPE_SHIFT; // Length-delimited message.
+
+const uint64_t TYPE_BYTES = 12ULL << FIELD_TYPE_SHIFT; // Arbitrary byte array.
+const uint64_t TYPE_UINT32 = 13ULL << FIELD_TYPE_SHIFT; // uint32, varint on the wire
+const uint64_t TYPE_ENUM = 14ULL << FIELD_TYPE_SHIFT; // Enum, varint on the wire
+const uint64_t TYPE_SFIXED32 = 15ULL << FIELD_TYPE_SHIFT; // int32, exactly four bytes on the wire
+const uint64_t TYPE_SFIXED64 = 16ULL << FIELD_TYPE_SHIFT; // int64, exactly eight bytes on the wire
+const uint64_t TYPE_SINT32 = 17ULL << FIELD_TYPE_SHIFT; // int32, ZigZag-encoded varint on the wire
+const uint64_t TYPE_SINT64 = 18ULL << FIELD_TYPE_SHIFT; // int64, ZigZag-encoded varint on the wire
+
+//
+// FieldId flags for whether the field is single, repeated or packed.
+// TODO: packed is not supported yet.
+//
+const uint64_t FIELD_COUNT_SHIFT = 40;
+const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_UNKNOWN = 0;
+const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_PACKED = 4ULL << FIELD_COUNT_SHIFT;
+
+ProtoOutputStream::ProtoOutputStream(int fd)
+ :mBuffer(),
+ mFd(fd),
+ mCopyBegin(0),
+ mCompact(false),
+ mDepth(0),
+ mObjectId(0),
+ mExpectedObjectToken(0LL)
+{
+}
+
+ProtoOutputStream::~ProtoOutputStream()
+{
+}
+
+bool
+ProtoOutputStream::write(uint64_t fieldId, double val)
+{
+ if (mCompact) return false;
+ const uint32_t id = (uint32_t)fieldId;
+ switch (fieldId & FIELD_TYPE_MASK) {
+ case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
+ case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
+ case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
+ case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
+ case TYPE_INT32: writeInt32Impl(id, (int)val); break;
+ case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
+ case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
+ case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
+ case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
+ case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
+ case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
+ case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
+ default:
+ ALOGW("Field type %d is not supported when writing double val.",
+ (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+ return false;
+ }
+ return true;
+}
+
+bool
+ProtoOutputStream::write(uint64_t fieldId, float val)
+{
+ if (mCompact) return false;
+ const uint32_t id = (uint32_t)fieldId;
+ switch (fieldId & FIELD_TYPE_MASK) {
+ case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
+ case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
+ case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
+ case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
+ case TYPE_INT32: writeInt32Impl(id, (int)val); break;
+ case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
+ case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
+ case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
+ case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
+ case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
+ case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
+ case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
+ default:
+ ALOGW("Field type %d is not supported when writing float val.",
+ (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+ return false;
+ }
+ return true;
+}
+
+bool
+ProtoOutputStream::write(uint64_t fieldId, int val)
+{
+ if (mCompact) return false;
+ const uint32_t id = (uint32_t)fieldId;
+ switch (fieldId & FIELD_TYPE_MASK) {
+ case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
+ case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
+ case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
+ case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
+ case TYPE_INT32: writeInt32Impl(id, (int)val); break;
+ case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
+ case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
+ case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
+ case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
+ case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
+ case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
+ case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
+ case TYPE_ENUM: writeEnumImpl(id, (int)val); break;
+ case TYPE_BOOL: writeBoolImpl(id, val != 0); break;
+ default:
+ ALOGW("Field type %d is not supported when writing int val.",
+ (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+ return false;
+ }
+ return true;
+}
+
+bool
+ProtoOutputStream::write(uint64_t fieldId, long long val)
+{
+ if (mCompact) return false;
+ const uint32_t id = (uint32_t)fieldId;
+ switch (fieldId & FIELD_TYPE_MASK) {
+ case TYPE_DOUBLE: writeDoubleImpl(id, (double)val); break;
+ case TYPE_FLOAT: writeFloatImpl(id, (float)val); break;
+ case TYPE_INT64: writeInt64Impl(id, (long long)val); break;
+ case TYPE_UINT64: writeUint64Impl(id, (uint64_t)val); break;
+ case TYPE_INT32: writeInt32Impl(id, (int)val); break;
+ case TYPE_FIXED64: writeFixed64Impl(id, (uint64_t)val); break;
+ case TYPE_FIXED32: writeFixed32Impl(id, (uint32_t)val); break;
+ case TYPE_UINT32: writeUint32Impl(id, (uint32_t)val); break;
+ case TYPE_SFIXED32: writeSFixed32Impl(id, (int)val); break;
+ case TYPE_SFIXED64: writeSFixed64Impl(id, (long long)val); break;
+ case TYPE_SINT32: writeZigzagInt32Impl(id, (int)val); break;
+ case TYPE_SINT64: writeZigzagInt64Impl(id, (long long)val); break;
+ case TYPE_ENUM: writeEnumImpl(id, (int)val); break;
+ case TYPE_BOOL: writeBoolImpl(id, val != 0); break;
+ default:
+ ALOGW("Field type %d is not supported when writing long long val.",
+ (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+ return false;
+ }
+ return true;
+}
+
+bool
+ProtoOutputStream::write(uint64_t fieldId, bool val)
+{
+ if (mCompact) return false;
+ const uint32_t id = (uint32_t)fieldId;
+ switch (fieldId & FIELD_TYPE_MASK) {
+ case TYPE_BOOL:
+ writeBoolImpl(id, val);
+ return true;
+ default:
+ ALOGW("Field type %d is not supported when writing bool val.",
+ (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+ return false;
+ }
+}
+
+bool
+ProtoOutputStream::write(uint64_t fieldId, string val)
+{
+ if (mCompact) return false;
+ const uint32_t id = (uint32_t)fieldId;
+ switch (fieldId & FIELD_TYPE_MASK) {
+ case TYPE_STRING:
+ writeUtf8StringImpl(id, val.c_str(), val.size());
+ return true;
+ default:
+ ALOGW("Field type %d is not supported when writing string val.",
+ (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+ return false;
+ }
+}
+
+bool
+ProtoOutputStream::write(uint64_t fieldId, const char* val)
+{
+ if (mCompact) return false;
+ const uint32_t id = (uint32_t)fieldId;
+ int size = 0;
+ while (val[size] != '\0') size++;
+ switch (fieldId & FIELD_TYPE_MASK) {
+ case TYPE_STRING:
+ writeUtf8StringImpl(id, val, size);
+ return true;
+ default:
+ ALOGW("Field type %d is not supported when writing char[] val.",
+ (int)((fieldId & FIELD_TYPE_MASK) >> FIELD_TYPE_SHIFT));
+ return false;
+ }
+}
+
+/**
+ * Make a token.
+ * Bits 61-63 - tag size (So we can go backwards later if the object had not data)
+ * - 3 bits, max value 7, max value needed 5
+ * Bit 60 - true if the object is repeated
+ * Bits 59-51 - depth (For error checking)
+ * - 9 bits, max value 512, when checking, value is masked (if we really
+ * are more than 512 levels deep)
+ * Bits 32-50 - objectId (For error checking)
+ * - 19 bits, max value 524,288. that's a lot of objects. IDs will wrap
+ * because of the overflow, and only the tokens are compared.
+ * Bits 0-31 - offset of the first size field in the buffer.
+ */
+long long
+makeToken(int tagSize, bool repeated, int depth, int objectId, int sizePos) {
+ return ((0x07L & (long long)tagSize) << 61)
+ | (repeated ? (1LL << 60) : 0)
+ | (0x01ffL & (long long)depth) << 51
+ | (0x07ffffL & (long long)objectId) << 32
+ | (0x0ffffffffL & (long long)sizePos);
+}
+
+/**
+ * Get the encoded tag size from the token.
+ */
+static int getTagSizeFromToken(long long token) {
+ return (int)(0x7 & (token >> 61));
+}
+
+/**
+ * Get the nesting depth of startObject calls from the token.
+ */
+static int getDepthFromToken(long long token) {
+ return (int)(0x01ff & (token >> 51));
+}
+
+/**
+ * Get the location of the childRawSize (the first 32 bit size field) in this object.
+ */
+static int getSizePosFromToken(long long token) {
+ return (int)token;
+}
+
+long long
+ProtoOutputStream::start(uint64_t fieldId)
+{
+ if ((fieldId & FIELD_TYPE_MASK) != TYPE_MESSAGE) {
+ ALOGE("Can't call start for non-message type field: 0x%llx", (long long)fieldId);
+ return 0;
+ }
+
+ uint32_t id = (uint32_t)fieldId;
+ mBuffer.writeHeader(id, WIRE_TYPE_LENGTH_DELIMITED);
+
+ size_t sizePos = mBuffer.wp()->pos();
+
+ mDepth++;
+ mObjectId++;
+ mBuffer.writeRawFixed64(mExpectedObjectToken); // push previous token into stack.
+
+ mExpectedObjectToken = makeToken(get_varint_size(id),
+ (bool)(fieldId & FIELD_COUNT_REPEATED), mDepth, mObjectId, sizePos);
+ return mExpectedObjectToken;
+}
+
+void
+ProtoOutputStream::end(long long token)
+{
+ if (token != mExpectedObjectToken) {
+ ALOGE("Unexpected token: 0x%llx, should be 0x%llx", token, mExpectedObjectToken);
+ return;
+ }
+
+ int depth = getDepthFromToken(token);
+ if (depth != (mDepth & 0x01ff)) {
+ ALOGE("Unexpected depth: %d, should be %d", depth, mDepth);
+ return;
+ }
+ mDepth--;
+
+ int sizePos = getSizePosFromToken(token);
+ // number of bytes written in this start-end session.
+ int childRawSize = mBuffer.wp()->pos() - sizePos - 8;
+
+ // retrieve the old token from stack.
+ mBuffer.ep()->rewind()->move(sizePos);
+ mExpectedObjectToken = mBuffer.readRawFixed64();
+
+ // If raw size is larger than 0, write the negative value here to indicate a compact is needed.
+ if (childRawSize > 0) {
+ mBuffer.editRawFixed32(sizePos, -childRawSize);
+ mBuffer.editRawFixed32(sizePos+4, -1);
+ } else {
+ // reset wp which erase the header tag of the message when its size is 0.
+ mBuffer.wp()->rewind()->move(sizePos - getTagSizeFromToken(token));
+ }
+}
+
+bool
+ProtoOutputStream::compact() {
+ if (mCompact) return true;
+ if (mDepth != 0) {
+ ALOGE("Can't compact when depth(%d) is not zero. Missing calls to end.", mDepth);
+ return false;
+ }
+ // record the size of the original buffer.
+ size_t rawBufferSize = mBuffer.size();
+ if (rawBufferSize == 0) return true; // nothing to do if the buffer is empty;
+
+ // reset edit pointer and recursively compute encoded size of messages.
+ mBuffer.ep()->rewind();
+ if (editEncodedSize(rawBufferSize) == 0) {
+ ALOGE("Failed to editEncodedSize.");
+ return false;
+ }
+
+ // reset both edit pointer and write pointer, and compact recursively.
+ mBuffer.ep()->rewind();
+ mBuffer.wp()->rewind();
+ if (!compactSize(rawBufferSize)) {
+ ALOGE("Failed to compactSize.");
+ return false;
+ }
+ // copy the reset to the buffer.
+ if (mCopyBegin < rawBufferSize) {
+ mBuffer.copy(mCopyBegin, rawBufferSize - mCopyBegin);
+ }
+
+ // mark true means it is not legal to write to this ProtoOutputStream anymore
+ mCompact = true;
+ return true;
+}
+
+/**
+ * First compaction pass. Iterate through the data, and fill in the
+ * nested object sizes so the next pass can compact them.
+ */
+size_t
+ProtoOutputStream::editEncodedSize(size_t rawSize)
+{
+ size_t objectStart = mBuffer.ep()->pos();
+ size_t objectEnd = objectStart + rawSize;
+ size_t encodedSize = 0;
+ int childRawSize, childEncodedSize;
+ size_t childEncodedSizePos;
+
+ while (mBuffer.ep()->pos() < objectEnd) {
+ uint32_t tag = (uint32_t)mBuffer.readRawVarint();
+ encodedSize += get_varint_size(tag);
+ switch (read_wire_type(tag)) {
+ case WIRE_TYPE_VARINT:
+ do {
+ encodedSize++;
+ } while ((mBuffer.readRawByte() & 0x80) != 0);
+ break;
+ case WIRE_TYPE_FIXED64:
+ encodedSize += 8;
+ mBuffer.ep()->move(8);
+ break;
+ case WIRE_TYPE_LENGTH_DELIMITED:
+ childRawSize = (int)mBuffer.readRawFixed32();
+ childEncodedSizePos = mBuffer.ep()->pos();
+ childEncodedSize = (int)mBuffer.readRawFixed32();
+ if (childRawSize >= 0 && childRawSize == childEncodedSize) {
+ mBuffer.ep()->move(childRawSize);
+ } else if (childRawSize < 0 && childEncodedSize == -1){
+ childEncodedSize = editEncodedSize(-childRawSize);
+ mBuffer.editRawFixed32(childEncodedSizePos, childEncodedSize);
+ } else {
+ ALOGE("Bad raw or encoded values: raw=%d, encoded=%d at %zu",
+ childRawSize, childEncodedSize, childEncodedSizePos);
+ return 0;
+ }
+ encodedSize += get_varint_size(childEncodedSize) + childEncodedSize;
+ break;
+ case WIRE_TYPE_FIXED32:
+ encodedSize += 4;
+ mBuffer.ep()->move(4);
+ break;
+ default:
+ ALOGE("Unexpected wire type %d in editEncodedSize at [%zu, %zu]",
+ read_wire_type(tag), objectStart, objectEnd);
+ return 0;
+ }
+ }
+ return encodedSize;
+}
+
+/**
+ * Second compaction pass. Iterate through the data, and copy the data
+ * forward in the buffer, converting the pairs of uint32s into a single
+ * unsigned varint of the size.
+ */
+bool
+ProtoOutputStream::compactSize(size_t rawSize)
+{
+ size_t objectStart = mBuffer.ep()->pos();
+ size_t objectEnd = objectStart + rawSize;
+ int childRawSize, childEncodedSize;
+
+ while (mBuffer.ep()->pos() < objectEnd) {
+ uint32_t tag = (uint32_t)mBuffer.readRawVarint();
+ switch (read_wire_type(tag)) {
+ case WIRE_TYPE_VARINT:
+ while ((mBuffer.readRawByte() & 0x80) != 0) {}
+ break;
+ case WIRE_TYPE_FIXED64:
+ mBuffer.ep()->move(8);
+ break;
+ case WIRE_TYPE_LENGTH_DELIMITED:
+ mBuffer.copy(mCopyBegin, mBuffer.ep()->pos() - mCopyBegin);
+
+ childRawSize = (int)mBuffer.readRawFixed32();
+ childEncodedSize = (int)mBuffer.readRawFixed32();
+ mCopyBegin = mBuffer.ep()->pos();
+
+ // write encoded size to buffer.
+ mBuffer.writeRawVarint32(childEncodedSize);
+ if (childRawSize >= 0 && childRawSize == childEncodedSize) {
+ mBuffer.ep()->move(childEncodedSize);
+ } else if (childRawSize < 0){
+ if (!compactSize(-childRawSize)) return false;
+ } else {
+ ALOGE("Bad raw or encoded values: raw=%d, encoded=%d",
+ childRawSize, childEncodedSize);
+ return false;
+ }
+ break;
+ case WIRE_TYPE_FIXED32:
+ mBuffer.ep()->move(4);
+ break;
+ default:
+ ALOGE("Unexpected wire type %d in compactSize at [%zu, %zu]",
+ read_wire_type(tag), objectStart, objectEnd);
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool write_all(int fd, uint8_t const* buf, size_t size)
+{
+ while (size > 0) {
+ ssize_t amt = ::write(fd, buf, size);
+ if (amt < 0) {
+ return false;
+ }
+ size -= amt;
+ buf += amt;
+ }
+ return true;
+}
+
+bool
+ProtoOutputStream::flush()
+{
+ if (mFd < 0) return false;
+ if (!compact()) return false;
+
+ EncodedBuffer::iterator it = mBuffer.begin();
+ while (it.readBuffer() != NULL) {
+ if (!write_all(mFd, it.readBuffer(), it.currentToRead())) return false;
+ it.rp()->move(it.currentToRead());
+ }
+ return true;
+}
+
+
+// =========================================================================
+// Private functions
+
+/**
+ * bit_cast
+ */
+template <class From, class To>
+inline To bit_cast(From const &from) {
+ To to;
+ memcpy(&to, &from, sizeof(to));
+ return to;
+}
+
+inline void
+ProtoOutputStream::writeDoubleImpl(uint32_t id, double val)
+{
+ if (val == 0.0) return;
+ mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
+ mBuffer.writeRawFixed64(bit_cast<double, uint64_t>(val));
+}
+
+inline void
+ProtoOutputStream::writeFloatImpl(uint32_t id, float val)
+{
+ if (val == 0.0) return;
+ mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
+ mBuffer.writeRawFixed32(bit_cast<float, uint32_t>(val));
+}
+
+inline void
+ProtoOutputStream::writeInt64Impl(uint32_t id, long long val)
+{
+ if (val == 0) return;
+ mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
+ mBuffer.writeRawVarint64((uint64_t)val);
+}
+
+inline void
+ProtoOutputStream::writeInt32Impl(uint32_t id, int val)
+{
+ if (val == 0) return;
+ mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
+ mBuffer.writeRawVarint32((uint32_t)val);
+}
+
+inline void
+ProtoOutputStream::writeUint64Impl(uint32_t id, uint64_t val)
+{
+ if (val == 0) return;
+ mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
+ mBuffer.writeRawVarint64(val);
+}
+
+inline void
+ProtoOutputStream::writeUint32Impl(uint32_t id, uint32_t val)
+{
+ if (val == 0) return;
+ mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
+ mBuffer.writeRawVarint32(val);
+}
+
+inline void
+ProtoOutputStream::writeFixed64Impl(uint32_t id, uint64_t val)
+{
+ if (val == 0) return;
+ mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
+ mBuffer.writeRawFixed64(val);
+}
+
+inline void
+ProtoOutputStream::writeFixed32Impl(uint32_t id, uint32_t val)
+{
+ if (val == 0) return;
+ mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
+ mBuffer.writeRawFixed32(val);
+}
+
+inline void
+ProtoOutputStream::writeSFixed64Impl(uint32_t id, long long val)
+{
+ if (val == 0) return;
+ mBuffer.writeHeader(id, WIRE_TYPE_FIXED64);
+ mBuffer.writeRawFixed64((uint64_t)val);
+}
+
+inline void
+ProtoOutputStream::writeSFixed32Impl(uint32_t id, int val)
+{
+ if (val == 0) return;
+ mBuffer.writeHeader(id, WIRE_TYPE_FIXED32);
+ mBuffer.writeRawFixed32((uint32_t)val);
+}
+
+inline void
+ProtoOutputStream::writeZigzagInt64Impl(uint32_t id, long long val)
+{
+ if (val == 0) return;
+ mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
+ mBuffer.writeRawVarint64((val << 1) ^ (val >> 63));
+}
+
+inline void
+ProtoOutputStream::writeZigzagInt32Impl(uint32_t id, int val)
+{
+ if (val == 0) return;
+ mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
+ mBuffer.writeRawVarint32((val << 1) ^ (val >> 31));
+}
+
+inline void
+ProtoOutputStream::writeEnumImpl(uint32_t id, int val)
+{
+ mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
+ mBuffer.writeRawVarint32((uint32_t) val);
+}
+
+inline void
+ProtoOutputStream::writeBoolImpl(uint32_t id, bool val)
+{
+ if (!val) return;
+ mBuffer.writeHeader(id, WIRE_TYPE_VARINT);
+ mBuffer.writeRawVarint32(val ? 1 : 0);
+}
+
+inline void
+ProtoOutputStream::writeUtf8StringImpl(uint32_t id, const char* val, size_t size)
+{
+ if (val == NULL || size == 0) return;
+ mBuffer.writeHeader(id, WIRE_TYPE_LENGTH_DELIMITED);
+ mBuffer.writeRawFixed32(size);
+ mBuffer.writeRawFixed32(size);
+ for (size_t i=0; i<size; i++) {
+ mBuffer.writeRawByte((uint8_t)val[i]);
+ }
+}
+
+} // util
+} // android
+
diff --git a/libs/protoutil/src/protobuf.cpp b/libs/protoutil/src/protobuf.cpp
index ec5325c..1c7eef9 100644
--- a/libs/protoutil/src/protobuf.cpp
+++ b/libs/protoutil/src/protobuf.cpp
@@ -22,17 +22,28 @@
uint8_t
read_wire_type(uint32_t varint)
{
- return (uint8_t) (varint & 0x07);
+ return (uint8_t) (varint & WIRE_TYPE_MASK);
}
uint32_t
read_field_id(uint32_t varint)
{
- return varint >> 3;
+ return varint >> FIELD_ID_SHIFT;
+}
+
+size_t
+get_varint_size(uint64_t varint)
+{
+ size_t size = 1;
+ while ((varint & ~0x7F)) {
+ size++;
+ varint >>= 7;
+ }
+ return size;
}
uint8_t*
-write_raw_varint(uint8_t* buf, uint32_t val)
+write_raw_varint(uint8_t* buf, uint64_t val)
{
uint8_t* p = buf;
while (true) {
@@ -49,7 +60,7 @@
uint8_t*
write_length_delimited_tag_header(uint8_t* buf, uint32_t fieldId, size_t size)
{
- buf = write_raw_varint(buf, (fieldId << 3) | 2);
+ buf = write_raw_varint(buf, (fieldId << FIELD_ID_SHIFT) | WIRE_TYPE_LENGTH_DELIMITED);
buf = write_raw_varint(buf, size);
return buf;
}
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 1f5edfa..ba41a7b 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -2584,22 +2584,21 @@
ExifAttribute.createUShort(Integer.parseInt(height), mExifByteOrder));
}
- // Note that the rotation angle from MediaMetadataRetriever for heif images
- // are CCW, while rotation in ExifInterface orientations are CW.
String rotation = retriever.extractMetadata(
MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
if (rotation != null) {
int orientation = ExifInterface.ORIENTATION_NORMAL;
+ // all rotation angles in CW
switch (Integer.parseInt(rotation)) {
case 90:
- orientation = ExifInterface.ORIENTATION_ROTATE_270;
+ orientation = ExifInterface.ORIENTATION_ROTATE_90;
break;
case 180:
orientation = ExifInterface.ORIENTATION_ROTATE_180;
break;
case 270:
- orientation = ExifInterface.ORIENTATION_ROTATE_90;
+ orientation = ExifInterface.ORIENTATION_ROTATE_270;
break;
}
diff --git a/media/java/android/media/tv/ITvInputHardware.aidl b/media/java/android/media/tv/ITvInputHardware.aidl
index 96223ba..94c1013 100644
--- a/media/java/android/media/tv/ITvInputHardware.aidl
+++ b/media/java/android/media/tv/ITvInputHardware.aidl
@@ -40,12 +40,6 @@
void setStreamVolume(float volume);
/**
- * Dispatch key event to HDMI service. The events would be automatically converted to
- * HDMI CEC commands. If the hardware is not representing an HDMI port, this method will fail.
- */
- boolean dispatchKeyEventToHdmi(in KeyEvent event);
-
- /**
* Override default audio sink from audio policy. When override is on, it is
* TvInputService's responsibility to adjust to audio configuration change
* (for example, when the audio sink becomes unavailable or more desirable
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index d7a9ede..fd1f2cf 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -2590,12 +2590,9 @@
}
}
+ /** @removed */
public boolean dispatchKeyEventToHdmi(KeyEvent event) {
- try {
- return mInterface.dispatchKeyEventToHdmi(event);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
+ return false;
}
public void overrideAudioSink(int audioType, String audioAddress, int samplingRate,
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 7f6980d..28827e6 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -24,7 +24,7 @@
#include <media/IMediaHTTPService.h>
#include <media/MediaPlayerInterface.h>
#include <media/MediaAnalyticsItem.h>
-#include <media/stagefright/Utils.h> // for FOURCC definition
+#include <media/stagefright/foundation/ByteUtils.h> // for FOURCC definition
#include <stdio.h>
#include <assert.h>
#include <limits.h>
diff --git a/native/graphics/jni/Android.bp b/native/graphics/jni/Android.bp
index d456950..0704e35 100644
--- a/native/graphics/jni/Android.bp
+++ b/native/graphics/jni/Android.bp
@@ -12,6 +12,32 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+cc_library_shared {
+ name: "libjnigraphics",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+
+ // our source files
+ //
+ srcs: ["bitmap.cpp"],
+
+ shared_libs: [
+ "libandroid_runtime",
+ ],
+
+ arch: {
+ arm: {
+ // TODO: This is to work around b/24465209. Remove after root cause is fixed
+ ldflags: ["-Wl,--hash-style=both"],
+ },
+ },
+}
+
// The headers module is in frameworks/native/Android.bp.
ndk_library {
name: "libjnigraphics",
diff --git a/native/graphics/jni/Android.mk b/native/graphics/jni/Android.mk
deleted file mode 100644
index 7a40e62..0000000
--- a/native/graphics/jni/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-BASE_PATH := $(call my-dir)
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# setup for skia optimizations
-#
-ifneq ($(ARCH_ARM_HAVE_VFP),true)
- LOCAL_CFLAGS += -DSK_SOFTWARE_FLOAT
-endif
-
-ifeq ($(ARCH_ARM_HAVE_NEON),true)
- LOCAL_CFLAGS += -D__ARM_HAVE_NEON
-endif
-
-# our source files
-#
-LOCAL_SRC_FILES:= \
- bitmap.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libandroid_runtime \
- libui \
- libandroidfw
-
-LOCAL_C_INCLUDES += \
- frameworks/base/core/jni/android/graphics
-
-LOCAL_MODULE:= libjnigraphics
-
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-
-# TODO: This is to work around b/24465209. Remove after root cause is fixed
-LOCAL_LDFLAGS_arm := -Wl,--hash-style=both
-
-include $(BUILD_SHARED_LIBRARY)
-
diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp
index bf5cabb..ff14832 100644
--- a/native/graphics/jni/bitmap.cpp
+++ b/native/graphics/jni/bitmap.cpp
@@ -15,7 +15,7 @@
*/
#include <android/bitmap.h>
-#include <Bitmap.h>
+#include <android/graphics/Bitmap.h>
int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
AndroidBitmapInfo* info) {
@@ -56,4 +56,3 @@
}
return ANDROID_BITMAP_RESULT_SUCCESS;
}
-
diff --git a/packages/CaptivePortalLogin/res/values-in/strings.xml b/packages/CaptivePortalLogin/res/values-in/strings.xml
index 7fa3a0a..f9f6481 100644
--- a/packages/CaptivePortalLogin/res/values-in/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-in/strings.xml
@@ -7,6 +7,6 @@
<string name="action_bar_label" msgid="917235635415966620">"Masuk ke jaringan"</string>
<string name="action_bar_title" msgid="5645564790486983117">"Login ke %1$s"</string>
<string name="ssl_error_warning" msgid="6653188881418638872">"Jaringan yang ingin Anda masuki mengalami masalah keamanan."</string>
- <string name="ssl_error_example" msgid="647898534624078900">"Misalnya, laman masuk mungkin bukan milik organisasi yang ditampilkan."</string>
+ <string name="ssl_error_example" msgid="647898534624078900">"Misalnya, halaman masuk mungkin bukan milik organisasi yang ditampilkan."</string>
<string name="ssl_error_continue" msgid="6492718244923937110">"Tetap lanjutkan melalui browser"</string>
</resources>
diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml
index f54b6fb4..291009e 100644
--- a/packages/ExtServices/AndroidManifest.xml
+++ b/packages/ExtServices/AndroidManifest.xml
@@ -42,6 +42,15 @@
</intent-filter>
</service>
+ <service android:name=".notification.Assistant"
+ android:label="@string/notification_assistant"
+ android:permission="android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.service.notification.NotificationAssistantService" />
+ </intent-filter>
+ </service>
+
<library android:name="android.ext.services"/>
</application>
diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml
index 531e517..a2e65bc 100644
--- a/packages/ExtServices/res/values/strings.xml
+++ b/packages/ExtServices/res/values/strings.xml
@@ -16,4 +16,7 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name">Android Services Library</string>
+
+ <string name="notification_assistant">Notification Assistant</string>
+ <string name="prompt_block_reason">Too many dismissals:views</string>
</resources>
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
new file mode 100644
index 0000000..f535368
--- /dev/null
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -0,0 +1,165 @@
+/**
+ * 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.ext.services.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+import static android.service.notification.NotificationListenerService.Ranking
+ .USER_SENTIMENT_NEGATIVE;
+
+import android.app.INotificationManager;
+import android.content.Context;
+import android.ext.services.R;
+import android.os.Bundle;
+import android.service.notification.Adjustment;
+import android.service.notification.NotificationAssistantService;
+import android.service.notification.NotificationStats;
+import android.service.notification.StatusBarNotification;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Slog;
+
+import java.util.ArrayList;
+
+/**
+ * Notification assistant that provides guidance on notification channel blocking
+ */
+public class Assistant extends NotificationAssistantService {
+ private static final String TAG = "ExtAssistant";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private static final ArrayList<Integer> DISMISS_WITH_PREJUDICE = new ArrayList<>();
+ static {
+ DISMISS_WITH_PREJUDICE.add(REASON_CANCEL);
+ DISMISS_WITH_PREJUDICE.add(REASON_LISTENER_CANCEL);
+ }
+
+ // key : impressions tracker
+ // TODO: persist across reboots
+ ArrayMap<String, ChannelImpressions> mkeyToImpressions = new ArrayMap<>();
+ // SBN key : channel id
+ ArrayMap<String, String> mLiveNotifications = new ArrayMap<>();
+
+ private Ranking mFakeRanking = null;
+
+ @Override
+ public Adjustment onNotificationEnqueued(StatusBarNotification sbn) {
+ if (DEBUG) Log.i(TAG, "ENQUEUED " + sbn.getKey());
+ return null;
+ }
+
+ @Override
+ public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
+ if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey());
+ try {
+ Ranking ranking = getRanking(sbn.getKey(), rankingMap);
+ if (ranking != null && ranking.getChannel() != null) {
+ String key = getKey(
+ sbn.getPackageName(), sbn.getUserId(), ranking.getChannel().getId());
+ ChannelImpressions ci = mkeyToImpressions.getOrDefault(key,
+ new ChannelImpressions());
+ if (ranking.getImportance() > IMPORTANCE_MIN && ci.shouldTriggerBlock()) {
+ adjustNotification(createNegativeAdjustment(
+ sbn.getPackageName(), sbn.getKey(), sbn.getUserId()));
+ }
+ mkeyToImpressions.put(key, ci);
+ mLiveNotifications.put(sbn.getKey(), ranking.getChannel().getId());
+ }
+ } catch (Throwable e) {
+ Log.e(TAG, "Error occurred processing post", e);
+ }
+ }
+
+ @Override
+ public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
+ NotificationStats stats, int reason) {
+ try {
+ String channelId = mLiveNotifications.remove(sbn.getKey());
+ String key = getKey(sbn.getPackageName(), sbn.getUserId(), channelId);
+ ChannelImpressions ci = mkeyToImpressions.getOrDefault(key, new ChannelImpressions());
+ if (stats.hasSeen()) {
+ ci.incrementViews();
+ }
+ if (DISMISS_WITH_PREJUDICE.contains(reason)
+ && !sbn.isAppGroup()
+ && !sbn.getNotification().isGroupChild()
+ && !stats.hasInteracted()
+ && stats.getDismissalSurface() != NotificationStats.DISMISSAL_AOD
+ && stats.getDismissalSurface() != NotificationStats.DISMISSAL_PEEK
+ && stats.getDismissalSurface() != NotificationStats.DISMISSAL_OTHER) {
+ if (DEBUG) Log.i(TAG, "increment dismissals");
+ ci.incrementDismissals();
+ } else {
+ if (DEBUG) Slog.i(TAG, "reset streak");
+ ci.resetStreak();
+ }
+ mkeyToImpressions.put(key, ci);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Error occurred processing removal", e);
+ }
+ }
+
+ @Override
+ public void onNotificationSnoozedUntilContext(StatusBarNotification sbn,
+ String snoozeCriterionId) {
+ }
+
+ @Override
+ public void onListenerConnected() {
+ if (DEBUG) Log.i(TAG, "CONNECTED");
+ try {
+ for (StatusBarNotification sbn : getActiveNotifications()) {
+ onNotificationPosted(sbn);
+ }
+ } catch (Throwable e) {
+ Log.e(TAG, "Error occurred on connection", e);
+ }
+ }
+
+ private String getKey(String pkg, int userId, String channelId) {
+ return pkg + "|" + userId + "|" + channelId;
+ }
+
+ private Ranking getRanking(String key, RankingMap rankingMap) {
+ if (mFakeRanking != null) {
+ return mFakeRanking;
+ }
+ Ranking ranking = new Ranking();
+ rankingMap.getRanking(key, ranking);
+ return ranking;
+ }
+
+ private Adjustment createNegativeAdjustment(String packageName, String key, int user) {
+ if (DEBUG) Log.d(TAG, "User probably doesn't want " + key);
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
+ return new Adjustment(packageName, key, signals,
+ getContext().getString(R.string.prompt_block_reason), user);
+ }
+
+ // for testing
+ protected void setFakeRanking(Ranking ranking) {
+ mFakeRanking = ranking;
+ }
+
+ protected void setNoMan(INotificationManager noMan) {
+ mNoMan = noMan;
+ }
+
+ protected void setContext(Context context) {
+ mSystemContext = context;
+ }
+}
\ No newline at end of file
diff --git a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java b/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
new file mode 100644
index 0000000..30567cc
--- /dev/null
+++ b/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
@@ -0,0 +1,137 @@
+/**
+ * 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.ext.services.notification;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+public final class ChannelImpressions implements Parcelable {
+ private static final String TAG = "ExtAssistant.CI";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ static final double DISMISS_TO_VIEW_RATIO_LIMIT = .8;
+ static final int STREAK_LIMIT = 2;
+
+ private int mDismissals = 0;
+ private int mViews = 0;
+ private int mStreak = 0;
+
+ public ChannelImpressions() {
+ }
+
+ public ChannelImpressions(int dismissals, int views) {
+ mDismissals = dismissals;
+ mViews = views;
+ }
+
+ protected ChannelImpressions(Parcel in) {
+ mDismissals = in.readInt();
+ mViews = in.readInt();
+ mStreak = in.readInt();
+ }
+
+ public int getStreak() {
+ return mStreak;
+ }
+
+ public int getDismissals() {
+ return mDismissals;
+ }
+
+ public int getViews() {
+ return mViews;
+ }
+
+ public void incrementDismissals() {
+ mDismissals++;
+ mStreak++;
+ }
+
+ public void incrementViews() {
+ mViews++;
+ }
+
+ public void resetStreak() {
+ mStreak = 0;
+ }
+
+ public boolean shouldTriggerBlock() {
+ if (getViews() == 0) {
+ return false;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "should trigger? " + getDismissals() + " " + getViews() + " " + getStreak());
+ }
+ return ((double) getDismissals() / getViews()) > DISMISS_TO_VIEW_RATIO_LIMIT
+ && getStreak() > STREAK_LIMIT;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mDismissals);
+ dest.writeInt(mViews);
+ dest.writeInt(mStreak);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<ChannelImpressions> CREATOR = new Creator<ChannelImpressions>() {
+ @Override
+ public ChannelImpressions createFromParcel(Parcel in) {
+ return new ChannelImpressions(in);
+ }
+
+ @Override
+ public ChannelImpressions[] newArray(int size) {
+ return new ChannelImpressions[size];
+ }
+ };
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ ChannelImpressions that = (ChannelImpressions) o;
+
+ if (mDismissals != that.mDismissals) return false;
+ if (mViews != that.mViews) return false;
+ return mStreak == that.mStreak;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mDismissals;
+ result = 31 * result + mViews;
+ result = 31 * result + mStreak;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("ChannelImpressions{");
+ sb.append("mDismissals=").append(mDismissals);
+ sb.append(", mViews=").append(mViews);
+ sb.append(", mStreak=").append(mStreak);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/packages/ExtServices/tests/Android.mk b/packages/ExtServices/tests/Android.mk
index cb3c352..92afbeb 100644
--- a/packages/ExtServices/tests/Android.mk
+++ b/packages/ExtServices/tests/Android.mk
@@ -12,7 +12,8 @@
mockito-target-minus-junit4 \
espresso-core \
truth-prebuilt \
- legacy-android-test
+ legacy-android-test \
+ testables
# Include all test java files.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
new file mode 100644
index 0000000..4e5e9f9
--- /dev/null
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java
@@ -0,0 +1,268 @@
+/**
+ * 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.ext.services.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+
+import static org.mockito.ArgumentMatchers.any;
+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;
+
+import android.app.INotificationManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.content.Intent;
+import android.ext.services.R;
+import android.os.UserHandle;
+import android.service.notification.Adjustment;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.NotificationStats;
+import android.service.notification.StatusBarNotification;
+import android.support.test.InstrumentationRegistry;
+import android.test.ServiceTestCase;
+import android.testing.TestableContext;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class AssistantTest extends ServiceTestCase<Assistant> {
+
+ private static final String PKG1 = "pkg1";
+ private static final int UID1 = 1;
+ private static final NotificationChannel P1C1 =
+ new NotificationChannel("one", "", IMPORTANCE_LOW);
+ private static final NotificationChannel P1C2 =
+ new NotificationChannel("p1c2", "", IMPORTANCE_DEFAULT);
+ private static final NotificationChannel P1C3 =
+ new NotificationChannel("p1c3", "", IMPORTANCE_MIN);
+ private static final String PKG2 = "pkg2";
+
+ private static final int UID2 = 2;
+ private static final NotificationChannel P2C1 =
+ new NotificationChannel("one", "", IMPORTANCE_LOW);
+
+ @Mock INotificationManager mNoMan;
+
+ Assistant mAssistant;
+
+ @Rule
+ public final TestableContext mContext =
+ new TestableContext(InstrumentationRegistry.getContext(), null);
+
+ public AssistantTest() {
+ super(Assistant.class);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ Intent startIntent =
+ new Intent("android.service.notification.NotificationAssistantService");
+ startIntent.setPackage("android.ext.services");
+ bindService(startIntent);
+ mAssistant = getService();
+ mAssistant.setNoMan(mNoMan);
+ }
+
+ private StatusBarNotification generateSbn(String pkg, int uid, NotificationChannel channel,
+ String tag, String groupKey) {
+ Notification n = new Notification.Builder(mContext, channel.getId())
+ .setContentTitle("foo")
+ .setGroup(groupKey)
+ .build();
+
+ StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, 0, tag, uid, uid, n,
+ UserHandle.SYSTEM, null, 0);
+
+ return sbn;
+ }
+
+ private Ranking generateRanking(StatusBarNotification sbn, NotificationChannel channel) {
+ Ranking mockRanking = mock(Ranking.class);
+ when(mockRanking.getChannel()).thenReturn(channel);
+ when(mockRanking.getImportance()).thenReturn(channel.getImportance());
+ when(mockRanking.getKey()).thenReturn(sbn.getKey());
+ when(mockRanking.getOverrideGroupKey()).thenReturn(null);
+ return mockRanking;
+ }
+
+ private void almostBlockChannel(String pkg, int uid, NotificationChannel channel) {
+ for (int i = 0; i < ChannelImpressions.STREAK_LIMIT; i++) {
+ dismissBadNotification(pkg, uid, channel, String.valueOf(i));
+ }
+ }
+
+ private void dismissBadNotification(String pkg, int uid, NotificationChannel channel,
+ String tag) {
+ StatusBarNotification sbn = generateSbn(pkg, uid, channel, tag, null);
+ mAssistant.setFakeRanking(generateRanking(sbn, channel));
+ mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+ mAssistant.setFakeRanking(mock(Ranking.class));
+ NotificationStats stats = new NotificationStats();
+ stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
+ stats.setSeen();
+ mAssistant.onNotificationRemoved(
+ sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
+ }
+
+ @Test
+ public void testNoAdjustmentForInitialPost() throws Exception {
+ StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, null, null);
+
+ mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
+ mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+
+ verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any());
+ }
+
+ @Test
+ public void testTriggerAdjustment() throws Exception {
+ almostBlockChannel(PKG1, UID1, P1C1);
+ dismissBadNotification(PKG1, UID1, P1C1, "trigger!");
+
+ StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null);
+ mAssistant.setFakeRanking(generateRanking(sbn, P1C1));
+ mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+
+ ArgumentCaptor<Adjustment> captor = ArgumentCaptor.forClass(Adjustment.class);
+ verify(mNoMan, times(1)).applyAdjustmentFromAssistant(any(), captor.capture());
+ assertEquals(sbn.getKey(), captor.getValue().getKey());
+ assertEquals(Ranking.USER_SENTIMENT_NEGATIVE,
+ captor.getValue().getSignals().getInt(Adjustment.KEY_USER_SENTIMENT));
+ }
+
+ @Test
+ public void testMinCannotTriggerAdjustment() throws Exception {
+ almostBlockChannel(PKG1, UID1, P1C3);
+ dismissBadNotification(PKG1, UID1, P1C3, "trigger!");
+
+ StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C3, "new one!", null);
+ mAssistant.setFakeRanking(generateRanking(sbn, P1C3));
+ mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+
+ verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any());
+ }
+
+ @Test
+ public void testGroupCannotTriggerAdjustment() throws Exception {
+ almostBlockChannel(PKG1, UID1, P1C1);
+
+ StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", "I HAVE A GROUP");
+ mAssistant.setFakeRanking(mock(Ranking.class));
+ NotificationStats stats = new NotificationStats();
+ stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
+ stats.setSeen();
+ mAssistant.onNotificationRemoved(
+ sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
+
+ sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null);
+ mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+
+ verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any());
+ }
+
+ @Test
+ public void testAodCannotTriggerAdjustment() throws Exception {
+ almostBlockChannel(PKG1, UID1, P1C1);
+
+ StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null);
+ mAssistant.setFakeRanking(mock(Ranking.class));
+ NotificationStats stats = new NotificationStats();
+ stats.setDismissalSurface(NotificationStats.DISMISSAL_AOD);
+ stats.setSeen();
+ mAssistant.onNotificationRemoved(
+ sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
+
+ sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null);
+ mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+
+ verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any());
+ }
+
+ @Test
+ public void testInteractedCannotTriggerAdjustment() throws Exception {
+ almostBlockChannel(PKG1, UID1, P1C1);
+
+ StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null);
+ mAssistant.setFakeRanking(mock(Ranking.class));
+ NotificationStats stats = new NotificationStats();
+ stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
+ stats.setSeen();
+ stats.setExpanded();
+ mAssistant.onNotificationRemoved(
+ sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL);
+
+ sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null);
+ mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+
+ verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any());
+ }
+
+ @Test
+ public void testAppDismissedCannotTriggerAdjustment() throws Exception {
+ almostBlockChannel(PKG1, UID1, P1C1);
+
+ StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null);
+ mAssistant.setFakeRanking(mock(Ranking.class));
+ NotificationStats stats = new NotificationStats();
+ stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE);
+ stats.setSeen();
+ mAssistant.onNotificationRemoved(
+ sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_APP_CANCEL);
+
+ sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null);
+ mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+
+ verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any());
+ }
+
+ @Test
+ public void testAppSeparation() throws Exception {
+ almostBlockChannel(PKG1, UID1, P1C1);
+ dismissBadNotification(PKG1, UID1, P1C1, "trigger!");
+
+ StatusBarNotification sbn = generateSbn(PKG2, UID2, P2C1, "new app!", null);
+ mAssistant.setFakeRanking(generateRanking(sbn, P2C1));
+ mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+
+ verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any());
+ }
+
+ @Test
+ public void testChannelSeparation() throws Exception {
+ almostBlockChannel(PKG1, UID1, P1C1);
+ dismissBadNotification(PKG1, UID1, P1C1, "trigger!");
+
+ StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C2, "new app!", null);
+ mAssistant.setFakeRanking(generateRanking(sbn, P1C2));
+ mAssistant.onNotificationPosted(sbn, mock(RankingMap.class));
+
+ verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any());
+ }
+}
diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java
new file mode 100644
index 0000000..a8c9fa3
--- /dev/null
+++ b/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java
@@ -0,0 +1,85 @@
+/**
+ * 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.ext.services.notification;
+
+import static android.ext.services.notification.ChannelImpressions.STREAK_LIMIT;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class ChannelImpressionsTest {
+
+ @Test
+ public void testNoResultNoBlock() {
+ ChannelImpressions ci = new ChannelImpressions();
+ assertFalse(ci.shouldTriggerBlock());
+ }
+
+ @Test
+ public void testNoStreakNoBlock() {
+ ChannelImpressions ci = new ChannelImpressions();
+
+ for (int i = 0; i < STREAK_LIMIT - 1; i++) {
+ ci.incrementViews();
+ ci.incrementDismissals();
+ }
+
+ assertFalse(ci.shouldTriggerBlock());
+ }
+
+ @Test
+ public void testNoStreakNoBlock_breakStreak() {
+ ChannelImpressions ci = new ChannelImpressions();
+
+ for (int i = 0; i < STREAK_LIMIT; i++) {
+ ci.incrementViews();
+ ci.incrementDismissals();
+ if (i == STREAK_LIMIT - 1) {
+ ci.resetStreak();
+ }
+ }
+
+ assertFalse(ci.shouldTriggerBlock());
+ }
+
+ @Test
+ public void testStreakBlock() {
+ ChannelImpressions ci = new ChannelImpressions();
+
+ for (int i = 0; i <= STREAK_LIMIT; i++) {
+ ci.incrementViews();
+ ci.incrementDismissals();
+ }
+
+ assertTrue(ci.shouldTriggerBlock());
+ }
+
+ @Test
+ public void testRatio_NoBlockEvenWithStreak() {
+ ChannelImpressions ci = new ChannelImpressions();
+
+ for (int i = 0; i < STREAK_LIMIT; i++) {
+ ci.incrementViews();
+ ci.incrementDismissals();
+ ci.incrementViews();
+ }
+
+ assertFalse(ci.shouldTriggerBlock());
+ }
+}
diff --git a/packages/PrintSpooler/res/values-vi/strings.xml b/packages/PrintSpooler/res/values-vi/strings.xml
index a181424..6e8c7a8 100644
--- a/packages/PrintSpooler/res/values-vi/strings.xml
+++ b/packages/PrintSpooler/res/values-vi/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_label" msgid="4469836075319831821">"Print Spooler"</string>
- <string name="more_options_button" msgid="2243228396432556771">"Thêm tùy chọn"</string>
+ <string name="more_options_button" msgid="2243228396432556771">"Tùy chọn khác"</string>
<string name="label_destination" msgid="9132510997381599275">"Đích"</string>
<string name="label_copies" msgid="3634531042822968308">"Bản sao"</string>
<string name="label_copies_summary" msgid="3861966063536529540">"Bản sao:"</string>
diff --git a/packages/PrintSpooler/tests/outofprocess/Android.mk b/packages/PrintSpooler/tests/outofprocess/Android.mk
index 3c02453..149be74 100644
--- a/packages/PrintSpooler/tests/outofprocess/Android.mk
+++ b/packages/PrintSpooler/tests/outofprocess/Android.mk
@@ -21,7 +21,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator mockito-target-minus-junit4
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator mockito-target-minus-junit4 print-test-util-lib
LOCAL_PACKAGE_NAME := PrintSpoolerOutOfProcessTests
LOCAL_COMPATIBILITY_SUITE := device-tests
diff --git a/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml b/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
index 4a05f6f..307cc93 100644
--- a/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
+++ b/packages/PrintSpooler/tests/outofprocess/AndroidManifest.xml
@@ -21,10 +21,12 @@
<application>
<uses-library android:name="android.test.runner" />
- <activity android:name=".PrintTestActivity"/>
+ <activity
+ android:name="android.print.test.PrintDocumentActivity"
+ android:theme="@style/NoAnimation" />
<service
- android:name=".mockservice.MockPrintService"
+ android:name="android.print.test.services.FirstPrintService"
android:permission="android.permission.BIND_PRINT_SERVICE">
<intent-filter>
@@ -37,13 +39,15 @@
</service>
<activity
- android:name=".mockservice.SettingsActivity"
+ android:name="android.print.test.services.SettingsActivity"
android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"
+ android:theme="@style/NoAnimation"
android:exported="true">
</activity>
<activity
- android:name=".mockservice.AddPrintersActivity"
+ android:name="android.print.test.services.AddPrintersActivity"
+ android:theme="@style/NoAnimation"
android:exported="true">
</activity>
diff --git a/packages/PrintSpooler/tests/outofprocess/res/values/themes.xml b/packages/PrintSpooler/tests/outofprocess/res/values/themes.xml
new file mode 100644
index 0000000..49eb257
--- /dev/null
+++ b/packages/PrintSpooler/tests/outofprocess/res/values/themes.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ 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.
+ -->
+<resources>
+ <style name="NoAnimation" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:windowAnimationStyle">@null</item>
+ </style>
+</resources>
diff --git a/packages/PrintSpooler/tests/outofprocess/res/xml/printservice.xml b/packages/PrintSpooler/tests/outofprocess/res/xml/printservice.xml
index 9eecf45..a6282b1 100644
--- a/packages/PrintSpooler/tests/outofprocess/res/xml/printservice.xml
+++ b/packages/PrintSpooler/tests/outofprocess/res/xml/printservice.xml
@@ -17,4 +17,4 @@
-->
<print-service xmlns:android="http://schemas.android.com/apk/res/android"
- android:addPrintersActivity="com.android.printspooler.outofprocess.tests.mockservice.AddPrintersActivity" />
+ android:addPrintersActivity="android.print.test.services.AddPrintersActivity" />
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/BasePrintTest.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/BasePrintTest.java
deleted file mode 100644
index 9a7f362..0000000
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/BasePrintTest.java
+++ /dev/null
@@ -1,275 +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.printspooler.outofprocess.tests;
-
-import static android.content.pm.PackageManager.GET_META_DATA;
-import static android.content.pm.PackageManager.GET_SERVICES;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doCallRealMethod;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.annotation.NonNull;
-import android.app.Instrumentation;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.CancellationSignal;
-import android.os.ParcelFileDescriptor;
-import android.print.PrintAttributes;
-import android.print.PrintDocumentAdapter;
-import android.print.PrintManager;
-import android.print.PrinterId;
-import android.printservice.CustomPrinterIconCallback;
-import android.printservice.PrintJob;
-import android.printservice.PrintService;
-import android.provider.Settings;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.uiautomator.UiDevice;
-
-import com.android.printspooler.outofprocess.tests.mockservice.PrintServiceCallbacks;
-import com.android.printspooler.outofprocess.tests.mockservice.PrinterDiscoverySessionCallbacks;
-import com.android.printspooler.outofprocess.tests.mockservice.StubbablePrinterDiscoverySession;
-
-import org.junit.After;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Rule;
-import org.mockito.stubbing.Answer;
-
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.List;
-
-/**
- * This is the base class for print tests.
- */
-abstract class BasePrintTest {
- protected static final long OPERATION_TIMEOUT = 30000;
- private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success";
- private static final int CURRENT_USER_ID = -2; // Mirrors UserHandle.USER_CURRENT
- private static String sDisabledPrintServicesBefore;
-
- private android.print.PrintJob mPrintJob;
-
- private static Instrumentation sInstrumentation;
- private static UiDevice sUiDevice;
-
- @Rule
- public ActivityTestRule<PrintTestActivity> mActivityRule =
- new ActivityTestRule<>(PrintTestActivity.class, false, true);
-
- /**
- * Return the UI device
- *
- * @return the UI device
- */
- public UiDevice getUiDevice() {
- return sUiDevice;
- }
-
- protected static Instrumentation getInstrumentation() {
- return sInstrumentation;
- }
-
- @BeforeClass
- public static void setUpClass() throws Exception {
- sInstrumentation = InstrumentationRegistry.getInstrumentation();
- assumeTrue(sInstrumentation.getContext().getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_PRINTING));
-
- sUiDevice = UiDevice.getInstance(sInstrumentation);
-
- // Make sure we start with a clean slate.
- clearPrintSpoolerData();
-
- disablePrintServices(sInstrumentation.getTargetContext().getPackageName());
-
- // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2
- // Dexmaker is used by mockito.
- System.setProperty("dexmaker.dexcache", getInstrumentation()
- .getTargetContext().getCacheDir().getPath());
- }
-
- @AfterClass
- public static void tearDownClass() throws Exception {
- enablePrintServices();
- }
-
- @Before
- public void unlockScreen() throws Exception {
- // Unlock screen.
- runShellCommand("input keyevent KEYCODE_WAKEUP");
- runShellCommand("wm dismiss-keyguard");
- }
-
- @After
- public void exitActivities() throws Exception {
- // Exit print spooler
- getUiDevice().pressBack();
- getUiDevice().pressBack();
- }
-
- protected android.print.PrintJob print(@NonNull final PrintDocumentAdapter adapter,
- final PrintAttributes attributes) {
- // Initiate printing as if coming from the app.
- getInstrumentation().runOnMainSync(() -> {
- PrintManager printManager = (PrintManager) getActivity()
- .getSystemService(Context.PRINT_SERVICE);
- mPrintJob = printManager.print("Print job", adapter, attributes);
- });
-
- return mPrintJob;
- }
-
- protected PrintTestActivity getActivity() {
- return mActivityRule.getActivity();
- }
-
- public static String runShellCommand(String cmd)
- throws IOException {
- ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation().executeShellCommand(cmd);
- byte[] buf = new byte[512];
- int bytesRead;
- FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
- StringBuilder stdout = new StringBuilder();
- while ((bytesRead = fis.read(buf)) != -1) {
- stdout.append(new String(buf, 0, bytesRead));
- }
- fis.close();
- return stdout.toString();
- }
-
- protected static void clearPrintSpoolerData() throws Exception {
- assertTrue("failed to clear print spooler data", runShellCommand(
- String.format("pm clear --user %d %s", CURRENT_USER_ID,
- PrintManager.PRINT_SPOOLER_PACKAGE_NAME)).contains(
- PM_CLEAR_SUCCESS_OUTPUT));
- }
-
- /**
- * Disable all print services beside the ones we want to leave enabled.
- *
- * @param packageToLeaveEnabled The package of the services to leave enabled.
- */
- private static void disablePrintServices(String packageToLeaveEnabled) throws IOException {
- Instrumentation instrumentation = getInstrumentation();
-
- sDisabledPrintServicesBefore = runShellCommand(
- "settings get secure " + Settings.Secure.DISABLED_PRINT_SERVICES);
-
- Intent printServiceIntent = new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
- List<ResolveInfo> installedServices = instrumentation.getContext().getPackageManager()
- .queryIntentServices(printServiceIntent, GET_SERVICES | GET_META_DATA);
-
- StringBuilder builder = new StringBuilder();
- for (ResolveInfo service : installedServices) {
- if (packageToLeaveEnabled.equals(service.serviceInfo.packageName)) {
- continue;
- }
- if (builder.length() > 0) {
- builder.append(":");
- }
- builder.append(new ComponentName(service.serviceInfo.packageName,
- service.serviceInfo.name).flattenToString());
- }
-
- runShellCommand(
- "settings put secure " + Settings.Secure.DISABLED_PRINT_SERVICES + " " + builder);
- }
-
- /**
- * Revert {@link #disablePrintServices(String)}
- */
- private static void enablePrintServices() throws IOException {
- runShellCommand("settings put secure " + Settings.Secure.DISABLED_PRINT_SERVICES + " "
- + sDisabledPrintServicesBefore);
- }
-
- @SuppressWarnings("unchecked")
- protected PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks(
- Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery,
- Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking,
- Answer<Void> onRequestCustomPrinterIcon, Answer<Void> onStopPrinterStateTracking,
- Answer<Void> onDestroy) {
- PrinterDiscoverySessionCallbacks callbacks = mock(PrinterDiscoverySessionCallbacks.class);
-
- doCallRealMethod().when(callbacks).setSession(any(StubbablePrinterDiscoverySession.class));
- when(callbacks.getSession()).thenCallRealMethod();
-
- if (onStartPrinterDiscovery != null) {
- doAnswer(onStartPrinterDiscovery).when(callbacks).onStartPrinterDiscovery(
- any(List.class));
- }
- if (onStopPrinterDiscovery != null) {
- doAnswer(onStopPrinterDiscovery).when(callbacks).onStopPrinterDiscovery();
- }
- if (onValidatePrinters != null) {
- doAnswer(onValidatePrinters).when(callbacks).onValidatePrinters(
- any(List.class));
- }
- if (onStartPrinterStateTracking != null) {
- doAnswer(onStartPrinterStateTracking).when(callbacks).onStartPrinterStateTracking(
- any(PrinterId.class));
- }
- if (onRequestCustomPrinterIcon != null) {
- doAnswer(onRequestCustomPrinterIcon).when(callbacks).onRequestCustomPrinterIcon(
- any(PrinterId.class), any(CancellationSignal.class),
- any(CustomPrinterIconCallback.class));
- }
- if (onStopPrinterStateTracking != null) {
- doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking(
- any(PrinterId.class));
- }
- if (onDestroy != null) {
- doAnswer(onDestroy).when(callbacks).onDestroy();
- }
-
- return callbacks;
- }
-
- protected PrintServiceCallbacks createMockPrintServiceCallbacks(
- Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks,
- Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) {
- final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class);
-
- doCallRealMethod().when(service).setService(any(PrintService.class));
- when(service.getService()).thenCallRealMethod();
-
- if (onCreatePrinterDiscoverySessionCallbacks != null) {
- doAnswer(onCreatePrinterDiscoverySessionCallbacks).when(service)
- .onCreatePrinterDiscoverySessionCallbacks();
- }
- if (onPrintJobQueued != null) {
- doAnswer(onPrintJobQueued).when(service).onPrintJobQueued(any(PrintJob.class));
- }
- if (onRequestCancelPrintJob != null) {
- doAnswer(onRequestCancelPrintJob).when(service).onRequestCancelPrintJob(
- any(PrintJob.class));
- }
-
- return service;
- }
-}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/PrintTestActivity.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/PrintTestActivity.java
deleted file mode 100644
index 4905a0b..0000000
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/PrintTestActivity.java
+++ /dev/null
@@ -1,32 +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.printspooler.outofprocess.tests;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-public class PrintTestActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
- | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
- | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
- }
-}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
index 78a0cac..7ebf93d 100644
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
@@ -30,6 +30,11 @@
import android.print.PrinterId;
import android.print.PrinterInfo;
import android.print.pdf.PrintedPdfDocument;
+import android.print.test.BasePrintTest;
+import android.print.test.services.AddPrintersActivity;
+import android.print.test.services.FirstPrintService;
+import android.print.test.services.PrinterDiscoverySessionCallbacks;
+import android.print.test.services.StubbablePrinterDiscoverySession;
import android.support.test.filters.LargeTest;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiObject;
@@ -38,11 +43,6 @@
import android.support.test.uiautomator.Until;
import android.util.Log;
-import com.android.printspooler.outofprocess.tests.mockservice.AddPrintersActivity;
-import com.android.printspooler.outofprocess.tests.mockservice.MockPrintService;
-import com.android.printspooler.outofprocess.tests.mockservice.PrinterDiscoverySessionCallbacks;
-import com.android.printspooler.outofprocess.tests.mockservice.StubbablePrinterDiscoverySession;
-
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -62,10 +62,6 @@
public class WorkflowTest extends BasePrintTest {
private static final String LOG_TAG = WorkflowTest.class.getSimpleName();
- private static float sWindowAnimationScaleBefore;
- private static float sTransitionAnimationScaleBefore;
- private static float sAnimatiorDurationScaleBefore;
-
private PrintAttributes.MediaSize mFirst;
private boolean mSelectPrinter;
private PrintAttributes.MediaSize mSecond;
@@ -91,7 +87,7 @@
throws TimeoutException, InterruptedException {
long startTime = System.currentTimeMillis();
while (condition.get()) {
- long timeLeft = OPERATION_TIMEOUT - (System.currentTimeMillis() - startTime);
+ long timeLeft = OPERATION_TIMEOUT_MILLIS - (System.currentTimeMillis() - startTime);
if (timeLeft < 0) {
throw new TimeoutException();
}
@@ -156,7 +152,7 @@
*/
private void setMockPrintServiceCallbacks(StubbablePrinterDiscoverySession[] sessionRef,
ArrayList<String> trackedPrinters, PrintAttributes.MediaSize mediaSize) {
- MockPrintService.setCallbacks(createMockPrintServiceCallbacks(
+ FirstPrintService.setCallbacks(createMockPrintServiceCallbacks(
inv -> createMockPrinterDiscoverySessionCallbacks(inv2 -> {
synchronized (sessionRef) {
sessionRef[0] = ((PrinterDiscoverySessionCallbacks) inv2.getMock())
@@ -243,7 +239,7 @@
callback.onWriteFailed(e.getMessage());
}
}
- }, null);
+ }, (PrintAttributes) null);
}
@Parameterized.Parameters
@@ -303,7 +299,7 @@
} else {
Log.i(LOG_TAG, "Waiting for error message");
assertNotNull(getUiDevice().wait(Until.findObject(
- By.text("This printer isn't available right now.")), OPERATION_TIMEOUT));
+ By.text("This printer isn't available right now.")), OPERATION_TIMEOUT_MILLIS));
}
setPrinter("All printers\u2026");
@@ -316,7 +312,7 @@
() -> addPrinter(session[0], "2nd printer", mSecond));
// This executes the observer registered above
- clickOn(new UiSelector().text(MockPrintService.class.getCanonicalName())
+ clickOn(new UiSelector().text(FirstPrintService.class.getCanonicalName())
.resourceId("com.android.printspooler:id/title"));
getUiDevice().pressBack();
@@ -342,7 +338,8 @@
} else {
Log.i(LOG_TAG, "Waiting for error message");
assertNotNull(getUiDevice().wait(Until.findObject(
- By.text("This printer isn't available right now.")), OPERATION_TIMEOUT));
+ By.text("This printer isn't available right now.")),
+ OPERATION_TIMEOUT_MILLIS));
}
Log.i(LOG_TAG, "Waiting for 1st printer to be not tracked");
@@ -370,7 +367,8 @@
} else {
Log.i(LOG_TAG, "Waiting for error message");
assertNotNull(getUiDevice().wait(Until.findObject(
- By.text("This printer isn't available right now.")), OPERATION_TIMEOUT));
+ By.text("This printer isn't available right now.")),
+ OPERATION_TIMEOUT_MILLIS));
}
Log.i(LOG_TAG, "Waiting for 1st printer to be tracked");
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/AddPrintersActivity.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/AddPrintersActivity.java
deleted file mode 100644
index 2ea4e7d..0000000
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/AddPrintersActivity.java
+++ /dev/null
@@ -1,52 +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.printspooler.outofprocess.tests.mockservice;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-
-import java.util.ArrayList;
-
-public class AddPrintersActivity extends Activity {
- private static final ArrayList<Runnable> sObservers = new ArrayList<>();
-
- public static void addObserver(@NonNull Runnable observer) {
- synchronized (sObservers) {
- sObservers.add(observer);
- }
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- synchronized (sObservers) {
- for (Runnable sObserver : sObservers) {
- sObserver.run();
- }
- }
-
- finish();
- }
-
- public static void clearObservers() {
- synchronized (sObservers) {
- sObservers.clear();
- }
- }
-}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/MockPrintService.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/MockPrintService.java
deleted file mode 100644
index 3a23113..0000000
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/MockPrintService.java
+++ /dev/null
@@ -1,40 +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.printspooler.outofprocess.tests.mockservice;
-
-public class MockPrintService extends StubbablePrintService {
-
- private static final Object sLock = new Object();
-
- private static PrintServiceCallbacks sCallbacks;
-
- public static void setCallbacks(PrintServiceCallbacks callbacks) {
- synchronized (sLock) {
- sCallbacks = callbacks;
- }
- }
-
- @Override
- protected PrintServiceCallbacks getCallbacks() {
- synchronized (sLock) {
- if (sCallbacks != null) {
- sCallbacks.setService(this);
- }
- return sCallbacks;
- }
- }
-}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrintServiceCallbacks.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrintServiceCallbacks.java
deleted file mode 100644
index 07baa0f..0000000
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrintServiceCallbacks.java
+++ /dev/null
@@ -1,39 +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.printspooler.outofprocess.tests.mockservice;
-
-import android.printservice.PrintJob;
-import android.printservice.PrintService;
-
-public abstract class PrintServiceCallbacks {
-
- private PrintService mService;
-
- public PrintService getService() {
- return mService;
- }
-
- public void setService(PrintService service) {
- mService = service;
- }
-
- public abstract PrinterDiscoverySessionCallbacks onCreatePrinterDiscoverySessionCallbacks();
-
- public abstract void onRequestCancelPrintJob(PrintJob printJob);
-
- public abstract void onPrintJobQueued(PrintJob printJob);
-}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrinterDiscoverySessionCallbacks.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrinterDiscoverySessionCallbacks.java
deleted file mode 100644
index 5c1260c..0000000
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/PrinterDiscoverySessionCallbacks.java
+++ /dev/null
@@ -1,51 +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.printspooler.outofprocess.tests.mockservice;
-
-import android.os.CancellationSignal;
-import android.print.PrinterId;
-import android.printservice.CustomPrinterIconCallback;
-
-import java.util.List;
-
-public abstract class PrinterDiscoverySessionCallbacks {
-
- private StubbablePrinterDiscoverySession mSession;
-
- public void setSession(StubbablePrinterDiscoverySession session) {
- mSession = session;
- }
-
- public StubbablePrinterDiscoverySession getSession() {
- return mSession;
- }
-
- public abstract void onStartPrinterDiscovery(List<PrinterId> priorityList);
-
- public abstract void onStopPrinterDiscovery();
-
- public abstract void onValidatePrinters(List<PrinterId> printerIds);
-
- public abstract void onStartPrinterStateTracking(PrinterId printerId);
-
- public abstract void onRequestCustomPrinterIcon(PrinterId printerId,
- CancellationSignal cancellationSignal, CustomPrinterIconCallback callback);
-
- public abstract void onStopPrinterStateTracking(PrinterId printerId);
-
- public abstract void onDestroy();
-}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrintService.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrintService.java
deleted file mode 100644
index be9d19b..0000000
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrintService.java
+++ /dev/null
@@ -1,52 +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.printspooler.outofprocess.tests.mockservice;
-
-import android.printservice.PrintJob;
-import android.printservice.PrintService;
-import android.printservice.PrinterDiscoverySession;
-
-public abstract class StubbablePrintService extends PrintService {
-
- @Override
- public PrinterDiscoverySession onCreatePrinterDiscoverySession() {
- PrintServiceCallbacks callbacks = getCallbacks();
- if (callbacks != null) {
- return new StubbablePrinterDiscoverySession(this,
- getCallbacks().onCreatePrinterDiscoverySessionCallbacks());
- }
- return null;
- }
-
- @Override
- public void onRequestCancelPrintJob(PrintJob printJob) {
- PrintServiceCallbacks callbacks = getCallbacks();
- if (callbacks != null) {
- callbacks.onRequestCancelPrintJob(printJob);
- }
- }
-
- @Override
- public void onPrintJobQueued(PrintJob printJob) {
- PrintServiceCallbacks callbacks = getCallbacks();
- if (callbacks != null) {
- callbacks.onPrintJobQueued(printJob);
- }
- }
-
- protected abstract PrintServiceCallbacks getCallbacks();
-}
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrinterDiscoverySession.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrinterDiscoverySession.java
deleted file mode 100644
index a828cd6..0000000
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/mockservice/StubbablePrinterDiscoverySession.java
+++ /dev/null
@@ -1,95 +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.printspooler.outofprocess.tests.mockservice;
-
-import android.os.CancellationSignal;
-import android.print.PrinterId;
-import android.printservice.CustomPrinterIconCallback;
-import android.printservice.PrintService;
-import android.printservice.PrinterDiscoverySession;
-import android.support.annotation.NonNull;
-
-import java.util.List;
-
-public class StubbablePrinterDiscoverySession extends PrinterDiscoverySession {
- private final PrintService mService;
- private final PrinterDiscoverySessionCallbacks mCallbacks;
-
- public StubbablePrinterDiscoverySession(PrintService service,
- PrinterDiscoverySessionCallbacks callbacks) {
- mService = service;
- mCallbacks = callbacks;
- if (mCallbacks != null) {
- mCallbacks.setSession(this);
- }
- }
-
- public PrintService getService() {
- return mService;
- }
-
- @Override
- public void onStartPrinterDiscovery(@NonNull List<PrinterId> priorityList) {
- if (mCallbacks != null) {
- mCallbacks.onStartPrinterDiscovery(priorityList);
- }
- }
-
- @Override
- public void onStopPrinterDiscovery() {
- if (mCallbacks != null) {
- mCallbacks.onStopPrinterDiscovery();
- }
- }
-
- @Override
- public void onValidatePrinters(@NonNull List<PrinterId> printerIds) {
- if (mCallbacks != null) {
- mCallbacks.onValidatePrinters(printerIds);
- }
- }
-
- @Override
- public void onStartPrinterStateTracking(@NonNull PrinterId printerId) {
- if (mCallbacks != null) {
- mCallbacks.onStartPrinterStateTracking(printerId);
- }
- }
-
- @Override
- public void onRequestCustomPrinterIcon(@NonNull PrinterId printerId,
- @NonNull CancellationSignal cancellationSignal,
- @NonNull CustomPrinterIconCallback callback) {
- if (mCallbacks != null) {
- mCallbacks.onRequestCustomPrinterIcon(printerId, cancellationSignal, callback);
- }
- }
-
- @Override
- public void onStopPrinterStateTracking(@NonNull PrinterId printerId) {
- if (mCallbacks != null) {
- mCallbacks.onStopPrinterStateTracking(printerId);
- }
- }
-
- @Override
- public void onDestroy() {
- if (mCallbacks != null) {
- mCallbacks.onDestroy();
- }
- }
-}
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index cf9515e..a7a7ea9 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Kon nie instellings vir <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> oopmaak nie"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Die invoermetode kan dalk alle teks wat jy invoer, versamel, insluitend persoonlike data soos wagwoorde en kredietkaartnommers. Dit kom van die program <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Wil jy dié invoermetode gebruik?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Let wel: Ná \'n herselflaai kan hierdie program nie begin voordat jy jou foon ontsluit het nie"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS-registrasiestaat"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Geregistreer"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Nie geregistreer nie"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Onbeskikbaar"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index a84ee81..bff8fe2 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"የ<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> ቅንብሮች መክፈት አልተሳካም"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"ይህ ግቤት ስልት የሚትተይበውን ፅሁፍ ሁሉ፣ እንደይለፍ ቃል እና የብድር ካርድ ጨምሮ የግል ውሂብ ምናልባት መሰብሰብ ይችላል። ከትግበራው ይመጣል። <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ይህን ግቤት ስልትይጠቀም?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"ማስታወሻ፦ እንደገና ከማስነሳት በኋላ ይህ መተግበሪያ ስልክዎን እስከሚከፍቱት ድረስ ሊጀምር አይችልም"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"የIMS ምዝገባ ቀን"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"የተመዘገበ"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"አልተመዘገበም"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"አይገኝም"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index b9a16b96..457a6dd 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"تعذّر فتح الإعدادات لـ <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"يمكن أن يكون أسلوب الإدخال هذا قادرًا على جمع كل النصوص التي تكتبها، بما في ذلك البيانات الشخصية مثل كلمات المرور وأرقام بطاقات الائتمان. يتم الحصول على هذا الأسلوب من التطبيق <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. هل تريد استخدام أسلوب الإدخال هذا؟"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"ملاحظة: بعد إعادة التشغيل، يتعذر بدء هذا التطبيق إلى أن تلغي قفل هاتفك."</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"حالة تسجيل IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"مُسجَّل"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"غير مُسجَّل"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"غير متاح"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 0a86706..8b453ab 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> üçün ayarları açmaq alınmadı"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Bu daxiletmə metodu yazdığınız bütün mətni toplaya bilər. Buna kredit kart kimi şəxsi məlumat aid ola bilər. Kökü <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> tətbiqindədir. Bu daxiletmə metodu istifadə olunsun?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Qeyd: Yenidən yüklənmədən sonra, bu cihazın kilidini açmamış tətbiq başlaya bilməz"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS qeydiyyat statusu"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Qeydiyyatlı"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Qeydiyyatsız"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Əlçatmazdır"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 1e84d3d..ba0de57 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Otvaranje podešavanja za aplikaciju <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> nije uspelo"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Ovaj metod unosa možda može da prikuplja sav tekst koji unosite, uključujući lične podatke, kao što su lozinke i brojevi kreditnih kartica. Potiče od aplikacije <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Želite li da koristite ovaj metod unosa?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Napomena: Posle restartovanja ova aplikacija ne može da se pokrene dok ne otključate telefon"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Status IMS registracije"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrovan je"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Nije registrovan"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Nedostupno"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index e47bc43..49a18b6 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Не атрымалася адкрыць параметры <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Гэты метад уводу можа збіраць увесь тэкст, які ўводзіцца, у тым лiку такiя персанальныя дадзеныя, як паролі і нумары крэдытных карт. Ён выкарыстоўваецца прыкладаннем <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Выкарыстоўваць гэты метад уводу?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Заўвага. Пасля перазагрузкі гэта праграма не зможа запусціцца, пакуль вы не разблакіруеце тэлефон"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Стан рэгістрацыі IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Зарэгістраваны"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Не зарэгістраваны"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Адсутнічае"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 77beb4c..8fbef19 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Отварянето на настройките за <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> не бе успешно"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Този метод за въвеждане може да събира целия въведен от вас текст, включително лични данни като пароли и номера на кредитни карти. Той произлиза от приложението <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Искате ли да го използвате?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Забележка: След рестартиране това приложение не може да се стартира, докато не отключите телефона си"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Състояние на регистрацията за незабавни съобщения"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Регистрирано"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Не е регистрирано"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Няма данни"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 424e7b9..ec48835 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> জন্য সেটিংস খুলতে ব্যর্থ হয়েছে"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"এই ইনপুট পদ্ধতিটি হয়তো পাসওয়ার্ড এবং ক্রেডিট কার্ড নম্বর সহ আপনার টাইপ করা সমস্ত টেক্সট সংগ্রহ করতে সক্ষম হতে পারে। এটি <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> অ্যাপ থেকে এসেছে। এই ইনপুট পদ্ধতিটি ব্যবহার করবেন?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"দ্রষ্টব্য: পুনরায় চালু করার পরে, আপনি আপনার ফোন আনলক না করা পর্যন্ত এই অ্যাপটিকে চালু করতে পারবেন না"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS রেজিস্ট্রেশনের স্থিতি"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"রেজিস্টার করা"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"রেজিস্টার করা নয়"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"অনুপলব্ধ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 97cbfbf..a331286 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -110,7 +110,7 @@
<string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string>
<string name="data_usage_uninstalled_apps" msgid="614263770923231598">"Uklonjene aplikacije"</string>
<string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"Uklonjene aplikacije i korisnici"</string>
- <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB dijeljenje veze"</string>
+ <string name="tether_settings_title_usb" msgid="6688416425801386511">"Povezivanje mobitela USB-om"</string>
<string name="tether_settings_title_wifi" msgid="3277144155960302049">"Prijenosna pristupna tačka"</string>
<string name="tether_settings_title_bluetooth" msgid="355855408317564420">"Dijeljenje Bluetooth veze"</string>
<string name="tether_settings_title_usb_bluetooth" msgid="5355828977109785001">"Dijeljenje veze"</string>
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Nije uspjelo otvaranje postavki za <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Ovaj način unosa može prikupiti sav tekst koji otkucate, uključujući lične podatke kao što su lozinke i brojevi kreditnih kartica. Način omogućava aplikacija <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Da li želite koristiti ovaj način unosa?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Napomena: Nakon ponovnog pokretanja, ova aplikacija se neće moći pokrenuti dok ne otključate telefon"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Stanje IMS registracije"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrirano"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Nije registrirano"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Nije dostupno"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 479cf38..145f61c 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"No s\'ha pogut obrir la configuració de: <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Pot ser que aquest mètode d\'introducció pugui recopilar tot el que escriviu, incloses dades personals, com ara contrasenyes i números de targetes de crèdit. Ve de l\'aplicació <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Voleu utilitzar aquest mètode d\'introducció?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Nota: després de reiniciar, l\'aplicació no s\'iniciarà fins que no desbloquegis el telèfon"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Estat del registre d\'IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrat"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Sense registrar"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"No disponible"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 97a76f7..994afae 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Nastavení aplikace <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> se nepodařilo otevřít"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Prostřednictvím této vstupní metody zadávání dat lze shromažďovat zadaný text včetně osobních údajů, jako jsou hesla a čísla platebních karet. Metoda je poskytována aplikací <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Chcete tuto metodu zadávání dat použít?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Poznámka: Po restartování se tato aplikace nespustí, dokud telefon neodemknete."</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Stav registrace IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrováno"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Neregistrováno"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Není k dispozici"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 6a006cc..e916853 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -329,7 +329,7 @@
<string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"Konvertér…"</string>
<string name="convert_to_file_encryption_done" msgid="7859766358000523953">"Allerede filkrypteret"</string>
<string name="title_convert_fbe" msgid="1263622876196444453">"Konverterer til filbaseret kryptering"</string>
- <string name="convert_to_fbe_warning" msgid="6139067817148865527">"Konvertér datapartitionen til filbaseret kryptering.\nAdvarsel! Alle dine data vil blive slettet.\n Dette er en alfafunktion, og den fungerer muligvis ikke korrekt.\n Tryk på \"Ryd og konvertér…\" for at fortsætte."</string>
+ <string name="convert_to_fbe_warning" msgid="6139067817148865527">"Konvertér datapartitionen til filbaseret kryptering.\nAdvarsel! Alle dine data vil blive fuldstændigt slettet.\n Dette er en alfafunktion, og den fungerer muligvis ikke korrekt.\n Tryk på \"Ryd og konvertér…\" for at fortsætte."</string>
<string name="button_convert_fbe" msgid="5152671181309826405">"Ryd og konvertér…"</string>
<string name="picture_color_mode" msgid="4560755008730283695">"Farvetilstand for billeder"</string>
<string name="picture_color_mode_desc" msgid="1141891467675548590">"Brug sRGB"</string>
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Indstillingerne for <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> kunne ikke åbnes"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Denne inputmetode kan muligvis indsamle al indtastet tekst, f.eks. personlige data såsom adgangskoder og kreditkortnumre. Den kommer fra appen <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Vil du anvende denne inputmetode?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Bemærk! Efter en genstart kan denne app ikke starte, før du låser din telefon op"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Status for IMS-registrering"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registreret"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Ikke registreret"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Utilgængelig"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 2203bc6..1ec4fee 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Einstellungen für <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> konnten nicht geöffnet werden."</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Diese Eingabemethode kann den gesamten von dir eingegebenen Text erfassen, einschließlich personenbezogener Daten wie Passwörter und Kreditkartennummern. Sie ist Teil der App \"<xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>\". Möchtest du diese Eingabemethode verwenden?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Hinweis: Nach einem Neustart wird diese App erst gestartet, wenn du dein Smartphone entsperrst"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS-Registrierungsstatus"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registriert"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Nicht registriert"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Nicht verfügbar"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 514cfd8..2548a5d 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Δεν ήταν δυνατό το άνοιγμα των ρυθμίσεων για την εφαρμογή <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Αυτή η μέθοδος εισαγωγής ενδέχεται να έχει τη δυνατότητα να συλλέξει όλα τα κείμενα που πληκτρολογείτε, συμπεριλαμβανομένων προσωπικών δεδομένων, όπως είναι οι κωδικοί πρόσβασης και οι αριθμοί πιστωτικής κάρτας. Προέρχεται από την εφαρμογή <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Να γίνει χρήση αυτής της μεθόδου εισαγωγής;"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Σημείωση: Μετά από μια επανεκκίνηση, δεν είναι δυνατή η έναρξη αυτής της συσκευής προτού ξεκλειδώσετε το τηλέφωνό σας"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Κατάσταση εγγραφής υπηρεσίας IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Εγγεγραμμένη"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Μη εγγεγραμμένη"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Μη διαθέσιμο"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index aa5af8e..9e24004 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Failed to open settings for <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"This input method may be able to collect all the text that you type, including personal data like passwords and credit card numbers. It comes from the app <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Use this input method?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Note: After a reboot, this app can\'t start until you unlock your phone"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS registration state"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registered"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Not registered"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Unavailable"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index aa5af8e..9e24004 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Failed to open settings for <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"This input method may be able to collect all the text that you type, including personal data like passwords and credit card numbers. It comes from the app <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Use this input method?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Note: After a reboot, this app can\'t start until you unlock your phone"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS registration state"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registered"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Not registered"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Unavailable"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index aa5af8e..9e24004 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Failed to open settings for <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"This input method may be able to collect all the text that you type, including personal data like passwords and credit card numbers. It comes from the app <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Use this input method?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Note: After a reboot, this app can\'t start until you unlock your phone"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS registration state"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registered"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Not registered"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Unavailable"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index aa5af8e..9e24004 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Failed to open settings for <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"This input method may be able to collect all the text that you type, including personal data like passwords and credit card numbers. It comes from the app <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Use this input method?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Note: After a reboot, this app can\'t start until you unlock your phone"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS registration state"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registered"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Not registered"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Unavailable"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 66d8fe0..77c0e4f 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Failed to open settings for <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"This input method may be able to collect all the text you type, including personal data like passwords and credit card numbers. It comes from the app <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Use this input method?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Note: After a reboot, this app can\'t start until you unlock your phone"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS registration state"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registered"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Not registered"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Unavailable"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 743700e..ff26eef 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Se produjo un error al abrir la configuración de <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>."</string>
<string name="ime_security_warning" msgid="4135828934735934248">"El método de entrada puede recopilar todo el texto que escribas, incluidos los datos personales como contraseñas y números de tarjetas de crédito. Proviene de la aplicación <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. ¿Deseas utilizar este método de entrada?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Nota: Luego de reiniciar el dispositivo, esta app no podrá iniciarse hasta que desbloquees tu teléfono"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Estado de registro de IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrado"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Sin registrar"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"No disponible"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index d964728..20bab13 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Error al abrir los ajustes de <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Este método de entrada puede registrar todo lo que escribas, incluidos datos personales, como las contraseñas y los números de las tarjetas de crédito. Procede de la aplicación <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. ¿Quieres usar este método de entrada?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Nota: Después de reiniciar, tienes que desbloquear el teléfono para que esta aplicación se pueda iniciar"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Estado del registro de IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrado"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"No registrado"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"No disponible"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index cfcc4c6..a0341fb 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Rakenduse <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> seadete avamine ebaõnnestus"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"See sisestusmeetod võib koguda kogu teie sisestatava teksti, sh isikuandmed (nt paroolid ja krediitkaardinumbrid). See pärineb rakendusest <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Kas soovite seda sisestusmeetodit kasutada?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Märkus. Pärast taaskäivitamist ei saa see rakendus käivituda enne, kui olete telefoni avanud"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS-i registreerimise olek"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registreeritud"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Ei ole registreeritud"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Pole saadaval"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index d7595e8..e8d9a5e 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Ezin izan dira <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> aplikazioaren ezarpenak ireki."</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Idazketa-metodoak idazten duzun testu guztia bil dezake, pasahitzak eta kreditu-txarteleko zenbakiak bezalako datu pertsonalak barne. <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> aplikazioak egin du eskaera. Idazketa-metodo hori erabili nahi duzu?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Oharra: berrabiarazi ondoren, ezin izango da abiarazi aplikazio hau telefonoa desblokeatzen duzun arte"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS erregistratzearen egoera"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Erregistratuta"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Erregistratu gabe"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Ez dago erabilgarri"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index f582486..326192d 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"تنظیمات <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> بازنشد"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"این روش ورودی ممکن است بتواند تمام متنی را که تایپ میکنید جمعآوری کند، از جمله اطلاعات شخصی مانند گذرواژهها و شمارههای کارت اعتباری. این روش توسط برنامه <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ارائه میشود. از این روش ورودی استفاده میکنید؟"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"توجه: بعد از راهاندازی تا زمانیکه قفل تلفنتان را باز نکنید، این برنامه نمیتواند شروع شود"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"وضعیت ثبت IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"ثبتشده"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"ثبت نشده است"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"در دسترس نیست"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 83073d1..6321086 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Sovelluksen <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> asetuksia ei voi avata"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Tämä syöttötapa saattaa kerätä kaiken kirjoittamasi tekstin, mukaan luettuna henkilökohtaiset tiedot kuten salasanat ja luottokortin numerot. Se on lähtöisin sovelluksesta <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Käytetäänkö tätä syöttötapaa?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Huom. Jotta voit käynnistää tämän sovelluksen uudelleenkäynnistyksen jälkeen, sinun täytyy avata puhelimen lukitus."</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS-rekisteröinnin tila"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Rekisteröity"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Ei rekisteröity"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Ei käytettävissä"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 54d6864..00c02ce 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Échec de l\'ouverture des paramètres de l\'application <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>."</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Ce mode de saisie est susceptible d\'enregistrer le texte que vous saisissez, y compris vos données personnelles, telles que les mots de passe et les numéros de carte de paiement. Il provient de l\'application <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Voulez-vous vraiment l\'activer?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Remarque : Après un redémarrage, vous ne pouvez pas lancer cette application tant que vous n\'avez pas déverrouillé votre téléphone."</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"État d\'enregistrement IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Enregistré"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Non enregistré"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Non accessible"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index e35d330..a5625e4 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Échec de l\'ouverture des paramètres de l\'application <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>."</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Ce mode de saisie est susceptible d\'enregistrer le texte que vous saisissez, y compris vos données personnelles, telles que les mots de passe et les numéros de carte de paiement. Il provient de l\'application <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Voulez-vous vraiment l\'activer ?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Remarque : Après un redémarrage, vous ne pouvez pas lancer cette application tant que vous n\'avez pas déverrouillé votre téléphone."</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"État de l\'enregistrement IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Enregistré"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Non enregistré"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Non disponible"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 8291cb1..e1ea694 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Non se puido abrir a configuración de <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"É posible que este método de entrada poida recompilar todo o texto que escribas, incluídos os datos persoais como os contrasinais e os números de tarxetas de crédito. Provén da aplicación <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Queres usar este método de entrada?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Nota: Tras un reinicio, non se pode iniciar esta aplicación ata que desbloquees o teléfono"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Estado de rexistro de IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Rexistrado"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Non rexistrado"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Non dispoñible"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index ca14118..0e8dd47 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> માટેની સેટિંગ્સ ખોલવામાં નિષ્ફળ"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"આ ઇનપુટ પદ્ધતિ પાસવર્ડ્સ અને ક્રેડિટ કાર્ડ નંબર જેવી વ્યક્તિગત માહિતી સહિત તમે લખો છો તે તમામ ટેક્સ્ટ એકત્રિત કરવા માટે સક્ષમ હોઈ શકે છે. તે ઍપ્લિકેશન <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> માંથી આવે છે. આ ઇનપુટ પદ્ધતિ વાપરીએ?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"નોંધ: રીબૂટ કર્યાં પછી, જ્યાં સુધી તમે તમારો ફોન અનલૉક કરશો નહીં ત્યાં સુધી આ ઍપ્લિકેશન શરૂ થઈ શકશે નહીં"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS રજિસ્ટ્રેશનની સ્થિતિ"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"રજિસ્ટર કરેલ"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"રજિસ્ટર કરેલ નથી"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"અનુપલબ્ધ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index bde80f2..28bc68a 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> के लिए सेटिंग खोलने में विफल रहा"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"यह इनपुट विधि, पासवर्ड और क्रेडिट कार्ड नंबर जैसे निजी डेटा सहित आपके द्वारा लिखे जाने वाले सभी लेख को एकत्र कर सकती है. यह <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ऐप्स से आती है. इस इनपुट विधि का उपयोग करें?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"नोट: पुनः बूट करने के बाद, यह ऐप्लिकेशन तब तक शुरू नहीं हो सकता है जब तक कि आप अपना फ़ोन अनलॉक ना कर लें"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS रजिस्ट्रेशन की स्थिति"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"रजिस्टर है"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"रजिस्टर नहीं है"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"अनुपलब्ध"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index ec2b9cf..b5c5439 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Otvaranje postavki za aplikaciju <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> nije uspjelo"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Ovaj način unosa možda može prikupljati sav tekst koji unosite, uključujući osobne podatke poput zaporki i brojeva kreditnih kartica. To omogućuje aplikacija <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Upotrijebiti taj način unosa?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Napomena: ova se aplikacija ne može pokrenuti nakon ponovnog pokretanja dok ne otključate telefon"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Stanje registracije IMS-a"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrirano"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Nije registrirano"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Nije dostupno"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 3fe6a78..47d6c98 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Nem sikerült megnyitni a(z) <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> beállításait."</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Ez a beviteli módszer alkalmas lehet a beírt szövegek – köztük az olyan személyes adatok, mint a jelszavak és a hitelkártyaszámok - összegyűjtésére. A <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> alkalmazás kapcsolta be. Használja ezt a módszert?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Megjegyzés: Újraindítás után ez az alkalmazás csak a telefon feloldását követően indul el"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS-regisztráció állapota"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Regisztrált"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Nem regisztrált"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Nem érhető el"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 119e24c..6f11cd8 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Չստացվեց ցույց տալ <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>-ի տվյալները"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Այս ներմուծման եղանակը հնարավորություն է տալիս հավաքել ձեր մուտքագրած ողջ տեքստը՝ ընդգրկելով անձնական տեղեկություններ, ինչպես գաղտնաբառն ու բանկային քարտի համարները: Սկզբնաղբյուրը <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> հավելվածն է: Կիրառե՞լ այս եղանակը:"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Ուշադրություն. Վերաբեռնումից հետո այս հավելվածը չի գործարկվի մինչև չապակողպեք հեռախոսը"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS ծառայության գրանցման կարգավիճակը"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Գրանցված է"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Գրանցված չէ"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Անհասանելի"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index c2db508..b4a58bb 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Gagal membuka setelan untuk <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Metode masukan ini mungkin dapat mengumpulkan semua teks yang Anda ketik, termasuk data pribadi seperti sandi dan nomor kartu kredit. Metode ini berasal dari aplikasi <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Gunakan metode masukan ini?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Catatan: Setelah boot ulang, aplikasi ini tidak dapat dimulai hingga kunci ponsel dibuka"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Status pendaftaran IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Terdaftar"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Tidak terdaftar"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Tidak Tersedia"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 883e909..29b1b5d 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Ekki tókst að opna stillingar <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Þessi innsláttaraðferð getur hugsanlega skráð allan texta sem þú slærð inn, þ. á m. persónuupplýsingar á borð við aðgangsorð og kreditkortanúmer. Hún kemur frá forritinu <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Viltu nota þessa innsláttaraðferð?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Athugaðu: Eftir endurræsingu er ekki hægt að ræsa þetta forrit fyrr en þú tekur símann úr lás"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Staða IMS-skráningar"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Skráð"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Ekki skráð"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Ekki tiltækt"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index e53e4da..46004c3 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Impossibile aprire le impostazioni di <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Questo metodo di immissione potrebbe riuscire a raccogliere tutto il testo digitato, compresi i dati personali come password e numeri di carte di credito. Deriva dall\'applicazione <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Utilizzare questo metodo di immissione?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Nota: dopo il riavvio, devi sbloccare il telefono per poter avviare l\'app."</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Stato di registrazione IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrato"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Non registrato"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Non disponibile"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 363b064..24f4684 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"פתיחת הגדרות עבור <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> נכשלה"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"ייתכן ששיטת קלט זו תוכל לאסוף את כל הטקסט שאתה מקליד, כולל נתונים אישיים כגון סיסמאות ומספרי כרטיס אשראי. היא מגיעה מהאפליקציה <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. האם להשתמש בשיטת קלט זו?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"שים לב: לאחר הפעלה מחדש של המכשיר, ניתן להפעיל את האפליקציה רק לאחר שתבטל את נעילת הטלפון"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"סטטוס הרשמה ל-IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"רשום"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"לא רשום"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"לא זמין"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index dfd199a..bb3f5a9 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -390,4 +390,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>の設定を開くことができませんでした"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"この入力方法を選択すると、すべての入力内容の収集をアプリ(<xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>)に許可することになります。これにはパスワードやクレジットカード番号などの個人情報も含まれます。この入力方法を使用しますか?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"注: 再起動後、スマートフォンのロックを解除するまでこのアプリを起動することはできません"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS 登録ステータス"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"登録済み"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"未登録"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"不明"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 7fac639..2280261 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>-ისთვის პარამეტრების გახსნა ვერ მოხერხდა"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"შეყვანის ამ მეთოდმა შესაძლოა მოახერხოს თქვენი აკრეფილი ყველა ტექსტის, მათ შორის პერსონალური მონაცემების, პაროლებისა და საკრედიტო ბარათის ნომრების შენახვა. ეს მეთოდი ეკუთვნის <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>-ს. გამოვიყენო შეყვანის ეს მეთოდი?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"შენიშვნა: გადატვირთვის შემდეგ, ეს აპი ვერ გაეშვება, სანამ ტელეფონს არ განბლოკავთ"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS რეგისტრაციის სტატუსი"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"რეგისტრირებული"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"არარეგისტრირებული"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"მიუწვდომელია"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 3b806a5..03bd3cd 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> қолданбасы үшін параметрлерді ашу орындалмады"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Бұл енгізу әдісі сіз терген барлық мәтінді, кілтсөз және кредит карта нөмірлері сияқты жеке ақпаратты қоса, жинауы мүмкін. Бұл <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> қолданбасы арқылы жасалады. Осы әдіс қолданылсын ба?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Ескертпе: қайта жүктегеннен кейін, телефонның құлпын ашпайынша, бұл қолданба іске қосылмайды"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS тіркеу күйі"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Тіркелген"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Тіркелмеген"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Қол жетімсіз"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 2160bae..6414141 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"បានបរាជ័យក្នុងការបើកការកំណត់សម្រាប់ <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"វិធីសាស្ត្របញ្ចូលនេះអាចប្រមូលអត្ថបទដែលអ្នកវាយទាំងអស់ រួមមានទិន្នន័យផ្ទាល់ខ្លួន ដូចជ ពាក្យសម្ងាត់ និងលេខកាតឥណទាន។ វាបានមកពីកម្មវិធី <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ។ ប្រើវិធីសាស្ត្របញ្ចូលនេះ?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"ចំណាំ៖ បន្ទាប់ពីបិទបើកឡើងវិញហើយ កម្មវិធីនេះមិនអាចចាប់ផ្តើមបានទេ រហូតទាល់តែអ្នកដោះសោទូរស័ព្ទរបស់អ្នក"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"ស្ថានភាពនៃការចុះឈ្មោះ IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"បានចុះឈ្មោះ"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"មិនបានចុះឈ្មោះ"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"មិនមាន"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index a47a92c..d108dd6 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -121,7 +121,7 @@
<string name="running_process_item_user_label" msgid="3129887865552025943">"ಬಳಕೆದಾರ: <xliff:g id="USER_NAME">%1$s</xliff:g>"</string>
<string name="launch_defaults_some" msgid="313159469856372621">"ಕೆಲವು ಡೀಫಾಲ್ಟ್ಗಳನ್ನು ಹೊಂದಿಸಲಾಗಿದೆ"</string>
<string name="launch_defaults_none" msgid="4241129108140034876">"ಡೀಫಾಲ್ಟ್ಗಳನ್ನು ಹೊಂದಿಸಲಾಗಿಲ್ಲ"</string>
- <string name="tts_settings" msgid="8186971894801348327">"ಪಠ್ಯದಿಂದ ಧ್ವನಿಗೆ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
+ <string name="tts_settings" msgid="8186971894801348327">"ಪಠ್ಯದಿಂದ ಧ್ವನಿಯ ಸೆಟ್ಟಿಂಗ್ಗಳು"</string>
<string name="tts_settings_title" msgid="1237820681016639683">"ಧ್ವನಿಗೆ-ಪಠ್ಯದ ಔಟ್ಪುಟ್"</string>
<string name="tts_default_rate_title" msgid="6030550998379310088">"ಧ್ವನಿಯ ಪ್ರಮಾಣ"</string>
<string name="tts_default_rate_summary" msgid="4061815292287182801">"ಪಠ್ಯವನ್ನು ಹೇಳಿದ ವೇಗ"</string>
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> ಗಾಗಿ ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ತೆರೆಯಲು ವಿಫಲವಾಗಿದೆ"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"ವೈಯಕ್ತಿಕ ಡೇಟಾಗಳಾದ ಪಾಸ್ವರ್ಡ್ಗಳು ಮತ್ತು ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್ ಸಂಖ್ಯೆಗಳನ್ನು ಒಳಗೊಂಡಂತೆ ನೀವು ಟೈಪ್ ಮಾಡುವ ಎಲ್ಲ ಪಠ್ಯವನ್ನು ಸಂಗ್ರಹಿಸಲು ಈ ಇನ್ಪುಟ್ ವಿಧಾನಕ್ಕೆ ಸಾಧ್ಯವಾಗಬಹುದು. ಇದು <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ಅಪ್ಲಿಕೇಶನ್ನಿಂದ ಬರುತ್ತದೆ. ಈ ಇನ್ಪುಟ್ ವಿಧಾನವನ್ನು ಬಳಸುವುದೇ?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"ಗಮನಿಸಿ: ರೀಬೂಟ್ ನಂತರ, ನಿಮ್ಮ ಫೋನ್ ಅನ್ನು ನೀವು ಅನ್ಲಾಕ್ ಮಾಡುವ ತನಕ ಈ ಆಪ್ ಪ್ರಾರಂಭಗೊಳ್ಳುವುದಿಲ್ಲ"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS ನೋಂದಣಿ ಸ್ಥಿತಿ"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"ನೋಂದಾಯಿಸಲಾಗಿದೆ"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"ನೋಂದಾಯಿಸಲಾಗಿಲ್ಲ"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"ಲಭ್ಯವಿಲ್ಲ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 852c78e..ec03fb1 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> 설정을 열지 못했음"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"<xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> 앱에서 지원하는 이 입력 방법을 사용하면 비밀번호 및 신용카드 번호와 같은 개인 정보를 비롯하여 입력한 모든 텍스트가 수집될 수 있습니다. 사용하시겠습니까?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"참고: 재부팅한 후 이 앱은 휴대전화를 잠금 해제해야 시작됩니다."</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS 등록 상태"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"등록됨"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"등록되지 않음"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"사용할 수 없음"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 380d0ff..883811d 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> тууралоолору ачылган жок"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Бул киргизүү ыкмасы сиз терген бардык тексттер, сырсөздөр жана кредиттик карталар сыяктуу жеке маалыматтарды кошо чогултушу мүмкүн. Бул <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> колдонмосу менен байланыштуу. Ушул киргизүү ыкма колдонулсунбу?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Эскертүү: Өчүрүп-күйгүзгөндөн кийин, бул колдонмо телефондун кулпусу ачылмайынча иштебейт"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS каттоо абалы"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Катталган"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Катталган эмес"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Жеткиликсиз"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index eb10dbb..464b490 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"ລົ້ມເຫລວໃນການເປີດການຕັ້ງຄ່າຂອງ <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"ວິທີການປ້ອນຂໍ້ມູນນີ້ ອາດສາມາດເກັບກຳທຸກຂໍ້ຄວາມທີ່ທ່ານພິມ, ຮວມເຖິງຂໍ້ມູນສ່ວນໂຕເຊັ່ນລະຫັດຜ່ານ ແລະໝາຍເລກບັດເຄຣດິດນຳ. ມັນມາຈາກແອັບຯ <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. ທ່ານຕ້ອງການໃຊ້ວິທີການປ້ອນຂໍ້ມູນນີ້ບໍ່?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"ໝາຍເຫດ: ຫຼັງຈາກເປີດຂຶ້ນມາໃໝ່ແລ້ວ, ແອັບນີ້ຈະບໍ່ສາມາດເລີ່ມໄດ້ຈົນກວ່າທ່ານຈະປົດລັອກໂທລະສັບຂອງທ່ານ"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"ສະຖານະການລົງທະບຽນ IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"ລົງທະບຽນແລ້ວ"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"ບໍ່ໄດ້ລົງທະບຽນ"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"ບໍ່ມີຂໍ້ມູນ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index adf6f8a..fd3bc18 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Nepavyko atidaryti „<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>“ nustatymų"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Naudojant šį įvesties metodą galima rinkti visą įvedamą tekstą, įskaitant asmeninius duomenis, pvz., slaptažodžius ir kredito kortelių numerius. Jis pateikiamas naudojant programą „<xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>“. Naudoti šį įvesties metodą?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Pastaba: paleidus iš naujo nebus galima paleisti programos, kol neatrakinsite telefono"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS registracijos būsena"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Užregistruota"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Neužregistruota"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Užimta"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 280900a..a6d308a 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Neizdevās atvērt lietotnes <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> iestatījumus."</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Izmantojot šo ievades metodi, var tikt vākta informācija par visu ierakstīto tekstu, tostarp personiskiem datiem, piemēram, parolēm un kredītkaršu numuriem. Šī metode ir saistīta ar lietotni <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Vai lietot šo ievades metodi?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Piezīme. Šo lietotni pēc atkārtotas palaišanas nevarēs startēt, kamēr netiks atbloķēts tālrunis."</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS reģistrācijas statuss"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Reģistrēts"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Nav reģistrēts"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Nepieejams"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 51697bd..8882cb7 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Подесувањата за <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> не се отворија"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Овој метод на внес може да го собере сиот текст кој го пишувате, вклучувајќи и лични податоци како што се, лозинки и броеви на кредитни картички. Тоа го прави апликацијата <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Користи го овој метод на внес?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Забелешка: по рестартирање, апликацијава не може да се вклучи додека не го отклучите телефонот"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Состојба на IMS-регистрација"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Регистриран"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Не е регистриран"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Недостапен"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index d96607e..8f711b3 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> എന്നതിനായുള്ള ക്രമീകരണങ്ങൾ തുറക്കുന്നതിൽ പരാജയപ്പെട്ടു"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"പാസ്വേഡുകൾ, ക്രെഡിറ്റ് കാർഡ് നമ്പറുകൾ എന്നിവ പോലുള്ള വ്യക്തിഗതമായ ഡാറ്റയുൾപ്പെടെ നിങ്ങൾ ടൈപ്പുചെയ്യുന്ന എല്ലാ വാചകവും ഈ ടൈപ്പുചെയ്യൽ രീതിയ്ക്ക് ശേഖരിക്കാനായേക്കും. ഇത് <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> അപ്ലിക്കേഷനിൽ നിന്നും വരുന്നു. ഈ ടൈപ്പുചെയ്യൽ രീതി ഉപയോഗിക്കണോ?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"ശ്രദ്ധിക്കുക: റീബൂട്ടിന് ശേഷം, ഫോൺ അൺലോക്കുചെയ്യുന്നത് വരെ ഈ ആപ്പ് ആരംഭിക്കാൻ കഴിയില്ല"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS രജിസ്ട്രേഷൻ നില"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"രജിസ്റ്റർ ചെയ്തു"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"രജിസ്റ്റർ ചെയ്തിട്ടില്ല"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"ലഭ്യമല്ല"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index e13886d..2e1f5e6 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>-н тохиргоог нээж чадсангүй"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Энэ оруулах арга нь таны нууц үгс, зээлийн картын дугаар гэх мэт бичсэн хувийн мэдээллийг цуглуулах боломжтой байж болно. Үүнийг <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> апп нийлүүлдэг. Энэ оруулах аргыг ашиглах уу?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Санамж: Дахин асаасны дараа энэ апп нь таныг утасны түгжээгээ тайлах хүртэл эхлэх боломжгүй"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS бүртгэлийн байдал"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Бүртгэсэн"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Бүртгээгүй"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Байхгүй"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index ae61916..63e2e87 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> साठी सेटिंग्ज उघडण्यात अयशस्वी"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"ही इनपुट पद्धत संकेतशब्द आणि क्रेडिट कार्ड नंबर यासह, आपण टाइप करता तो सर्व मजकूर संकलित करण्यात सक्षम होऊ शकते. ही <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> अॅपवरून येते. ही इनपुट पद्धत वापरायची?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"टीप: रीबूट केल्यानंतर, तुम्ही आपला फोन अनलॉक करे पर्यंत हे अॅप सुरू होऊ शकत नाही"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS नोंदणी स्थिती"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"नोंदवलेले"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"नोंदवलेले नाही"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"अनुपलब्ध"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index a44e5b1..9d81cea 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Gagal membuka tetapan untuk <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Kaedah input ini mungkin boleh mengumpulkan semua teks yang anda taipkan, termasuk data peribadi seperti kata laluan dan nombor kad kredit. Ia datang daripada aplikasi <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Gunakan kaedah input ini?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Perhatian: Selepas but semula, apl ini tidak dapat dimulakan sehingga anda membuka kunci telefon"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Keadaan pendaftaran IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Berdaftar"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Tidak didaftarkan"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Tidak tersedia"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index af95eac..aa241a9 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>အတွက် ဆက်တင်းများဖွင့်ရန် မအောင်မြင်ပါ။"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"ဤထည့်သွင်းမှုနည်းလမ်းမှာ သင့်ကိုယ်ရေးအချက်အလက်များဖြစ်သော စကားဝှက်များနှင့် ကရက်ဒစ်ကဒ်နံပါတ်စသည်တို့ကို ရယူသွားမည်ဖြစ်သည်။ <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>အပလီကေးရှင်းမှလာပါသည်။ ဤထည့်သွင်းမှုနည်းလမ်းကို အသုံးပြုမည်လား?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"မှတ်ချက် − ပြန်လည်စတင်ပြီးနောက် သင့်ဖုန်းကိုလော့ခ်မဖွင့်မချင်း ဤအက်ပ်ကို အသုံးပြု၍မရပါ"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS မှတ်ပုံတင်ခြင်း အခြေအနေ"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"မှတ်ပုံတင်ထားသည်"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"မှတ်ပုံတင်မထားပါ"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"မရရှိနိုင်ပါ။"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 13d27ac..daf0051 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Kunne ikke åpne innstillingene for <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Denne inndatametoden har tilgang til all tekst du skriver, blant annet personlige opplysninger som for eksempel passord og kredittkortnumre. Den kommer fra appen <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Vil du bruke denne inndatametoden?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Merk: Etter en omstart kan ikke denne appen starte før du låser opp telefonen din"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Tilstand for IMS-registrering"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrert"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Ikke registrert"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Ikke tilgjengelig"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 58c5ffc..105c442 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>का लागि सेटिङहरू खोल्न विफल भयो।"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"यस इनपुट विधिले तपाईँले टाइप गर्नुहुने सम्पूर्ण पाठ बटु्ल्न सक्छ, व्यक्तिगत डेटा जस्तै पासवर्ड र क्रेडिट कार्ड नम्बर लगायतका। यो <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> अनुप्रयोगबाट आउँदछ। यो इनपुट विधि प्रयोग गर्ने हो?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"टिपोट: पुनःबुट पछि तपाईँले आफ्नो फोनलाई अनलक नगरेसम्म यो अनुप्रयोग सुरु हुन सक्दैन"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS दर्ताको स्थिति"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"दर्ता गरिएको"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"दर्ता नगरिएको"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"अनुपलब्ध"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 5cce79e..d8ae320 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Instellingen openen voor <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> mislukt"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Deze invoermethode verzamelt mogelijk alle tekst die je typt, inclusief persoonlijke gegevens zoals wachtwoorden en creditcardnummers. De methode is afkomstig uit de app <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Deze invoermethode inschakelen?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Opmerking: Wanneer je telefoon opnieuw is opgestart, kan deze app pas worden gestart nadat je je telefoon hebt ontgrendeld"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS-registratiestatus"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Geregistreerd"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Niet geregistreerd"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Niet beschikbaar"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index becb559..b54d296 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> ਲਈ ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹਣ ਵਿੱਚ ਅਸਫਲ"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"ਇਹ ਇਨਪੁੱਟ ਵਿਧੀ ਤੁਹਾਡੇ ਵੱਲੋਂ ਟਾਈਪ ਕੀਤਾ ਜਾਣ ਵਾਲੀ ਸਾਰੀ ਲਿਖਤ ਇਕੱਤਰ ਕਰਨ ਵਿੱਚ ਸਮਰੱਥ ਹੋ ਸਕਦੀ ਹੈ, ਨਿੱਜੀ ਡਾਟਾ ਸਮੇਤ ਜਿਵੇਂ ਪਾਸਵਰਡ ਅਤੇ ਕ੍ਰੈਡਿਟ ਕਾਰਡ ਨੰਬਰ। ਇਹ <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ਐਪ ਤੋਂ ਆਉਂਦਾ ਹੈ। ਕੀ ਇਹ ਸਪੈੱਲ ਚੈਕਰ ਵਰਤਣਾ ਹੈ?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"ਨੋਟ ਕਰੋ: ਰੀਬੂਟ ਤੋਂ ਬਾਅਦ, ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਆਪਣਾ ਫ਼ੋਨ ਅਣਲਾਕ ਨਹੀਂ ਕਰਦੇ ਤਦ ਤੱਕ ਇਹ ਐਪ ਚਾਲੂ ਨਹੀਂ ਹੋ ਸਕਦੀ"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS ਰਜਿਸਟਰੇਸ਼ਨ ਸਥਿਤੀ"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"ਰਜਿਸਟਰ ਕੀਤੀ ਗਈ"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"ਰਜਿਸਟਰ ਨਹੀਂ ਕੀਤੀ ਗਈ"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"ਅਣਉਪਲਬਧ"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index e3c69fb..4810279 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Nie udało się otworzyć ustawień aplikacji <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Ta metoda wprowadzania tekstu może gromadzić cały wpisywany tekst, w tym dane osobowe takie jak hasła czy numery kart kredytowych. Pochodzi ona z aplikacji <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Użyć jej?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Uwaga: po restarcie ta aplikacja będzie mogła uruchomić się dopiero po odblokowaniu telefonu"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Stan rejestracji IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Zarejestrowane"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Niezarejestrowane"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Niedostępny"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 88c69eb..7a080a2 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Falha ao abrir as configurações de <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Este método de entrada pode coletar todo o texto que você digita, incluindo dados pessoais, como senhas e números de cartão de crédito. É um método do app <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Usar este método de entrada?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Observação: após uma reinicialização, não é possível iniciar este app até que você desbloqueie seu smartphone"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Estado do registro de IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrado"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Não registrado"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Não disponível"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index dd73f3c..9229e13 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Falha ao abrir as definições para <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Este método de introdução pode permitir a recolha de todo o texto que digitar, incluindo dados pessoais como, por exemplo, palavras-passe e números de cartões de crédito. Decorre da aplicação <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Utilizar este método de introdução?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Nota: após reiniciar, só é possível iniciar esta aplicação quando o telemóvel for desbloqueado."</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Estado do registo IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registado"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Não registado"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Indisponível"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 88c69eb..7a080a2 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Falha ao abrir as configurações de <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Este método de entrada pode coletar todo o texto que você digita, incluindo dados pessoais, como senhas e números de cartão de crédito. É um método do app <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Usar este método de entrada?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Observação: após uma reinicialização, não é possível iniciar este app até que você desbloqueie seu smartphone"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Estado do registro de IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrado"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Não registrado"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Não disponível"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 93ad315..df34d58 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Deschiderea setărilor pentru <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> a eșuat"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Această metodă de introducere de text poate culege în întregime textul introdus, inclusiv datele personale, cum ar fi parolele și numerele cardurilor de credit. Metoda provine de la aplicația <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Utilizați această metodă de introducere de text?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Notă: după repornire, această aplicație nu poate porni până nu deblocați telefonul"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Situația înregistrării IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Înregistrat"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Neînregistrat"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Indisponibilă"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index ee5169b..3752cb3 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Не удалось открыть настройки приложения \"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>\""</string>
<string name="ime_security_warning" msgid="4135828934735934248">"При использовании этого способа могут собираться все вводимые данные, в том числе пароли и номера кредитных карт. Запрос поступил от приложения \"<xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>\". Использовать этот способ ввода?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Примечание. Чтобы запустить это приложение после перезагрузки, разблокируйте экран."</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Статус регистрации сервиса IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Зарегистрирован"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Не зарегистрирован"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Недоступно"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 1ce6786..36fd952 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> සඳහා සැකසීම් විවෘත කිරීම අසාර්ථක විය"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"මෙම ආදාන ක්රමය මුරපද සහ ණයපත් අංක වැනි පෞද්ගලික දත්ත ඇතුළුව ඔබ ටයිප් කරන පෙළ එකතු කිරීමට හැකිය, එය <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> යෙදුමන් ලැබේ. මෙම ආදාන ක්රමය භාවිතා කරන්නද?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"සටහන: නැවත පණ ගැන්වීමකට පසුව, ඔබ ඔබේ දුරකථනය අගුලු හරින තෙක් මෙම යෙදුම ආරම්භ කළ නොහැකිය"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS ලියාපදිංචි තත්ත්වය"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"ලියාපදිංචි වී ඇත"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"ලියාපදිංචි වී නැත"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"ලබාගත නොහැක"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index b1d2fa3..2eea457 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Nastavenia aplikácie <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> sa nepodarilo otvoriť"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Pri tejto metóde vstupu sa môže zhromažďovať zadávaný text vrátane osobných údajov, ako sú heslá alebo čísla kreditných kariet. Táto metóda je poskytovaná aplikáciou <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Chcete použiť túto metódu vstupu?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Poznámka: Po reštartovaní sa táto aplikácia spustí až vtedy, keď odomknete telefón"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Stav registrácie IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrované"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Neregistrované"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Nie je k dispozícii"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 21badd6..e7e6212 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Ni mogoče odpreti nastavitev za <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Ta način vnosa lahko morda zbere besedilo, ki ga vtipkate, vključno z osebnimi podatki, kot so gesla in številke kreditnih kartic. Omogoča ga aplikacija <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Ali želite uporabiti ta način vnosa?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Opomba: po vnovičnem zagonu te aplikacije ni mogoče zagnati, če ne odklenete telefona."</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Stanje registracije IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrirana"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Ni registrirana"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Ni na voljo"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 54cde3c..f557d81 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Dështoi në hapjen e cilësimeve për <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Kjo metodë hyrjeje mund të mbledhë të gjithë tekstin që shkruan, përfshirë të dhënat personale si fjalëkalimet dhe numrat e kartave të kreditit. Kjo vjen nga aplikacioni <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Të përdoret kjo metodë hyrjeje?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Shënim: Pas një rinisjeje, ky aplikacion nuk mund të niset derisa të shkyçësh telefonin"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Gjendja e regjistrimit të IMS-së"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Regjistruar"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Paregjistruar"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Nuk ofrohet"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 4e0bee0..1c0fb24 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Отварање подешавања за апликацију <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> није успело"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Овај метод уноса можда може да прикупља сав текст који уносите, укључујући личне податке, као што су лозинке и бројеви кредитних картица. Потиче од апликације <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Желите ли да користите овај метод уноса?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Напомена: После рестартовања ова апликација не може да се покрене док не откључате телефон"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Статус IMS регистрације"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Регистрован je"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Није регистрован"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Недоступно"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index dea265d..c233bec 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Det gick inte att öppna inställningarna för <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Den här inmatningsmetoden kan samla all text som du skriver, inklusive personliga uppgifter som lösenord och kreditkortsnummer. Den kommer från appen <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Vill du använda inmatningsmetoden?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Obs! När du har startat om enheten måste du låsa upp mobilen innan du kan starta den här appen"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS-registrering"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registrerad"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Ej registrerad"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Inte tillgängligt"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 7126594..700153b 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Imeshindwa kufungua mipangilio ya <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Huenda mbinu hii ya kuingiza ikakusanya maandishi yote unayoandika, pamoja na data ya kibinafsi kama vile manenosiri na nambari za kadi za mkopo. Inatoka kwa programu <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Je, ungependa kutumia mbinu hii ya kingiza?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Kumbuka: Baada ya kuwasha tena programu hii, hutaweza kuitumia hadi utakapofungua simu yako"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Hali ya usajili wa IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Imesajiliwa"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Haijasajiliwa"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Hapatikani"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 4f563fa..5ea41c4 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> க்கான அமைப்புகளைத் திறப்பதில் தோல்வி"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"இந்த உள்ளீட்டு முறையானது, கடவுச்சொற்கள் மற்றும் கிரெடிட் கார்டு எண்கள் போன்ற தனிப்பட்ட தகவல் உள்பட நீங்கள் உள்ளிடும் எல்லா உரையையும் சேகரிக்கக்கூடும். இது <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> பயன்பாட்டிலிருந்து வந்துள்ளது. இந்த உள்ளீட்டு முறையைப் பயன்படுத்தவா?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"குறிப்பு: மறுதொடக்கம் செய்த பிறகு, மொபைலைத் திறக்கும் வரை இந்தப் பயன்பாட்டால் தொடங்க முடியாது"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS பதிவின் நிலை"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"பதிவு செய்யப்பட்டது"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"பதிவு செய்யப்படவில்லை"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"கிடைக்கவில்லை"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 0d1ca375..7daa89e 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> యొక్క సెట్టింగ్లను తెరవడం విఫలమైంది"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"ఈ ఇన్పుట్ పద్ధతి మీరు టైప్ చేసే మొత్తం వచనాన్ని అలాగే పాస్వర్డ్లు మరియు క్రెడిట్ కార్డు నంబర్ల వంటి వ్యక్తిగత డేటాను సేకరించగలదు. ఇది <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> అనువర్తనంలో అందించబడుతుంది. ఈ ఇన్పుట్ పద్ధతిని ఉపయోగించాలా?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"గమనిక: రీబూట్ చేసాక, మీరు మీ ఫోన్ను అన్లాక్ చేసే వరకు ఈ యాప్ ప్రారంభం కాదు"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS నమోదు స్థితి"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"నమోదు చేయబడింది"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"నమోదు కాలేదు"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"అందుబాటులో లేదు"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index f7ba20a..37fba990 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"ไม่สามารถเปิดการตั้งค่าสำหรับ <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"วิธีการป้อนข้อมูลนี้อาจเก็บข้อความทั้งหมดที่คุณพิมพ์ รวมถึงข้อมูลส่วนบุคคล เช่น รหัสผ่านและหมายเลขบัตรเครดิต โดยมาจากแอปพลิเคชัน <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ใช้วิธีการป้อนข้อมูลนี้หรือไม่"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"หมายเหตุ: หลังจากเริ่มต้นใหม่ แอปนี้จะไม่สามารถเริ่มการทำงานได้จนกว่าคุณจะปลดล็อกโทรศัพท์"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"สถานะการลงทะเบียน IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"ลงทะเบียนแล้ว"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"ไม่ได้ลงทะเบียน"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"ไม่ว่าง"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 10d8102..25227a3 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Nabigong buksan ang mga setting para sa <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Maaaring magawa ng pamamaraan ng pag-input na ito na kolektahin ang lahat ng tekstong iyong tina-type, kabilang ang personal na data katulad ng mga password at mga numero ng credit card. Nagmumula ito sa app na <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Gamitin ang pamamaraan ng pag-input na ito?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Tandaan: Pagkatapos ng pag-reboot, hindi makakapagsimula ang app na ito hangga\'t hindi mo ina-unlock ang iyong telepono"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Status ng pagpaparehistro ng IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Nakarehistro"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Hindi nakarehistro"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Hindi available"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 3d8d0d3..893d8b3 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> ayarları açılamadı"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Bu giriş yöntemi, şifreler ve kredi kartı numaraları gibi kişisel veriler de dahil olmak üzere yazdığınız tüm metni toplayabilir. Bu yöntem, <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> uygulamasının bir özelliğidir. Bu giriş yöntemini kullanmak istiyor musunuz?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Not: Yeniden başlatma sonrasında, telefonunuzun kilidi açılıncaya kadar bu uygulama başlatılamaz"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS kaydı durumu"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Kaydettirildi"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Kaydettirilmedi"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Kullanılamıyor"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 7d2c6fd..f3ed643 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Не вдалося відкрити налаштування для програми <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Під час використання цього методу введення може записуватись весь текст, який ви вводите, зокрема паролі й номери кредитних карток. Цей метод запитує програма <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Використати цей метод введення?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Примітка: щоб запустити цей додаток після перезавантаження, спершу потрібно буде розблокувати телефон"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Статус реєстрації IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Зареєстровано"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Не зареєстровано"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Недоступно"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 63c040e..550280d 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> کیلئے ترتیبات کھولنے میں ناکام ہوگیا"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"اندراج کا یہ طریقہ آپ کے ٹائپ کردہ سبھی متن کو جمع کر سکتا ہے، بشمول ذاتی ڈیٹا جیسے پاس ورڈز اور کریڈٹ کارڈ نمبرز۔ یہ <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ایپ سے آتا ہے۔ اندراج کا یہ طریقہ استعمال کریں؟"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"نوٹ: ریبوٹ کرنے کے بعد یہ ایپ تب تک شروع نہیں ہو سکتی جب تک آپ اپنا فون غیر مقفل نہ کر لیں"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS رجسٹریشن کی صورتحال"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"رجسٹر شدہ"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"رجسٹر نہیں ہے"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"غیر دستیاب"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index fbe9026..055613b 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> sozlamalarini ochmadi"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Ushbu yozish usuli barcha yozgan matnlaringizni to‘plab olishi mumkin, jumladan kredit karta raqamlari va parollar kabi shaxsiy ma‘lumotlarni ham. Usul <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> ilovasi bilan o‘rnatiladi. Ushbu usuldan foydalanilsinmi?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Eslatma: O‘chirib-yoqilgandan so‘ng, bu ilova to telefoningiz qulfdan chiqarilmaguncha ishga tushmaydi"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS registratsiyasi holati"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Registratsiya qilingan"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Registratsiya qilinmagan"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Mavjud emas"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 07d0999..bc5df8f 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Không thể mở cài đặt cho <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Phương thức nhập này có thể thu thập tất cả văn bản bạn nhập, bao gồm dữ liệu cá nhân như mật khẩu và số thẻ tín dụng. Phương thức nhập này đến từ ứng dụng <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Sử dụng phương thức nhập này?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Lưu ý: Sau khi khởi động lại, ứng dụng này không thể khởi động cho đến khi bạn mở khóa điện thoại"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Trạng thái đăng ký IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Đã đăng ký"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Chưa được đăng ký"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Không có"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 1850f61..4bbc976 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"无法打开 <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> 的设置"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"此输入法可能会收集您输入的所有内容,包括密码和信用卡号等个人数据。它来自“<xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>”应用。要使用此输入法吗?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"注意:重新启动后,您必须将手机解锁才能运行此应用"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS 注册状态"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"已注册"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"未注册"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"无法获取"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 379a1f1..e70b63b 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"無法開啟 <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> 的設定"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"這個輸入法可能會收集您輸入的所有文字,包括密碼和信用卡號碼等個人資料。這個輸入法由 <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g> 應用程式提供,您要使用嗎?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"注意:重新啟動後,您必須解鎖手機,才可開始使用此應用程式"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS 註冊狀態"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"已註冊"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"未註冊"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"無法使用"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 2abca68..38ec6c7 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"無法開啟「<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>」的設定"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"這個輸入法是由「<xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>」應用程式所提供,它可能會收集你輸入的所有文字,包括密碼和信用卡號等個人資料。要啟用這個輸入法嗎?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"注意:重新啟動後,你必須將手機解鎖,才能執行這個應用程式"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"IMS 註冊狀態"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"已註冊"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"未註冊"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"無法取得"</string>
</resources>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 1de1312..1c15d54 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -388,4 +388,8 @@
<string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Yehlulekile ukuvula izilungiselelo ze-<xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g>"</string>
<string name="ime_security_warning" msgid="4135828934735934248">"Indlela yokufakwayo ingase ikwazi ukuqoqa wonke umbhalo owuthayiphayo, kuhlanganise idatha yomuntu siqu njengamaphasiwedi nezinombolo zekhadi lesikoloto. Iphuma kuhlelo lokusebenza <xliff:g id="IME_APPLICATION_NAME">%1$s</xliff:g>. Sebenzisa indlela yokufaka?"</string>
<string name="direct_boot_unaware_dialog_message" msgid="7870273558547549125">"Yazi: Ngemuva kokuqalisa, lolu hlelo lokusebenza alukwazi ukuqala uze uvule ifoni yakho"</string>
+ <string name="ims_reg_title" msgid="7609782759207241443">"Isimo sokubhaliswa se-IMS"</string>
+ <string name="ims_reg_status_registered" msgid="933003316932739188">"Kubhalisiwe"</string>
+ <string name="ims_reg_status_not_registered" msgid="6529783773485229486">"Akubhalisiwe"</string>
+ <string name="status_unavailable" msgid="7862009036663793314">"Ayitholakali"</string>
</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index fbfa725..3d083b1 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -289,7 +289,8 @@
<string name="launch_defaults_some">Some defaults set</string>
<!-- Launch defaults preference summary with none set [CHAR LIMIT=40] -->
<string name="launch_defaults_none">No defaults set</string>
-
+ <!-- DO NOT TRANSLATE Empty summary for dynamic preferences -->
+ <string name="summary_empty" translatable="false"></string>
<!-- Text-To-Speech (TTS) settings --><skip />
<!-- Name of the TTS package as listed by the package manager. -->
<string name="tts_settings">Text-to-speech settings</string>
@@ -982,4 +983,14 @@
<!-- [CHAR LIMIT=NONE] Dialog body explaining that the app just selected by the user will not work after a reboot until until after the user enters their credentials, such as a PIN or password. -->
<string name="direct_boot_unaware_dialog_message">Note: After a reboot, this app can\'t start until you unlock your phone</string>
+ <!--Label of IMS registration header -->
+ <string name="ims_reg_title">"IMS registration state"</string>
+ <!--Used when IMS registration state is registered -->
+ <string name="ims_reg_status_registered">"Registered"</string>
+ <!--Used when IMS registration state is not registered -->
+ <string name="ims_reg_status_not_registered">"Not registered"</string>
+
+ <!-- About phone, status item value if the actual value is not available. -->
+ <string name="status_unavailable">Unavailable</string>
+
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
index 47cbb77..6aae226 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractEnableAdbPreferenceController.java
@@ -28,17 +28,20 @@
import android.support.v7.preference.TwoStatePreference;
import android.text.TextUtils;
-import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.ConfirmationDialogController;
-public abstract class AbstractEnableAdbPreferenceController extends AbstractPreferenceController
- implements ConfirmationDialogController {
+public abstract class AbstractEnableAdbPreferenceController extends
+ DeveloperOptionsPreferenceController implements ConfirmationDialogController {
private static final String KEY_ENABLE_ADB = "enable_adb";
public static final String ACTION_ENABLE_ADB_STATE_CHANGED =
"com.android.settingslib.development.AbstractEnableAdbController."
+ "ENABLE_ADB_STATE_CHANGED";
- private SwitchPreference mPreference;
+ public static final int ADB_SETTING_ON = 1;
+ public static final int ADB_SETTING_OFF = 0;
+
+
+ protected SwitchPreference mPreference;
public AbstractEnableAdbPreferenceController(Context context) {
super(context);
@@ -64,12 +67,13 @@
private boolean isAdbEnabled() {
final ContentResolver cr = mContext.getContentResolver();
- return Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, 0) != 0;
+ return Settings.Global.getInt(cr, Settings.Global.ADB_ENABLED, ADB_SETTING_OFF)
+ != ADB_SETTING_OFF;
}
@Override
public void updateState(Preference preference) {
- ((TwoStatePreference)preference).setChecked(isAdbEnabled());
+ ((TwoStatePreference) preference).setChecked(isAdbEnabled());
}
public void enablePreference(boolean enabled) {
@@ -105,7 +109,7 @@
protected void writeAdbSetting(boolean enabled) {
Settings.Global.putInt(mContext.getContentResolver(),
- Settings.Global.ADB_ENABLED, enabled ? 1 : 0);
+ Settings.Global.ADB_ENABLED, enabled ? ADB_SETTING_ON : ADB_SETTING_OFF);
notifyStateChanged();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java
index c167723..f79be7e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java
@@ -26,29 +26,34 @@
import android.support.v7.preference.PreferenceScreen;
import com.android.settingslib.R;
-import com.android.settingslib.core.AbstractPreferenceController;
-public abstract class AbstractLogdSizePreferenceController extends AbstractPreferenceController
- implements Preference.OnPreferenceChangeListener {
+public abstract class AbstractLogdSizePreferenceController extends
+ DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener {
public static final String ACTION_LOGD_SIZE_UPDATED = "com.android.settingslib.development."
+ "AbstractLogdSizePreferenceController.LOGD_SIZE_UPDATED";
public static final String EXTRA_CURRENT_LOGD_VALUE = "CURRENT_LOGD_VALUE";
+ @VisibleForTesting
+ static final String LOW_RAM_CONFIG_PROPERTY_KEY = "ro.config.low_ram";
private static final String SELECT_LOGD_SIZE_KEY = "select_logd_size";
@VisibleForTesting
static final String SELECT_LOGD_SIZE_PROPERTY = "persist.logd.size";
static final String SELECT_LOGD_TAG_PROPERTY = "persist.log.tag";
// Tricky, isLoggable only checks for first character, assumes silence
static final String SELECT_LOGD_TAG_SILENCE = "Settings";
- private static final String SELECT_LOGD_SNET_TAG_PROPERTY = "persist.log.tag.snet_event_log";
+ @VisibleForTesting
+ static final String SELECT_LOGD_SNET_TAG_PROPERTY = "persist.log.tag.snet_event_log";
private static final String SELECT_LOGD_RUNTIME_SNET_TAG_PROPERTY = "log.tag.snet_event_log";
private static final String SELECT_LOGD_DEFAULT_SIZE_PROPERTY = "ro.logd.size";
@VisibleForTesting
static final String SELECT_LOGD_DEFAULT_SIZE_VALUE = "262144";
private static final String SELECT_LOGD_SVELTE_DEFAULT_SIZE_VALUE = "65536";
// 32768 is merely a menu marker, 64K is our lowest log buffer size we replace it with.
- private static final String SELECT_LOGD_MINIMUM_SIZE_VALUE = "65536";
+ @VisibleForTesting
+ static final String SELECT_LOGD_MINIMUM_SIZE_VALUE = "65536";
static final String SELECT_LOGD_OFF_SIZE_MARKER_VALUE = "32768";
+ @VisibleForTesting
+ static final String DEFAULT_SNET_TAG = "I";
private ListPreference mLogdSize;
@@ -57,11 +62,6 @@
}
@Override
- public boolean isAvailable() {
- return true;
- }
-
- @Override
public String getPreferenceKey() {
return SELECT_LOGD_SIZE_KEY;
}
@@ -160,7 +160,7 @@
if ((snetValue == null) || (snetValue.length() == 0)) {
snetValue = SystemProperties.get(SELECT_LOGD_RUNTIME_SNET_TAG_PROPERTY);
if ((snetValue == null) || (snetValue.length() == 0)) {
- SystemProperties.set(SELECT_LOGD_SNET_TAG_PROPERTY, "I");
+ SystemProperties.set(SELECT_LOGD_SNET_TAG_PROPERTY, DEFAULT_SNET_TAG);
}
}
// Silence all log sources, security logs notwithstanding
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java
index 502fb17..67553adc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java
@@ -30,16 +30,15 @@
import android.text.TextUtils;
import com.android.settingslib.R;
-import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.ConfirmationDialogController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnCreate;
import com.android.settingslib.core.lifecycle.events.OnDestroy;
-public abstract class AbstractLogpersistPreferenceController extends AbstractPreferenceController
- implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnCreate, OnDestroy,
- ConfirmationDialogController {
+public abstract class AbstractLogpersistPreferenceController extends
+ DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
+ LifecycleObserver, OnCreate, OnDestroy, ConfirmationDialogController {
private static final String SELECT_LOGPERSIST_KEY = "select_logpersist";
private static final String SELECT_LOGPERSIST_PROPERTY = "persist.logd.logpersistd";
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
new file mode 100644
index 0000000..f68c04f
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/development/DeveloperOptionsPreferenceController.java
@@ -0,0 +1,76 @@
+/*
+ * 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.settingslib.development;
+
+import android.content.Context;
+
+import com.android.settingslib.core.AbstractPreferenceController;
+
+/**
+ * This controller is used handle changes for the master switch in the developer options page.
+ *
+ * All Preference Controllers that are a part of the developer options page should inherit this
+ * class.
+ */
+public abstract class DeveloperOptionsPreferenceController extends
+ AbstractPreferenceController {
+
+ public DeveloperOptionsPreferenceController(Context context) {
+ super(context);
+ }
+
+ /**
+ * Child classes should override this method to create custom logic for hiding preferences.
+ *
+ * @return true if the preference is to be displayed.
+ */
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ /**
+ * Called when developer options is enabled
+ */
+ public void onDeveloperOptionsEnabled() {
+ if (isAvailable()) {
+ onDeveloperOptionsSwitchEnabled();
+ }
+ }
+
+ /**
+ * Called when developer options is disabled
+ */
+ public void onDeveloperOptionsDisabled() {
+ if (isAvailable()) {
+ onDeveloperOptionsSwitchDisabled();
+ }
+ }
+
+ /**
+ * Called when developer options is enabled and the preference is available
+ */
+ protected void onDeveloperOptionsSwitchEnabled() {
+ }
+
+ /**
+ * Called when developer options is disabled and the preference is available
+ */
+ protected void onDeveloperOptionsSwitchDisabled() {
+ }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java
new file mode 100644
index 0000000..ba358f8
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractBluetoothAddressPreferenceController.java
@@ -0,0 +1,85 @@
+/*
+ * 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.settingslib.deviceinfo;
+
+import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
+
+import com.android.settingslib.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+/**
+ * Preference controller for bluetooth address
+ */
+public abstract class AbstractBluetoothAddressPreferenceController
+ extends AbstractConnectivityPreferenceController {
+
+ @VisibleForTesting
+ static final String KEY_BT_ADDRESS = "bt_address";
+
+ private static final String[] CONNECTIVITY_INTENTS = {
+ BluetoothAdapter.ACTION_STATE_CHANGED
+ };
+
+ private Preference mBtAddress;
+
+ public AbstractBluetoothAddressPreferenceController(Context context, Lifecycle lifecycle) {
+ super(context, lifecycle);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return BluetoothAdapter.getDefaultAdapter() != null;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_BT_ADDRESS;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mBtAddress = screen.findPreference(KEY_BT_ADDRESS);
+ updateConnectivity();
+ }
+
+ @Override
+ protected String[] getConnectivityIntents() {
+ return CONNECTIVITY_INTENTS;
+ }
+
+ @SuppressLint("HardwareIds")
+ @Override
+ protected void updateConnectivity() {
+ BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter();
+ if (bluetooth != null && mBtAddress != null) {
+ String address = bluetooth.isEnabled() ? bluetooth.getAddress() : null;
+ if (!TextUtils.isEmpty(address)) {
+ // Convert the address to lowercase for consistency with the wifi MAC address.
+ mBtAddress.setSummary(address.toLowerCase());
+ } else {
+ mBtAddress.setSummary(R.string.status_unavailable);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java
new file mode 100644
index 0000000..c6552f7
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractConnectivityPreferenceController.java
@@ -0,0 +1,114 @@
+/*
+ * 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.settingslib.deviceinfo;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.Message;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Base class for preference controllers which listen to connectivity broadcasts
+ */
+public abstract class AbstractConnectivityPreferenceController
+ extends AbstractPreferenceController implements LifecycleObserver, OnStart, OnStop {
+
+ private final BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (ArrayUtils.contains(getConnectivityIntents(), action)) {
+ getHandler().sendEmptyMessage(EVENT_UPDATE_CONNECTIVITY);
+ }
+ }
+ };
+
+ private static final int EVENT_UPDATE_CONNECTIVITY = 600;
+
+ private Handler mHandler;
+
+ public AbstractConnectivityPreferenceController(Context context, Lifecycle lifecycle) {
+ super(context);
+ if (lifecycle != null) {
+ lifecycle.addObserver(this);
+ }
+ }
+
+ @Override
+ public void onStop() {
+ mContext.unregisterReceiver(mConnectivityReceiver);
+ }
+
+ @Override
+ public void onStart() {
+ final IntentFilter connectivityIntentFilter = new IntentFilter();
+ final String[] intents = getConnectivityIntents();
+ for (String intent : intents) {
+ connectivityIntentFilter.addAction(intent);
+ }
+
+ mContext.registerReceiver(mConnectivityReceiver, connectivityIntentFilter,
+ android.Manifest.permission.CHANGE_NETWORK_STATE, null);
+ }
+
+ protected abstract String[] getConnectivityIntents();
+
+ protected abstract void updateConnectivity();
+
+ private Handler getHandler() {
+ if (mHandler == null) {
+ mHandler = new ConnectivityEventHandler(this);
+ }
+ return mHandler;
+ }
+
+ private static class ConnectivityEventHandler extends Handler {
+ private WeakReference<AbstractConnectivityPreferenceController> mPreferenceController;
+
+ public ConnectivityEventHandler(AbstractConnectivityPreferenceController activity) {
+ mPreferenceController = new WeakReference<>(activity);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ AbstractConnectivityPreferenceController preferenceController
+ = mPreferenceController.get();
+ if (preferenceController == null) {
+ return;
+ }
+
+ switch (msg.what) {
+ case EVENT_UPDATE_CONNECTIVITY:
+ preferenceController.updateConnectivity();
+ break;
+ default:
+ throw new IllegalStateException("Unknown message " + msg.what);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java
new file mode 100644
index 0000000..bb8404b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractImsStatusPreferenceController.java
@@ -0,0 +1,95 @@
+/*
+ * 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.settingslib.deviceinfo;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
+import android.os.PersistableBundle;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import com.android.settingslib.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+/**
+ * Preference controller for IMS status
+ */
+public abstract class AbstractImsStatusPreferenceController
+ extends AbstractConnectivityPreferenceController {
+
+ @VisibleForTesting
+ static final String KEY_IMS_REGISTRATION_STATE = "ims_reg_state";
+
+ private static final String[] CONNECTIVITY_INTENTS = {
+ BluetoothAdapter.ACTION_STATE_CHANGED,
+ ConnectivityManager.CONNECTIVITY_ACTION,
+ WifiManager.LINK_CONFIGURATION_CHANGED_ACTION,
+ WifiManager.NETWORK_STATE_CHANGED_ACTION,
+ };
+
+ private Preference mImsStatus;
+
+ public AbstractImsStatusPreferenceController(Context context,
+ Lifecycle lifecycle) {
+ super(context, lifecycle);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ CarrierConfigManager configManager = mContext.getSystemService(CarrierConfigManager.class);
+ int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+ PersistableBundle config = null;
+ if (configManager != null) {
+ config = configManager.getConfigForSubId(subId);
+ }
+ return config != null && config.getBoolean(
+ CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL);
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_IMS_REGISTRATION_STATE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mImsStatus = screen.findPreference(KEY_IMS_REGISTRATION_STATE);
+ updateConnectivity();
+ }
+
+ @Override
+ protected String[] getConnectivityIntents() {
+ return CONNECTIVITY_INTENTS;
+ }
+
+ @Override
+ protected void updateConnectivity() {
+ int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+ if (mImsStatus != null) {
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
+ mImsStatus.setSummary((tm != null && tm.isImsRegistered(subId)) ?
+ R.string.ims_reg_status_registered : R.string.ims_reg_status_not_registered);
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java
new file mode 100644
index 0000000..ded3022
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractIpAddressPreferenceController.java
@@ -0,0 +1,112 @@
+/*
+ * 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.settingslib.deviceinfo;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.wifi.WifiManager;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settingslib.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import java.net.InetAddress;
+import java.util.Iterator;
+
+/**
+ * Preference controller for IP address
+ */
+public abstract class AbstractIpAddressPreferenceController
+ extends AbstractConnectivityPreferenceController {
+
+ @VisibleForTesting
+ static final String KEY_IP_ADDRESS = "wifi_ip_address";
+
+ private static final String[] CONNECTIVITY_INTENTS = {
+ ConnectivityManager.CONNECTIVITY_ACTION,
+ WifiManager.LINK_CONFIGURATION_CHANGED_ACTION,
+ WifiManager.NETWORK_STATE_CHANGED_ACTION,
+ };
+
+ private Preference mIpAddress;
+ private final ConnectivityManager mCM;
+
+ public AbstractIpAddressPreferenceController(Context context, Lifecycle lifecycle) {
+ super(context, lifecycle);
+ mCM = context.getSystemService(ConnectivityManager.class);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_IP_ADDRESS;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mIpAddress = screen.findPreference(KEY_IP_ADDRESS);
+ updateConnectivity();
+ }
+
+ @Override
+ protected String[] getConnectivityIntents() {
+ return CONNECTIVITY_INTENTS;
+ }
+
+ @Override
+ protected void updateConnectivity() {
+ String ipAddress = getDefaultIpAddresses(mCM);
+ if (ipAddress != null) {
+ mIpAddress.setSummary(ipAddress);
+ } else {
+ mIpAddress.setSummary(R.string.status_unavailable);
+ }
+ }
+
+ /**
+ * Returns the default link's IP addresses, if any, taking into account IPv4 and IPv6 style
+ * addresses.
+ * @param cm ConnectivityManager
+ * @return the formatted and newline-separated IP addresses, or null if none.
+ */
+ private static String getDefaultIpAddresses(ConnectivityManager cm) {
+ LinkProperties prop = cm.getActiveLinkProperties();
+ return formatIpAddresses(prop);
+ }
+
+ private static String formatIpAddresses(LinkProperties prop) {
+ if (prop == null) return null;
+ Iterator<InetAddress> iter = prop.getAllAddresses().iterator();
+ // If there are no entries, return null
+ if (!iter.hasNext()) return null;
+ // Concatenate all available addresses, newline separated
+ StringBuilder addresses = new StringBuilder();
+ while (iter.hasNext()) {
+ addresses.append(iter.next().getHostAddress());
+ if (iter.hasNext()) addresses.append("\n");
+ }
+ return addresses.toString();
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java
new file mode 100644
index 0000000..ac61ade
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractUptimePreferenceController.java
@@ -0,0 +1,119 @@
+/*
+ * 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.settingslib.deviceinfo;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.format.DateUtils;
+
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Preference controller for uptime
+ */
+public abstract class AbstractUptimePreferenceController extends AbstractPreferenceController
+ implements LifecycleObserver, OnStart, OnStop {
+
+ @VisibleForTesting
+ static final String KEY_UPTIME = "up_time";
+ private static final int EVENT_UPDATE_STATS = 500;
+
+ private Preference mUptime;
+ private Handler mHandler;
+
+ public AbstractUptimePreferenceController(Context context, Lifecycle lifecycle) {
+ super(context);
+ if (lifecycle != null) {
+ lifecycle.addObserver(this);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ getHandler().sendEmptyMessage(EVENT_UPDATE_STATS);
+ }
+
+ @Override
+ public void onStop() {
+ getHandler().removeMessages(EVENT_UPDATE_STATS);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_UPTIME;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mUptime = screen.findPreference(KEY_UPTIME);
+ updateTimes();
+ }
+
+ private Handler getHandler() {
+ if (mHandler == null) {
+ mHandler = new MyHandler(this);
+ }
+ return mHandler;
+ }
+
+ private void updateTimes() {
+ mUptime.setSummary(DateUtils.formatDuration(SystemClock.elapsedRealtime()));
+ }
+
+ private static class MyHandler extends Handler {
+ private WeakReference<AbstractUptimePreferenceController> mStatus;
+
+ public MyHandler(AbstractUptimePreferenceController activity) {
+ mStatus = new WeakReference<>(activity);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ AbstractUptimePreferenceController status = mStatus.get();
+ if (status == null) {
+ return;
+ }
+
+ switch (msg.what) {
+ case EVENT_UPDATE_STATS:
+ status.updateTimes();
+ sendEmptyMessageDelayed(EVENT_UPDATE_STATS, 1000);
+ break;
+
+ default:
+ throw new IllegalStateException("Unknown message " + msg.what);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
new file mode 100644
index 0000000..d57b64f
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/AbstractWifiMacAddressPreferenceController.java
@@ -0,0 +1,88 @@
+/*
+ * 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.settingslib.deviceinfo;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.support.annotation.VisibleForTesting;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
+
+import com.android.settingslib.R;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+/**
+ * Preference controller for WIFI MAC address
+ */
+public abstract class AbstractWifiMacAddressPreferenceController
+ extends AbstractConnectivityPreferenceController {
+
+ @VisibleForTesting
+ static final String KEY_WIFI_MAC_ADDRESS = "wifi_mac_address";
+
+ private static final String[] CONNECTIVITY_INTENTS = {
+ ConnectivityManager.CONNECTIVITY_ACTION,
+ WifiManager.LINK_CONFIGURATION_CHANGED_ACTION,
+ WifiManager.NETWORK_STATE_CHANGED_ACTION,
+ };
+
+ private Preference mWifiMacAddress;
+ private final WifiManager mWifiManager;
+
+ public AbstractWifiMacAddressPreferenceController(Context context, Lifecycle lifecycle) {
+ super(context, lifecycle);
+ mWifiManager = context.getSystemService(WifiManager.class);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_WIFI_MAC_ADDRESS;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mWifiMacAddress = screen.findPreference(KEY_WIFI_MAC_ADDRESS);
+ updateConnectivity();
+ }
+
+ @Override
+ protected String[] getConnectivityIntents() {
+ return CONNECTIVITY_INTENTS;
+ }
+
+ @SuppressLint("HardwareIds")
+ @Override
+ protected void updateConnectivity() {
+ WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+ String macAddress = wifiInfo == null ? null : wifiInfo.getMacAddress();
+ if (!TextUtils.isEmpty(macAddress)) {
+ mWifiMacAddress.setSummary(macAddress);
+ } else {
+ mWifiMacAddress.setSummary(R.string.status_unavailable);
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
index 56b8441..9c34763 100644
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
+++ b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
@@ -261,7 +261,7 @@
if (requiredAccountType == null) {
return true;
}
- AccountManager accountManager = AccountManager.get(mContext);
+ AccountManager accountManager = mContext.getSystemService(AccountManager.class);
Account[] accounts = accountManager.getAccountsByType(requiredAccountType);
boolean satisfiesRequiredAccount = accounts.length > 0;
if (!satisfiesRequiredAccount) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 12455d8..c56e1da 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -291,15 +291,6 @@
}
/**
- * Force a scan for wifi networks to happen now.
- */
- public void forceScan() {
- if (mWifiManager.isWifiEnabled() && mScanner != null) {
- mScanner.forceScan();
- }
- }
-
- /**
* Temporarily stop scanning for wifi networks.
*/
public void pauseScanning() {
@@ -789,14 +780,6 @@
}
}
- public static List<AccessPoint> getCurrentAccessPoints(Context context, boolean includeSaved,
- boolean includeScans) {
- WifiTracker tracker = new WifiTracker(context, null, includeSaved, includeScans);
- tracker.forceUpdate();
- tracker.copyAndNotifyListeners(false /*notifyListeners*/);
- return tracker.getAccessPoints();
- }
-
@VisibleForTesting
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -986,11 +969,6 @@
}
}
- void forceScan() {
- removeMessages(MSG_SCAN);
- sendEmptyMessage(MSG_SCAN);
- }
-
void pause() {
mRetry = 0;
removeMessages(MSG_SCAN);
diff --git a/packages/SettingsLib/tests/robotests/Android.mk b/packages/SettingsLib/tests/robotests/Android.mk
index 55b635e..bc1a834 100644
--- a/packages/SettingsLib/tests/robotests/Android.mk
+++ b/packages/SettingsLib/tests/robotests/Android.mk
@@ -42,11 +42,12 @@
# Include the testing libraries (JUnit4 + Robolectric libs).
LOCAL_STATIC_JAVA_LIBRARIES := \
mockito-robolectric-prebuilt \
+ platform-robolectric-android-all-stubs \
truth-prebuilt
LOCAL_JAVA_LIBRARIES := \
junit \
- platform-robolectric-prebuilt
+ platform-robolectric-3.4.2-prebuilt
LOCAL_INSTRUMENTATION_FOR := SettingsLibShell
LOCAL_MODULE := SettingsLibRoboTests
@@ -69,4 +70,6 @@
LOCAL_TEST_PACKAGE := SettingsLibShell
-include prebuilts/misc/common/robolectric/run_robotests.mk
+LOCAL_ROBOTEST_TIMEOUT := 36000
+
+include prebuilts/misc/common/robolectric/3.4.2/run_robotests.mk
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
index c3a505a..ca366ea 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
@@ -20,8 +20,11 @@
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
+
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
@@ -56,10 +59,10 @@
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private RestrictedLockUtils.Proxy mProxy;
- private static final int mUserId = 194;
- private static final int mProfileId = 160;
- private static final ComponentName mAdmin1 = new ComponentName("admin1", "admin1class");
- private static final ComponentName mAdmin2 = new ComponentName("admin2", "admin2class");
+ private final int mUserId = 194;
+ private final int mProfileId = 160;
+ private final ComponentName mAdmin1 = new ComponentName("admin1", "admin1class");
+ private final ComponentName mAdmin2 = new ComponentName("admin2", "admin2class");
@Before
public void setUp() {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
index 8099eb1..698e442 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/SettingsLibRobolectricTestRunner.java
@@ -53,15 +53,15 @@
static void getIncludedResourcePaths(String packageName, List<ResourcePath> paths) {
paths.add(new ResourcePath(
- packageName,
+ null,
Fs.fileFromPath("./frameworks/base/packages/SettingsLib/res"),
null));
paths.add(new ResourcePath(
- packageName,
+ null,
Fs.fileFromPath("./frameworks/base/core/res/res"),
null));
paths.add(new ResourcePath(
- packageName,
+ null,
Fs.fileFromPath("./frameworks/support/v7/appcompat/res"),
null));
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TestConfig.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TestConfig.java
index 31abecd..3af9768 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TestConfig.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/TestConfig.java
@@ -17,7 +17,7 @@
package com.android.settingslib;
public class TestConfig {
- public static final int SDK_VERSION = 23;
+ public static final int SDK_VERSION = 25;
public static final String MANIFEST_PATH =
"frameworks/base/packages/SettingsLib/tests/robotests/AndroidManifest.xml";
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
index d6bca0e..1290391 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/lifecycle/LifecycleTest.java
@@ -35,9 +35,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
+import org.robolectric.android.controller.ActivityController;
+import org.robolectric.android.controller.FragmentController;
import org.robolectric.annotation.Config;
-import org.robolectric.util.ActivityController;
-import org.robolectric.util.FragmentController;
import static com.google.common.truth.Truth.assertThat;
@@ -168,7 +168,7 @@
Robolectric.buildFragment(TestDialogFragment.class);
TestDialogFragment fragment = fragmentController.get();
- fragmentController.attach().create().start().resume();
+ fragmentController.create().start().resume();
fragment.onCreateOptionsMenu(null, null);
fragment.onPrepareOptionsMenu(null);
fragment.onOptionsItemSelected(null);
@@ -192,7 +192,7 @@
Robolectric.buildFragment(TestFragment.class);
TestFragment fragment = fragmentController.get();
- fragmentController.attach().create().start().resume();
+ fragmentController.create().start().resume();
fragment.onCreateOptionsMenu(null, null);
fragment.onPrepareOptionsMenu(null);
fragment.onOptionsItemSelected(null);
@@ -235,7 +235,7 @@
OptionItemAccepter accepter = new OptionItemAccepter();
fragment.getLifecycle().addObserver(accepter);
- fragmentController.attach().create().start().resume();
+ fragmentController.create().start().resume();
fragment.onCreateOptionsMenu(null, null);
fragment.onPrepareOptionsMenu(null);
fragment.onOptionsItemSelected(null);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
index 94bfd8f..26d3570 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
@@ -16,9 +16,29 @@
package com.android.settingslib.development;
+import static com.android.settingslib.development.AbstractLogdSizePreferenceController
+ .DEFAULT_SNET_TAG;
+import static com.android.settingslib.development.AbstractLogdSizePreferenceController
+ .LOW_RAM_CONFIG_PROPERTY_KEY;
+import static com.android.settingslib.development.AbstractLogdSizePreferenceController
+ .SELECT_LOGD_MINIMUM_SIZE_VALUE;
+import static com.android.settingslib.development.AbstractLogdSizePreferenceController
+ .SELECT_LOGD_OFF_SIZE_MARKER_VALUE;
+import static com.android.settingslib.development.AbstractLogdSizePreferenceController
+ .SELECT_LOGD_SIZE_PROPERTY;
+import static com.android.settingslib.development.AbstractLogdSizePreferenceController
+ .SELECT_LOGD_SNET_TAG_PROPERTY;
+import static com.android.settingslib.development.AbstractLogdSizePreferenceController
+ .SELECT_LOGD_TAG_PROPERTY;
+import static com.android.settingslib.development.AbstractLogdSizePreferenceController
+ .SELECT_LOGD_TAG_SILENCE;
+
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
+import android.content.Context;
import android.os.SystemProperties;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.PreferenceScreen;
@@ -27,6 +47,7 @@
import com.android.settingslib.SettingsLibRobolectricTestRunner;
import com.android.settingslib.TestConfig;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -45,22 +66,43 @@
@Mock
private PreferenceScreen mPreferenceScreen;
+ /**
+ * List Values
+ *
+ * 0: off
+ * 1: 64k
+ * 2: 256k
+ * 3: 1M
+ * 4: 4M
+ * 5: 16M
+ */
+ private String[] mListValues;
+ private String[] mListSummaries;
+ private Context mContext;
private AbstractLogdSizePreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mController = new AbstractLogdSizePreferenceController(RuntimeEnvironment.application) {};
-
+ mContext = RuntimeEnvironment.application;
+ mController = new AbstractLogdSizePreferenceController(RuntimeEnvironment.application) {
+ };
+ mListValues = mContext.getResources().getStringArray(R.array.select_logd_size_values);
+ mListSummaries = mContext.getResources().getStringArray(R.array.select_logd_size_summaries);
doReturn(mListPreference).when(mPreferenceScreen)
.findPreference(mController.getPreferenceKey());
mController.displayPreference(mPreferenceScreen);
}
+ @After
+ public void tearDown() {
+ SystemPropertiesTestImpl.clear();
+ }
+
@Test
- public void testUpateLogdSizeValues_lowRamEntries() {
- SystemProperties.set("ro.config.low_ram", "true");
+ public void testUpdateLogdSizeValues_lowRamEntries() {
+ SystemProperties.set(LOW_RAM_CONFIG_PROPERTY_KEY, "true");
mController.updateLogdSizeValues();
verify(mListPreference).setEntries(R.array.select_logd_size_lowram_titles);
}
@@ -77,4 +119,79 @@
verify(mListPreference).setValue(
AbstractLogdSizePreferenceController.SELECT_LOGD_OFF_SIZE_MARKER_VALUE);
}
+
+ @Test
+ public void onPreferenceChange_noTagsSizeValueOff_shouldSetTagAndSnetTagAndSet64KSize() {
+ mController.onPreferenceChange(mListPreference, SELECT_LOGD_OFF_SIZE_MARKER_VALUE);
+
+ final String tag = SystemProperties.get(SELECT_LOGD_TAG_PROPERTY);
+ final String logSize = SystemProperties.get(SELECT_LOGD_SIZE_PROPERTY);
+ final String snetTag = SystemProperties.get(SELECT_LOGD_SNET_TAG_PROPERTY);
+
+ assertThat(tag).isEqualTo(SELECT_LOGD_TAG_SILENCE);
+ assertThat(logSize).isEqualTo(SELECT_LOGD_MINIMUM_SIZE_VALUE);
+ assertThat(snetTag).isEqualTo(DEFAULT_SNET_TAG);
+ }
+
+ @Test
+ public void onPreferenceChange_noTagsSizeValue64K_shouldNotSetTagAndSet64KSize() {
+ mController.onPreferenceChange(mListPreference, SELECT_LOGD_MINIMUM_SIZE_VALUE);
+
+ final String tag = SystemProperties.get(SELECT_LOGD_TAG_PROPERTY);
+ final String logSize = SystemProperties.get(SELECT_LOGD_SIZE_PROPERTY);
+ final String snetTag = SystemProperties.get(SELECT_LOGD_SNET_TAG_PROPERTY);
+
+ assertThat(tag).isEmpty();
+ assertThat(logSize).isEqualTo(SELECT_LOGD_MINIMUM_SIZE_VALUE);
+ assertThat(snetTag).isEmpty();
+ }
+
+ @Test
+ public void onPreferenceChange_set1M_shouldUpdateSettingLogSizeTo1M() {
+ mController.onPreferenceChange(mListPreference, mListValues[3]);
+
+ final String logSize = SystemProperties.get(SELECT_LOGD_SIZE_PROPERTY);
+
+ assertThat(logSize).isEqualTo(mListValues[3]);
+ }
+
+ @Test
+ public void onPreferenceChange_noValue_shouldUpdateSettingToEmpty() {
+ mController.onPreferenceChange(mListPreference, "" /* new value */);
+
+ final String logSize = SystemProperties.get(SELECT_LOGD_SIZE_PROPERTY);
+
+ assertThat(logSize).isEmpty();
+ }
+
+ @Test
+ public void updateLogdSizeValues_noValueSet_shouldSetDefaultTo64K() {
+ SystemProperties.set(SELECT_LOGD_SIZE_PROPERTY, "" /* new value */);
+
+ mController.updateLogdSizeValues();
+
+ verify(mListPreference).setValue(mListValues[2]);
+ verify(mListPreference).setSummary(mListSummaries[2]);
+ }
+
+ @Test
+ public void updateLogdSizeValues_noValueSetLowRamSet_shouldSetDefaultTo64K() {
+ SystemProperties.set(LOW_RAM_CONFIG_PROPERTY_KEY, Boolean.toString(true));
+ SystemProperties.set(SELECT_LOGD_SIZE_PROPERTY, "" /* new value */);
+
+ mController.updateLogdSizeValues();
+
+ verify(mListPreference).setValue(mListValues[1]);
+ verify(mListPreference).setSummary(mListSummaries[1]);
+ }
+
+ @Test
+ public void updateLogdSizeValues_64KSet_shouldSet64K() {
+ SystemProperties.set(SELECT_LOGD_SIZE_PROPERTY, mListValues[1]);
+
+ mController.updateLogdSizeValues();
+
+ verify(mListPreference).setValue(mListValues[1]);
+ verify(mListPreference).setSummary(mListSummaries[1]);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropertiesTestImpl.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropertiesTestImpl.java
index 2f89d86..6977e09 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropertiesTestImpl.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropertiesTestImpl.java
@@ -54,4 +54,8 @@
public static void set(String key, String val) {
sProperties.put(key, val);
}
+
+ public static synchronized void clear() {
+ sProperties.clear();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java
new file mode 100644
index 0000000..1de7a7a
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/BluetoothAddressPreferenceControllerTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.settingslib.deviceinfo;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class BluetoothAddressPreferenceControllerTest {
+ @Mock
+ private Context mContext;
+ @Mock
+ private Lifecycle mLifecycle;
+ @Mock
+ private PreferenceScreen mScreen;
+ @Mock
+ private Preference mPreference;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ doReturn(mPreference).when(mScreen)
+ .findPreference(AbstractBluetoothAddressPreferenceController.KEY_BT_ADDRESS);
+ }
+
+ @Implements(BluetoothAdapter.class)
+ public static class ShadowEmptyBluetoothAdapter {
+ @Implementation
+ public static BluetoothAdapter getDefaultAdapter() {
+ return null;
+ }
+ }
+
+ @Test
+ @Config(shadows = ShadowEmptyBluetoothAdapter.class)
+ public void testNoBluetooth() {
+ final AbstractBluetoothAddressPreferenceController bluetoothAddressPreferenceController =
+ new ConcreteBluetoothAddressPreferenceController(mContext, mLifecycle);
+
+ assertWithMessage("Should not show pref if no bluetooth")
+ .that(bluetoothAddressPreferenceController.isAvailable())
+ .isFalse();
+ }
+
+ @Test
+ public void testHasBluetooth() {
+ final AbstractBluetoothAddressPreferenceController bluetoothAddressPreferenceController =
+ new ConcreteBluetoothAddressPreferenceController(mContext, mLifecycle);
+
+ assertWithMessage("Should show pref if bluetooth is present")
+ .that(bluetoothAddressPreferenceController.isAvailable())
+ .isTrue();
+ }
+
+ @Test
+ public void testHasBluetoothStateChangedFilter() {
+ final AbstractBluetoothAddressPreferenceController bluetoothAddressPreferenceController =
+ new ConcreteBluetoothAddressPreferenceController(mContext, mLifecycle);
+
+ assertWithMessage("Filter should have BluetoothAdapter.ACTION_STATE_CHANGED")
+ .that(bluetoothAddressPreferenceController.getConnectivityIntents())
+ .asList().contains(BluetoothAdapter.ACTION_STATE_CHANGED);
+ }
+
+ private static class ConcreteBluetoothAddressPreferenceController
+ extends AbstractBluetoothAddressPreferenceController {
+
+ public ConcreteBluetoothAddressPreferenceController(Context context,
+ Lifecycle lifecycle) {
+ super(context, lifecycle);
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
new file mode 100644
index 0000000..362dbd9
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ConnectivityPreferenceControllerTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.settingslib.deviceinfo;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.os.Handler;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ConnectivityPreferenceControllerTest {
+ @Mock
+ private Context mContext;
+
+ @Mock
+ private Lifecycle mLifecycle;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testBroadcastReceiver() {
+ final AbstractConnectivityPreferenceController preferenceController =
+ spy(new ConcreteConnectivityPreferenceController(mContext, mLifecycle));
+
+ final ArgumentCaptor<BroadcastReceiver> receiverArgumentCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ final ArgumentCaptor<IntentFilter> filterArgumentCaptor =
+ ArgumentCaptor.forClass(IntentFilter.class);
+
+ doReturn(new String[] {"Filter1", "Filter2"})
+ .when(preferenceController).getConnectivityIntents();
+
+ preferenceController.onStart();
+
+ verify(mContext, times(1))
+ .registerReceiver(receiverArgumentCaptor.capture(),
+ filterArgumentCaptor.capture(),
+ anyString(), nullable(Handler.class));
+
+ final BroadcastReceiver receiver = receiverArgumentCaptor.getValue();
+ final IntentFilter filter = filterArgumentCaptor.getValue();
+
+ assertWithMessage("intent filter should match 'Filter1'")
+ .that(filter.matchAction("Filter1"))
+ .isTrue();
+ assertWithMessage("intent filter should match 'Filter2'")
+ .that(filter.matchAction("Filter2"))
+ .isTrue();
+
+ preferenceController.onStop();
+
+ verify(mContext, times(1)).unregisterReceiver(receiver);
+ }
+
+ private static class ConcreteConnectivityPreferenceController
+ extends AbstractConnectivityPreferenceController {
+
+
+ public ConcreteConnectivityPreferenceController(Context context,
+ Lifecycle lifecycle) {
+ super(context, lifecycle);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return false;
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return null;
+ }
+
+ @Override
+ protected String[] getConnectivityIntents() {
+ return new String[0];
+ }
+
+ @Override
+ protected void updateConnectivity() {
+
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java
new file mode 100644
index 0000000..112ee64
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/ImsStatusPreferenceControllerTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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.settingslib.deviceinfo;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.os.PersistableBundle;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class ImsStatusPreferenceControllerTest {
+ @Mock
+ private Context mContext;
+ @Mock
+ private Lifecycle mLifecycle;
+ @Mock
+ private PreferenceScreen mScreen;
+ @Mock
+ private Preference mPreference;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ doReturn(mPreference).when(mScreen)
+ .findPreference(AbstractImsStatusPreferenceController.KEY_IMS_REGISTRATION_STATE);
+ }
+
+ @Test
+ @Config(shadows = ShadowSubscriptionManager.class)
+ public void testIsAvailable() {
+ CarrierConfigManager carrierConfigManager = mock(CarrierConfigManager.class);
+ doReturn(carrierConfigManager).when(mContext).getSystemService(CarrierConfigManager.class);
+
+ PersistableBundle config = new PersistableBundle(1);
+ config.putBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, true);
+ doReturn(config).when(carrierConfigManager).getConfigForSubId(anyInt());
+
+ final AbstractImsStatusPreferenceController imsStatusPreferenceController =
+ new ConcreteImsStatusPreferenceController(mContext, mLifecycle);
+
+ assertWithMessage("Should be available when IMS registration is true").that(
+ imsStatusPreferenceController.isAvailable()).isTrue();
+
+ config.putBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
+
+ assertWithMessage("Should not be available when IMS registration is false")
+ .that(imsStatusPreferenceController.isAvailable()).isFalse();
+
+ doReturn(null).when(carrierConfigManager).getConfigForSubId(anyInt());
+
+ assertWithMessage("Should not be available when IMS registration is false")
+ .that(imsStatusPreferenceController.isAvailable()).isFalse();
+
+ doReturn(null).when(mContext).getSystemService(CarrierConfigManager.class);
+
+ assertWithMessage("Should not be available when CarrierConfigManager service is null")
+ .that(imsStatusPreferenceController.isAvailable()).isFalse();
+ }
+
+ @Implements(SubscriptionManager.class)
+ public static class ShadowSubscriptionManager {
+ @Implementation
+ public static int getDefaultDataSubscriptionId() {
+ return 1234;
+ }
+ }
+
+ private static class ConcreteImsStatusPreferenceController
+ extends AbstractImsStatusPreferenceController {
+
+ public ConcreteImsStatusPreferenceController(Context context,
+ Lifecycle lifecycle) {
+ super(context, lifecycle);
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
new file mode 100644
index 0000000..d0ecae3
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/IpAddressPreferenceControllerTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.settingslib.deviceinfo;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class IpAddressPreferenceControllerTest {
+ @Mock
+ private Context mContext;
+ @Mock
+ private Lifecycle mLifecycle;
+ @Mock
+ private PreferenceScreen mScreen;
+ @Mock
+ private Preference mPreference;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ doReturn(mPreference).when(mScreen)
+ .findPreference(AbstractIpAddressPreferenceController.KEY_IP_ADDRESS);
+ }
+
+ @Test
+ public void testHasIntentFilters() {
+ final AbstractIpAddressPreferenceController ipAddressPreferenceController =
+ new ConcreteIpAddressPreferenceController(mContext, mLifecycle);
+ final List<String> expectedIntents = Arrays.asList(
+ ConnectivityManager.CONNECTIVITY_ACTION,
+ WifiManager.LINK_CONFIGURATION_CHANGED_ACTION,
+ WifiManager.NETWORK_STATE_CHANGED_ACTION);
+
+
+ assertWithMessage("Intent filter should contain expected intents")
+ .that(ipAddressPreferenceController.getConnectivityIntents())
+ .asList().containsAllIn(expectedIntents);
+ }
+
+ private static class ConcreteIpAddressPreferenceController extends
+ AbstractIpAddressPreferenceController {
+
+ public ConcreteIpAddressPreferenceController(Context context,
+ Lifecycle lifecycle) {
+ super(context, lifecycle);
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
new file mode 100644
index 0000000..f68533b
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/UptimePreferenceControllerTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.settingslib.deviceinfo;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.os.SystemClock;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.format.DateUtils;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.ShadowLooper;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class UptimePreferenceControllerTest {
+ @Mock
+ private Context mContext;
+ @Mock
+ private Lifecycle mLifecycle;
+ @Mock
+ private PreferenceScreen mScreen;
+ @Mock
+ private Preference mPreference;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ doReturn(mPreference).when(mScreen)
+ .findPreference(AbstractUptimePreferenceController.KEY_UPTIME);
+ }
+
+ @Test
+ public void testDisplayPreference() {
+ final AbstractUptimePreferenceController uptimePreferenceController =
+ new ConcreteUptimePreferenceController(mContext, mLifecycle);
+
+ uptimePreferenceController.displayPreference(mScreen);
+
+ // SystemClock is shadowed so it shouldn't advance unexpectedly while the test is running
+ verify(mPreference).setSummary(DateUtils.formatDuration(SystemClock.elapsedRealtime()));
+ }
+
+ @Test
+ public void testUptimeTick() {
+ final AbstractUptimePreferenceController uptimePreferenceController =
+ new ConcreteUptimePreferenceController(mContext, null /* lifecycle */);
+
+ uptimePreferenceController.displayPreference(mScreen);
+
+ verify(mPreference, times(1)).setSummary(any(CharSequence.class));
+
+ uptimePreferenceController.onStart();
+
+ verify(mPreference, times(2)).setSummary(any(CharSequence.class));
+
+ ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+
+ verify(mPreference, times(3)).setSummary(any(CharSequence.class));
+
+ ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+
+ verify(mPreference, times(4)).setSummary(any(CharSequence.class));
+ }
+
+ private static class ConcreteUptimePreferenceController
+ extends AbstractUptimePreferenceController {
+ public ConcreteUptimePreferenceController(Context context,
+ Lifecycle lifecycle) {
+ super(context, lifecycle);
+ }
+ }
+
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
new file mode 100644
index 0000000..265a60b
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/deviceinfo/WifiMacAddressPreferenceControllerTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.settingslib.deviceinfo;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settingslib.R;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+import java.util.Arrays;
+import java.util.List;
+
+@SuppressLint("HardwareIds")
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class WifiMacAddressPreferenceControllerTest {
+ @Mock
+ private Context mContext;
+ @Mock
+ private Lifecycle mLifecycle;
+ @Mock
+ private PreferenceScreen mScreen;
+ @Mock
+ private Preference mPreference;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ doReturn(mPreference).when(mScreen)
+ .findPreference(AbstractWifiMacAddressPreferenceController.KEY_WIFI_MAC_ADDRESS);
+ }
+
+ @Test
+ public void testHasIntentFilters() {
+ final AbstractWifiMacAddressPreferenceController wifiMacAddressPreferenceController =
+ new ConcreteWifiMacAddressPreferenceController(mContext, mLifecycle);
+ final List<String> expectedIntents = Arrays.asList(
+ ConnectivityManager.CONNECTIVITY_ACTION,
+ WifiManager.LINK_CONFIGURATION_CHANGED_ACTION,
+ WifiManager.NETWORK_STATE_CHANGED_ACTION);
+
+
+ assertWithMessage("Intent filter should contain expected intents")
+ .that(wifiMacAddressPreferenceController.getConnectivityIntents())
+ .asList().containsAllIn(expectedIntents);
+ }
+
+ @Test
+ public void testWifiMacAddress() {
+ final WifiManager wifiManager = mock(WifiManager.class);
+ final WifiInfo wifiInfo = mock(WifiInfo.class);
+ doReturn("00:11:22:33:44:55").when(wifiInfo).getMacAddress();
+
+ doReturn(null).when(wifiManager).getConnectionInfo();
+ doReturn(wifiManager).when(mContext).getSystemService(WifiManager.class);
+
+ final AbstractWifiMacAddressPreferenceController wifiMacAddressPreferenceController =
+ new ConcreteWifiMacAddressPreferenceController(mContext, mLifecycle);
+
+ wifiMacAddressPreferenceController.displayPreference(mScreen);
+
+ verify(mPreference).setSummary(R.string.status_unavailable);
+
+ doReturn(wifiInfo).when(wifiManager).getConnectionInfo();
+
+ wifiMacAddressPreferenceController.displayPreference(mScreen);
+
+ verify(mPreference).setSummary("00:11:22:33:44:55");
+ }
+
+ private static class ConcreteWifiMacAddressPreferenceController
+ extends AbstractWifiMacAddressPreferenceController {
+
+ public ConcreteWifiMacAddressPreferenceController(Context context,
+ Lifecycle lifecycle) {
+ super(context, lifecycle);
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index 3e90435..dad3a28 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -29,6 +29,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.RuntimeEnvironment.application;
+import static org.robolectric.shadow.api.Shadow.extract;
import android.app.ActivityManager;
import android.content.ContentResolver;
@@ -66,7 +67,6 @@
import org.robolectric.annotation.Config;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
-import org.robolectric.internal.ShadowExtractor;
import java.util.ArrayList;
import java.util.Collections;
@@ -79,7 +79,6 @@
shadows = {TileUtilsTest.TileUtilsShadowRemoteViews.class})
public class TileUtilsTest {
- @Mock
private Context mContext;
@Mock
private PackageManager mPackageManager;
@@ -97,6 +96,7 @@
@Before
public void setUp() throws NameNotFoundException {
+ mContext = spy(application);
MockitoAnnotations.initMocks(this);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.getResourcesForApplication(anyString())).thenReturn(mResources);
@@ -456,8 +456,7 @@
assertThat(tile.remoteViews).isNotNull();
assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
// Make sure the summary TextView got a new text string.
- TileUtilsShadowRemoteViews shadowRemoteViews =
- (TileUtilsShadowRemoteViews) ShadowExtractor.extract(tile.remoteViews);
+ TileUtilsShadowRemoteViews shadowRemoteViews = extract(tile.remoteViews);
assertThat(shadowRemoteViews.overrideViewId).isEqualTo(android.R.id.summary);
assertThat(shadowRemoteViews.overrideText).isEqualTo("new summary text");
}
@@ -494,8 +493,7 @@
assertThat(tile.remoteViews).isNotNull();
assertThat(tile.remoteViews.getLayoutId()).isEqualTo(R.layout.user_preference);
// Make sure the summary TextView didn't get any text view updates.
- TileUtilsShadowRemoteViews shadowRemoteViews =
- (TileUtilsShadowRemoteViews) ShadowExtractor.extract(tile.remoteViews);
+ TileUtilsShadowRemoteViews shadowRemoteViews = extract(tile.remoteViews);
assertThat(shadowRemoteViews.overrideViewId).isNull();
assertThat(shadowRemoteViews.overrideText).isNull();
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
index f31d2e1..db599a7 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
@@ -18,8 +18,11 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.robolectric.RuntimeEnvironment.application;
+import static org.robolectric.shadow.api.Shadow.extract;
+
+import android.app.ApplicationPackageManager;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ResolveInfo;
@@ -34,22 +37,21 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
-import org.robolectric.res.ResourceLoader;
-import org.robolectric.res.builder.DefaultPackageManager;
-import org.robolectric.res.builder.RobolectricPackageManager;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowApplicationPackageManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@RunWith(SettingsLibRobolectricTestRunner.class)
-@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
+ shadows = SuggestionParserTest.TestPackageManager.class)
public class SuggestionParserTest {
- private Context mContext;
- private RobolectricPackageManager mPackageManager;
+ private TestPackageManager mPackageManager;
private SuggestionParser mSuggestionParser;
private SuggestionCategory mMultipleCategory;
private SuggestionCategory mExclusiveCategory;
@@ -61,11 +63,8 @@
@Before
public void setUp() {
- RuntimeEnvironment.setRobolectricPackageManager(
- new TestPackageManager(RuntimeEnvironment.getAppResourceLoader()));
- mContext = RuntimeEnvironment.application;
- mPackageManager = RuntimeEnvironment.getRobolectricPackageManager();
- mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
+ mPackageManager = extract(application.getPackageManager());
+ mPrefs = PreferenceManager.getDefaultSharedPreferences(application);
mSuggestion = new Tile();
mSuggestion.intent = new Intent("action");
mSuggestion.intent.setComponent(new ComponentName("pkg", "cls"));
@@ -81,7 +80,7 @@
mExpiredExclusiveCategory.exclusive = true;
mExpiredExclusiveCategory.exclusiveExpireDaysInMillis = 0;
- mSuggestionParser = new SuggestionParser(mContext, mPrefs,
+ mSuggestionParser = new SuggestionParser(application, mPrefs,
Arrays.asList(mMultipleCategory, mExclusiveCategory, mExpiredExclusiveCategory),
"0");
@@ -199,7 +198,7 @@
final Tile suggestion = mSuggestionsBeforeDismiss.get(0);
if (mSuggestionParser.dismissSuggestion(suggestion)) {
- RuntimeEnvironment.getRobolectricPackageManager().removeResolveInfosForIntent(
+ mPackageManager.removeResolveInfosForIntent(
new Intent(Intent.ACTION_MAIN).addCategory(mMultipleCategory.category),
suggestion.intent.getComponent().getPackageName());
}
@@ -207,13 +206,10 @@
mMultipleCategory, mSuggestionsAfterDismiss, isSmartSuggestionEnabled);
}
- private static class TestPackageManager extends DefaultPackageManager {
+ @Implements(ApplicationPackageManager.class)
+ public static class TestPackageManager extends ShadowApplicationPackageManager {
- TestPackageManager(ResourceLoader appResourceLoader) {
- super(appResourceLoader);
- }
-
- @Override
+ @Implementation
public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId) {
return super.queryIntentActivities(intent, flags);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/SettingsLibShadowResources.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/SettingsLibShadowResources.java
index a376dcd..b53cc37 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/SettingsLibShadowResources.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/shadow/SettingsLibShadowResources.java
@@ -16,7 +16,7 @@
package com.android.settingslib.testutils.shadow;
-import static org.robolectric.internal.Shadow.directlyOn;
+import static org.robolectric.shadow.api.Shadow.directlyOn;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index ec6f831..67fb4d9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -720,9 +720,6 @@
Settings.Global.DEVICE_IDLE_CONSTANTS,
GlobalSettingsProto.DEVICE_IDLE_CONSTANTS);
dumpSetting(s, p,
- Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH,
- GlobalSettingsProto.DEVICE_IDLE_CONSTANTS_WATCH);
- dumpSetting(s, p,
Settings.Global.APP_IDLE_CONSTANTS,
GlobalSettingsProto.APP_IDLE_CONSTANTS);
dumpSetting(s, p,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 87971cb..7b8d0db 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -273,6 +273,8 @@
</intent-filter>
<meta-data android:name="com.android.settings.category"
android:value="com.android.settings.category.ia.system" />
+ <meta-data android:name="com.android.settings.summary"
+ android:resource="@string/summary_empty"/>
</activity>
<activity-alias android:name=".DemoMode"
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java
index 1f633da..95ff13b 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/GlobalActions.java
@@ -29,6 +29,9 @@
default void showShutdownUi(boolean isReboot, String reason) {
}
+ default void destroy() {
+ }
+
@ProvidesInterface(version = GlobalActionsManager.VERSION)
public interface GlobalActionsManager {
int VERSION = 1;
diff --git a/packages/SystemUI/res-keyguard/values-bs/strings.xml b/packages/SystemUI/res-keyguard/values-bs/strings.xml
index c49dd4f..29e862d 100644
--- a/packages/SystemUI/res-keyguard/values-bs/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bs/strings.xml
@@ -21,14 +21,14 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="3171996292755059205">"Zaključavanje tastature"</string>
- <string name="keyguard_password_enter_pin_code" msgid="3420548423949593123">"Upišite PIN kôd"</string>
- <string name="keyguard_password_enter_puk_code" msgid="670683628782925409">"Upišite PUK kôd za SIM karticu i novi PIN kôd"</string>
+ <string name="keyguard_password_enter_pin_code" msgid="3420548423949593123">"Upišite PIN"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="670683628782925409">"Upišite PUK kôd za SIM karticu i novi PIN"</string>
<string name="keyguard_password_enter_puk_prompt" msgid="3747778500166059332">"PUK kôd za SIM karticu"</string>
- <string name="keyguard_password_enter_pin_prompt" msgid="8188243197504453830">"Novi PIN kôd za SIM karticu"</string>
+ <string name="keyguard_password_enter_pin_prompt" msgid="8188243197504453830">"Novi PIN za SIM karticu"</string>
<string name="keyguard_password_entry_touch_hint" msgid="5790410752696806482"><font size="17">"Dodirnite da upišete lozinku"</font></string>
<string name="keyguard_password_enter_password_code" msgid="595980919238127672">"Upišite lozinku za otključavanje"</string>
- <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Upišite PIN kôd za otključavanje"</string>
- <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Pogrešan PIN kôd."</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="7504123374204446086">"Upišite PIN za otključavanje"</string>
+ <string name="keyguard_password_wrong_pin_code" msgid="6535018036285012028">"Pogrešan PIN."</string>
<string name="keyguard_charged" msgid="2222329688813033109">"Napunjeno"</string>
<string name="keyguard_plugged_in" msgid="89308975354638682">"Punjenje"</string>
<string name="keyguard_plugged_in_charging_fast" msgid="8869226755413795173">"Brzo punjenje"</string>
@@ -46,8 +46,8 @@
<string name="keyguard_sim_locked_message" msgid="953766009432168127">"SIM kartica je zaključana."</string>
<string name="keyguard_sim_puk_locked_message" msgid="1772789643694942073">"SIM kartica je zaključana PUK kodom."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="3586601150825821675">"Otključavanje SIM kartice…"</string>
- <string name="keyguard_accessibility_pin_area" msgid="703175752097279029">"Prostor za PIN kôd"</string>
- <string name="keyguard_accessibility_sim_pin_area" msgid="912702510825058921">"Prostor za PIN kôd za SIM karticu"</string>
+ <string name="keyguard_accessibility_pin_area" msgid="703175752097279029">"Prostor za PIN"</string>
+ <string name="keyguard_accessibility_sim_pin_area" msgid="912702510825058921">"Prostor za PIN za SIM karticu"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="136979425761438705">"Prostor za PUK kôd za SIM karticu"</string>
<string name="keyguard_accessibility_next_alarm" msgid="5835196989158584991">"Naredni alarm je podešen za <xliff:g id="ALARM">%1$s</xliff:g>"</string>
<string name="keyboardview_keycode_delete" msgid="6883116827512721630">"Izbriši"</string>
@@ -56,29 +56,29 @@
<string name="kg_forgot_pattern_button_text" msgid="534245177645252620">"Zaboravili ste uzorak?"</string>
<string name="kg_wrong_pattern" msgid="7620081431514773802">"Pogrešan uzorak"</string>
<string name="kg_wrong_password" msgid="4580683060277329277">"Pogrešna lozinka"</string>
- <string name="kg_wrong_pin" msgid="4785660766909463466">"Pogrešan PIN kôd"</string>
+ <string name="kg_wrong_pin" msgid="4785660766909463466">"Pogrešan PIN"</string>
<plurals name="kg_too_many_failed_attempts_countdown" formatted="false" msgid="4368805541257003755">
<item quantity="one">Pokušajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> sekundu.</item>
<item quantity="few">Pokušajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> sekunde.</item>
<item quantity="other">Pokušajte ponovo za <xliff:g id="NUMBER">%d</xliff:g> sekundi.</item>
</plurals>
<string name="kg_pattern_instructions" msgid="5547646893001491340">"Nacrtajte uzorak"</string>
- <string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Unesite PIN kôd SIM kartice."</string>
- <string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Unesite PIN kôd SIM kartice operatera \"<xliff:g id="CARRIER">%1$s</xliff:g>\""</string>
+ <string name="kg_sim_pin_instructions" msgid="6389000973113699187">"Unesite PIN SIM kartice."</string>
+ <string name="kg_sim_pin_instructions_multi" msgid="1643757228644271861">"Unesite PIN SIM kartice operatera \"<xliff:g id="CARRIER">%1$s</xliff:g>\""</string>
<string name="kg_sim_lock_instructions_esim" msgid="4957650659201013804">"Onemogućite eSIM karticu za korištenje uređaja bez mobilne usluge."</string>
- <string name="kg_pin_instructions" msgid="4069609316644030034">"Unesite PIN kôd"</string>
+ <string name="kg_pin_instructions" msgid="4069609316644030034">"Unesite PIN"</string>
<string name="kg_password_instructions" msgid="136952397352976538">"Unesite lozinku"</string>
<string name="kg_puk_enter_puk_hint" msgid="2288964170039899277">"SIM kartica je sada onemogućena. Unesite PUK kôd da nastavite. Za više informacija obratite se operateru."</string>
<string name="kg_puk_enter_puk_hint_multi" msgid="1373131883510840794">"Operater SIM kartice \"<xliff:g id="CARRIER">%1$s</xliff:g>\" sada je onemogućen. Unesite PUK kôd da nastavite. Za više informacija obratite se operateru."</string>
- <string name="kg_puk_enter_pin_hint" msgid="3137789674920391087">"Unesite željeni PIN kôd"</string>
- <string name="kg_enter_confirm_pin_hint" msgid="3089485999116759671">"Potvrdite željeni PIN kôd"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="3137789674920391087">"Unesite željeni PIN"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="3089485999116759671">"Potvrdite željeni PIN"</string>
<string name="kg_sim_unlock_progress_dialog_message" msgid="4471738151810900114">"Otključavanje SIM kartice…"</string>
- <string name="kg_invalid_sim_pin_hint" msgid="3057533256729513335">"Unesite PIN kôd koji sadrži 4 do 8 brojeva."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="3057533256729513335">"Unesite PIN koji sadrži 4 do 8 brojeva."</string>
<string name="kg_invalid_sim_puk_hint" msgid="6003602401368264144">"PUK kôd treba sadržavati najmanje 8 brojeva."</string>
<string name="kg_invalid_puk" msgid="5399287873762592502">"Ponovo unesite ispravan PUK kôd. Ponovljeni pokušaji će trajno onemogućiti SIM karticu."</string>
- <string name="kg_invalid_confirm_pin_hint" product="default" msgid="5672736555427444330">"PIN kodovi se ne poklapaju"</string>
+ <string name="kg_invalid_confirm_pin_hint" product="default" msgid="5672736555427444330">"PIN-ovi se ne poklapaju"</string>
<string name="kg_login_too_many_attempts" msgid="6604574268387867255">"Previše puta ste pokušali otključati uređaj crtanjem uzorka"</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8637788033282252027">"Pogrešno ste unijeli PIN kôd <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="8637788033282252027">"Pogrešno ste unijeli PIN <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
<string name="kg_too_many_failed_password_attempts_dialog_message" msgid="7724148763268377734">"Pogrešno ste unijeli lozinku <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4820967667848302092">"Pogrešno ste nacrtali svoj uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. \n\nPokušajte ponovo za <xliff:g id="NUMBER_1">%2$d</xliff:g> sek."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="1629351522209932316">"Pokušali ste <xliff:g id="NUMBER_0">%1$d</xliff:g> puta neispravno otključati tablet. U slučaju još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, tablet će se vratiti na fabričke postavke i svi podaci će se izbrisati."</string>
@@ -95,11 +95,11 @@
<string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="8476407539834855">"Pokušali ste <xliff:g id="NUMBER">%d</xliff:g> puta neispravno otključati telefon. Poslovni profil će se ukloniti i svi podaci s profila će se izbrisati."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="956706236554092172">"Pogrešno ste nacrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. U slučaju još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, od vas će se tražiti da tablet otključate koristeći račun e-pošte.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> sek."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="8364140853305528449">"Pogrešno ste nacrtali uzorak za otključavanje <xliff:g id="NUMBER_0">%1$d</xliff:g> puta. U slučaju još <xliff:g id="NUMBER_1">%2$d</xliff:g> pokušaja bez uspjeha, od vas će se tražiti da telefon otključate koristeći račun e-pošte.\n\n Pokušajte ponovo za <xliff:g id="NUMBER_2">%3$d</xliff:g> sek."</string>
- <string name="kg_password_wrong_pin_code_pukked" msgid="3389829202093674267">"PIN kôd za SIM karticu je netačan. Za otključavanje uređaja sada se morate obratiti svom operateru."</string>
+ <string name="kg_password_wrong_pin_code_pukked" msgid="3389829202093674267">"PIN za SIM karticu je netačan. Za otključavanje uređaja sada se morate obratiti svom operateru."</string>
<plurals name="kg_password_wrong_pin_code" formatted="false" msgid="4314341367727055967">
- <item quantity="one">PIN kôd za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
- <item quantity="few">PIN kôd za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
- <item quantity="other">PIN kôd za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
+ <item quantity="one">PIN za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaj.</item>
+ <item quantity="few">PIN za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
+ <item quantity="other">PIN za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja.</item>
</plurals>
<string name="kg_password_wrong_puk_code_dead" msgid="3329017604125179374">"SIM kartica je neupotrebljiva. Obratite se svom operateru."</string>
<plurals name="kg_password_wrong_puk_code" formatted="false" msgid="2287504898931957513">
@@ -107,20 +107,20 @@
<item quantity="few">PUK kôd za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva.</item>
<item quantity="other">PUK kôd za SIM karticu je netačan. Imate još <xliff:g id="NUMBER_1">%d</xliff:g> pokušaja prije nego što SIM kartica postane trajno neupotrebljiva.</item>
</plurals>
- <string name="kg_password_pin_failed" msgid="8769990811451236223">"Korištenje PIN koda za SIM karticu nije uspjelo!"</string>
+ <string name="kg_password_pin_failed" msgid="8769990811451236223">"Korištenje PIN-a za SIM karticu nije uspjelo!"</string>
<string name="kg_password_puk_failed" msgid="1331621440873439974">"Korištenje PUK koda za SIM karticu nije uspjelo!"</string>
<string name="kg_pin_accepted" msgid="7637293533973802143">"Kôd je prihvaćen"</string>
<string name="keyguard_carrier_default" msgid="4274828292998453695">"Nema mreže."</string>
<string name="accessibility_ime_switch_button" msgid="2695096475319405612">"Promjena načina unosa"</string>
<string name="airplane_mode" msgid="3807209033737676010">"Način rada u avionu"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="7246972020562621506">"Potreban je uzorak nakon što se uređaj ponovo pokrene"</string>
- <string name="kg_prompt_reason_restart_pin" msgid="6303592361322290145">"Potreban je PIN kôd nakon što se uređaj ponovo pokrene"</string>
+ <string name="kg_prompt_reason_restart_pin" msgid="6303592361322290145">"Potreban je PIN nakon što se uređaj ponovo pokrene"</string>
<string name="kg_prompt_reason_restart_password" msgid="6984641181515902406">"Potrebna je lozinka nakon što se uređaj ponovo pokrene"</string>
<string name="kg_prompt_reason_timeout_pattern" msgid="5304487696073914063">"Uzorak je potreban radi dodatne sigurnosti"</string>
- <string name="kg_prompt_reason_timeout_pin" msgid="8851462864335757813">"PIN kôd je potreban radi dodatne sigurnosti"</string>
+ <string name="kg_prompt_reason_timeout_pin" msgid="8851462864335757813">"PIN je potreban radi dodatne sigurnosti"</string>
<string name="kg_prompt_reason_timeout_password" msgid="6563904839641583441">"Lozinka je potrebna radi dodatne sigurnosti"</string>
<string name="kg_prompt_reason_switch_profiles_pattern" msgid="3398054847288438444">"Potreban je uzorak nakon prelaska na drugi profil"</string>
- <string name="kg_prompt_reason_switch_profiles_pin" msgid="7426368139226961699">"Potreban je PIN kôd nakon prelaska na drugi profil"</string>
+ <string name="kg_prompt_reason_switch_profiles_pin" msgid="7426368139226961699">"Potreban je PIN nakon prelaska na drugi profil"</string>
<string name="kg_prompt_reason_switch_profiles_password" msgid="8383831046318421845">"Potrebna je lozinka nakon prelaska na drugi profil"</string>
<string name="kg_prompt_reason_device_admin" msgid="3452168247888906179">"Uređaj je zaključao administrator"</string>
<string name="kg_prompt_reason_user_request" msgid="8236951765212462286">"Uređaj je ručno zaključan"</string>
@@ -130,9 +130,9 @@
<item quantity="other">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sati. Potvrdite uzorak.</item>
</plurals>
<plurals name="kg_prompt_reason_time_pin" formatted="false" msgid="34586942088144385">
- <item quantity="one">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sat. Potvrdite PIN kôd.</item>
- <item quantity="few">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sata. Potvrdite PIN kôd.</item>
- <item quantity="other">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sati. Potvrdite PIN kôd.</item>
+ <item quantity="one">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sat. Potvrdite PIN.</item>
+ <item quantity="few">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sata. Potvrdite PIN.</item>
+ <item quantity="other">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sati. Potvrdite PIN.</item>
</plurals>
<plurals name="kg_prompt_reason_time_password" formatted="false" msgid="257297696215346527">
<item quantity="one">Uređaj nije otključavan <xliff:g id="NUMBER_1">%d</xliff:g> sat. Potvrdite lozinku.</item>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index e561239..4bae85f 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -536,8 +536,8 @@
<string name="activity_not_found" msgid="348423244327799974">"L\'aplicació no està instal·lada al dispositiu"</string>
<string name="clock_seconds" msgid="7689554147579179507">"Mostra els segons del rellotge"</string>
<string name="clock_seconds_desc" msgid="6282693067130470675">"Mostra els segons del rellotge a la barra d\'estat. Això pot afectar la durada de la bateria."</string>
- <string name="qs_rearrange" msgid="8060918697551068765">"Reorganitza Configuració ràpida"</string>
- <string name="show_brightness" msgid="6613930842805942519">"Mostra la brillantor a Configuració ràpida"</string>
+ <string name="qs_rearrange" msgid="8060918697551068765">"Reorganitza la configuració ràpida"</string>
+ <string name="show_brightness" msgid="6613930842805942519">"Mostra la brillantor a configuració ràpida"</string>
<string name="experimental" msgid="6198182315536726162">"Experimental"</string>
<string name="enable_bluetooth_title" msgid="5027037706500635269">"Vols activar el Bluetooth?"</string>
<string name="enable_bluetooth_message" msgid="9106595990708985385">"Per connectar el teclat amb la tauleta, primer has d\'activar el Bluetooth."</string>
@@ -705,7 +705,7 @@
<string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g> s\'ha afegit a la posició <xliff:g id="POSITION">%2$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> s\'ha suprimit"</string>
<string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> s\'ha mogut a la posició <xliff:g id="POSITION">%2$d</xliff:g>"</string>
- <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Editor de la configuració ràpida."</string>
+ <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"Editor de configuració ràpida."</string>
<string name="accessibility_desc_notification_icon" msgid="8352414185263916335">"Notificació de <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="dock_forced_resizable" msgid="5914261505436217520">"És possible que l\'aplicació no funcioni amb la pantalla dividida."</string>
<string name="dock_non_resizeble_failed_to_dock_text" msgid="3871617304250207291">"L\'aplicació no admet la pantalla dividida."</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 38ac499..d3a15df 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -758,7 +758,7 @@
<string name="lockscreen_unlock_left" msgid="2043092136246951985">"Zkratka vlevo také odemyká"</string>
<string name="lockscreen_unlock_right" msgid="1529992940510318775">"Zkratka vpravo také odemyká"</string>
<string name="lockscreen_none" msgid="4783896034844841821">"Žádné"</string>
- <string name="tuner_launch_app" msgid="1527264114781925348">"Spustit aplikaci <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="tuner_launch_app" msgid="1527264114781925348">"Do aplikace <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="tuner_other_apps" msgid="4726596850501162493">"Další aplikace"</string>
<string name="tuner_circle" msgid="2340998864056901350">"Kruh"</string>
<string name="tuner_plus" msgid="6792960658533229675">"Plus"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 70ee57a..62d668c 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -288,7 +288,7 @@
<string name="quick_settings_location_off_label" msgid="7464544086507331459">"Localización desactivada"</string>
<string name="quick_settings_media_device_label" msgid="1302906836372603762">"Dispositivo multimedia"</string>
<string name="quick_settings_rssi_label" msgid="7725671335550695589">"RSSI"</string>
- <string name="quick_settings_rssi_emergency_only" msgid="2713774041672886750">"Só chamadas de emerxencia"</string>
+ <string name="quick_settings_rssi_emergency_only" msgid="2713774041672886750">"Só chamadas de urxencia"</string>
<string name="quick_settings_settings_label" msgid="5326556592578065401">"Configuración"</string>
<string name="quick_settings_time_label" msgid="4635969182239736408">"Hora"</string>
<string name="quick_settings_user_label" msgid="5238995632130897840">"Eu"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 89ea9f1..6b79e33 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -79,7 +79,7 @@
<string name="usb_preference_title" msgid="6551050377388882787">"USB ဖိုင်ပြောင်း ရွေးမှုများ"</string>
<string name="use_mtp_button_title" msgid="4333504413563023626">"မီဒီယာပလေရာအနေဖြင့် တပ်ဆင်ရန် (MTP)"</string>
<string name="use_ptp_button_title" msgid="7517127540301625751">"ကင်မရာအနေဖြင့် တပ်ဆင်ရန် (PTP)"</string>
- <string name="installer_cd_button_title" msgid="2312667578562201583">"Macအတွက်Andriodဖိုင်ပြောင်းအပ်ပလီကေးရှင်းထည့်ခြင်း"</string>
+ <string name="installer_cd_button_title" msgid="2312667578562201583">"Mac အတွက် Android File Transfer အက်ပ်ထည့်ခြင်း"</string>
<string name="accessibility_back" msgid="567011538994429120">"နောက်သို့"</string>
<string name="accessibility_home" msgid="8217216074895377641">"ပင်မစာမျက်နှာ"</string>
<string name="accessibility_menu" msgid="316839303324695949">"မီနူး"</string>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index 7225ba9..432b406 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -66,7 +66,11 @@
// again when the PUK locked SIM is re-entered.
case ABSENT: {
KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId);
- mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ // onSimStateChanged callback can fire when the SIM PIN lock is not currently
+ // active and mCallback is null.
+ if (mCallback != null) {
+ mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ }
break;
}
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index 171cf23..7f79008 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -72,7 +72,11 @@
// move into the READY state and the PUK lock keyguard should be removed.
case READY: {
KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId);
- mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ // mCallback can be null if onSimStateChanged callback is called when keyguard
+ // isn't active.
+ if (mCallback != null) {
+ mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ }
break;
}
default:
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 4b37715..592dda0 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -83,7 +83,6 @@
private boolean mMenuRowIntercepting;
private boolean mLongPressSent;
- private LongPressListener mLongPressListener;
private Runnable mWatchLongPress;
private final long mLongPressTimeout;
@@ -115,10 +114,6 @@
mFlingAnimationUtils = new FlingAnimationUtils(context, getMaxEscapeAnimDuration() / 1000f);
}
- public void setLongPressListener(LongPressListener listener) {
- mLongPressListener = listener;
- }
-
public void setDensityScale(float densityScale) {
mDensityScale = densityScale;
}
@@ -257,7 +252,7 @@
}
}
- public void removeLongPressCallback() {
+ public void cancelLongPress() {
if (mWatchLongPress != null) {
mHandler.removeCallbacks(mWatchLongPress);
mWatchLongPress = null;
@@ -288,33 +283,27 @@
mInitialTouchPos = getPos(ev);
mPerpendicularInitialTouchPos = getPerpendicularPos(ev);
mTranslation = getTranslation(mCurrView);
- if (mLongPressListener != null) {
- if (mWatchLongPress == null) {
- mWatchLongPress = new Runnable() {
- @Override
- public void run() {
- if (mCurrView != null && !mLongPressSent) {
- mLongPressSent = true;
+ if (mWatchLongPress == null) {
+ mWatchLongPress = new Runnable() {
+ @Override
+ public void run() {
+ if (mCurrView != null && !mLongPressSent) {
+ mLongPressSent = true;
+ mCurrView.getLocationOnScreen(mTmpPos);
+ final int x = (int) ev.getRawX() - mTmpPos[0];
+ final int y = (int) ev.getRawY() - mTmpPos[1];
+ if (mCurrView instanceof ExpandableNotificationRow) {
mCurrView.sendAccessibilityEvent(
AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
- mCurrView.getLocationOnScreen(mTmpPos);
- final int x = (int) ev.getRawX() - mTmpPos[0];
- final int y = (int) ev.getRawY() - mTmpPos[1];
- MenuItem menuItem = null;
- if (mCurrView instanceof ExpandableNotificationRow) {
- menuItem = ((ExpandableNotificationRow) mCurrView)
- .getProvider().getLongpressMenuItem(mContext);
- }
- if (menuItem != null) {
- mLongPressListener.onLongPress(mCurrView, x, y,
- menuItem);
- }
+ ExpandableNotificationRow currRow =
+ (ExpandableNotificationRow) mCurrView;
+ currRow.doLongClickCallback(x, y);
}
}
- };
- }
- mHandler.postDelayed(mWatchLongPress, mLongPressTimeout);
+ }
+ };
}
+ mHandler.postDelayed(mWatchLongPress, mLongPressTimeout);
}
break;
@@ -331,7 +320,7 @@
mDragging = true;
mInitialTouchPos = getPos(ev);
mTranslation = getTranslation(mCurrView);
- removeLongPressCallback();
+ cancelLongPress();
}
}
break;
@@ -343,7 +332,7 @@
mCurrView = null;
mLongPressSent = false;
mMenuRowIntercepting = false;
- removeLongPressCallback();
+ cancelLongPress();
if (captured) return true;
break;
}
@@ -586,7 +575,7 @@
// We are not doing anything, make sure the long press callback
// is not still ticking like a bomb waiting to go off.
- removeLongPressCallback();
+ cancelLongPress();
return false;
}
}
@@ -734,15 +723,4 @@
*/
float getFalsingThresholdFactor();
}
-
- /**
- * Equivalent to View.OnLongClickListener with coordinates
- */
- public interface LongPressListener {
- /**
- * Equivalent to {@link View.OnLongClickListener#onLongClick(View)} with coordinates
- * @return whether the longpress was handled
- */
- boolean onLongPress(View v, int x, int y, MenuItem item);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index dc626fb..851b78c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -84,9 +84,6 @@
case DOZE_REQUEST_PULSE:
pulseWhileDozing(mMachine.getPulseReason());
break;
- case DOZE_PULSE_DONE:
- mHost.abortPulsing();
- break;
case INITIALIZED:
mHost.startDozing();
break;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
index 09a08f0..f06cda0 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
@@ -31,6 +31,7 @@
public class GlobalActionsComponent extends SystemUI implements Callbacks, GlobalActionsManager {
+ private GlobalActions mPlugin;
private Extension<GlobalActions> mExtension;
private IStatusBarService mBarService;
@@ -41,10 +42,19 @@
mExtension = Dependency.get(ExtensionController.class).newExtension(GlobalActions.class)
.withPlugin(GlobalActions.class)
.withDefault(() -> new GlobalActionsImpl(mContext))
+ .withCallback(this::onExtensionCallback)
.build();
+ mPlugin = mExtension.get();
SysUiServiceProvider.getComponent(mContext, CommandQueue.class).addCallbacks(this);
}
+ private void onExtensionCallback(GlobalActions newPlugin) {
+ if (mPlugin != null) {
+ mPlugin.destroy();
+ }
+ mPlugin = newPlugin;
+ }
+
@Override
public void handleShowShutdownUi(boolean isReboot, String reason) {
mExtension.get().showShutdownUi(isReboot, reason);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 189badf..d82f9cd 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -16,27 +16,6 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
-import com.android.internal.R;
-import com.android.internal.colorextraction.ColorExtractor;
-import com.android.internal.colorextraction.ColorExtractor.GradientColors;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.internal.util.EmergencyAffordanceManager;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.TelephonyProperties;
-import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.Dependency;
-import com.android.systemui.HardwareUiLayout;
-import com.android.systemui.Interpolators;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
-import com.android.systemui.statusbar.notification.NotificationUtils;
-import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.volume.VolumeDialogMotion.LogAccelerateInterpolator;
-import com.android.systemui.volume.VolumeDialogMotion.LogDecelerateInterpolator;
-
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.app.ActivityManager;
import android.app.Dialog;
import android.app.WallpaperManager;
@@ -69,7 +48,6 @@
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
-import android.util.MathUtils;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
@@ -86,7 +64,23 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.internal.R;
+import com.android.internal.colorextraction.ColorExtractor;
+import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.colorextraction.drawable.GradientDrawable;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.TelephonyProperties;
+import com.android.internal.util.EmergencyAffordanceManager;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.Dependency;
+import com.android.systemui.HardwareUiLayout;
+import com.android.systemui.Interpolators;
+import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.volume.VolumeDialogMotion.LogAccelerateInterpolator;
import java.util.ArrayList;
import java.util.List;
@@ -96,7 +90,8 @@
* may show depending on whether the keyguard is showing, and whether the device
* is provisioned.
*/
-class GlobalActionsDialog implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
+class GlobalActionsDialog implements DialogInterface.OnDismissListener,
+ DialogInterface.OnClickListener {
static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
@@ -195,6 +190,14 @@
}
}
+ /**
+ * Dismiss the global actions dialog, if it's currently shown
+ */
+ public void dismissDialog() {
+ mHandler.removeMessages(MESSAGE_DISMISS);
+ mHandler.sendEmptyMessage(MESSAGE_DISMISS);
+ }
+
private void awakenIfNecessary() {
if (mDreamManager != null) {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 2cf230c..3563437 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -14,12 +14,12 @@
package com.android.systemui.globalactions;
+import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
+
import android.app.Dialog;
import android.app.KeyguardManager;
-import android.app.WallpaperColors;
import android.app.WallpaperManager;
import android.content.Context;
-import android.graphics.Color;
import android.graphics.Point;
import android.view.ViewGroup;
import android.view.Window;
@@ -32,12 +32,14 @@
import com.android.internal.colorextraction.drawable.GradientDrawable;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
+import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.GlobalActions;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
-public class GlobalActionsImpl implements GlobalActions {
+public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks {
private static final float SHUTDOWN_SCRIM_ALPHA = 0.95f;
@@ -45,15 +47,23 @@
private final KeyguardMonitor mKeyguardMonitor;
private final DeviceProvisionedController mDeviceProvisionedController;
private GlobalActionsDialog mGlobalActions;
+ private boolean mDisabled;
public GlobalActionsImpl(Context context) {
mContext = context;
mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
+ SysUiServiceProvider.getComponent(context, CommandQueue.class).addCallbacks(this);
+ }
+
+ @Override
+ public void destroy() {
+ SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this);
}
@Override
public void showGlobalActions(GlobalActionsManager manager) {
+ if (mDisabled) return;
if (mGlobalActions == null) {
mGlobalActions = new GlobalActionsDialog(mContext, manager);
}
@@ -107,4 +117,14 @@
d.show();
}
+
+ @Override
+ public void disable(int state1, int state2, boolean animate) {
+ final boolean disabled = (state2 & DISABLE2_GLOBAL_ACTIONS) != 0;
+ if (disabled == mDisabled) return;
+ mDisabled = disabled;
+ if (disabled && mGlobalActions != null) {
+ mGlobalActions.dismissDialog();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index 58243b4..f8996aa 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -16,7 +16,6 @@
package com.android.systemui.pip.phone;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -73,7 +72,7 @@
*/
TaskStackListener mTaskStackListener = new TaskStackListener() {
@Override
- public void onActivityPinned(String packageName, int userId, int taskId) {
+ public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
mTouchHandler.onActivityPinned();
mMediaController.onActivityPinned();
mMenuController.onActivityPinned();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index d71d4de..9fb201b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -16,7 +16,6 @@
package com.android.systemui.pip.phone;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index eef0e7d..21a836c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -16,7 +16,7 @@
package com.android.systemui.pip.phone;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -161,13 +161,7 @@
mMenuController.hideMenuWithoutResize();
mHandler.post(() -> {
try {
- if (skipAnimation) {
- mActivityManager.moveTasksToFullscreenStack(PINNED_STACK_ID, true /* onTop */);
- } else {
- mActivityManager.resizeStack(PINNED_STACK_ID, null /* bounds */,
- true /* allowResizeInDockedMode */, true /* preserveWindows */,
- true /* animate */, EXPAND_STACK_TO_FULLSCREEN_DURATION);
- }
+ mActivityManager.dismissPip(!skipAnimation, EXPAND_STACK_TO_FULLSCREEN_DURATION);
} catch (RemoteException e) {
Log.e(TAG, "Error expanding PiP activity", e);
}
@@ -185,7 +179,7 @@
mMenuController.hideMenuWithoutResize();
mHandler.post(() -> {
try {
- mActivityManager.removeStack(PINNED_STACK_ID);
+ mActivityManager.removeStacksInWindowingModes(new int[]{ WINDOWING_MODE_PINNED });
} catch (RemoteException e) {
Log.e(TAG, "Failed to remove PiP", e);
}
@@ -540,7 +534,7 @@
return true;
}
- mActivityManager.resizeStack(PINNED_STACK_ID, toBounds,
+ mActivityManager.resizeStack(stackInfo.stackId, toBounds,
false /* allowResizeInDockedMode */, true /* preserveWindows */,
true /* animate */, duration);
mBounds.set(toBounds);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
index 84410f2..2f53de9 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipUtils.java
@@ -16,7 +16,6 @@
package com.android.systemui.pip.phone;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 6e85549..e0445c1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -53,7 +53,7 @@
import java.util.ArrayList;
import java.util.List;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -123,6 +123,7 @@
private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED;
private boolean mInitialized;
private int mPipTaskId = TASK_ID_NO_PIP;
+ private int mPinnedStackId = INVALID_STACK_ID;
private ComponentName mPipComponentName;
private MediaController mPipMediaController;
private String[] mLastPackagesResourceGranted;
@@ -338,9 +339,11 @@
mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveMediaSessionListener);
if (removePipStack) {
try {
- mActivityManager.removeStack(PINNED_STACK_ID);
+ mActivityManager.removeStack(mPinnedStackId);
} catch (RemoteException e) {
Log.e(TAG, "removeStack failed", e);
+ } finally {
+ mPinnedStackId = INVALID_STACK_ID;
}
}
for (int i = mListeners.size() - 1; i >= 0; --i) {
@@ -426,7 +429,7 @@
}
try {
int animationDurationMs = -1;
- mActivityManager.resizeStack(PINNED_STACK_ID, mCurrentPipBounds,
+ mActivityManager.resizeStack(mPinnedStackId, mCurrentPipBounds,
true, true, true, animationDurationMs);
} catch (RemoteException e) {
Log.e(TAG, "resizeStack failed", e);
@@ -657,7 +660,7 @@
}
@Override
- public void onActivityPinned(String packageName, int userId, int taskId) {
+ public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
if (DEBUG) Log.d(TAG, "onActivityPinned()");
if (!checkCurrentUserId(mContext, DEBUG)) {
return;
@@ -668,6 +671,7 @@
return;
}
if (DEBUG) Log.d(TAG, "PINNED_STACK:" + stackInfo);
+ mPinnedStackId = stackInfo.stackId;
mPipTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
mPipComponentName = ComponentName.unflattenFromString(
stackInfo.taskNames[stackInfo.taskNames.length - 1]);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index dc83333..3e2a5f3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -16,9 +16,9 @@
package com.android.systemui.recents;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.View.MeasureSpec;
import android.app.ActivityManager;
@@ -173,7 +173,7 @@
}
@Override
- public void onActivityPinned(String packageName, int userId, int taskId) {
+ public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
// Check this is for the right user
if (!checkCurrentUserId(mContext, false /* debug */)) {
return;
@@ -873,7 +873,9 @@
getThumbnailTransitionActivityOptions(ActivityManager.RunningTaskInfo runningTask,
Rect windowOverrideRect) {
final boolean isLowRamDevice = Recents.getConfiguration().isLowRamDevice;
- if (runningTask != null && runningTask.stackId == FREEFORM_WORKSPACE_STACK_ID) {
+ if (runningTask != null
+ && runningTask.configuration.windowConfiguration.getWindowingMode()
+ == WINDOWING_MODE_FREEFORM) {
ArrayList<AppTransitionAnimationSpec> specs = new ArrayList<>();
ArrayList<Task> tasks = mDummyStackView.getStack().getStackTasks();
TaskStackLayoutAlgorithm stackLayout = mDummyStackView.getStackAlgorithm();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 4580b1f..bddf9a5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -16,8 +16,6 @@
package com.android.systemui.recents.misc;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -177,7 +175,7 @@
public void onTaskStackChangedBackground() { }
public void onTaskStackChanged() { }
public void onTaskSnapshotChanged(int taskId, TaskSnapshot snapshot) { }
- public void onActivityPinned(String packageName, int userId, int taskId) { }
+ public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { }
public void onActivityUnpinned() { }
public void onPinnedActivityRestartAttempt(boolean clearedTask) { }
public void onPinnedStackAnimationStarted() { }
@@ -232,10 +230,11 @@
}
@Override
- public void onActivityPinned(String packageName, int userId, int taskId)
+ public void onActivityPinned(String packageName, int userId, int taskId, int stackId)
throws RemoteException {
mHandler.removeMessages(H.ON_ACTIVITY_PINNED);
- mHandler.obtainMessage(H.ON_ACTIVITY_PINNED, userId, taskId, packageName).sendToTarget();
+ mHandler.obtainMessage(H.ON_ACTIVITY_PINNED,
+ new PinnedActivityInfo(packageName, userId, taskId, stackId)).sendToTarget();
}
@Override
@@ -618,13 +617,6 @@
}
/**
- * Returns whether the given stack id is the freeform workspace stack id.
- */
- public static boolean isFreeformStack(int stackId) {
- return stackId == FREEFORM_WORKSPACE_STACK_ID;
- }
-
- /**
* @return whether there are any docked tasks for the current user.
*/
public boolean hasDockedTask() {
@@ -734,14 +726,12 @@
}
}
- /**
- * Moves a task into another stack.
- */
- public void moveTaskToStack(int taskId, int stackId) {
+ /** Set the task's windowing mode. */
+ public void setTaskWindowingMode(int taskId, int windowingMode) {
if (mIam == null) return;
try {
- mIam.positionTaskInStack(taskId, stackId, 0);
+ mIam.setTaskWindowingMode(taskId, windowingMode, false /* onTop */);
} catch (RemoteException | IllegalArgumentException e) {
e.printStackTrace();
}
@@ -1132,7 +1122,7 @@
if (mIam == null) {
return;
}
- if (taskKey.stackId == DOCKED_STACK_ID) {
+ if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
// We show non-visible docked tasks in Recents, but we always want to launch
// them in the fullscreen stack.
if (options == null) {
@@ -1314,6 +1304,20 @@
void onStartActivityResult(boolean succeeded);
}
+ private class PinnedActivityInfo {
+ final String mPackageName;
+ final int mUserId;
+ final int mTaskId;
+ final int mStackId;
+
+ PinnedActivityInfo(String packageName, int userId, int taskId, int stackId) {
+ mPackageName = packageName;
+ mUserId = userId;
+ mTaskId = taskId;
+ mStackId = stackId;
+ }
+ }
+
private final class H extends Handler {
private static final int ON_TASK_STACK_CHANGED = 1;
private static final int ON_TASK_SNAPSHOT_CHANGED = 2;
@@ -1349,9 +1353,10 @@
break;
}
case ON_ACTIVITY_PINNED: {
+ final PinnedActivityInfo info = (PinnedActivityInfo) msg.obj;
for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
- mTaskStackListeners.get(i).onActivityPinned((String) msg.obj, msg.arg1,
- msg.arg2);
+ mTaskStackListeners.get(i).onActivityPinned(
+ info.mPackageName, info.mUserId, info.mTaskId, info.mStackId);
}
break;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/HighResThumbnailLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/HighResThumbnailLoader.java
index 48fa6c3..6414ea1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/HighResThumbnailLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/HighResThumbnailLoader.java
@@ -187,7 +187,7 @@
}
@Override
- public void onTaskStackIdChanged() {
+ public void onTaskWindowingModeChanged() {
}
private final Runnable mLoader = new Runnable() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 8d31730..d5e0313 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -16,6 +16,8 @@
package com.android.systemui.recents.model;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -155,12 +157,13 @@
ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
// Compose the task key
- Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.stackId, t.baseIntent,
+ final int windowingMode = t.configuration.windowConfiguration.getWindowingMode();
+ Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, windowingMode, t.baseIntent,
t.userId, t.firstActiveTime, t.lastActiveTime);
// This task is only shown in the stack if it satisfies the historical time or min
// number of tasks constraints. Freeform tasks are also always shown.
- boolean isFreeformTask = SystemServicesProxy.isFreeformStack(t.stackId);
+ boolean isFreeformTask = windowingMode == WINDOWING_MODE_FREEFORM;
boolean isStackTask;
if (Recents.getConfiguration().isGridEnabled) {
// When grid layout is enabled, we only show the first
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 9e6bf85..abdb5cb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -16,6 +16,8 @@
package com.android.systemui.recents.model;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Intent;
@@ -46,8 +48,8 @@
public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData);
/* Notifies when a task has been unbound */
public void onTaskDataUnloaded();
- /* Notifies when a task's stack id has changed. */
- public void onTaskStackIdChanged();
+ /* Notifies when a task's windowing mode has changed. */
+ public void onTaskWindowingModeChanged();
}
/* The Task Key represents the unique primary key for the task */
@@ -55,7 +57,7 @@
@ViewDebug.ExportedProperty(category="recents")
public final int id;
@ViewDebug.ExportedProperty(category="recents")
- public int stackId;
+ public int windowingMode;
@ViewDebug.ExportedProperty(category="recents")
public final Intent baseIntent;
@ViewDebug.ExportedProperty(category="recents")
@@ -67,10 +69,10 @@
private int mHashCode;
- public TaskKey(int id, int stackId, Intent intent, int userId, long firstActiveTime,
+ public TaskKey(int id, int windowingMode, Intent intent, int userId, long firstActiveTime,
long lastActiveTime) {
this.id = id;
- this.stackId = stackId;
+ this.windowingMode = windowingMode;
this.baseIntent = intent;
this.userId = userId;
this.firstActiveTime = firstActiveTime;
@@ -78,8 +80,8 @@
updateHashCode();
}
- public void setStackId(int stackId) {
- this.stackId = stackId;
+ public void setWindowingMode(int windowingMode) {
+ this.windowingMode = windowingMode;
updateHashCode();
}
@@ -93,7 +95,9 @@
return false;
}
TaskKey otherKey = (TaskKey) o;
- return id == otherKey.id && stackId == otherKey.stackId && userId == otherKey.userId;
+ return id == otherKey.id
+ && windowingMode == otherKey.windowingMode
+ && userId == otherKey.userId;
}
@Override
@@ -103,12 +107,12 @@
@Override
public String toString() {
- return "id=" + id + " stackId=" + stackId + " user=" + userId + " lastActiveTime=" +
- lastActiveTime;
+ return "id=" + id + " windowingMode=" + windowingMode + " user=" + userId
+ + " lastActiveTime=" + lastActiveTime;
}
private void updateHashCode() {
- mHashCode = Objects.hash(id, stackId, userId);
+ mHashCode = Objects.hash(id, windowingMode, userId);
}
}
@@ -277,14 +281,12 @@
this.group = group;
}
- /**
- * Updates the stack id of this task.
- */
- public void setStackId(int stackId) {
- key.setStackId(stackId);
+ /** Updates the task's windowing mode. */
+ public void setWindowingMode(int windowingMode) {
+ key.setWindowingMode(windowingMode);
int callbackCount = mCallbacks.size();
for (int i = 0; i < callbackCount; i++) {
- mCallbacks.get(i).onTaskStackIdChanged();
+ mCallbacks.get(i).onTaskWindowingModeChanged();
}
}
@@ -293,7 +295,7 @@
*/
public boolean isFreeformTask() {
SystemServicesProxy ssp = Recents.getSystemServices();
- return ssp.hasFreeformWorkspaceSupport() && ssp.isFreeformStack(key.stackId);
+ return ssp.hasFreeformWorkspaceSupport() && key.windowingMode == WINDOWING_MODE_FREEFORM;
}
/** Notifies the callback listeners that this task has been loaded */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyCache.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyCache.java
index be99f93..247a654 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyCache.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskKeyCache.java
@@ -45,7 +45,7 @@
final V getAndInvalidateIfModified(Task.TaskKey key) {
Task.TaskKey lastKey = mKeys.get(key.id);
if (lastKey != null) {
- if ((lastKey.stackId != key.stackId) ||
+ if ((lastKey.windowingMode != key.windowingMode) ||
(lastKey.lastActiveTime != key.lastActiveTime)) {
// The task has updated (been made active since the last time it was put into the
// LRU cache) or the stack id for the task has changed, invalidate that cache item
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 6e3be09..fdae917 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -18,8 +18,8 @@
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.DOCKED_BOTTOM;
import static android.view.WindowManager.DOCKED_INVALID;
import static android.view.WindowManager.DOCKED_LEFT;
@@ -115,7 +115,7 @@
/**
* Moves the given task.
*/
- public void moveTaskToStack(Task task, int insertIndex, int newStackId) {
+ public void setTaskWindowingMode(Task task, int insertIndex, int windowingMode) {
int taskIndex = indexOf(task);
if (taskIndex != insertIndex) {
mTasks.remove(taskIndex);
@@ -127,7 +127,7 @@
// Update the stack id now, after we've moved the task, and before we update the
// filtered tasks
- task.setStackId(newStackId);
+ task.setWindowingMode(windowingMode);
updateFilteredTasks();
}
@@ -590,17 +590,15 @@
mCb = cb;
}
- /**
- * Moves the given task to either the front of the freeform workspace or the stack.
- */
- public void moveTaskToStack(Task task, int newStackId) {
+ /** Sets the windowing mode for a given task. */
+ public void setTaskWindowingMode(Task task, int windowingMode) {
// Find the index to insert into
ArrayList<Task> taskList = mStackTaskList.getTasks();
int taskCount = taskList.size();
- if (!task.isFreeformTask() && (newStackId == FREEFORM_WORKSPACE_STACK_ID)) {
+ if (!task.isFreeformTask() && (windowingMode == WINDOWING_MODE_FREEFORM)) {
// Insert freeform tasks at the front
- mStackTaskList.moveTaskToStack(task, taskCount, newStackId);
- } else if (task.isFreeformTask() && (newStackId == FULLSCREEN_WORKSPACE_STACK_ID)) {
+ mStackTaskList.setTaskWindowingMode(task, taskCount, windowingMode);
+ } else if (task.isFreeformTask() && (windowingMode == WINDOWING_MODE_FULLSCREEN)) {
// Insert after the first stacked task
int insertIndex = 0;
for (int i = taskCount - 1; i >= 0; i--) {
@@ -609,7 +607,7 @@
break;
}
}
- mStackTaskList.moveTaskToStack(task, insertIndex, newStackId);
+ mStackTaskList.setTaskWindowingMode(task, insertIndex, windowingMode);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index c7edb9a..e460bf8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -16,10 +16,6 @@
package com.android.systemui.recents.views;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.app.ActivityOptions.OnAnimationStartedListener;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 7ede0c5..3160ee0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -16,9 +16,8 @@
package com.android.systemui.recents.views;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -2094,18 +2093,18 @@
}
boolean isFreeformTask = event.task.isFreeformTask();
- boolean hasChangedStacks =
+ boolean hasChangedWindowingMode =
(!isFreeformTask && event.dropTarget == mFreeformWorkspaceDropTarget) ||
(isFreeformTask && event.dropTarget == mStackDropTarget);
- if (hasChangedStacks) {
+ if (hasChangedWindowingMode) {
// Move the task to the right position in the stack (ie. the front of the stack if
// freeform or the front of the stack if fullscreen). Note, we MUST move the tasks
// before we update their stack ids, otherwise, the keys will have changed.
if (event.dropTarget == mFreeformWorkspaceDropTarget) {
- mStack.moveTaskToStack(event.task, FREEFORM_WORKSPACE_STACK_ID);
+ mStack.setTaskWindowingMode(event.task, WINDOWING_MODE_FREEFORM);
} else if (event.dropTarget == mStackDropTarget) {
- mStack.moveTaskToStack(event.task, FULLSCREEN_WORKSPACE_STACK_ID);
+ mStack.setTaskWindowingMode(event.task, WINDOWING_MODE_FULLSCREEN);
}
updateLayoutAlgorithm(true /* boundScroll */);
@@ -2114,7 +2113,7 @@
@Override
public void run() {
SystemServicesProxy ssp = Recents.getSystemServices();
- ssp.moveTaskToStack(event.task.key.id, event.task.key.stackId);
+ ssp.setTaskWindowingMode(event.task.key.id, event.task.key.windowingMode);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index c64f6df..032d966 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -16,8 +16,6 @@
package com.android.systemui.recents.views;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
@@ -647,7 +645,7 @@
}
@Override
- public void onTaskStackIdChanged() {
+ public void onTaskWindowingModeChanged() {
// Force rebind the header, the thumbnail does not change due to stack changes
mHeaderView.bindToTask(mTask, mTouchExplorationEnabled, mIsDisabledInSafeMode);
mHeaderView.onTaskDataLoaded();
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index f7c0411..85a6062 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -16,7 +16,6 @@
package com.android.systemui.stackdivider;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.view.WindowManager.DOCKED_INVALID;
import android.app.ActivityManager;
@@ -88,8 +87,7 @@
@Override
public void run() {
try {
- ActivityManager.getService().moveTasksToFullscreenStack(
- DOCKED_STACK_ID, false /* onTop */);
+ ActivityManager.getService().dismissSplitScreenMode(false /* onTop */);
} catch (RemoteException e) {
Log.w(TAG, "Failed to remove stack: " + e);
}
@@ -100,8 +98,7 @@
@Override
public void run() {
try {
- ActivityManager.getService().resizeStack(
- DOCKED_STACK_ID, null, true, true, false, -1);
+ ActivityManager.getService().dismissSplitScreenMode(true /* onTop */);
} catch (RemoteException e) {
Log.w(TAG, "Failed to resize stack: " + e);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 966e789..6c5f4b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -36,6 +36,7 @@
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Property;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.NotificationHeaderView;
@@ -174,6 +175,11 @@
private boolean mShowNoBackground;
private ExpandableNotificationRow mNotificationParent;
private OnExpandClickListener mOnExpandClickListener;
+
+ // Listener will be called when receiving a long click event.
+ // Use #setLongPressPosition to optionally assign positional data with the long press.
+ private LongPressListener mLongPressListener;
+
private boolean mGroupExpansionChanging;
/**
@@ -788,6 +794,10 @@
mOnExpandClickListener = onExpandClickListener;
}
+ public void setLongPressListener(LongPressListener longPressListener) {
+ mLongPressListener = longPressListener;
+ }
+
@Override
public void setOnClickListener(@Nullable OnClickListener l) {
super.setOnClickListener(l);
@@ -1338,6 +1348,47 @@
}
}
+ private void doLongClickCallback() {
+ doLongClickCallback(getWidth() / 2, getHeight() / 2);
+ }
+
+ public void doLongClickCallback(int x, int y) {
+ createMenu();
+ MenuItem menuItem = getProvider().getLongpressMenuItem(mContext);
+ if (mLongPressListener != null && menuItem != null) {
+ mLongPressListener.onLongPress(this, x, y, menuItem);
+ }
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (KeyEvent.isConfirmKey(keyCode)) {
+ event.startTracking();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (KeyEvent.isConfirmKey(keyCode)) {
+ if (!event.isCanceled()) {
+ performClick();
+ }
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+ if (KeyEvent.isConfirmKey(keyCode)) {
+ doLongClickCallback();
+ return true;
+ }
+ return false;
+ }
+
public void resetTranslation() {
if (mTranslateAnim != null) {
mTranslateAnim.cancel();
@@ -2205,6 +2256,7 @@
@Override
public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfoInternal(info);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
if (canViewBeDismissed()) {
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_DISMISS);
}
@@ -2244,6 +2296,9 @@
case AccessibilityNodeInfo.ACTION_EXPAND:
mExpandClickListener.onClick(this);
return true;
+ case AccessibilityNodeInfo.ACTION_LONG_CLICK:
+ doLongClickCallback();
+ return true;
}
return false;
}
@@ -2332,4 +2387,15 @@
protected void setChildrenContainer(NotificationChildrenContainer childrenContainer) {
mChildrenContainer = childrenContainer;
}
+
+ /**
+ * Equivalent to View.OnLongClickListener with coordinates
+ */
+ public interface LongPressListener {
+ /**
+ * Equivalent to {@link View.OnLongClickListener#onLongClick(View)} with coordinates
+ * @return whether the longpress was handled
+ */
+ boolean onLongPress(View v, int x, int y, MenuItem item);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 2cff79d..6cfd42f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -43,6 +43,7 @@
import android.util.Log;
import android.util.Property;
import android.util.TypedValue;
+import android.view.View;
import android.view.ViewDebug;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.Interpolator;
@@ -142,6 +143,7 @@
private float[] mMatrix;
private ColorMatrixColorFilter mMatrixColorFilter;
private boolean mIsInShelf;
+ private Runnable mLayoutRunnable;
public StatusBarIconView(Context context, String slot, StatusBarNotification sbn) {
this(context, slot, sbn, false);
@@ -796,6 +798,24 @@
}
}
+ /**
+ * This method returns the drawing rect for the view which is different from the regular
+ * drawing rect, since we layout all children at position 0 and usually the translation is
+ * neglected. The standard implementation doesn't account for translation.
+ *
+ * @param outRect The (scrolled) drawing bounds of the view.
+ */
+ @Override
+ public void getDrawingRect(Rect outRect) {
+ super.getDrawingRect(outRect);
+ float translationX = getTranslationX();
+ float translationY = getTranslationY();
+ outRect.left += translationX;
+ outRect.right += translationX;
+ outRect.top += translationY;
+ outRect.bottom += translationY;
+ }
+
public void setIsInShelf(boolean isInShelf) {
mIsInShelf = isInShelf;
}
@@ -804,6 +824,19 @@
return mIsInShelf;
}
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ if (mLayoutRunnable != null) {
+ mLayoutRunnable.run();
+ mLayoutRunnable = null;
+ }
+ }
+
+ public void executeOnLayout(Runnable runnable) {
+ mLayoutRunnable = runnable;
+ }
+
public interface OnVisibilityChangedListener {
void onVisibilityChanged(int newVisibility);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 59d3e0a..2ad881f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -44,6 +44,7 @@
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
+import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
@@ -247,11 +248,12 @@
}
/**
- * Returns the {@link com.android.systemui.SwipeHelper.LongPressListener} that will be
- * triggered when a notification card is long-pressed.
+ * Returns the
+ * {@link com.android.systemui.statusbar.ExpandableNotificationRow.LongPressListener} that will
+ * be triggered when a notification card is long-pressed.
*/
@Override
- protected SwipeHelper.LongPressListener getNotificationLongClicker() {
+ protected ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
// For the automative use case, we do not want to the user to be able to interact with
// a notification other than a regular click. As a result, just return null for the
// long click listener.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 021b451..8afb849 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -384,7 +384,7 @@
if (mDozeParameters.getAlwaysOn()) {
// Setting power states can happen after we push out the frame. Make sure we
// stay fully opaque until the power state request reaches the lower levels.
- setDozeInFrontAlphaDelayed(mAodFrontScrimOpacity, 30);
+ setDozeInFrontAlphaDelayed(mAodFrontScrimOpacity, 100);
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 41a69b4..40fe50f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -4,10 +4,8 @@
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Rect;
-import android.graphics.drawable.Icon;
import android.support.annotation.NonNull;
import android.support.v4.util.ArrayMap;
-import android.support.v4.util.ArraySet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
@@ -269,18 +267,26 @@
*/
private void applyNotificationIconsTint() {
for (int i = 0; i < mNotificationIcons.getChildCount(); i++) {
- StatusBarIconView v = (StatusBarIconView) mNotificationIcons.getChildAt(i);
- boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L));
- int color = StatusBarIconView.NO_COLOR;
- boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mNotificationColorUtil);
- if (colorize) {
- color = DarkIconDispatcher.getTint(mTintArea, v, mIconTint);
+ final StatusBarIconView iv = (StatusBarIconView) mNotificationIcons.getChildAt(i);
+ if (iv.getWidth() != 0) {
+ updateTintForIcon(iv);
+ } else {
+ iv.executeOnLayout(() -> updateTintForIcon(iv));
}
- v.setStaticDrawableColor(color);
- v.setDecorColor(mIconTint);
}
}
+ private void updateTintForIcon(StatusBarIconView v) {
+ boolean isPreL = Boolean.TRUE.equals(v.getTag(R.id.icon_is_pre_L));
+ int color = StatusBarIconView.NO_COLOR;
+ boolean colorize = !isPreL || NotificationUtils.isGrayscale(v, mNotificationColorUtil);
+ if (colorize) {
+ color = DarkIconDispatcher.getTint(mTintArea, v, mIconTint);
+ }
+ v.setStaticDrawableColor(color);
+ v.setDecorColor(mIconTint);
+ }
+
public void setDark(boolean dark) {
mNotificationIcons.setDark(dark, false, 0);
mShelfIcons.setDark(dark, false, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 7b11ace..af03440 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -704,7 +704,7 @@
mInitialHeightOnTouch = mQsExpansionHeight;
mQsTracking = true;
mIntercepting = false;
- mNotificationStackScroller.removeLongPressCallback();
+ mNotificationStackScroller.cancelLongPress();
}
break;
case MotionEvent.ACTION_POINTER_UP:
@@ -740,7 +740,7 @@
mInitialTouchY = y;
mInitialTouchX = x;
mIntercepting = false;
- mNotificationStackScroller.removeLongPressCallback();
+ mNotificationStackScroller.cancelLongPress();
return true;
}
break;
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 d3bb17f..3a6819e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -102,6 +102,7 @@
import android.os.Vibrator;
import android.provider.Settings;
import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
@@ -159,7 +160,6 @@
import com.android.systemui.Prefs;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
-import com.android.systemui.SwipeHelper;
import com.android.systemui.SystemUI;
import com.android.systemui.SystemUIFactory;
import com.android.systemui.UiOffloadThread;
@@ -1818,7 +1818,9 @@
final int id = n.getId();
final int userId = n.getUserId();
try {
- mBarService.onNotificationClear(pkg, tag, id, userId);
+ // TODO: record actual dismissal surface
+ mBarService.onNotificationClear(pkg, tag, id, userId, n.getKey(),
+ NotificationStats.DISMISSAL_OTHER);
if (FORCE_REMOTE_INPUT_HISTORY
&& mKeysKeptForRemoteInput.contains(n.getKey())) {
mKeysKeptForRemoteInput.remove(n.getKey());
@@ -4835,7 +4837,7 @@
@Override
public void onTouchSlopExceeded() {
- mStackScroller.removeLongPressCallback();
+ mStackScroller.cancelLongPress();
mStackScroller.checkSnoozeLeavebehind();
}
@@ -5467,7 +5469,7 @@
@Override
public void onDoubleTap(float screenX, float screenY) {
- if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null
+ if (screenX > 0 && screenY > 0 && mAmbientIndicationContainer != null
&& mAmbientIndicationContainer.getVisibility() == View.VISIBLE) {
mAmbientIndicationContainer.getLocationOnScreen(mTmpInt2);
float viewX = screenX - mTmpInt2[0];
@@ -6293,14 +6295,15 @@
true /* removeControls */, x, y, true /* resetMenu */);
}
- protected SwipeHelper.LongPressListener getNotificationLongClicker() {
- return new SwipeHelper.LongPressListener() {
+ protected ExpandableNotificationRow.LongPressListener getNotificationLongClicker() {
+ return new ExpandableNotificationRow.LongPressListener() {
@Override
public boolean onLongPress(View v, final int x, final int y,
MenuItem item) {
if (!(v instanceof ExpandableNotificationRow)) {
return false;
}
+
if (v.getWindowToken() == null) {
Log.e(TAG, "Trying to show notification guts, but not attached to window");
return false;
@@ -6315,7 +6318,7 @@
closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
true /* removeControls */, -1 /* x */, -1 /* y */,
true /* resetMenu */);
- return false;
+ return true;
}
bindGuts(row, item);
NotificationGuts guts = row.getGuts();
@@ -6595,6 +6598,7 @@
row.setRemoteViewClickHandler(mOnClickHandler);
row.setInflationCallback(this);
row.setSecureStateProvider(this::isKeyguardCurrentlySecure);
+ row.setLongPressListener(getNotificationLongClicker());
// Get the app name.
// Note that Notification.Builder#bindHeaderAppName has similar logic
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 75532d9..1e14626 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -240,7 +240,7 @@
* motion.
*/
private int mMaxScrollAfterExpand;
- private SwipeHelper.LongPressListener mLongPressListener;
+ private ExpandableNotificationRow.LongPressListener mLongPressListener;
private NotificationMenuRowPlugin mCurrMenuRow;
private View mTranslatingParentView;
@@ -410,7 +410,6 @@
mExpandHelper.setEventSource(this);
mExpandHelper.setScrollAdapter(this);
mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, this, getContext());
- mSwipeHelper.setLongPressListener(mLongPressListener);
mStackScrollAlgorithm = createStackScrollAlgorithm(context);
initView(context);
mFalsingManager = FalsingManager.getInstance(context);
@@ -884,8 +883,7 @@
return firstChild != null ? firstChild.getMinHeight() : mCollapsedSize;
}
- public void setLongPressListener(SwipeHelper.LongPressListener listener) {
- mSwipeHelper.setLongPressListener(listener);
+ public void setLongPressListener(ExpandableNotificationRow.LongPressListener listener) {
mLongPressListener = listener;
}
@@ -1175,7 +1173,7 @@
if (v instanceof ExpandableNotificationRow) {
((ExpandableNotificationRow) v).setUserLocked(userLocked);
}
- removeLongPressCallback();
+ cancelLongPress();
requestDisallowInterceptTouchEvent(true);
}
@@ -2581,7 +2579,7 @@
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
super.requestDisallowInterceptTouchEvent(disallowIntercept);
if (disallowIntercept) {
- mSwipeHelper.removeLongPressCallback();
+ cancelLongPress();
}
}
@@ -3302,7 +3300,7 @@
mIsBeingDragged = isDragged;
if (isDragged) {
requestDisallowInterceptTouchEvent(true);
- removeLongPressCallback();
+ cancelLongPress();
}
}
@@ -3310,7 +3308,7 @@
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (!hasWindowFocus) {
- removeLongPressCallback();
+ cancelLongPress();
}
}
@@ -3324,7 +3322,7 @@
@Override
public void requestDisallowLongPress() {
- removeLongPressCallback();
+ cancelLongPress();
}
@Override
@@ -3332,8 +3330,8 @@
mDisallowDismissInThisMotion = true;
}
- public void removeLongPressCallback() {
- mSwipeHelper.removeLongPressCallback();
+ public void cancelLongPress() {
+ mSwipeHelper.cancelLongPress();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
index b835909..5ec3dff 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/DelayedWakeLock.java
@@ -23,7 +23,7 @@
*/
public class DelayedWakeLock implements WakeLock {
- private static final long RELEASE_DELAY_MS = 100;
+ private static final long RELEASE_DELAY_MS = 120;
private final Handler mHandler;
private final WakeLock mInner;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/model/HighResThumbnailLoaderTest.java b/packages/SystemUI/tests/src/com/android/systemui/recents/model/HighResThumbnailLoaderTest.java
index 5a8c6dd..767e124 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/model/HighResThumbnailLoaderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/model/HighResThumbnailLoaderTest.java
@@ -16,6 +16,7 @@
package com.android.systemui.recents.model;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.anyBoolean;
@@ -59,7 +60,7 @@
MockitoAnnotations.initMocks(this);
mLoader = new HighResThumbnailLoader(mMockSystemServicesProxy, Looper.getMainLooper(),
false);
- mTask.key = new TaskKey(0, 0, null, 0, 0, 0);
+ mTask.key = new TaskKey(0, WINDOWING_MODE_UNDEFINED, null, 0, 0, 0);
when(mMockSystemServicesProxy.getTaskThumbnail(anyInt(), anyBoolean()))
.thenReturn(mThumbnailData);
mLoader.setVisible(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index c0de004..3b401a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -21,9 +21,11 @@
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import com.android.internal.app.NightDisplayController;
+import com.android.systemui.Dependency;
import com.android.systemui.Prefs;
import com.android.systemui.Prefs.Key;
import com.android.systemui.SysuiTestCase;
@@ -45,6 +47,8 @@
@Before
public void setUp() throws Exception {
+ mDependency.injectTestDependency(Dependency.BG_LOOPER,
+ TestableLooper.get(this).getLooper());
Prefs.putBoolean(mContext, Key.QS_NIGHTDISPLAY_ADDED, false);
mQsTileHost = Mockito.mock(QSTileHost.class);
mAutoTileManager = new AutoTileManager(mContext, mQsTileHost);
diff --git a/packages/VpnDialogs/res/values-cs/strings.xml b/packages/VpnDialogs/res/values-cs/strings.xml
index 7a3d515..47d950f 100644
--- a/packages/VpnDialogs/res/values-cs/strings.xml
+++ b/packages/VpnDialogs/res/values-cs/strings.xml
@@ -31,6 +31,6 @@
<string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Změnit nastavení VPN"</string>
<string name="configure" msgid="4905518375574791375">"Konfigurovat"</string>
<string name="disconnect" msgid="971412338304200056">"Odpojit"</string>
- <string name="open_app" msgid="3717639178595958667">"Spustit aplikaci"</string>
+ <string name="open_app" msgid="3717639178595958667">"Do aplikace"</string>
<string name="dismiss" msgid="6192859333764711227">"Zavřít"</string>
</resources>
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index e17a6a6..6a8a222 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4665,6 +4665,16 @@
// OS: P
DIALOG_CLEAR_ADB_KEYS = 1223;
+ // Open: Settings > Developer options > Quick setting tile config
+ // CATEGORY: SETTINGS
+ // OS: P
+ DEVELOPMENT_QS_TILE_CONFIG = 1224;
+
+ // OPEN: Settings > Developer options > Store logger data persistently on device > Info dialog
+ // CATEGORY: SETTINGS
+ // OS: P
+ DIALOG_LOG_PERSIST = 1225;
+
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
}
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index f9f6bf7d..b32be73 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -1328,7 +1328,7 @@
const void* ptr = bitmap.getPixels();
jlong id = (jlong)(uintptr_t)rsAllocationCreateFromBitmap((RsContext)con,
(RsType)type, (RsAllocationMipmapControl)mip,
- ptr, bitmap.getSize(), usage);
+ ptr, bitmap.computeByteSize(), usage);
return id;
}
@@ -1356,7 +1356,7 @@
const void* ptr = bitmap.getPixels();
jlong id = (jlong)(uintptr_t)rsAllocationCubeCreateFromBitmap((RsContext)con,
(RsType)type, (RsAllocationMipmapControl)mip,
- ptr, bitmap.getSize(), usage);
+ ptr, bitmap.computeByteSize(), usage);
return id;
}
@@ -1371,7 +1371,7 @@
const void* ptr = bitmap.getPixels();
rsAllocation2DData((RsContext)con, (RsAllocation)alloc, 0, 0,
0, RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X,
- w, h, ptr, bitmap.getSize(), 0);
+ w, h, ptr, bitmap.computeByteSize(), 0);
}
static void
@@ -1381,7 +1381,7 @@
GraphicsJNI::getSkBitmap(_env, jbitmap, &bitmap);
void* ptr = bitmap.getPixels();
- rsAllocationCopyToBitmap((RsContext)con, (RsAllocation)alloc, ptr, bitmap.getSize());
+ rsAllocationCopyToBitmap((RsContext)con, (RsAllocation)alloc, ptr, bitmap.computeByteSize());
bitmap.notifyPixelsChanged();
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 862070a..880f236 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -216,9 +216,12 @@
serviceComponent = ComponentName.unflattenFromString(componentName);
serviceInfo = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
0, mUserId);
+ if (serviceInfo == null) {
+ Slog.e(TAG, "Bad AutofillService name: " + componentName);
+ }
} catch (RuntimeException | RemoteException e) {
- Slog.e(TAG, "Bad autofill service name " + componentName + ": " + e);
- return;
+ Slog.e(TAG, "Error getting service info for '" + componentName + "': " + e);
+ serviceInfo = null;
}
}
try {
@@ -228,21 +231,24 @@
if (sDebug) Slog.d(TAG, "Set component for user " + mUserId + " as " + mInfo);
} else {
mInfo = null;
- if (sDebug) Slog.d(TAG, "Reset component for user " + mUserId);
- }
- final boolean isEnabled = isEnabled();
- if (wasEnabled != isEnabled) {
- if (!isEnabled) {
- final int sessionCount = mSessions.size();
- for (int i = sessionCount - 1; i >= 0; i--) {
- final Session session = mSessions.valueAt(i);
- session.removeSelfLocked();
- }
+ if (sDebug) {
+ Slog.d(TAG, "Reset component for user " + mUserId + " (" + componentName + ")");
}
- sendStateToClients(false);
}
} catch (Exception e) {
- Slog.e(TAG, "Bad AutofillService '" + componentName + "': " + e);
+ Slog.e(TAG, "Bad AutofillServiceInfo for '" + componentName + "': " + e);
+ mInfo = null;
+ }
+ final boolean isEnabled = isEnabled();
+ if (wasEnabled != isEnabled) {
+ if (!isEnabled) {
+ final int sessionCount = mSessions.size();
+ for (int i = sessionCount - 1; i >= 0; i--) {
+ final Session session = mSessions.valueAt(i);
+ session.removeSelfLocked();
+ }
+ }
+ sendStateToClients(false);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 11478fe..ed00ffe 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -56,9 +56,11 @@
import android.service.autofill.FillContext;
import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
+import android.service.autofill.InternalSanitizer;
import android.service.autofill.InternalValidator;
import android.service.autofill.SaveInfo;
import android.service.autofill.SaveRequest;
+import android.service.autofill.Transformation;
import android.service.autofill.ValueFinder;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -856,6 +858,8 @@
return true;
}
+ final ArrayMap<AutofillId, InternalSanitizer> sanitizers = createSanitizers(saveInfo);
+
// Cache used to make sure changed fields do not belong to a dataset.
final ArrayMap<AutofillId, AutofillValue> currentValues = new ArrayMap<>();
final ArraySet<AutofillId> allIds = new ArraySet<>();
@@ -895,6 +899,7 @@
break;
}
}
+ value = getSanitizedValue(sanitizers, id, value);
currentValues.put(id, value);
final AutofillValue filledValue = viewState.getAutofilledValue();
@@ -1037,6 +1042,48 @@
return true;
}
+ @Nullable
+ private ArrayMap<AutofillId, InternalSanitizer> createSanitizers(@Nullable SaveInfo saveInfo) {
+ if (saveInfo == null) return null;
+
+ final InternalSanitizer[] sanitizerKeys = saveInfo.getSanitizerKeys();
+ if (sanitizerKeys == null) return null;
+
+ final int size = sanitizerKeys.length ;
+ final ArrayMap<AutofillId, InternalSanitizer> sanitizers = new ArrayMap<>(size);
+ if (sDebug) Slog.d(TAG, "Service provided " + size + " sanitizers");
+ final AutofillId[][] sanitizerValues = saveInfo.getSanitizerValues();
+ for (int i = 0; i < size; i++) {
+ final InternalSanitizer sanitizer = sanitizerKeys[i];
+ final AutofillId[] ids = sanitizerValues[i];
+ if (sDebug) {
+ Slog.d(TAG, "sanitizer #" + i + " (" + sanitizer + ") for ids "
+ + Arrays.toString(ids));
+ }
+ for (AutofillId id : ids) {
+ sanitizers.put(id, sanitizer);
+ }
+ }
+ return sanitizers;
+ }
+
+ @NonNull
+ private AutofillValue getSanitizedValue(
+ @Nullable ArrayMap<AutofillId, InternalSanitizer> sanitizers,
+ @NonNull AutofillId id,
+ @NonNull AutofillValue value) {
+ if (sanitizers == null) return value;
+
+ final InternalSanitizer sanitizer = sanitizers.get(id);
+ if (sanitizer == null) {
+ return value;
+ }
+
+ final AutofillValue sanitized = sanitizer.sanitize(value);
+ if (sDebug) Slog.d(TAG, "Value for " + id + "(" + value + ") sanitized to " + sanitized);
+ return sanitized;
+ }
+
/**
* Returns whether the session is currently showing the save UI
*/
@@ -1100,6 +1147,9 @@
return;
}
+ final ArrayMap<AutofillId, InternalSanitizer> sanitizers =
+ createSanitizers(getSaveInfoLocked());
+
final int numContexts = mContexts.size();
for (int contextNum = 0; contextNum < numContexts; contextNum++) {
@@ -1126,7 +1176,9 @@
}
if (sVerbose) Slog.v(TAG, "callSaveLocked(): updating " + id + " to " + value);
- node.updateAutofillValue(value);
+ final AutofillValue sanitizedValue = getSanitizedValue(sanitizers, id, value);
+
+ node.updateAutofillValue(sanitizedValue);
}
// Sanitize structure before it's sent to service.
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index f4dbb5a..eabe21f 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -401,45 +401,53 @@
@Override
public void onUnlockUser(int userId) {
if (userId == UserHandle.USER_SYSTEM) {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
- sInstance.initialize(userId);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
- // Migrate legacy setting
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
- if (!backupSettingMigrated(userId)) {
- if (DEBUG) {
- Slog.i(TAG, "Backup enable apparently not migrated");
- }
- final ContentResolver r = sInstance.mContext.getContentResolver();
- final int enableState = Settings.Secure.getIntForUser(r,
- Settings.Secure.BACKUP_ENABLED, -1, userId);
- if (enableState >= 0) {
- if (DEBUG) {
- Slog.i(TAG, "Migrating enable state " + (enableState != 0));
- }
- writeBackupEnableState(enableState != 0, userId);
- Settings.Secure.putStringForUser(r,
- Settings.Secure.BACKUP_ENABLED, null, userId);
- } else {
- if (DEBUG) {
- Slog.i(TAG, "Backup not yet configured; retaining null enable state");
- }
- }
- }
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
- try {
- sInstance.setBackupEnabled(readBackupEnableState(userId));
- } catch (RemoteException e) {
- // can't happen; it's a local object
- }
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ sInstance.unlockSystemUser();
}
}
}
+ // Called through the trampoline from onUnlockUser(), then we buck the work
+ // off to the background thread to keep the unlock time down.
+ public void unlockSystemUser() {
+ mBackupHandler.post(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
+ sInstance.initialize(UserHandle.USER_SYSTEM);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+ // Migrate legacy setting
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
+ if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) {
+ if (DEBUG) {
+ Slog.i(TAG, "Backup enable apparently not migrated");
+ }
+ final ContentResolver r = sInstance.mContext.getContentResolver();
+ final int enableState = Settings.Secure.getIntForUser(r,
+ Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM);
+ if (enableState >= 0) {
+ if (DEBUG) {
+ Slog.i(TAG, "Migrating enable state " + (enableState != 0));
+ }
+ writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM);
+ Settings.Secure.putStringForUser(r,
+ Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM);
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "Backup not yet configured; retaining null enable state");
+ }
+ }
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
+ try {
+ sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM));
+ } catch (RemoteException e) {
+ // can't happen; it's a local object
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ });
+ }
+
class ProvisionedObserver extends ContentObserver {
public ProvisionedObserver(Handler handler) {
super(handler);
diff --git a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
index 5dfa630..041f9ed 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
@@ -39,6 +39,8 @@
*/
public interface BackupManagerServiceInterface {
+ void unlockSystemUser();
+
// Utility: build a new random integer token
int generateRandomIntegerToken();
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index fe4902f..f298065 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -548,45 +548,53 @@
@Override
public void onUnlockUser(int userId) {
if (userId == UserHandle.USER_SYSTEM) {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
- sInstance.initialize(userId);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
- // Migrate legacy setting
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
- if (!backupSettingMigrated(userId)) {
- if (DEBUG) {
- Slog.i(TAG, "Backup enable apparently not migrated");
- }
- final ContentResolver r = sInstance.mContext.getContentResolver();
- final int enableState = Settings.Secure.getIntForUser(r,
- Settings.Secure.BACKUP_ENABLED, -1, userId);
- if (enableState >= 0) {
- if (DEBUG) {
- Slog.i(TAG, "Migrating enable state " + (enableState != 0));
- }
- writeBackupEnableState(enableState != 0, userId);
- Settings.Secure.putStringForUser(r,
- Settings.Secure.BACKUP_ENABLED, null, userId);
- } else {
- if (DEBUG) {
- Slog.i(TAG, "Backup not yet configured; retaining null enable state");
- }
- }
- }
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
-
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
- try {
- sInstance.setBackupEnabled(readBackupEnableState(userId));
- } catch (RemoteException e) {
- // can't happen; it's a local object
- }
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ sInstance.unlockSystemUser();
}
}
}
+ // Called through the trampoline from onUnlockUser(), then we buck the work
+ // off to the background thread to keep the unlock time down.
+ public void unlockSystemUser() {
+ mBackupHandler.post(() -> {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup init");
+ sInstance.initialize(UserHandle.USER_SYSTEM);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+ // Migrate legacy setting
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup migrate");
+ if (!backupSettingMigrated(UserHandle.USER_SYSTEM)) {
+ if (DEBUG) {
+ Slog.i(TAG, "Backup enable apparently not migrated");
+ }
+ final ContentResolver r = sInstance.mContext.getContentResolver();
+ final int enableState = Settings.Secure.getIntForUser(r,
+ Settings.Secure.BACKUP_ENABLED, -1, UserHandle.USER_SYSTEM);
+ if (enableState >= 0) {
+ if (DEBUG) {
+ Slog.i(TAG, "Migrating enable state " + (enableState != 0));
+ }
+ writeBackupEnableState(enableState != 0, UserHandle.USER_SYSTEM);
+ Settings.Secure.putStringForUser(r,
+ Settings.Secure.BACKUP_ENABLED, null, UserHandle.USER_SYSTEM);
+ } else {
+ if (DEBUG) {
+ Slog.i(TAG, "Backup not yet configured; retaining null enable state");
+ }
+ }
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
+ try {
+ sInstance.setBackupEnabled(readBackupEnableState(UserHandle.USER_SYSTEM));
+ } catch (RemoteException e) {
+ // can't happen; it's a local object
+ }
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ });
+ }
+
// Bookkeeping of in-flight operations for timeout etc. purposes. The operation
// token is the index of the entry in the pending-operations list.
public static final int OP_PENDING = 0;
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
index 245bc1d..9739e38 100644
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ b/services/backup/java/com/android/server/backup/Trampoline.java
@@ -139,6 +139,13 @@
}
}
+ void unlockSystemUser() {
+ BackupManagerServiceInterface svc = mService;
+ if (svc != null) {
+ svc.unlockSystemUser();
+ }
+ }
+
public void setBackupServiceActive(final int userHandle, boolean makeActive) {
// Only the DPM should be changing the active state of backup
final int caller = binderGetCallingUid();
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index abbc89e..2d9baf6 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -307,6 +307,12 @@
*/
private int[] mTempWhitelistAppIdArray = new int[0];
+ /**
+ * Apps in the system whitelist that have been taken out (probably because the user wanted to).
+ * They can be restored back by calling restoreAppToSystemWhitelist(String).
+ */
+ private ArrayMap<String, Integer> mRemovedFromSystemWhitelistApps = new ArrayMap<>();
+
private static final int EVENT_NULL = 0;
private static final int EVENT_NORMAL = 1;
private static final int EVENT_LIGHT_IDLE = 2;
@@ -760,17 +766,15 @@
public long NOTIFICATION_WHITELIST_DURATION;
private final ContentResolver mResolver;
- private final boolean mHasWatch;
+ private final boolean mSmallBatteryDevice;
private final KeyValueListParser mParser = new KeyValueListParser(',');
public Constants(Handler handler, ContentResolver resolver) {
super(handler);
mResolver = resolver;
- mHasWatch = getContext().getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WATCH);
- mResolver.registerContentObserver(Settings.Global.getUriFor(
- mHasWatch ? Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH
- : Settings.Global.DEVICE_IDLE_CONSTANTS),
+ mSmallBatteryDevice = ActivityManager.isSmallBatteryDevice();
+ mResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.DEVICE_IDLE_CONSTANTS),
false, this);
updateConstants();
}
@@ -784,8 +788,7 @@
synchronized (DeviceIdleController.this) {
try {
mParser.setString(Settings.Global.getString(mResolver,
- mHasWatch ? Settings.Global.DEVICE_IDLE_CONSTANTS_WATCH
- : Settings.Global.DEVICE_IDLE_CONSTANTS));
+ Settings.Global.DEVICE_IDLE_CONSTANTS));
} catch (IllegalArgumentException e) {
// Failed to parse the settings string, log this and move on
// with defaults.
@@ -815,7 +818,7 @@
MIN_DEEP_MAINTENANCE_TIME = mParser.getLong(
KEY_MIN_DEEP_MAINTENANCE_TIME,
!COMPRESS_TIME ? 30 * 1000L : 5 * 1000L);
- long inactiveTimeoutDefault = (mHasWatch ? 15 : 30) * 60 * 1000L;
+ long inactiveTimeoutDefault = (mSmallBatteryDevice ? 15 : 30) * 60 * 1000L;
INACTIVE_TIMEOUT = mParser.getLong(KEY_INACTIVE_TIMEOUT,
!COMPRESS_TIME ? inactiveTimeoutDefault : (inactiveTimeoutDefault / 10));
SENSING_TIMEOUT = mParser.getLong(KEY_SENSING_TIMEOUT,
@@ -825,7 +828,7 @@
LOCATION_ACCURACY = mParser.getFloat(KEY_LOCATION_ACCURACY, 20);
MOTION_INACTIVE_TIMEOUT = mParser.getLong(KEY_MOTION_INACTIVE_TIMEOUT,
!COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L);
- long idleAfterInactiveTimeout = (mHasWatch ? 15 : 30) * 60 * 1000L;
+ long idleAfterInactiveTimeout = (mSmallBatteryDevice ? 15 : 30) * 60 * 1000L;
IDLE_AFTER_INACTIVE_TIMEOUT = mParser.getLong(KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
!COMPRESS_TIME ? idleAfterInactiveTimeout
: (idleAfterInactiveTimeout / 10));
@@ -1162,6 +1165,38 @@
}
}
+ @Override public void removeSystemPowerWhitelistApp(String name) {
+ if (DEBUG) {
+ Slog.d(TAG, "removeAppFromSystemWhitelist(name = " + name + ")");
+ }
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+ null);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ removeSystemPowerWhitelistAppInternal(name);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override public void restoreSystemPowerWhitelistApp(String name) {
+ if (DEBUG) {
+ Slog.d(TAG, "restoreAppToSystemWhitelist(name = " + name + ")");
+ }
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+ null);
+ long ident = Binder.clearCallingIdentity();
+ try {
+ restoreSystemPowerWhitelistAppInternal(name);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ public String[] getRemovedSystemPowerWhitelistApps() {
+ return getRemovedSystemPowerWhitelistAppsInternal();
+ }
+
@Override public String[] getSystemPowerWhitelistExceptIdle() {
return getSystemPowerWhitelistExceptIdleInternal();
}
@@ -1504,6 +1539,42 @@
}
}
+ void resetSystemPowerWhitelistInternal() {
+ synchronized (this) {
+ mPowerSaveWhitelistApps.putAll(mRemovedFromSystemWhitelistApps);
+ mRemovedFromSystemWhitelistApps.clear();
+ reportPowerSaveWhitelistChangedLocked();
+ updateWhitelistAppIdsLocked();
+ writeConfigFileLocked();
+ }
+ }
+
+ public boolean restoreSystemPowerWhitelistAppInternal(String name) {
+ synchronized (this) {
+ if (!mRemovedFromSystemWhitelistApps.containsKey(name)) {
+ return false;
+ }
+ mPowerSaveWhitelistApps.put(name, mRemovedFromSystemWhitelistApps.remove(name));
+ reportPowerSaveWhitelistChangedLocked();
+ updateWhitelistAppIdsLocked();
+ writeConfigFileLocked();
+ return true;
+ }
+ }
+
+ public boolean removeSystemPowerWhitelistAppInternal(String name) {
+ synchronized (this) {
+ if (!mPowerSaveWhitelistApps.containsKey(name)) {
+ return false;
+ }
+ mRemovedFromSystemWhitelistApps.put(name, mPowerSaveWhitelistApps.remove(name));
+ reportPowerSaveWhitelistChangedLocked();
+ updateWhitelistAppIdsLocked();
+ writeConfigFileLocked();
+ return true;
+ }
+ }
+
public boolean addPowerSaveWhitelistExceptIdleInternal(String name) {
synchronized (this) {
try {
@@ -1565,6 +1636,17 @@
}
}
+ public String[] getRemovedSystemPowerWhitelistAppsInternal() {
+ synchronized (this) {
+ int size = mRemovedFromSystemWhitelistApps.size();
+ final String[] apps = new String[size];
+ for (int i = 0; i < size; i++) {
+ apps[i] = mRemovedFromSystemWhitelistApps.keyAt(i);
+ }
+ return apps;
+ }
+ }
+
public String[] getUserPowerWhitelistInternal() {
synchronized (this) {
int size = mPowerSaveWhitelistUserApps.size();
@@ -2481,21 +2563,31 @@
}
String tagName = parser.getName();
- if (tagName.equals("wl")) {
- String name = parser.getAttributeValue(null, "n");
- if (name != null) {
- try {
- ApplicationInfo ai = pm.getApplicationInfo(name,
- PackageManager.MATCH_ANY_USER);
- mPowerSaveWhitelistUserApps.put(ai.packageName,
- UserHandle.getAppId(ai.uid));
- } catch (PackageManager.NameNotFoundException e) {
+ switch (tagName) {
+ case "wl":
+ String name = parser.getAttributeValue(null, "n");
+ if (name != null) {
+ try {
+ ApplicationInfo ai = pm.getApplicationInfo(name,
+ PackageManager.MATCH_ANY_USER);
+ mPowerSaveWhitelistUserApps.put(ai.packageName,
+ UserHandle.getAppId(ai.uid));
+ } catch (PackageManager.NameNotFoundException e) {
+ }
}
- }
- } else {
- Slog.w(TAG, "Unknown element under <config>: "
- + parser.getName());
- XmlUtils.skipCurrentTag(parser);
+ break;
+ case "un-wl":
+ final String packageName = parser.getAttributeValue(null, "n");
+ if (mPowerSaveWhitelistApps.containsKey(packageName)) {
+ mRemovedFromSystemWhitelistApps.put(packageName,
+ mPowerSaveWhitelistApps.remove(packageName));
+ }
+ break;
+ default:
+ Slog.w(TAG, "Unknown element under <config>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ break;
}
}
@@ -2556,6 +2648,11 @@
out.attribute(null, "n", name);
out.endTag(null, "wl");
}
+ for (int i = 0; i < mRemovedFromSystemWhitelistApps.size(); i++) {
+ out.startTag(null, "un-wl");
+ out.attribute(null, "n", mRemovedFromSystemWhitelistApps.keyAt(i));
+ out.endTag(null, "un-wl");
+ }
out.endTag(null, "config");
out.endDocument();
}
@@ -2584,6 +2681,13 @@
pw.println(" Print currently whitelisted apps.");
pw.println(" whitelist [package ...]");
pw.println(" Add (prefix with +) or remove (prefix with -) packages.");
+ pw.println(" sys-whitelist [package ...|reset]");
+ pw.println(" Prefix the package with '-' to remove it from the system whitelist or '+'"
+ + " to put it back in the system whitelist.");
+ pw.println(" Note that only packages that were"
+ + " earlier removed from the system whitelist can be added back.");
+ pw.println(" reset will reset the whitelist to the original state");
+ pw.println(" Prints the system whitelist if no arguments are specified");
pw.println(" except-idle-whitelist [package ...|reset]");
pw.println(" Prefix the package with '+' to add it to whitelist or "
+ "'=' to check if it is already whitelisted");
@@ -2944,6 +3048,50 @@
} finally {
Binder.restoreCallingIdentity(token);
}
+ } else if ("sys-whitelist".equals(cmd)) {
+ String arg = shell.getNextArg();
+ if (arg != null) {
+ getContext().enforceCallingOrSelfPermission(
+ android.Manifest.permission.DEVICE_POWER, null);
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if ("reset".equals(arg)) {
+ resetSystemPowerWhitelistInternal();
+ } else {
+ do {
+ if (arg.length() < 1
+ || (arg.charAt(0) != '-' && arg.charAt(0) != '+')) {
+ pw.println("Package must be prefixed with + or - " + arg);
+ return -1;
+ }
+ final char op = arg.charAt(0);
+ final String pkg = arg.substring(1);
+ switch (op) {
+ case '+':
+ if (restoreSystemPowerWhitelistAppInternal(pkg)) {
+ pw.println("Restored " + pkg);
+ }
+ break;
+ case '-':
+ if (removeSystemPowerWhitelistAppInternal(pkg)) {
+ pw.println("Removed " + pkg);
+ }
+ break;
+ }
+ } while ((arg = shell.getNextArg()) != null);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ } else {
+ synchronized (this) {
+ for (int j=0; j<mPowerSaveWhitelistApps.size(); j++) {
+ pw.print(mPowerSaveWhitelistApps.keyAt(j));
+ pw.print(",");
+ pw.println(mPowerSaveWhitelistApps.valueAt(j));
+ }
+ }
+ }
} else {
return shell.handleDefaultCommands(cmd);
}
@@ -3027,6 +3175,14 @@
pw.println(mPowerSaveWhitelistApps.keyAt(i));
}
}
+ size = mRemovedFromSystemWhitelistApps.size();
+ if (size > 0) {
+ pw.println(" Removed from whitelist system apps:");
+ for (int i = 0; i < size; i++) {
+ pw.print(" ");
+ pw.println(mRemovedFromSystemWhitelistApps.keyAt(i));
+ }
+ }
size = mPowerSaveWhitelistUserApps.size();
if (size > 0) {
pw.println(" Whitelist user apps:");
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 046eb76..8b79b9d 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -373,12 +373,24 @@
if (mCurrentVibration.hasLongerTimeout(newOneShot.getTiming())
&& newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
if (DEBUG) {
- Slog.e(TAG, "Ignoring incoming vibration in favor of current vibration");
+ Slog.d(TAG, "Ignoring incoming vibration in favor of current vibration");
}
return;
}
}
+ // If the current vibration is repeating and the incoming one is non-repeating, then ignore
+ // the non-repeating vibration. This is so that we don't cancel vibrations that are meant
+ // to grab the attention of the user, like ringtones and alarms, in favor of one-shot
+ // vibrations that are likely quite short.
+ if (!isRepeatingVibration(effect)
+ && mCurrentVibration != null && isRepeatingVibration(mCurrentVibration.mEffect)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
+ }
+ return;
+ }
+
Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);
// Only link against waveforms since they potentially don't have a finish if
@@ -404,6 +416,16 @@
}
}
+ private static boolean isRepeatingVibration(VibrationEffect effect) {
+ if (effect instanceof VibrationEffect.Waveform) {
+ final VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) effect;
+ if (waveform.getRepeatIndex() >= 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private void addToPreviousVibrationsLocked(Vibration vib) {
if (mPreviousVibrations.size() > mPreviousVibrationsLimit) {
mPreviousVibrations.removeFirst();
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index 8bcbfbe..8839cfc 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -16,9 +16,6 @@
package com.android.server.am;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.getStackIdForWindowingMode;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -213,7 +210,11 @@
if (windowingMode == WINDOWING_MODE_UNDEFINED) {
// TODO: Should be okay to have stacks with with undefined windowing mode long term, but
// have to set them to something for now due to logic that depending on them.
- windowingMode = WINDOWING_MODE_FULLSCREEN;
+ windowingMode = getWindowingMode(); // Put in current display's windowing mode
+ if (windowingMode == WINDOWING_MODE_UNDEFINED) {
+ // Else fullscreen for now...
+ windowingMode = WINDOWING_MODE_FULLSCREEN;
+ }
}
final boolean inSplitScreenMode = hasSplitScreenStack();
@@ -228,24 +229,7 @@
windowingMode = WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
}
- int stackId = INVALID_STACK_ID;
- if (mDisplayId == DEFAULT_DISPLAY && (activityType == ACTIVITY_TYPE_STANDARD
- || activityType == ACTIVITY_TYPE_UNDEFINED)) {
- // TODO: Will be removed once we are no longer using static stack ids.
- stackId = getStackIdForWindowingMode(windowingMode);
- if (stackId == INVALID_STACK_ID) {
- // Whatever...put in fullscreen stack for now.
- stackId = FULLSCREEN_WORKSPACE_STACK_ID;
- }
- final T stack = getStack(stackId);
- if (stack != null) {
- return stack;
- }
- }
-
- if (stackId == INVALID_STACK_ID) {
- stackId = mSupervisor.getNextStackId();
- }
+ final int stackId = mSupervisor.getNextStackId();
final T stack = createStackUnchecked(windowingMode, activityType, stackId, onTop);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ec83b02..a1a7a32 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -27,17 +27,11 @@
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.getWindowingModeForStackId;
-import static android.app.ActivityManager.StackId.isStaticStack;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
@@ -198,7 +192,6 @@
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityManager.StackId;
import android.app.ActivityManager.StackInfo;
import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityManagerInternal;
@@ -752,6 +745,8 @@
public boolean canShowErrorDialogs() {
return mShowDialogs && !mSleeping && !mShuttingDown
&& !mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)
+ && !mUserController.hasUserRestriction(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
+ mUserController.getCurrentUserId())
&& !(UserManager.isDeviceInDemoMode(mContext)
&& mUserController.getCurrentUser().isDemo());
}
@@ -1717,6 +1712,7 @@
static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
static final int TOP_APP_KILLED_BY_LMK_MSG = 73;
+ static final int NOTIFY_VR_KEYGUARD_MSG = 74;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -2109,7 +2105,8 @@
String text = mContext.getString(R.string.heavy_weight_notification,
context.getApplicationInfo().loadLabel(context.getPackageManager()));
Notification notification =
- new Notification.Builder(context, SystemNotificationChannels.DEVELOPER)
+ new Notification.Builder(context,
+ SystemNotificationChannels.HEAVY_WEIGHT_APP)
.setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
.setWhen(0)
.setOngoing(true)
@@ -2143,7 +2140,7 @@
}
try {
inm.cancelNotificationWithTag("android", null,
- SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, msg.arg1);
+ SystemMessage.NOTE_HEAVY_WEIGHT_NOTIFICATION, msg.arg1);
} catch (RuntimeException e) {
Slog.w(ActivityManagerService.TAG,
"Error canceling notification for service", e);
@@ -2384,6 +2381,9 @@
case NOTIFY_VR_SLEEPING_MSG: {
notifyVrManagerOfSleepState(msg.arg1 != 0);
} break;
+ case NOTIFY_VR_KEYGUARD_MSG: {
+ notifyVrManagerOfKeyguardState(msg.arg1 != 0);
+ } break;
case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
synchronized (ActivityManagerService.this) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
@@ -3236,10 +3236,12 @@
// stack implementation changes in the future, keep in mind that the use of the fullscreen
// stack is a means to move the activity to the main display and a moveActivityToDisplay()
// option would be a better choice here.
- if (r.requestedVrComponent != null && r.getStackId() >= FIRST_DYNAMIC_STACK_ID) {
+ if (r.requestedVrComponent != null && r.getDisplayId() != DEFAULT_DISPLAY) {
Slog.i(TAG, "Moving " + r.shortComponentName + " from stack " + r.getStackId()
+ " to main stack for VR");
- moveTaskToStack(r.getTask().taskId, FULLSCREEN_WORKSPACE_STACK_ID, true /* toTop */);
+ final ActivityStack stack = mStackSupervisor.getDefaultDisplay().getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, r.getActivityType(), true /* toTop */);
+ moveTaskToStack(r.getTask().taskId, stack.mStackId, true /* toTop */);
}
mHandler.sendMessage(
mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r));
@@ -3258,6 +3260,19 @@
vrService.onSleepStateChanged(isSleeping);
}
+ private void sendNotifyVrManagerOfKeyguardState(boolean isShowing) {
+ mHandler.sendMessage(
+ mHandler.obtainMessage(NOTIFY_VR_KEYGUARD_MSG, isShowing ? 1 : 0, 0));
+ }
+
+ private void notifyVrManagerOfKeyguardState(boolean isShowing) {
+ final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
+ if (vrService == null) {
+ return;
+ }
+ vrService.onKeyguardStateChanged(isShowing);
+ }
+
final void showAskCompatModeDialogLocked(ActivityRecord r) {
Message msg = Message.obtain();
msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG;
@@ -5076,11 +5091,12 @@
}
synchronized(this) {
- if (mHeavyWeightProcess == null) {
+ final ProcessRecord proc = mHeavyWeightProcess;
+ if (proc == null) {
return;
}
- ArrayList<ActivityRecord> activities = new ArrayList<>(mHeavyWeightProcess.activities);
+ ArrayList<ActivityRecord> activities = new ArrayList<>(proc.activities);
for (int i = 0; i < activities.size(); i++) {
ActivityRecord r = activities.get(i);
if (!r.finishing && r.isInStackLocked()) {
@@ -5090,7 +5106,7 @@
}
mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
- mHeavyWeightProcess.userId, 0));
+ proc.userId, 0));
mHeavyWeightProcess = null;
}
}
@@ -8065,8 +8081,8 @@
}
private boolean isInPictureInPictureMode(ActivityRecord r) {
- if (r == null || r.getStack() == null || !r.getStack().isPinnedStack() ||
- r.getStack().isInStackLocked(r) == null) {
+ if (r == null || r.getStack() == null || !r.inPinnedWindowingMode()
+ || r.getStack().isInStackLocked(r) == null) {
return false;
}
@@ -8160,7 +8176,7 @@
// Only update the saved args from the args that are set
r.pictureInPictureArgs.copyOnlySet(params);
- if (r.getStack().getStackId() == PINNED_STACK_ID) {
+ if (r.inPinnedWindowingMode()) {
// If the activity is already in picture-in-picture, update the pinned stack now
// if it is not already expanding to fullscreen. Otherwise, the arguments will
// be used the next time the activity enters PiP
@@ -9860,6 +9876,7 @@
}
rti.supportsSplitScreenMultiWindow = tr.supportsSplitScreenWindowingMode();
rti.resizeMode = tr.mResizeMode;
+ rti.configuration.setTo(tr.getConfiguration());
ActivityRecord base = null;
ActivityRecord top = null;
@@ -9985,14 +10002,15 @@
}
}
if ((flags & ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK) != 0) {
- if (stack != null && stack.isDockedStack() && stack.topTask() == tr) {
+ if (stack != null && stack.inSplitScreenPrimaryWindowingMode()
+ && stack.topTask() == tr) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
"Skipping, top task in docked stack: " + tr);
continue;
}
}
if ((flags & ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS) != 0) {
- if (stack != null && stack.isPinnedStack()) {
+ if (stack != null && stack.inPinnedWindowingMode()) {
if (DEBUG_RECENTS) Slog.d(TAG_RECENTS,
"Skipping, pinned stack task: " + tr);
continue;
@@ -10589,7 +10607,7 @@
}
final ActivityStack stack = r.getStack();
- if (stack == null || stack.mStackId != FREEFORM_WORKSPACE_STACK_ID) {
+ if (stack == null || !stack.inFreeformWindowingMode()) {
throw new IllegalStateException(
"exitFreeformMode: You can only go fullscreen from freeform.");
}
@@ -10608,6 +10626,42 @@
}
@Override
+ public void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setTaskWindowingMode()");
+ synchronized (this) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(taskId);
+ if (task == null) {
+ Slog.w(TAG, "setTaskWindowingMode: No task for id=" + taskId);
+ return;
+ }
+
+ if (DEBUG_STACK) Slog.d(TAG_STACK, "setTaskWindowingMode: moving task=" + taskId
+ + " to windowingMode=" + windowingMode + " toTop=" + toTop);
+ if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ mWindowManager.setDockedStackCreateState(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
+ null /* initialBounds */);
+ }
+
+ if (!task.isActivityTypeStandardOrUndefined()) {
+ throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move task "
+ + taskId + " to non-standard windowin mode=" + windowingMode);
+ }
+ final ActivityDisplay display = task.getStack().getDisplay();
+ final ActivityStack stack = display.getOrCreateStack(windowingMode,
+ task.getStack().getActivityType(), toTop);
+ // TODO: We should just change the windowing mode for the task vs. creating and
+ // moving it to a stack.
+ task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
+ "moveTaskToStack");
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ @Override
public void moveTaskToStack(int taskId, int stackId, boolean toTop) {
enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveTaskToStack()");
synchronized (this) {
@@ -10621,27 +10675,20 @@
if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToStack: moving task=" + taskId
+ " to stackId=" + stackId + " toTop=" + toTop);
- if (stackId == DOCKED_STACK_ID) {
- mWindowManager.setDockedStackCreateState(DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT,
- null /* initialBounds */);
- }
- ActivityStack stack = mStackSupervisor.getStack(stackId);
+ final ActivityStack stack = mStackSupervisor.getStack(stackId);
if (stack == null) {
- if (!isStaticStack(stackId)) {
- throw new IllegalStateException(
- "moveTaskToStack: No stack for stackId=" + stackId);
- }
- final ActivityDisplay display = task.getStack().getDisplay();
- final int windowingMode =
- getWindowingModeForStackId(stackId, display.hasSplitScreenStack());
- stack = display.getOrCreateStack(windowingMode,
- task.getStack().getActivityType(), toTop);
+ throw new IllegalStateException(
+ "moveTaskToStack: No stack for stackId=" + stackId);
}
if (!stack.isActivityTypeStandardOrUndefined()) {
throw new IllegalArgumentException("moveTaskToStack: Attempt to move task "
+ taskId + " to stack " + stackId);
}
+ if (stack.inSplitScreenPrimaryWindowingMode()) {
+ mWindowManager.setDockedStackCreateState(
+ DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
+ }
task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
"moveTaskToStack");
} finally {
@@ -10701,6 +10748,66 @@
}
/**
+ * Dismisses split-screen multi-window mode.
+ * @param toTop If true the current primary split-screen stack will be placed or left on top.
+ */
+ @Override
+ public void dismissSplitScreenMode(boolean toTop) {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()");
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ final ActivityStack stack =
+ mStackSupervisor.getDefaultDisplay().getSplitScreenStack();
+ if (toTop) {
+ mStackSupervisor.resizeStackLocked(stack, null /* destBounds */,
+ null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
+ true /* preserveWindows */, true /* allowResizeInDockedMode */,
+ !DEFER_RESUME);
+ } else {
+ mStackSupervisor.moveTasksToFullscreenStackLocked(stack, false /* onTop */);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
+ * Dismisses Pip
+ * @param animate True if the dismissal should be animated.
+ * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
+ * default animation duration should be used.
+ */
+ @Override
+ public void dismissPip(boolean animate, int animationDuration) {
+ enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "dismissPip()");
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (this) {
+ final PinnedActivityStack stack =
+ mStackSupervisor.getDefaultDisplay().getPinnedStack();
+
+ if (stack == null) {
+ return;
+ }
+ if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
+ throw new IllegalArgumentException("Stack: " + stack
+ + " doesn't support animated resize.");
+ }
+ if (animate) {
+ stack.animateResizePinnedStack(null /* sourceHintBounds */,
+ null /* destBounds */, animationDuration, false /* fromFullscreen */);
+ } else {
+ mStackSupervisor.moveTasksToFullscreenStackLocked(stack, true /* onTop */);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ /**
* Moves the top activity in the input stackId to the pinned stack.
*
* @param stackId Id of stack to move the top activity to pinned stack.
@@ -10746,7 +10853,12 @@
stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds,
animationDuration, false /* fromFullscreen */);
} else {
- mStackSupervisor.resizeStackLocked(stackId, destBounds, null /* tempTaskBounds */,
+ final ActivityStack stack = mStackSupervisor.getStack(stackId);
+ if (stack == null) {
+ Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
+ return;
+ }
+ mStackSupervisor.resizeStackLocked(stack, destBounds, null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, preserveWindows,
allowResizeInDockedMode, !DEFER_RESUME);
}
@@ -12549,6 +12661,7 @@
Binder.restoreCallingIdentity(ident);
}
}
+ sendNotifyVrManagerOfKeyguardState(showing);
}
@Override
@@ -12821,6 +12934,10 @@
throw new IllegalArgumentException("Provided bugreport type is not correct, value: "
+ bugreportType);
}
+ // Always log caller, even if it does not have permission to dump.
+ String type = extraOptions == null ? "bugreport" : extraOptions;
+ Slog.i(TAG, type + " requested by UID " + Binder.getCallingUid());
+
enforceCallingPermission(android.Manifest.permission.DUMP, "requestBugReport");
if (extraOptions != null) {
SystemProperties.set("dumpstate.options", extraOptions);
@@ -14579,6 +14696,9 @@
}
sb.append("\n");
}
+ if (process.info.isInstantApp()) {
+ sb.append("Instant-App: true\n");
+ }
}
}
@@ -20577,9 +20697,10 @@
/** Helper method that requests bounds from WM and applies them to stack. */
private void resizeStackWithBoundsFromWindowManager(int stackId, boolean deferResume) {
final Rect newStackBounds = new Rect();
- mStackSupervisor.getStack(stackId).getBoundsForNewConfiguration(newStackBounds);
+ final ActivityStack stack = mStackSupervisor.getStack(stackId);
+ stack.getBoundsForNewConfiguration(newStackBounds);
mStackSupervisor.resizeStackLocked(
- stackId, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */,
+ stack, !newStackBounds.isEmpty() ? newStackBounds : null /* bounds */,
null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
false /* preserveWindows */, false /* allowResizeInDockedMode */, deferResume);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 4c93423..f03d2d5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -73,10 +73,8 @@
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
import static android.app.ActivityManager.RESIZE_MODE_USER;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.INVALID_DISPLAY;
@@ -86,15 +84,6 @@
public static final String NO_CLASS_ERROR_CODE = "Error type 3";
private static final String SHELL_PACKAGE_NAME = "com.android.shell";
- // Is the object moving in a positive direction?
- private static final boolean MOVING_FORWARD = true;
- // Is the object moving in the horizontal plan?
- private static final boolean MOVING_HORIZONTALLY = true;
- // Is the object current point great then its target point?
- private static final boolean GREATER_THAN_TARGET = true;
- // Amount we reduce the stack size by when testing a task re-size.
- private static final int STACK_BOUNDS_INSET = 10;
-
// IPC interface to activity manager -- don't need to do additional security checks.
final IActivityManager mInterface;
@@ -1944,8 +1933,6 @@
return runStackInfo(pw);
case "move-top-activity-to-pinned-stack":
return runMoveTopActivityToPinnedStack(pw);
- case "size-docked-stack-test":
- return runStackSizeDockedStackTest(pw);
case "remove":
return runStackRemove(pw);
default:
@@ -2143,89 +2130,6 @@
return 0;
}
- int runStackSizeDockedStackTest(PrintWriter pw) throws RemoteException {
- final PrintWriter err = getErrPrintWriter();
- final int stepSize = Integer.parseInt(getNextArgRequired());
- final String side = getNextArgRequired();
- final String delayStr = getNextArg();
- final int delayMs = (delayStr != null) ? Integer.parseInt(delayStr) : 0;
-
- ActivityManager.StackInfo info = mInterface.getStackInfo(
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_UNDEFINED);
- if (info == null) {
- err.println("Docked stack doesn't exist");
- return -1;
- }
- if (info.bounds == null) {
- err.println("Docked stack doesn't have a bounds");
- return -1;
- }
- Rect bounds = info.bounds;
-
- final boolean horizontalGrowth = "l".equals(side) || "r".equals(side);
- final int changeSize = (horizontalGrowth ? bounds.width() : bounds.height()) / 2;
- int currentPoint;
- switch (side) {
- case "l":
- currentPoint = bounds.left;
- break;
- case "r":
- currentPoint = bounds.right;
- break;
- case "t":
- currentPoint = bounds.top;
- break;
- case "b":
- currentPoint = bounds.bottom;
- break;
- default:
- err.println("Unknown growth side: " + side);
- return -1;
- }
-
- final int startPoint = currentPoint;
- final int minPoint = currentPoint - changeSize;
- final int maxPoint = currentPoint + changeSize;
-
- int maxChange;
- pw.println("Shrinking docked stack side=" + side);
- pw.flush();
- while (currentPoint > minPoint) {
- maxChange = Math.min(stepSize, currentPoint - minPoint);
- currentPoint -= maxChange;
- setBoundsSide(bounds, side, currentPoint);
- int res = resizeStack(DOCKED_STACK_ID, bounds, delayMs);
- if (res < 0) {
- return res;
- }
- }
-
- pw.println("Growing docked stack side=" + side);
- pw.flush();
- while (currentPoint < maxPoint) {
- maxChange = Math.min(stepSize, maxPoint - currentPoint);
- currentPoint += maxChange;
- setBoundsSide(bounds, side, currentPoint);
- int res = resizeStack(DOCKED_STACK_ID, bounds, delayMs);
- if (res < 0) {
- return res;
- }
- }
-
- pw.println("Back to Original size side=" + side);
- pw.flush();
- while (currentPoint > startPoint) {
- maxChange = Math.min(stepSize, currentPoint - startPoint);
- currentPoint -= maxChange;
- setBoundsSide(bounds, side, currentPoint);
- int res = resizeStack(DOCKED_STACK_ID, bounds, delayMs);
- if (res < 0) {
- return res;
- }
- }
- return 0;
- }
-
void setBoundsSide(Rect bounds, String side, int value) {
switch (side) {
case "l":
@@ -2687,10 +2591,6 @@
pw.println(" Change docked stack to <LEFT,TOP,RIGHT,BOTTOM>");
pw.println(" and supplying temporary different task bounds indicated by");
pw.println(" <TASK_LEFT,TOP,RIGHT,BOTTOM>");
- pw.println(" size-docked-stack-test: <STEP_SIZE> <l|t|r|b> [DELAY_MS]");
- pw.println(" Test command for sizing docked stack by");
- pw.println(" <STEP_SIZE> increments from the side <l>eft, <t>op, <r>ight, or <b>ottom");
- pw.println(" applying the optional [DELAY_MS] between each step.");
pw.println(" move-top-activity-to-pinned-stack: <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>");
pw.println(" Moves the top activity from");
pw.println(" <STACK_ID> to the pinned stack using <LEFT,TOP,RIGHT,BOTTOM> for the");
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 7b0b942..f544d37 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -17,9 +17,7 @@
package com.android.server.am;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.ActivityManager.TaskDescription.ATTR_TASKDESCRIPTION_PREFIX;
import static android.app.ActivityOptions.ANIM_CLIP_REVEAL;
import static android.app.ActivityOptions.ANIM_CUSTOM;
@@ -661,8 +659,7 @@
return;
}
- final boolean inPictureInPictureMode = (task.getStackId() == PINNED_STACK_ID) &&
- (targetStackBounds != null);
+ final boolean inPictureInPictureMode = inPinnedWindowingMode() && targetStackBounds != null;
if (inPictureInPictureMode != mLastReportedPictureInPictureMode || forceUpdate) {
// Picture-in-picture mode changes also trigger a multi-window mode change as well, so
// update that here in order
@@ -684,10 +681,6 @@
}
}
- boolean isFreeform() {
- return task != null && task.getStackId() == FREEFORM_WORKSPACE_STACK_ID;
- }
-
@Override
protected int getChildCount() {
// {@link ActivityRecord} is a leaf node and has no children.
@@ -948,7 +941,7 @@
// update the initial multi-window modes so that the callbacks are scheduled correctly when
// the user leaves that mode.
mLastReportedMultiWindowMode = !task.mFullscreen;
- mLastReportedPictureInPictureMode = (task.getStackId() == PINNED_STACK_ID);
+ mLastReportedPictureInPictureMode = inPinnedWindowingMode();
}
void removeWindowContainer() {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 7eb6eda..05db922 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -16,20 +16,19 @@
package com.android.server.am;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.windowingModeToString;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
@@ -105,11 +104,13 @@
import android.app.AppGlobals;
import android.app.IActivityController;
import android.app.ResultInfo;
+import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
+import android.graphics.Point;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Binder;
@@ -352,6 +353,7 @@
private final SparseArray<Rect> mTmpBounds = new SparseArray<>();
private final SparseArray<Rect> mTmpInsetBounds = new SparseArray<>();
private final Rect mTmpRect2 = new Rect();
+ private final Point mTmpSize = new Point();
/** Run all ActivityStacks through this */
protected final ActivityStackSupervisor mStackSupervisor;
@@ -460,7 +462,7 @@
mWindowManager = mService.mWindowManager;
mStackId = stackId;
mCurrentUser = mService.mUserController.getCurrentUserId();
- mTaskPositioner = mStackId == FREEFORM_WORKSPACE_STACK_ID
+ mTaskPositioner = windowingMode == WINDOWING_MODE_FREEFORM
? new LaunchingTaskPositioner() : null;
mTmpRect2.setEmpty();
setWindowingMode(windowingMode);
@@ -471,7 +473,8 @@
}
T createStackWindowController(int displayId, boolean onTop, Rect outBounds) {
- return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds);
+ return (T) new StackWindowController(mStackId, this, displayId, onTop, outBounds,
+ mStackSupervisor.mWindowManager);
}
T getWindowContainerController() {
@@ -502,13 +505,14 @@
mBounds = bounds != null ? new Rect(bounds) : null;
mFullscreen = mBounds == null;
if (mTaskPositioner != null) {
- mTaskPositioner.setDisplay(activityDisplay.mDisplay);
+ activityDisplay.mDisplay.getSize(mTmpSize);
+ mTaskPositioner.setDisplaySize(mTmpSize);
mTaskPositioner.configure(mBounds);
}
onParentChanged();
activityDisplay.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM);
- if (mStackId == DOCKED_STACK_ID) {
+ if (inSplitScreenPrimaryWindowingMode()) {
// If we created a docked stack we want to resize it so it resizes all other stacks
// in the system.
mStackSupervisor.resizeDockedStackLocked(
@@ -817,14 +821,6 @@
return isActivityTypeHome() || isActivityTypeRecents();
}
- final boolean isDockedStack() {
- return mStackId == DOCKED_STACK_ID;
- }
-
- final boolean isPinnedStack() {
- return mStackId == PINNED_STACK_ID;
- }
-
final boolean isOnHomeDisplay() {
return mDisplayId == DEFAULT_DISPLAY;
}
@@ -1504,9 +1500,9 @@
* needed. A stack is considered translucent if it don't contain a visible or
* starting (about to be visible) activity that is fullscreen (opaque).
* @param starting The currently starting activity or null if there is none.
- * @param stackBehindId The id of the stack directly behind this one.
+ * @param stackBehind The stack directly behind this one.
*/
- private boolean isStackTranslucent(ActivityRecord starting, int stackBehindId) {
+ private boolean isStackTranslucent(ActivityRecord starting, ActivityStack stackBehind) {
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -1531,7 +1527,6 @@
return false;
}
- final ActivityStack stackBehind = mStackSupervisor.getStack(stackBehindId);
final boolean stackBehindHomeOrRecent = stackBehind != null
&& stackBehind.isHomeOrRecentsStack();
if (!isHomeOrRecentsStack() && r.frontOfTask && task.isOverHomeStack()
@@ -1578,7 +1573,6 @@
// Check position and visibility of this stack relative to the front stack on its display.
final ActivityStack topStack = getTopStackOnDisplay();
- final int topStackId = topStack.mStackId;
final int windowingMode = getWindowingMode();
final int activityType = getActivityType();
@@ -1586,7 +1580,7 @@
// If the assistant stack is focused and translucent, then the docked stack is always
// visible
if (topStack.isActivityTypeAssistant()) {
- return topStack.isStackTranslucent(starting, DOCKED_STACK_ID);
+ return topStack.isStackTranslucent(starting, this);
}
return true;
}
@@ -1611,18 +1605,16 @@
}
final ActivityStack stackBehindTop = (stackBehindTopIndex >= 0)
? displayStacks.get(stackBehindTopIndex) : null;
- int stackBehindTopId = INVALID_STACK_ID;
int stackBehindTopWindowingMode = WINDOWING_MODE_UNDEFINED;
int stackBehindTopActivityType = ACTIVITY_TYPE_UNDEFINED;
if (stackBehindTop != null) {
- stackBehindTopId = stackBehindTop.mStackId;
stackBehindTopWindowingMode = stackBehindTop.getWindowingMode();
stackBehindTopActivityType = stackBehindTop.getActivityType();
}
final boolean alwaysOnTop = topStack.getWindowConfiguration().isAlwaysOnTop();
if (topStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY || alwaysOnTop) {
- if (stackIndex == stackBehindTopIndex) {
+ if (this == stackBehindTop) {
// Stacks directly behind the docked or pinned stack are always visible.
return true;
} else if (alwaysOnTop && stackIndex == stackBehindTopIndex - 1) {
@@ -1631,14 +1623,13 @@
if (stackBehindTopWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
return true;
} else if (stackBehindTopActivityType == ACTIVITY_TYPE_ASSISTANT) {
- return displayStacks.get(stackBehindTopIndex).isStackTranslucent(
- starting, mStackId);
+ return stackBehindTop.isStackTranslucent(starting, this);
}
}
}
if (topStack.isBackdropToTranslucentActivity()
- && topStack.isStackTranslucent(starting, stackBehindTopId)) {
+ && topStack.isStackTranslucent(starting, stackBehindTop)) {
// Stacks behind the fullscreen or assistant stack with a translucent activity are
// always visible so they can act as a backdrop to the translucent activity.
// For example, dialog activities
@@ -1656,9 +1647,9 @@
}
}
- if (StackId.isStaticStack(mStackId)
- || isHomeOrRecentsStack() || isActivityTypeAssistant()) {
- // Visibility of any static stack should have been determined by the conditions above.
+ if (isOnHomeDisplay()) {
+ // Visibility of any stack on default display should have been determined by the
+ // conditions above.
return false;
}
@@ -1674,7 +1665,7 @@
return false;
}
- if (!stack.isStackTranslucent(starting, INVALID_STACK_ID)) {
+ if (!stack.isStackTranslucent(starting, null /* stackBehind */)) {
return false;
}
}
@@ -1800,7 +1791,8 @@
makeInvisible(r);
}
}
- if (mStackId == FREEFORM_WORKSPACE_STACK_ID) {
+ final int windowingMode = getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_FREEFORM) {
// The visibility of tasks and the activities they contain in freeform stack are
// determined individually unlike other stacks where the visibility or fullscreen
// status of an activity in a previous task affects other.
@@ -1815,7 +1807,8 @@
// show activities in the next application stack behind them vs. another
// task in the home stack like recents.
behindFullscreenActivity = true;
- } else if (mStackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+ } else if (windowingMode == WINDOWING_MODE_FULLSCREEN
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Skipping after task=" + task
+ " returning to non-application type=" + task.getTaskToReturnTo());
// Once we reach a fullscreen stack task that has a running activity and should
@@ -1851,6 +1844,32 @@
}
/**
+ * Returns true if this stack should be resized to match the bounds specified by
+ * {@link ActivityOptions#setLaunchBounds} when launching an activity into the stack.
+ */
+ boolean resizeStackWithLaunchBounds() {
+ return inPinnedWindowingMode();
+ }
+
+ /**
+ * Returns true if we try to maintain focus in the current stack when the top activity finishes.
+ */
+ private boolean keepFocusInStackIfPossible() {
+ final int windowingMode = getWindowingMode();
+ return windowingMode == WINDOWING_MODE_FREEFORM
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || windowingMode == WINDOWING_MODE_PINNED;
+ }
+
+ /**
+ * Returns true if the top task in the task is allowed to return home when finished and
+ * there are other tasks in the stack.
+ */
+ boolean allowTopTaskToReturnHome() {
+ return !inPinnedWindowingMode();
+ }
+
+ /**
* @return the top most visible activity that wants to dismiss Keyguard
*/
ActivityRecord getTopDismissingKeyguardActivity() {
@@ -1866,7 +1885,7 @@
*/
boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible,
boolean isTop) {
- final boolean isInPinnedStack = r.getStack().getStackId() == PINNED_STACK_ID;
+ final boolean isInPinnedStack = r.inPinnedWindowingMode();
final boolean keyguardShowing = mStackSupervisor.mKeyguardController.isKeyguardShowing(
mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY);
final boolean keyguardLocked = mStackSupervisor.mKeyguardController.isKeyguardLocked();
@@ -2616,7 +2635,7 @@
private boolean resumeTopActivityInNextFocusableStack(ActivityRecord prev,
ActivityOptions options, String reason) {
- if ((!mFullscreen || !isOnHomeDisplay()) && adjustFocusToNextFocusableStackLocked(reason)) {
+ if (adjustFocusToNextFocusableStackLocked(reason)) {
// Try to move focus to the next visible stack with a running activity if this
// stack is not covering the entire screen or is on a secondary display (with no home
// stack).
@@ -2745,9 +2764,8 @@
// make underlying task focused when this one will be finished.
int returnToType = isLastTaskOverHome
? task.getTaskToReturnTo() : ACTIVITY_TYPE_STANDARD;
- if (fromHomeOrRecents && StackId.allowTopTaskToReturnHome(mStackId)) {
- returnToType = topTask == null
- ? ACTIVITY_TYPE_HOME : topTask.getActivityType();
+ if (fromHomeOrRecents && allowTopTaskToReturnHome()) {
+ returnToType = topTask == null ? ACTIVITY_TYPE_HOME : topTask.getActivityType();
}
task.setTaskToReturnTo(returnToType);
}
@@ -2900,7 +2918,7 @@
// Ensure the caller has requested not to trigger auto-enter PiP
return false;
}
- if (pipCandidate == null || pipCandidate.getStackId() == PINNED_STACK_ID) {
+ if (pipCandidate == null || pipCandidate.inPinnedWindowingMode()) {
// Ensure that we do not trigger entering PiP an activity on the pinned stack
return false;
}
@@ -3290,7 +3308,7 @@
final String myReason = reason + " adjustFocus";
if (next != r) {
- if (next != null && StackId.keepFocusInStackIfPossible(mStackId) && isFocusable()) {
+ if (next != null && keepFocusInStackIfPossible() && isFocusable()) {
// For freeform, docked, and pinned stacks we always keep the focus within the
// stack as long as there is a running activity.
return;
@@ -3756,7 +3774,7 @@
if (mode == FINISH_IMMEDIATELY
|| (prevState == ActivityState.PAUSED
- && (mode == FINISH_AFTER_PAUSE || mStackId == PINNED_STACK_ID))
+ && (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode()))
|| finishingActivityInNonFocusedStack
|| prevState == STOPPING
|| prevState == STOPPED
@@ -4570,8 +4588,8 @@
mWindowContainerController.positionChildAtBottom(tr.getWindowContainerController());
}
- if (mStackId == PINNED_STACK_ID) {
- mStackSupervisor.removeStackLocked(PINNED_STACK_ID);
+ if (inPinnedWindowingMode()) {
+ mStackSupervisor.removeStackLocked(mStackId);
return true;
}
@@ -4686,7 +4704,7 @@
for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
final TaskRecord task = mTaskHistory.get(i);
if (task.isResizeable()) {
- if (mStackId == FREEFORM_WORKSPACE_STACK_ID) {
+ if (inFreeformWindowingMode()) {
// For freeform stack we don't adjust the size of the tasks to match that
// of the stack, but we do try to make sure the tasks are still contained
// with the bounds of the stack.
@@ -4899,7 +4917,7 @@
ci.numRunning = numRunning;
ci.supportsSplitScreenMultiWindow = task.supportsSplitScreenWindowingMode();
ci.resizeMode = task.mResizeMode;
- ci.configuration = task.getConfiguration();
+ ci.configuration.setTo(task.getConfiguration());
list.add(ci);
}
}
@@ -5088,7 +5106,7 @@
if (isAttached()) {
getDisplay().positionChildAtBottom(this);
}
- if (!isHomeOrRecentsStack()) {
+ if (!isActivityTypeHome()) {
remove();
}
}
@@ -5096,7 +5114,7 @@
task.setStack(null);
// Notify if a task from the pinned stack is being removed (or moved depending on the mode)
- if (mStackId == PINNED_STACK_ID) {
+ if (inPinnedWindowingMode()) {
mService.mTaskChangeNotificationController.notifyActivityUnpinned();
}
}
@@ -5247,7 +5265,9 @@
@Override
public String toString() {
return "ActivityStack{" + Integer.toHexString(System.identityHashCode(this))
- + " stackId=" + mStackId + ", " + mTaskHistory.size() + " tasks}";
+ + " stackId=" + mStackId + " type=" + activityTypeToString(getActivityType())
+ + " mode=" + windowingModeToString(getWindowingMode()) + ", "
+ + mTaskHistory.size() + " tasks}";
}
void onLockTaskPackagesUpdatedLocked() {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 42f3550..bac71c7 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -21,11 +21,7 @@
import static android.Manifest.permission.START_ANY_ACTIVITY;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
@@ -35,10 +31,13 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.windowingModeToString;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
@@ -295,7 +294,7 @@
DisplayManager mDisplayManager;
/** Counter for next free stack ID to use for dynamic activity stacks. */
- private int mNextFreeStackId = FIRST_DYNAMIC_STACK_ID;
+ private int mNextFreeStackId = 0;
/**
* Maps the task identifier that activities are currently being started in to the userId of the
@@ -2105,8 +2104,8 @@
// moveTaskToStackUncheckedLocked() should already placed the task on top,
// still need moveTaskToFrontLocked() below for any transition settings.
}
- if (StackId.resizeStackWithLaunchBounds(stack.mStackId)) {
- resizeStackLocked(stack.mStackId, bounds, null /* tempTaskBounds */,
+ if (stack.resizeStackWithLaunchBounds()) {
+ resizeStackLocked(stack, bounds, null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
true /* allowResizeInDockedMode */, !DEFER_RESUME);
} else {
@@ -2417,7 +2416,7 @@
case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return r.supportsSplitScreenWindowingMode();
}
- if (StackId.isDynamicStack(stack.mStackId)) {
+ if (!stack.isOnHomeDisplay()) {
return r.canBeLaunchedOnDisplay(displayId);
}
Slog.e(TAG, "isValidLaunchStack: Unexpected stack=" + stack);
@@ -2511,18 +2510,15 @@
return null;
}
- void resizeStackLocked(int stackId, Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds,
- boolean preserveWindows, boolean allowResizeInDockedMode, boolean deferResume) {
- if (stackId == DOCKED_STACK_ID) {
+ void resizeStackLocked(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
+ Rect tempTaskInsetBounds, boolean preserveWindows, boolean allowResizeInDockedMode,
+ boolean deferResume) {
+
+ if (stack.inSplitScreenPrimaryWindowingMode()) {
resizeDockedStackLocked(bounds, tempTaskBounds, tempTaskInsetBounds, null, null,
preserveWindows, deferResume);
return;
}
- final ActivityStack stack = getStack(stackId);
- if (stack == null) {
- Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
- return;
- }
final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenStack();
if (!allowResizeInDockedMode
@@ -2532,7 +2528,7 @@
return;
}
- Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stackId);
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId);
mWindowManager.deferSurfaceLayout();
try {
if (stack.supportsSplitScreenWindowingMode()) {
@@ -2584,8 +2580,7 @@
/**
* TODO: This should just change the windowing mode and resize vs. actually moving task around.
- * Can do that once we are no longer using static stack ids. Specially when
- * {@link ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} is removed.
+ * Can do that once we are no longer using static stack ids.
*/
private void moveTasksToFullscreenStackInSurfaceTransaction(ActivityStack fromStack,
int toDisplayId, boolean onTop) {
@@ -2606,7 +2601,7 @@
if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
continue;
}
- resizeStackLocked(otherStack.mStackId, null, null, null, PRESERVE_WINDOWS,
+ resizeStackLocked(otherStack, null, null, null, PRESERVE_WINDOWS,
true /* allowResizeInDockedMode */, DEFER_RESUME);
}
@@ -2625,38 +2620,41 @@
// the picture-in-picture mode.
final boolean schedulePictureInPictureModeChange = inPinnedWindowingMode;
final ArrayList<TaskRecord> tasks = fromStack.getAllTasks();
- final int size = tasks.size();
- final ActivityStack fullscreenStack = toDisplay.getOrCreateStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, onTop);
- if (onTop) {
- final int returnToType =
- toDisplay.getTopVisibleStackActivityType(WINDOWING_MODE_PINNED);
- for (int i = 0; i < size; i++) {
- final TaskRecord task = tasks.get(i);
- final boolean isTopTask = i == (size - 1);
- if (inPinnedWindowingMode) {
- // Update the return-to to reflect where the pinned stack task was moved
- // from so that we retain the stack that was previously visible if the
- // pinned stack is recreated. See moveActivityToPinnedStackLocked().
- task.setTaskToReturnTo(returnToType);
+ if (!tasks.isEmpty()) {
+ final int size = tasks.size();
+ final ActivityStack fullscreenStack = toDisplay.getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, onTop);
+
+ if (onTop) {
+ final int returnToType =
+ toDisplay.getTopVisibleStackActivityType(WINDOWING_MODE_PINNED);
+ for (int i = 0; i < size; i++) {
+ final TaskRecord task = tasks.get(i);
+ final boolean isTopTask = i == (size - 1);
+ if (inPinnedWindowingMode) {
+ // Update the return-to to reflect where the pinned stack task was moved
+ // from so that we retain the stack that was previously visible if the
+ // pinned stack is recreated. See moveActivityToPinnedStackLocked().
+ task.setTaskToReturnTo(returnToType);
+ }
+ // Defer resume until all the tasks have been moved to the fullscreen stack
+ task.reparent(fullscreenStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
+ isTopTask /* animate */, DEFER_RESUME,
+ schedulePictureInPictureModeChange,
+ "moveTasksToFullscreenStack - onTop");
}
- // Defer resume until all the tasks have been moved to the fullscreen stack
- task.reparent(fullscreenStack, ON_TOP, REPARENT_MOVE_STACK_TO_FRONT,
- isTopTask /* animate */, DEFER_RESUME,
- schedulePictureInPictureModeChange,
- "moveTasksToFullscreenStack - onTop");
- }
- } else {
- for (int i = 0; i < size; i++) {
- final TaskRecord task = tasks.get(i);
- // Position the tasks in the fullscreen stack in order at the bottom of the
- // stack. Also defer resume until all the tasks have been moved to the
- // fullscreen stack.
- task.reparent(fullscreenStack, i /* position */,
- REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME,
- schedulePictureInPictureModeChange,
- "moveTasksToFullscreenStack - NOT_onTop");
+ } else {
+ for (int i = 0; i < size; i++) {
+ final TaskRecord task = tasks.get(i);
+ // Position the tasks in the fullscreen stack in order at the bottom of the
+ // stack. Also defer resume until all the tasks have been moved to the
+ // fullscreen stack.
+ task.reparent(fullscreenStack, i /* position */,
+ REPARENT_LEAVE_STACK_IN_PLACE, !ANIMATE, DEFER_RESUME,
+ schedulePictureInPictureModeChange,
+ "moveTasksToFullscreenStack - NOT_onTop");
+ }
}
}
@@ -2741,7 +2739,7 @@
tempRect /* outStackBounds */,
otherTaskRect /* outTempTaskBounds */, true /* ignoreVisibility */);
- resizeStackLocked(current.mStackId, !tempRect.isEmpty() ? tempRect : null,
+ resizeStackLocked(current, !tempRect.isEmpty() ? tempRect : null,
!otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds,
tempOtherTaskInsetBounds, preserveWindows,
true /* allowResizeInDockedMode */, deferResume);
@@ -2976,8 +2974,7 @@
int getNextStackId() {
while (true) {
- if (mNextFreeStackId >= FIRST_DYNAMIC_STACK_ID
- && getStack(mNextFreeStackId) == null) {
+ if (getStack(mNextFreeStackId) == null) {
break;
}
mNextFreeStackId++;
@@ -3157,7 +3154,7 @@
// Resize the pinned stack to match the current size of the task the activity we are
// going to be moving is currently contained in. We do this to have the right starting
// animation bounds for the pinned stack to the desired bounds the caller wants.
- resizeStackLocked(PINNED_STACK_ID, task.mBounds, null /* tempTaskBounds */,
+ resizeStackLocked(stack, task.mBounds, null /* tempTaskBounds */,
null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
true /* allowResizeInDockedMode */, !DEFER_RESUME);
@@ -3216,8 +3213,7 @@
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
resumeFocusedStackTopActivityLocked();
- mService.mTaskChangeNotificationController.notifyActivityPinned(r.packageName, r.userId,
- r.getTask().taskId);
+ mService.mTaskChangeNotificationController.notifyActivityPinned(r);
}
/** Move activity with its stack to front and make the stack focused. */
@@ -3651,7 +3647,7 @@
// Also dismiss the pinned stack whenever we switch users. Removing the pinned stack will
// also cause all tasks to be moved to the fullscreen stack at a position that is
// appropriate.
- removeStackLocked(PINNED_STACK_ID);
+ removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
mUserStackInFront.put(mCurrentUser, focusStackId);
final int restoreStackId = mUserStackInFront.get(userId, mHomeStack.mStackId);
@@ -3894,7 +3890,9 @@
for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
pw.println();
- pw.println(" Stack #" + stack.mStackId + ":");
+ pw.println(" Stack #" + stack.mStackId
+ + ": type=" + activityTypeToString(getActivityType())
+ + " mode=" + windowingModeToString(getWindowingMode()));
pw.println(" mFullscreen=" + stack.mFullscreen);
pw.println(" isSleeping=" + stack.shouldSleepActivities());
pw.println(" mBounds=" + stack.mBounds);
@@ -4313,8 +4311,8 @@
// The task landed on an inappropriate display somehow, move it to the default
// display.
// TODO(multi-display): Find proper stack for the task on the default display.
- mService.moveTaskToStack(task.taskId, FULLSCREEN_WORKSPACE_STACK_ID,
- true /* toTop */);
+ mService.setTaskWindowingMode(task.taskId,
+ WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY, true /* toTop */);
launchOnSecondaryDisplayFailed = true;
} else {
// The task might have landed on a display different from requested.
@@ -4400,7 +4398,7 @@
void scheduleUpdatePictureInPictureModeIfNeeded(TaskRecord task, ActivityStack prevStack) {
final ActivityStack stack = task.getStack();
if (prevStack == null || prevStack == stack
- || (prevStack.mStackId != PINNED_STACK_ID && stack.mStackId != PINNED_STACK_ID)) {
+ || (!prevStack.inPinnedWindowingMode() && !stack.inPinnedWindowingMode())) {
return;
}
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index ccb9181..8300083 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -26,11 +26,6 @@
import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
-import static android.app.ActivityManager.StackId;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.isDynamicStack;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -186,6 +181,12 @@
private boolean mAvoidMoveToFront;
private boolean mPowerHintSent;
+ // We must track when we deliver the new intent since multiple code paths invoke
+ // {@link #deliverNewIntent}. This is due to early returns in the code path. This flag is used
+ // inside {@link #deliverNewIntent} to suppress duplicate requests and ensure the intent is
+ // delivered at most once.
+ private boolean mIntentDelivered;
+
private IVoiceInteractionSession mVoiceSession;
private IVoiceInteractor mVoiceInteractor;
@@ -243,6 +244,8 @@
mVoiceInteractor = null;
mUsingVr2dDisplay = false;
+
+ mIntentDelivered = false;
}
ActivityStarter(ActivityManagerService service, ActivityStackSupervisor supervisor) {
@@ -602,15 +605,19 @@
mSupervisor.reportTaskToFrontNoLaunch(mStartActivity);
}
- int startedActivityStackId = INVALID_STACK_ID;
+ ActivityStack startedActivityStack = null;
final ActivityStack currentStack = r.getStack();
if (currentStack != null) {
- startedActivityStackId = currentStack.mStackId;
+ startedActivityStack = currentStack;
} else if (mTargetStack != null) {
- startedActivityStackId = targetStack.mStackId;
+ startedActivityStack = targetStack;
}
- if (startedActivityStackId == DOCKED_STACK_ID) {
+ if (startedActivityStack == null) {
+ return;
+ }
+
+ if (startedActivityStack.inSplitScreenPrimaryWindowingMode()) {
final ActivityStack homeStack = mSupervisor.getDefaultDisplay().getStack(
WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME);
final boolean homeStackVisible = homeStack != null && homeStack.isVisible();
@@ -626,14 +633,14 @@
boolean clearedTask = (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
== (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK) && (mReuseTask != null);
- if (startedActivityStackId == PINNED_STACK_ID && (result == START_TASK_TO_FRONT
- || result == START_DELIVERED_TO_TOP || clearedTask)) {
+ if (startedActivityStack.inPinnedWindowingMode()
+ && (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP
+ || clearedTask)) {
// The activity was already running in the pinned stack so it wasn't started, but either
// brought to the front or the new intent was delivered to it since it was already in
// front. Notify anyone interested in this piece of information.
mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt(
clearedTask);
- return;
}
}
@@ -1077,9 +1084,7 @@
// so make sure the task now has the identity of the new intent.
top.getTask().setIntent(mStartActivity);
}
- ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask());
- top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
- mStartActivity.launchedFromPackage);
+ deliverNewIntent(top);
}
}
@@ -1141,7 +1146,6 @@
&& ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
|| mLaunchSingleTop || mLaunchSingleTask);
if (dontStart) {
- ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.getTask());
// For paranoia, make sure we have correctly resumed the top activity.
topStack.mLastPausedActivity = null;
if (mDoResume) {
@@ -1153,8 +1157,8 @@
// anything if that is the case, so this is it!
return START_RETURN_INTENT_TO_CALLER;
}
- top.deliverNewIntentLocked(
- mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
+
+ deliverNewIntent(top);
// Don't use mStartActivity.task to show the toast. We're not starting a new activity
// but reusing 'top'. Fields in mStartActivity may not be fully initialized.
@@ -1422,7 +1426,7 @@
// in any task/stack, however it could launch other activities like ResolverActivity,
// and we want those to stay in the original task.
if ((mStartActivity.isResolverActivity() || mStartActivity.noDisplay) && mSourceRecord != null
- && mSourceRecord.isFreeform()) {
+ && mSourceRecord.inFreeformWindowingMode()) {
mAddingToTask = true;
}
}
@@ -1745,13 +1749,10 @@
// desires.
if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop)
&& intentActivity.realActivity.equals(mStartActivity.realActivity)) {
- ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity,
- intentActivity.getTask());
if (intentActivity.frontOfTask) {
intentActivity.getTask().setIntent(mStartActivity);
}
- intentActivity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
- mStartActivity.launchedFromPackage);
+ deliverNewIntent(intentActivity);
} else if (!intentActivity.getTask().isSameIntentFilter(mStartActivity)) {
// In this case we are launching the root activity of the task, but with a
// different intent. We should start a new instance on top.
@@ -1799,15 +1800,8 @@
mNewTaskIntent != null ? mNewTaskIntent : mIntent, mVoiceSession,
mVoiceInteractor, !mLaunchTaskBehind /* toTop */);
addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask - mReuseTask");
- if (mLaunchBounds != null) {
- final int stackId = mTargetStack.mStackId;
- if (StackId.resizeStackWithLaunchBounds(stackId)) {
- mService.resizeStack(
- stackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
- } else {
- mStartActivity.getTask().updateOverrideConfiguration(mLaunchBounds);
- }
- }
+ updateBounds(mStartActivity.getTask(), mLaunchBounds);
+
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Starting new activity " + mStartActivity
+ " in new task " + mStartActivity.getTask());
} else {
@@ -1838,6 +1832,17 @@
return START_SUCCESS;
}
+ private void deliverNewIntent(ActivityRecord activity) {
+ if (mIntentDelivered) {
+ return;
+ }
+
+ ActivityStack.logStartActivity(AM_NEW_INTENT, activity, activity.getTask());
+ activity.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
+ mStartActivity.launchedFromPackage);
+ mIntentDelivered = true;
+ }
+
private int setTaskFromSourceRecord() {
if (mService.mLockTaskController.isLockTaskModeViolation(mSourceRecord.getTask())) {
Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
@@ -1894,7 +1899,7 @@
mKeepCurTransition = true;
if (top != null) {
ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask());
- top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
+ deliverNewIntent(top);
// For paranoia, make sure we have correctly resumed the top activity.
mTargetStack.mLastPausedActivity = null;
if (mDoResume) {
@@ -1913,7 +1918,7 @@
task.moveActivityToFrontLocked(top);
top.updateOptionsLocked(mOptions);
ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, task);
- top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
+ deliverNewIntent(top);
mTargetStack.mLastPausedActivity = null;
if (mDoResume) {
mSupervisor.resumeFocusedStackTopActivityLocked();
@@ -1949,14 +1954,12 @@
|| mLaunchSingleTop || mLaunchSingleTask) {
mTargetStack.moveTaskToFrontLocked(mInTask, mNoAnimation, mOptions,
mStartActivity.appTimeTracker, "inTaskToFront");
- ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.getTask());
if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
// We don't need to start a new activity, and the client said not to do
// anything if that is the case, so this is it!
return START_RETURN_INTENT_TO_CALLER;
}
- top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
- mStartActivity.launchedFromPackage);
+ deliverNewIntent(top);
return START_DELIVERED_TO_TOP;
}
}
@@ -1971,19 +1974,15 @@
}
if (mLaunchBounds != null) {
- mInTask.updateOverrideConfiguration(mLaunchBounds);
// TODO: Shouldn't we already know what stack to use by the time we get here?
ActivityStack stack = mSupervisor.getLaunchStack(null, null, mInTask, ON_TOP);
if (stack != mInTask.getStack()) {
mInTask.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE,
DEFER_RESUME, "inTaskToFront");
- stack = mInTask.getStack();
mTargetStack = mInTask.getStack();
}
- if (StackId.resizeStackWithLaunchBounds(stack.mStackId)) {
- mService.resizeStack(
- stack.mStackId, mLaunchBounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
- }
+
+ updateBounds(mInTask, mLaunchBounds);
}
mTargetStack.moveTaskToFrontLocked(
@@ -1996,6 +1995,19 @@
return START_SUCCESS;
}
+ void updateBounds(TaskRecord task, Rect bounds) {
+ if (bounds == null) {
+ return;
+ }
+
+ final ActivityStack stack = task.getStack();
+ if (stack != null && stack.resizeStackWithLaunchBounds()) {
+ mService.resizeStack(stack.mStackId, bounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
+ } else {
+ task.updateOverrideConfiguration(bounds);
+ }
+ }
+
private void setTaskToCurrentTopOrCreateNewTask() {
mTargetStack = computeStackFocus(mStartActivity, false, null /* bounds */, mLaunchFlags,
mOptions);
@@ -2106,7 +2118,7 @@
mSupervisor.getStacksOnDefaultDisplay();
for (int stackNdx = homeDisplayStacks.size() - 1; stackNdx >= 0; --stackNdx) {
stack = homeDisplayStacks.get(stackNdx);
- if (isDynamicStack(stack.mStackId)) {
+ if (!stack.isOnHomeDisplay()) {
if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG_FOCUS,
"computeStackFocus: Setting focused stack=" + stack);
return stack;
@@ -2125,7 +2137,6 @@
private boolean canLaunchIntoFocusedStack(ActivityRecord r, boolean newTask) {
final ActivityStack focusedStack = mSupervisor.mFocusedStack;
final boolean canUseFocusedStack;
- final int focusedStackId = mSupervisor.mFocusedStack.mStackId;
if (focusedStack.isActivityTypeAssistant()) {
canUseFocusedStack = r.isActivityTypeAssistant();
} else {
@@ -2148,7 +2159,7 @@
default:
// Dynamic stacks behave similarly to the fullscreen stack and can contain any
// resizeable task.
- canUseFocusedStack = isDynamicStack(focusedStackId)
+ canUseFocusedStack = !focusedStack.isOnHomeDisplay()
&& r.canBeLaunchedOnDisplay(focusedStack.mDisplayId);
}
}
@@ -2192,7 +2203,7 @@
return mSupervisor.mFocusedStack;
}
- if (parentStack != null && parentStack.isDockedStack()) {
+ if (parentStack != null && parentStack.inSplitScreenPrimaryWindowingMode()) {
// If parent was in docked stack, the natural place to launch another activity
// will be fullscreen, so it can appear alongside the docked window.
final int activityType = mSupervisor.resolveActivityType(r, mOptions, task);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index e839003..7c9cd00 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -220,7 +220,7 @@
// Shutdown the thread we made.
mWorker.shutdown();
}
-
+
public static IBatteryStats getService() {
if (sService != null) {
return sService;
@@ -611,21 +611,21 @@
mStats.notePhoneOnLocked();
}
}
-
+
public void notePhoneOff() {
enforceCallingPermission();
synchronized (mStats) {
mStats.notePhoneOffLocked();
}
}
-
+
public void notePhoneSignalStrength(SignalStrength signalStrength) {
enforceCallingPermission();
synchronized (mStats) {
mStats.notePhoneSignalStrengthLocked(signalStrength);
}
}
-
+
public void notePhoneDataConnectionState(int dataType, boolean hasData) {
enforceCallingPermission();
synchronized (mStats) {
@@ -647,7 +647,7 @@
mStats.noteWifiOnLocked();
}
}
-
+
public void noteWifiOff() {
enforceCallingPermission();
synchronized (mStats) {
@@ -806,7 +806,7 @@
mStats.noteFullWifiLockAcquiredLocked(uid);
}
}
-
+
public void noteFullWifiLockReleased(int uid) {
enforceCallingPermission();
synchronized (mStats) {
@@ -1043,7 +1043,7 @@
});
});
}
-
+
public long getAwakeTimeBattery() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.BATTERY_STATS, null);
@@ -1186,6 +1186,7 @@
int flags = 0;
boolean useCheckinFormat = false;
+ boolean toProto = false;
boolean isRealCheckin = false;
boolean noOutput = false;
boolean writeData = false;
@@ -1212,6 +1213,8 @@
} else if ("-c".equals(arg)) {
useCheckinFormat = true;
flags |= BatteryStats.DUMP_INCLUDE_HISTORY;
+ } else if ("--proto".equals(arg)) {
+ toProto = true;
} else if ("--charged".equals(arg)) {
flags |= BatteryStats.DUMP_CHARGED_ONLY;
} else if ("--daily".equals(arg)) {
@@ -1304,7 +1307,45 @@
}
}
- if (useCheckinFormat) {
+ if (toProto) {
+ List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(
+ PackageManager.MATCH_ANY_USER | PackageManager.MATCH_ALL);
+ if (isRealCheckin) {
+ // For a real checkin, first we want to prefer to use the last complete checkin
+ // file if there is one.
+ synchronized (mStats.mCheckinFile) {
+ if (mStats.mCheckinFile.exists()) {
+ try {
+ byte[] raw = mStats.mCheckinFile.readFully();
+ if (raw != null) {
+ Parcel in = Parcel.obtain();
+ in.unmarshall(raw, 0, raw.length);
+ in.setDataPosition(0);
+ BatteryStatsImpl checkinStats = new BatteryStatsImpl(
+ null, mStats.mHandler, null, mUserManagerUserInfoProvider);
+ checkinStats.readSummaryFromParcel(in);
+ in.recycle();
+ checkinStats.dumpProtoLocked(mContext, fd, apps, flags,
+ historyStart);
+ mStats.mCheckinFile.delete();
+ return;
+ }
+ } catch (IOException | ParcelFormatException e) {
+ Slog.w(TAG, "Failure reading checkin file "
+ + mStats.mCheckinFile.getBaseFile(), e);
+ }
+ }
+ }
+ }
+ if (DBG) Slog.d(TAG, "begin dumpProtoLocked from UID " + Binder.getCallingUid());
+ synchronized (mStats) {
+ mStats.dumpProtoLocked(mContext, fd, apps, flags, historyStart);
+ if (writeData) {
+ mStats.writeAsyncLocked();
+ }
+ }
+ if (DBG) Slog.d(TAG, "end dumpProtoLocked");
+ } else if (useCheckinFormat) {
List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(
PackageManager.MATCH_ANY_USER | PackageManager.MATCH_ALL);
if (isRealCheckin) {
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 5c48f90..ba541e6 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -16,7 +16,6 @@
package com.android.server.am;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
diff --git a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
index d652341..2c161cd 100644
--- a/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
+++ b/services/core/java/com/android/server/am/LaunchingTaskPositioner.java
@@ -24,8 +24,8 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Slog;
-import android.view.Display;
import android.view.Gravity;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
@@ -75,31 +75,10 @@
private int mDefaultFreeformHeight;
private int mDefaultFreeformStepHorizontal;
private int mDefaultFreeformStepVertical;
- private int mDisplayWidth;
- private int mDisplayHeight;
+ private final Point mDisplaySize = new Point();
- void setDisplay(Display display) {
- Point size = new Point();
- display.getSize(size);
- mDisplayWidth = size.x;
- mDisplayHeight = size.y;
- }
-
- void configure(Rect stackBounds) {
- if (stackBounds == null) {
- mAvailableRect.set(0, 0, mDisplayWidth, mDisplayHeight);
- } else {
- mAvailableRect.set(stackBounds);
- }
- int width = mAvailableRect.width();
- int height = mAvailableRect.height();
- mDefaultFreeformStartX = mAvailableRect.left + width / MARGIN_SIZE_DENOMINATOR;
- mDefaultFreeformStartY = mAvailableRect.top + height / MARGIN_SIZE_DENOMINATOR;
- mDefaultFreeformWidth = width / WINDOW_SIZE_DENOMINATOR;
- mDefaultFreeformHeight = height / WINDOW_SIZE_DENOMINATOR;
- mDefaultFreeformStepHorizontal = Math.max(width / STEP_DENOMINATOR, MINIMAL_STEP);
- mDefaultFreeformStepVertical = Math.max(height / STEP_DENOMINATOR, MINIMAL_STEP);
- mDefaultStartBoundsConfigurationSet = true;
+ void setDisplaySize(Point size) {
+ mDisplaySize.set(size.x, size.y);
}
/**
@@ -146,6 +125,54 @@
}
}
+ void configure(Rect availableSpace) {
+ if (availableSpace == null) {
+ mAvailableRect.set(0, 0, mDisplaySize.x, mDisplaySize.y);
+ } else {
+ mAvailableRect.set(availableSpace);
+ }
+
+ mDefaultFreeformStartX = getFreeformStartLeft(mAvailableRect);
+ mDefaultFreeformStartY = getFreeformStartTop(mAvailableRect);
+ mDefaultFreeformWidth = getFreeformWidth(mAvailableRect);
+ mDefaultFreeformHeight = getFreeformHeight(mAvailableRect);
+ mDefaultFreeformStepHorizontal = getHorizontalStep(mAvailableRect);
+ mDefaultFreeformStepVertical = getVerticalStep(mAvailableRect);
+ mDefaultStartBoundsConfigurationSet = true;
+ }
+
+ @VisibleForTesting
+ static int getFreeformStartLeft(Rect bounds) {
+ return bounds.left + bounds.width() / MARGIN_SIZE_DENOMINATOR;
+ }
+
+ @VisibleForTesting
+ static int getFreeformStartTop(Rect bounds) {
+ return bounds.top + bounds.height() / MARGIN_SIZE_DENOMINATOR;
+ }
+
+ @VisibleForTesting
+ static int getFreeformWidth(Rect bounds) {
+ return bounds.width() / WINDOW_SIZE_DENOMINATOR;
+ }
+
+ @VisibleForTesting
+ static int getFreeformHeight(Rect bounds) {
+ return bounds.height() / WINDOW_SIZE_DENOMINATOR;
+ }
+
+ @VisibleForTesting
+ static int getHorizontalStep(Rect bounds) {
+ return Math.max(bounds.width() / STEP_DENOMINATOR, MINIMAL_STEP);
+ }
+
+ @VisibleForTesting
+ static int getVerticalStep(Rect bounds) {
+ return Math.max(bounds.height() / STEP_DENOMINATOR, MINIMAL_STEP);
+ }
+
+
+
private int getFinalWidth(ActivityInfo.WindowLayout windowLayout) {
int width = mDefaultFreeformWidth;
if (windowLayout.width > 0) {
@@ -211,7 +238,7 @@
// Unfortunately there is already a task at that spot, so we need to look for some
// other place.
shiftStartingPoint(proposal, shiftPolicy);
- if (shiftedToFar(proposal, shiftPolicy)) {
+ if (shiftedTooFar(proposal, shiftPolicy)) {
// We don't want the task to go outside of the stack, because it won't look
// nice. Depending on the starting point we either restart, or immediately give up.
if (!allowRestart) {
@@ -237,7 +264,7 @@
task.updateOverrideConfiguration(proposal);
}
- private boolean shiftedToFar(Rect start, int shiftPolicy) {
+ private boolean shiftedTooFar(Rect start, int shiftPolicy) {
switch (shiftPolicy) {
case SHIFT_POLICY_HORIZONTAL_LEFT:
return start.left < mAvailableRect.left;
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
index 468b867..2726979 100644
--- a/services/core/java/com/android/server/am/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -43,7 +43,8 @@
@Override
PinnedStackWindowController createStackWindowController(int displayId, boolean onTop,
Rect outBounds) {
- return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds);
+ return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds,
+ mStackSupervisor.mWindowManager);
}
Rect getDefaultPictureInPictureBounds(float aspectRatio) {
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index ac85e6b..16995e5 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -33,6 +33,7 @@
import android.content.pm.ServiceInfo;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -517,14 +518,27 @@
} catch (PackageManager.NameNotFoundException e) {
}
}
- if (localForegroundNoti.getSmallIcon() == null
- || nm.getNotificationChannel(localPackageName, appUid,
+ if (nm.getNotificationChannel(localPackageName, appUid,
localForegroundNoti.getChannelId()) == null) {
+ int targetSdkVersion = Build.VERSION_CODES.O_MR1;
+ try {
+ final ApplicationInfo applicationInfo =
+ ams.mContext.getPackageManager().getApplicationInfoAsUser(
+ appInfo.packageName, 0, userId);
+ targetSdkVersion = applicationInfo.targetSdkVersion;
+ } catch (PackageManager.NameNotFoundException e) {
+ }
+ if (targetSdkVersion >= Build.VERSION_CODES.O_MR1) {
+ throw new RuntimeException(
+ "invalid channel for service notification: "
+ + foregroundNoti);
+ }
+ }
+ if (localForegroundNoti.getSmallIcon() == null) {
// Notifications whose icon is 0 are defined to not show
// a notification, silently ignoring it. We don't want to
// just ignore it, we want to prevent the service from
// being foreground.
- // Also every notification needs a channel.
throw new RuntimeException("invalid service notification: "
+ foregroundNoti);
}
diff --git a/services/core/java/com/android/server/am/TaskChangeNotificationController.java b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
index 6a986bb..5a7e7ce 100644
--- a/services/core/java/com/android/server/am/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/am/TaskChangeNotificationController.java
@@ -95,7 +95,8 @@
};
private final TaskStackConsumer mNotifyActivityPinned = (l, m) -> {
- l.onActivityPinned((String) m.obj, m.arg1, m.arg2);
+ final ActivityRecord r = (ActivityRecord) m.obj;
+ l.onActivityPinned(r.packageName, r.userId, r.getTask().taskId, r.getStackId());
};
private final TaskStackConsumer mNotifyActivityUnpinned = (l, m) -> {
@@ -278,10 +279,9 @@
}
/** Notifies all listeners when an Activity is pinned. */
- void notifyActivityPinned(String packageName, int userId, int taskId) {
+ void notifyActivityPinned(ActivityRecord r) {
mHandler.removeMessages(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG);
- final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG,
- userId, taskId, packageName);
+ final Message msg = mHandler.obtainMessage(NOTIFY_ACTIVITY_PINNED_LISTENERS_MSG, r);
forAllLocalListeners(mNotifyActivityPinned, msg);
msg.sendToTarget();
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 0d8df79..5491da10 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -18,10 +18,7 @@
import static android.app.ActivityManager.RESIZE_MODE_FORCED;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -31,6 +28,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
@@ -520,7 +518,7 @@
// All we can do for now is update the bounds so it can be used when the task is
// added to window manager.
updateOverrideConfiguration(bounds);
- if (getStackId() != FREEFORM_WORKSPACE_STACK_ID) {
+ if (!inFreeformWindowingMode()) {
// re-restore the task so it can have the proper stack association.
mService.mStackSupervisor.restoreRecentTaskLocked(this, null);
}
@@ -616,8 +614,7 @@
* @return whether the task was reparented
*/
// TODO: Inspect all call sites and change to just changing windowing mode of the stack vs.
- // re-parenting the task. Can only be done when we are no longer using static stack Ids like
- /** {@link ActivityManager.StackId#FULLSCREEN_WORKSPACE_STACK_ID} */
+ // re-parenting the task. Can only be done when we are no longer using static stack Ids.
boolean reparent(ActivityStack preferredStack, int position,
@ReparentMoveStackMode int moveStackMode, boolean animate, boolean deferResume,
boolean schedulePictureInPictureModeChange, String reason) {
@@ -630,12 +627,12 @@
return false;
}
- final int sourceStackId = getStackId();
- final int stackId = toStack.getStackId();
+ final int toStackWindowingMode = toStack.getWindowingMode();
final ActivityRecord topActivity = getTopActivity();
- final boolean mightReplaceWindow = StackId.replaceWindowsOnTaskMove(sourceStackId, stackId)
- && topActivity != null;
+ final boolean mightReplaceWindow =
+ replaceWindowsOnTaskMove(getWindowingMode(), toStackWindowingMode)
+ && topActivity != null;
if (mightReplaceWindow) {
// We are about to relaunch the activity because its configuration changed due to
// being maximized, i.e. size change. The activity will first remove the old window
@@ -707,10 +704,10 @@
toStack.prepareFreezingTaskBounds();
// Make sure the task has the appropriate bounds/size for the stack it is in.
- final int toStackWindowingMode = toStack.getWindowingMode();
final boolean toStackSplitScreenPrimary =
toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
- if (stackId == FULLSCREEN_WORKSPACE_STACK_ID
+ if ((toStackWindowingMode == WINDOWING_MODE_FULLSCREEN
+ || toStackWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY)
&& !Objects.equals(mBounds, toStack.mBounds)) {
kept = resize(toStack.mBounds, RESIZE_MODE_SYSTEM, !mightReplaceWindow,
deferResume);
@@ -751,7 +748,7 @@
// TODO: Handle incorrect request to move before the actual move, not after.
final boolean inSplitScreenMode = supervisor.getDefaultDisplay().hasSplitScreenStack();
supervisor.handleNonResizableTaskIfNeeded(this, preferredStack.getWindowingMode(),
- DEFAULT_DISPLAY, stackId);
+ DEFAULT_DISPLAY, toStack.mStackId);
boolean successful = (preferredStack == toStack);
if (successful && toStack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
@@ -761,6 +758,18 @@
return successful;
}
+ /**
+ * Returns true if the windows of tasks being moved to the target stack from the source
+ * stack should be replaced, meaning that window manager will keep the old window around
+ * until the new is ready.
+ * @hide
+ */
+ private static boolean replaceWindowsOnTaskMove(
+ int sourceWindowingMode, int targetWindowingMode) {
+ return sourceWindowingMode == WINDOWING_MODE_FREEFORM
+ || targetWindowingMode == WINDOWING_MODE_FREEFORM;
+ }
+
void cancelWindowTransition() {
mWindowContainerController.cancelWindowTransition();
}
@@ -1263,7 +1272,7 @@
mService.notifyTaskPersisterLocked(this, false);
}
- if (getStackId() == PINNED_STACK_ID) {
+ if (inPinnedWindowingMode()) {
// We normally notify listeners of task stack changes on pause, however pinned stack
// activities are normally in the paused state so no notification will be sent there
// before the activity is removed. We send it here so instead.
@@ -1493,7 +1502,7 @@
* @return True if the requested bounds are okay for a resizing request.
*/
private boolean canResizeToBounds(Rect bounds) {
- if (bounds == null || getStackId() != FREEFORM_WORKSPACE_STACK_ID) {
+ if (bounds == null || !inFreeformWindowingMode()) {
// Note: If not on the freeform workspace, we ignore the bounds.
return true;
}
@@ -1911,7 +1920,7 @@
// If the task has no requested minimal size, we'd like to enforce a minimal size
// so that the user can not render the task too small to manipulate. We don't need
// to do this for the pinned stack as the bounds are controlled by the system.
- if (getStackId() != PINNED_STACK_ID) {
+ if (!inPinnedWindowingMode()) {
if (minWidth == INVALID_MIN_SIZE) {
minWidth = mService.mStackSupervisor.mDefaultMinSizeOfResizeableTask;
}
@@ -2085,7 +2094,7 @@
return;
}
- if (inStack.mStackId == FREEFORM_WORKSPACE_STACK_ID) {
+ if (inStack.inFreeformWindowingMode()) {
if (!isResizeable()) {
throw new IllegalArgumentException("Can not position non-resizeable task="
+ this + " in stack=" + inStack);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 055c9f6..5a29594 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -631,7 +631,6 @@
void finishUserStopping(final int userId, final UserState uss) {
// On to the next.
final Intent shutdownIntent = new Intent(Intent.ACTION_SHUTDOWN);
- shutdownIntent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
// This is the result receiver for the final shutdown broadcast.
final IIntentReceiver shutdownReceiver = new IIntentReceiver.Stub() {
@Override
diff --git a/services/core/java/com/android/server/audio/AudioEventLogger.java b/services/core/java/com/android/server/audio/AudioEventLogger.java
index a2c0f76..9ebd75b 100644
--- a/services/core/java/com/android/server/audio/AudioEventLogger.java
+++ b/services/core/java/com/android/server/audio/AudioEventLogger.java
@@ -49,9 +49,9 @@
}
/**
- * Causes the string message for the event to appear in the verbose logcat.
+ * Causes the string message for the event to appear in the logcat.
* Here is an example of how to create a new event (a StringEvent), adding it to the logger
- * (an instance of AudioEventLogger) while also making it show in the verbose logcat:
+ * (an instance of AudioEventLogger) while also making it show in the logcat:
* <pre>
* myLogger.log(
* (new StringEvent("something for logcat and logger")).printLog(MyClass.TAG) );
@@ -60,7 +60,7 @@
* @return the same instance of the event
*/
public Event printLog(String tag) {
- Log.v(tag, eventToString());
+ Log.i(tag, eventToString());
return this;
}
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 7d742ff..c5f563c7 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -89,6 +89,9 @@
pw.println("\nMediaFocusControl dump time: "
+ DateFormat.getTimeInstance().format(new Date()));
dumpFocusStack(pw);
+ pw.println("\n");
+ // log
+ mEventLogger.dump(pw);
}
//=================================================================
@@ -120,6 +123,14 @@
private final static Object mAudioFocusLock = new Object();
/**
+ * Arbitrary maximum size of audio focus stack to prevent apps OOM'ing this process.
+ */
+ private static final int MAX_STACK_SIZE = 100;
+
+ private static final AudioEventLogger mEventLogger = new AudioEventLogger(50,
+ "focus commands as seen by MediaFocusControl");
+
+ /**
* Discard the current audio focus owner.
* Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
* focus), remove it from the stack, and clear the remote control display.
@@ -643,11 +654,14 @@
protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
int sdk) {
- Log.i(TAG, " AudioFocus requestAudioFocus() from uid/pid " + Binder.getCallingUid()
- + "/" + Binder.getCallingPid()
- + " clientId=" + clientId
- + " req=" + focusChangeHint
- + " flags=0x" + Integer.toHexString(flags));
+ mEventLogger.log((new AudioEventLogger.StringEvent(
+ "requestAudioFocus() from uid/pid " + Binder.getCallingUid()
+ + "/" + Binder.getCallingPid()
+ + " clientId=" + clientId + " callingPack=" + callingPackageName
+ + " req=" + focusChangeHint
+ + " flags=0x" + Integer.toHexString(flags)
+ + " sdk=" + sdk))
+ .printLog(TAG));
// we need a valid binder callback for clients
if (!cb.pingBinder()) {
Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
@@ -660,6 +674,11 @@
}
synchronized(mAudioFocusLock) {
+ if (mFocusStack.size() > MAX_STACK_SIZE) {
+ Log.e(TAG, "Max AudioFocus stack size reached, failing requestAudioFocus()");
+ return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+ }
+
boolean enteringRingOrCall = !mRingOrCallActive
& (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0);
if (enteringRingOrCall) { mRingOrCallActive = true; }
@@ -770,10 +789,12 @@
* */
protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId, AudioAttributes aa,
String callingPackageName) {
- // AudioAttributes are currently ignored, to be used for zones
- Log.i(TAG, " AudioFocus abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
- + "/" + Binder.getCallingPid()
- + " clientId=" + clientId);
+ // AudioAttributes are currently ignored, to be used for zones / a11y
+ mEventLogger.log((new AudioEventLogger.StringEvent(
+ "abandonAudioFocus() from uid/pid " + Binder.getCallingUid()
+ + "/" + Binder.getCallingPid()
+ + " clientId=" + clientId))
+ .printLog(TAG));
try {
// this will take care of notifying the new focus owner if needed
synchronized(mAudioFocusLock) {
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 6506cf7..4943173 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -184,11 +184,15 @@
}
}
+ private static final int FLAGS_FOR_SILENCE_OVERRIDE =
+ AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY |
+ AudioAttributes.FLAG_BYPASS_MUTE;
+
private void checkVolumeForPrivilegedAlarm(AudioPlaybackConfiguration apc, int event) {
if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED ||
apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
- if ((apc.getAudioAttributes().getAllFlags() &
- AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0 &&
+ if ((apc.getAudioAttributes().getAllFlags() & FLAGS_FOR_SILENCE_OVERRIDE)
+ == FLAGS_FOR_SILENCE_OVERRIDE &&
apc.getAudioAttributes().getUsage() == AudioAttributes.USAGE_ALARM &&
mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE,
apc.getClientPid(), apc.getClientUid()) ==
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 5583e86..d7cd81f 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1371,6 +1371,7 @@
sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
}
}
+ mUpstreamNetworkMonitor.setCurrentUpstream((ns != null) ? ns.network : null);
setUpstreamNetwork(ns);
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
index 057704a..cff216c 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
@@ -596,9 +596,10 @@
}
mNatUpdateCallbacksReceived++;
+ final String natDescription = String.format("%s (%s, %s) -> (%s, %s)",
+ protoName, srcAddr, srcPort, dstAddr, dstPort);
if (DBG) {
- mLog.log(String.format("NAT timeout update: %s (%s, %s) -> (%s, %s)",
- protoName, srcAddr, srcPort, dstAddr, dstPort));
+ mLog.log("NAT timeout update: " + natDescription);
}
final int timeoutSec = connectionTimeoutUpdateSecondsFor(proto);
@@ -609,7 +610,7 @@
NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_NETFILTER, msg);
} catch (ErrnoException e) {
mNatUpdateNetlinkErrors++;
- mLog.e("Error updating NAT conntrack entry: " + e
+ mLog.e("Error updating NAT conntrack entry >" + natDescription + "<: " + e
+ ", msg: " + NetlinkConstants.hexify(msg));
mLog.log("NAT timeout update callbacks received: " + mNatUpdateCallbacksReceived);
mLog.log("NAT timeout update netlink errors: " + mNatUpdateNetlinkErrors);
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index c5f7528..b35ed75 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -95,7 +95,10 @@
private NetworkCallback mDefaultNetworkCallback;
private NetworkCallback mMobileNetworkCallback;
private boolean mDunRequired;
- private Network mCurrentDefault;
+ // The current system default network (not really used yet).
+ private Network mDefaultInternetNetwork;
+ // The current upstream network used for tethering.
+ private Network mTetheringUpstreamNetwork;
public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, SharedLog log, int what) {
mContext = ctx;
@@ -130,10 +133,12 @@
releaseCallback(mDefaultNetworkCallback);
mDefaultNetworkCallback = null;
+ mDefaultInternetNetwork = null;
releaseCallback(mListenAllCallback);
mListenAllCallback = null;
+ mTetheringUpstreamNetwork = null;
mNetworkMap.clear();
}
@@ -207,7 +212,7 @@
break;
default:
/* If we've found an active upstream connection that's not DUN/HIPRI
- * we should stop any outstanding DUN/HIPRI start requests.
+ * we should stop any outstanding DUN/HIPRI requests.
*
* If we found NONE we don't want to do this as we want any previous
* requests to keep trying to bring up something we can use.
@@ -219,6 +224,10 @@
return typeStatePair.ns;
}
+ public void setCurrentUpstream(Network upstream) {
+ mTetheringUpstreamNetwork = upstream;
+ }
+
public Set<IpPrefix> getLocalPrefixes() {
return (Set<IpPrefix>) mLocalPrefixes.clone();
}
@@ -250,7 +259,7 @@
// These request*() calls can be deleted post oag/339444.
return;
}
- mCurrentDefault = network;
+ mDefaultInternetNetwork = network;
break;
case CALLBACK_MOBILE_REQUEST:
@@ -302,6 +311,13 @@
network, newNc));
}
+ // Log changes in upstream network signal strength, if available.
+ if (network.equals(mTetheringUpstreamNetwork) && newNc.hasSignalStrength()) {
+ final int newSignal = newNc.getSignalStrength();
+ final String prevSignal = getSignalStrength(prev.networkCapabilities);
+ mLog.logf("upstream network signal strength: %s -> %s", prevSignal, newSignal);
+ }
+
mNetworkMap.put(network, new NetworkState(
null, prev.linkProperties, newNc, network, null, null));
// TODO: If sufficient information is available to select a more
@@ -330,9 +346,21 @@
notifyTarget(EVENT_ON_LINKPROPERTIES, network);
}
+ private void handleSuspended(int callbackType, Network network) {
+ if (callbackType != CALLBACK_LISTEN_ALL) return;
+ if (!network.equals(mTetheringUpstreamNetwork)) return;
+ mLog.log("SUSPENDED current upstream: " + network);
+ }
+
+ private void handleResumed(int callbackType, Network network) {
+ if (callbackType != CALLBACK_LISTEN_ALL) return;
+ if (!network.equals(mTetheringUpstreamNetwork)) return;
+ mLog.log("RESUMED current upstream: " + network);
+ }
+
private void handleLost(int callbackType, Network network) {
if (callbackType == CALLBACK_TRACK_DEFAULT) {
- mCurrentDefault = null;
+ mDefaultInternetNetwork = null;
// Receiving onLost() for a default network does not necessarily
// mean the network is gone. We wait for a separate notification
// on either the LISTEN_ALL or MOBILE_REQUEST callbacks before
@@ -401,8 +429,15 @@
recomputeLocalPrefixes();
}
- // TODO: Handle onNetworkSuspended();
- // TODO: Handle onNetworkResumed();
+ @Override
+ public void onNetworkSuspended(Network network) {
+ handleSuspended(mCallbackType, network);
+ }
+
+ @Override
+ public void onNetworkResumed(Network network) {
+ handleResumed(mCallbackType, network);
+ }
@Override
public void onLost(Network network) {
@@ -467,4 +502,9 @@
return prefixSet;
}
+
+ private static String getSignalStrength(NetworkCapabilities nc) {
+ if (nc == null || !nc.hasSignalStrength()) return "unknown";
+ return Integer.toString(nc.getSignalStrength());
+ }
}
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 9aabdab..9a6e609 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -29,6 +29,7 @@
import android.os.Message;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.os.Trace;
import android.text.format.DateUtils;
import android.util.EventLog;
import android.util.MathUtils;
@@ -304,6 +305,7 @@
}
private void handleLightSensorEvent(long time, float lux) {
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "ALS", (int) lux);
mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
if (mAmbientLightRingBuffer.size() == 0) {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index ef6de4c..fddb81b 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -98,6 +98,12 @@
public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 1 << 9;
/**
+ * Flag: This display will destroy its content on removal.
+ * @hide
+ */
+ public static final int FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 10;
+
+ /**
* Touch attachment: Display does not receive touch.
*/
public static final int TOUCH_NONE = 0;
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index f8e5836..f930b52 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -683,8 +683,8 @@
// Configure auto-brightness.
boolean autoBrightnessEnabled = false;
if (mAutomaticBrightnessController != null) {
- final boolean autoBrightnessEnabledInDoze = mAllowAutoBrightnessWhileDozingConfig
- && (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND);
+ final boolean autoBrightnessEnabledInDoze =
+ mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state);
autoBrightnessEnabled = mPowerRequest.useAutoBrightness
&& (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
&& brightness < 0;
@@ -726,8 +726,7 @@
}
// Use default brightness when dozing unless overridden.
- if (brightness < 0 && (state == Display.STATE_DOZE
- || state == Display.STATE_DOZE_SUSPEND)) {
+ if (brightness < 0 && Display.isDozeState(state)) {
brightness = mScreenBrightnessDozeConfig;
}
@@ -777,7 +776,6 @@
// Skip the animation when the screen is off or suspended or transition to/from VR.
if (!mPendingScreenOff) {
if (mSkipScreenOnBrightnessRamp) {
-
if (state == Display.STATE_ON) {
if (mSkipRampState == RAMP_STATE_SKIP_NONE && mDozing) {
mInitialAutoBrightness = brightness;
@@ -794,15 +792,25 @@
}
}
- boolean wasOrWillBeInVr = (state == Display.STATE_VR || oldState == Display.STATE_VR);
- if ((state == Display.STATE_ON
- && mSkipRampState == RAMP_STATE_SKIP_NONE
- || state == Display.STATE_DOZE && !mBrightnessBucketsInDozeConfig)
- && !wasOrWillBeInVr) {
+ final boolean wasOrWillBeInVr =
+ (state == Display.STATE_VR || oldState == Display.STATE_VR);
+ final boolean initialRampSkip =
+ state == Display.STATE_ON && mSkipRampState != RAMP_STATE_SKIP_NONE;
+ // While dozing, sometimes the brightness is split into buckets. Rather than animating
+ // through the buckets, which is unlikely to be smooth in the first place, just jump
+ // right to the suggested brightness.
+ final boolean hasBrightnessBuckets =
+ Display.isDozeState(state) && mBrightnessBucketsInDozeConfig;
+ // If the color fade is totally covering the screen then we can change the backlight
+ // level without it being a noticeable jump since any actual content isn't yet visible.
+ final boolean isDisplayContentVisible =
+ mColorFadeEnabled && mPowerState.getColorFadeLevel() == 1.0f;
+ if (initialRampSkip || hasBrightnessBuckets
+ || wasOrWillBeInVr || !isDisplayContentVisible) {
+ animateScreenBrightness(brightness, 0);
+ } else {
animateScreenBrightness(brightness,
slowChange ? mBrightnessRampRateSlow : mBrightnessRampRateFast);
- } else {
- animateScreenBrightness(brightness, 0);
}
}
@@ -925,6 +933,7 @@
}
if (!reportOnly) {
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
mPowerState.setScreenState(state);
// Tell battery stats about the transition.
try {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index addad0b..78a5407 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -238,6 +238,9 @@
// For private displays by default content is destroyed on removal.
mBaseDisplayInfo.removeMode = Display.REMOVE_MODE_DESTROY_CONTENT;
}
+ if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL) != 0) {
+ mBaseDisplayInfo.removeMode = Display.REMOVE_MODE_DESTROY_CONTENT;
+ }
if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_PRESENTATION) != 0) {
mBaseDisplayInfo.flags |= Display.FLAG_PRESENTATION;
}
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index d6ab888..f86d576 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -24,6 +24,8 @@
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT;
+import static android.hardware.display.DisplayManager
+ .VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
import android.content.Context;
import android.hardware.display.IVirtualDisplayCallback;
@@ -363,6 +365,9 @@
if ((mFlags & VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT) != 0) {
mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
}
+ if ((mFlags & VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL) != 0) {
+ mInfo.flags |= DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL;
+ }
mInfo.type = Display.TYPE_VIRTUAL;
mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ?
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index b9a2d18..b102dde 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -205,9 +205,8 @@
}
private List<MediaSessionRecord> getActiveSessionsLocked(int userId) {
- List<MediaSessionRecord> records;
+ List<MediaSessionRecord> records = new ArrayList<>();
if (userId == UserHandle.USER_ALL) {
- records = new ArrayList<>();
int size = mUserRecords.size();
for (int i = 0; i < size; i++) {
records.addAll(mUserRecords.valueAt(i).mPriorityStack.getActiveSessions(userId));
@@ -216,9 +215,9 @@
FullUserRecord user = getFullUserRecordLocked(userId);
if (user == null) {
Log.w(TAG, "getSessions failed. Unknown user " + userId);
- return new ArrayList<>();
+ return records;
}
- records = user.mPriorityStack.getActiveSessions(userId);
+ records.addAll(user.mPriorityStack.getActiveSessions(userId));
}
// Return global priority session at the first whenever it's asked.
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index c1d7059..019c7c2 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -546,6 +546,17 @@
return null;
}
+ protected boolean isServiceTokenValidLocked(IInterface service) {
+ if (service == null) {
+ return false;
+ }
+ ManagedServiceInfo info = getServiceFromTokenLocked(service);
+ if (info != null) {
+ return true;
+ }
+ return false;
+ }
+
protected ManagedServiceInfo checkServiceTokenLocked(IInterface service) {
checkNotNull(service);
ManagedServiceInfo info = getServiceFromTokenLocked(service);
@@ -829,7 +840,12 @@
ServiceInfo info = mPm.getServiceInfo(component,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userIds[i]);
- if (info == null || !mConfig.bindPermission.equals(info.permission)) {
+ if (info == null) {
+ Slog.w(TAG, "Not binding " + getCaption() + " service " + component
+ + ": service not found");
+ continue;
+ }
+ if (!mConfig.bindPermission.equals(info.permission)) {
Slog.w(TAG, "Not binding " + getCaption() + " service " + component
+ ": it does not require the permission " + mConfig.bindPermission);
continue;
diff --git a/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java b/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java
index ce11268..3bfd93f 100644
--- a/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java
+++ b/services/core/java/com/android/server/notification/NotificationAdjustmentExtractor.java
@@ -35,7 +35,6 @@
if (DBG) Slog.d(TAG, "skipping empty notification");
return null;
}
-
record.applyAdjustments();
return null;
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 6a1401c..36bc096 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -16,6 +16,8 @@
package com.android.server.notification;
+import android.service.notification.NotificationStats;
+
import com.android.internal.statusbar.NotificationVisibility;
public interface NotificationDelegate {
@@ -24,7 +26,8 @@
void onNotificationClick(int callingUid, int callingPid, String key);
void onNotificationActionClick(int callingUid, int callingPid, String key, int actionIndex);
void onNotificationClear(int callingUid, int callingPid,
- String pkg, String tag, int id, int userId);
+ String pkg, String tag, int id, int userId, String key,
+ @NotificationStats.DismissalSurface int dismissalSurface);
void onNotificationError(int callingUid, int callingPid,
String pkg, String tag, int id,
int uid, int initialPid, String message, int userId);
@@ -35,4 +38,6 @@
NotificationVisibility[] newlyVisibleKeys,
NotificationVisibility[] noLongerVisibleKeys);
void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
+ void onNotificationDirectReplied(String key);
+ void onNotificationSettingsViewed(String key);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4cb5d0f..168f070 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -134,6 +134,7 @@
import android.service.notification.NotificationRecordProto;
import android.service.notification.NotificationServiceDumpProto;
import android.service.notification.NotificationServiceProto;
+import android.service.notification.NotificationStats;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.service.notification.ZenModeConfig;
@@ -281,6 +282,7 @@
private WindowManagerInternal mWindowManagerInternal;
private AlarmManager mAlarmManager;
private ICompanionDeviceManager mCompanionManager;
+ private AccessibilityManager mAccessibilityManager;
final IBinder mForegroundToken = new Binder();
private WorkerHandler mHandler;
@@ -678,7 +680,14 @@
@Override
public void onNotificationClear(int callingUid, int callingPid,
- String pkg, String tag, int id, int userId) {
+ String pkg, String tag, int id, int userId, String key,
+ @NotificationStats.DismissalSurface int dismissalSurface) {
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r != null) {
+ r.recordDismissalSurface(dismissalSurface);
+ }
+ }
cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
true, userId, REASON_CANCEL, null);
@@ -764,12 +773,35 @@
.setType(expanded ? MetricsEvent.TYPE_DETAIL
: MetricsEvent.TYPE_COLLAPSE));
}
+ if (expanded) {
+ r.recordExpanded();
+ }
EventLogTags.writeNotificationExpansion(key,
userAction ? 1 : 0, expanded ? 1 : 0,
r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
}
}
}
+
+ @Override
+ public void onNotificationDirectReplied(String key) {
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r != null) {
+ r.recordDirectReplied();
+ }
+ }
+ }
+
+ @Override
+ public void onNotificationSettingsViewed(String key) {
+ synchronized (mNotificationLock) {
+ NotificationRecord r = mNotificationsByKey.get(key);
+ if (r != null) {
+ r.recordViewedSettings();
+ }
+ }
+ }
};
@GuardedBy("mNotificationLock")
@@ -1190,6 +1222,12 @@
mUsageStats = us;
}
+ @VisibleForTesting
+ void setAccessibilityManager(AccessibilityManager am) {
+ mAccessibilityManager = am;
+ }
+
+
// TODO: All tests should use this init instead of the one-off setters above.
@VisibleForTesting
void init(Looper looper, IPackageManager packageManager,
@@ -1204,6 +1242,8 @@
Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE);
+ mAccessibilityManager =
+ (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
mAm = ActivityManager.getService();
mPackageManager = packageManager;
mPackageManagerClient = packageManagerClient;
@@ -3830,6 +3870,7 @@
} else {
mSnoozeHelper.snooze(r, mDuration);
}
+ r.recordSnoozed();
savePolicyFile();
}
}
@@ -3969,7 +4010,7 @@
Slog.e(TAG, "Not posting notification without small icon: " + notification);
if (old != null && !old.isCanceled) {
mListeners.notifyRemovedLocked(n,
- NotificationListenerService.REASON_ERROR);
+ NotificationListenerService.REASON_ERROR, null);
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -4085,13 +4126,16 @@
// These are set inside the conditional if the notification is allowed to make noise.
boolean hasValidVibrate = false;
boolean hasValidSound = false;
+ boolean sentAccessibilityEvent = false;
+ // If the notification will appear in the status bar, it should send an accessibility
+ // event
+ if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN) {
+ sendAccessibilityEvent(notification, record.sbn.getPackageName());
+ sentAccessibilityEvent = true;
+ }
if (aboveThreshold && isNotificationForCurrentUser(record)) {
- // If the notification will appear in the status bar, it should send an accessibility
- // event
- if (!record.isUpdate && record.getImportance() > IMPORTANCE_MIN) {
- sendAccessibilityEvent(notification, record.sbn.getPackageName());
- }
+
if (mSystemReady && mAudioManager != null) {
Uri soundUri = record.getSound();
hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
@@ -4109,6 +4153,10 @@
boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
+ if (!sentAccessibilityEvent) {
+ sendAccessibilityEvent(notification, record.sbn.getPackageName());
+ sentAccessibilityEvent = true;
+ }
if (DBG) Slog.v(TAG, "Interrupting!");
if (hasValidSound) {
mSoundNotificationKey = key;
@@ -4462,6 +4510,7 @@
ArrayList<String> groupKeyBefore = new ArrayList<>(N);
ArrayList<ArrayList<String>> overridePeopleBefore = new ArrayList<>(N);
ArrayList<ArrayList<SnoozeCriterion>> snoozeCriteriaBefore = new ArrayList<>(N);
+ ArrayList<Integer> userSentimentBefore = new ArrayList<>(N);
for (int i = 0; i < N; i++) {
final NotificationRecord r = mNotificationList.get(i);
orderBefore.add(r.getKey());
@@ -4471,6 +4520,7 @@
groupKeyBefore.add(r.getGroupKey());
overridePeopleBefore.add(r.getPeopleOverride());
snoozeCriteriaBefore.add(r.getSnoozeCriteria());
+ userSentimentBefore.add(r.getUserSentiment());
mRankingHelper.extractSignals(r);
}
mRankingHelper.sort(mNotificationList);
@@ -4482,7 +4532,8 @@
|| !Objects.equals(channelBefore.get(i), r.getChannel())
|| !Objects.equals(groupKeyBefore.get(i), r.getGroupKey())
|| !Objects.equals(overridePeopleBefore.get(i), r.getPeopleOverride())
- || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())) {
+ || !Objects.equals(snoozeCriteriaBefore.get(i), r.getSnoozeCriteria())
+ || !Objects.equals(userSentimentBefore.get(i), r.getUserSentiment())) {
mHandler.scheduleSendRankingUpdate();
return;
}
@@ -4626,8 +4677,7 @@
}
void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
- AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
- if (!manager.isEnabled()) {
+ if (!mAccessibilityManager.isEnabled()) {
return;
}
@@ -4641,7 +4691,7 @@
event.getText().add(tickerText);
}
- manager.sendAccessibilityEvent(event);
+ mAccessibilityManager.sendAccessibilityEvent(event);
}
/**
@@ -4675,6 +4725,10 @@
// Record caller.
recordCallerLocked(r);
+ if (r.getStats().getDismissalSurface() == NotificationStats.DISMISSAL_NOT_DISMISSED) {
+ r.recordDismissalSurface(NotificationStats.DISMISSAL_OTHER);
+ }
+
// tell the app
if (sendDelete) {
if (r.getNotification().deleteIntent != null) {
@@ -4695,7 +4749,7 @@
if (reason != REASON_SNOOZED) {
r.isCanceled = true;
}
- mListeners.notifyRemovedLocked(r.sbn, reason);
+ mListeners.notifyRemovedLocked(r.sbn, reason, r.getStats());
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -5289,6 +5343,7 @@
Bundle overridePeople = new Bundle();
Bundle snoozeCriteria = new Bundle();
Bundle showBadge = new Bundle();
+ Bundle userSentiment = new Bundle();
for (int i = 0; i < N; i++) {
NotificationRecord record = mNotificationList.get(i);
if (!isVisibleToListener(record.sbn, info)) {
@@ -5314,6 +5369,7 @@
overridePeople.putStringArrayList(key, record.getPeopleOverride());
snoozeCriteria.putParcelableArrayList(key, record.getSnoozeCriteria());
showBadge.putBoolean(key, record.canShowBadge());
+ userSentiment.putInt(key, record.getUserSentiment());
}
final int M = keys.size();
String[] keysAr = keys.toArray(new String[M]);
@@ -5324,7 +5380,7 @@
}
return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
- channels, overridePeople, snoozeCriteria, showBadge);
+ channels, overridePeople, snoozeCriteria, showBadge, userSentiment);
}
boolean hasCompanionDevice(ManagedServiceInfo info) {
@@ -5413,7 +5469,7 @@
@Override
protected Config getConfig() {
Config c = new Config();
- c.caption = "notification assistant service";
+ c.caption = "notification assistant";
c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
c.xmlTag = TAG_ENABLED_NOTIFICATION_ASSISTANTS;
c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
@@ -5456,8 +5512,6 @@
continue;
}
- final int importance = r.getImportance();
- final boolean fromUser = r.isImportanceFromUser();
final StatusBarNotification sbnToPost = trimCache.ForListener(info);
mHandler.post(new Runnable() {
@Override
@@ -5488,6 +5542,10 @@
final String snoozeCriterionId) {
TrimCache trimCache = new TrimCache(sbn);
for (final ManagedServiceInfo info : getServices()) {
+ boolean sbnVisible = isVisibleToListener(sbn, info);
+ if (!sbnVisible) {
+ continue;
+ }
final StatusBarNotification sbnToPost = trimCache.ForListener(info);
mHandler.post(new Runnable() {
@Override
@@ -5609,7 +5667,8 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED);
+ notifyRemoved(
+ info, oldSbnLightClone, update, null, REASON_USER_STOPPED);
}
});
continue;
@@ -5629,7 +5688,8 @@
* asynchronously notify all listeners about a removed notification
*/
@GuardedBy("mNotificationLock")
- public void notifyRemovedLocked(StatusBarNotification sbn, int reason) {
+ public void notifyRemovedLocked(StatusBarNotification sbn, int reason,
+ NotificationStats notificationStats) {
// make a copy in case changes are made to the underlying Notification object
// NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
// notification
@@ -5638,11 +5698,14 @@
if (!isVisibleToListener(sbn, info)) {
continue;
}
+ // Only assistants can get stats
+ final NotificationStats stats = mAssistants.isServiceTokenValidLocked(info.service)
+ ? notificationStats : null;
final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
mHandler.post(new Runnable() {
@Override
public void run() {
- notifyRemoved(info, sbnLight, update, reason);
+ notifyRemoved(info, sbnLight, update, stats, reason);
}
});
}
@@ -5747,14 +5810,14 @@
}
private void notifyRemoved(ManagedServiceInfo info, StatusBarNotification sbn,
- NotificationRankingUpdate rankingUpdate, int reason) {
+ NotificationRankingUpdate rankingUpdate, NotificationStats stats, int reason) {
if (!info.enabledAndUserMatches(sbn.getUserId())) {
return;
}
final INotificationListener listener = (INotificationListener) info.service;
StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
try {
- listener.onNotificationRemoved(sbnHolder, rankingUpdate, reason);
+ listener.onNotificationRemoved(sbnHolder, rankingUpdate, stats, reason);
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 77bf9e3..faa300f2 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -20,6 +20,8 @@
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.IMPORTANCE_LOW;
+import static android.service.notification.NotificationListenerService.Ranking
+ .USER_SENTIMENT_NEUTRAL;
import android.app.Notification;
import android.app.NotificationChannel;
@@ -41,6 +43,7 @@
import android.service.notification.Adjustment;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationRecordProto;
+import android.service.notification.NotificationStats;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
@@ -84,8 +87,6 @@
NotificationUsageStats.SingleNotificationStats stats;
boolean isCanceled;
- /** Whether the notification was seen by the user via one of the notification listeners. */
- boolean mIsSeen;
// These members are used by NotificationSignalExtractors
// to communicate with the ranking module.
@@ -136,6 +137,8 @@
private String mChannelIdLogTag;
private final List<Adjustment> mAdjustments;
+ private final NotificationStats mStats;
+ private int mUserSentiment;
@VisibleForTesting
public NotificationRecord(Context context, StatusBarNotification sbn,
@@ -156,6 +159,7 @@
mImportance = calculateImportance();
mLight = calculateLights();
mAdjustments = new ArrayList<>();
+ mStats = new NotificationStats();
}
private boolean isPreChannelsNotification() {
@@ -395,7 +399,7 @@
pw.println(prefix + "flags=0x" + Integer.toHexString(notification.flags));
pw.println(prefix + "pri=" + notification.priority);
pw.println(prefix + "key=" + sbn.getKey());
- pw.println(prefix + "seen=" + mIsSeen);
+ pw.println(prefix + "seen=" + mStats.hasSeen());
pw.println(prefix + "groupKey=" + getGroupKey());
pw.println(prefix + "fullscreenIntent=" + notification.fullScreenIntent);
pw.println(prefix + "contentIntent=" + notification.contentIntent);
@@ -572,6 +576,10 @@
adjustment.getSignals().getString(Adjustment.KEY_GROUP_KEY);
setOverrideGroupKey(groupOverrideKey);
}
+ if (signals.containsKey(Adjustment.KEY_USER_SENTIMENT)) {
+ setUserSentiment(adjustment.getSignals().getInt(
+ Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEUTRAL));
+ }
}
}
}
@@ -740,6 +748,7 @@
.setType(visible ? MetricsEvent.TYPE_OPEN : MetricsEvent.TYPE_CLOSE)
.addTaggedData(MetricsEvent.NOTIFICATION_SHADE_INDEX, rank));
if (visible) {
+ setSeen();
MetricsLogger.histogram(mContext, "note_freshness", getFreshnessMs(now));
}
EventLogTags.writeNotificationVisibility(getKey(), visible ? 1 : 0,
@@ -777,12 +786,12 @@
/** Check if any of the listeners have marked this notification as seen by the user. */
public boolean isSeen() {
- return mIsSeen;
+ return mStats.hasSeen();
}
/** Mark the notification as seen by the user. */
public void setSeen() {
- mIsSeen = true;
+ mStats.setSeen();
}
public void setAuthoritativeRank(int authoritativeRank) {
@@ -883,6 +892,38 @@
mSnoozeCriteria = snoozeCriteria;
}
+ private void setUserSentiment(int userSentiment) {
+ mUserSentiment = userSentiment;
+ }
+
+ public int getUserSentiment() {
+ return mUserSentiment;
+ }
+
+ public NotificationStats getStats() {
+ return mStats;
+ }
+
+ public void recordExpanded() {
+ mStats.setExpanded();
+ }
+
+ public void recordDirectReplied() {
+ mStats.setDirectReplied();
+ }
+
+ public void recordDismissalSurface(@NotificationStats.DismissalSurface int surface) {
+ mStats.setDismissalSurface(surface);
+ }
+
+ public void recordSnoozed() {
+ mStats.setSnoozed();
+ }
+
+ public void recordViewedSettings() {
+ mStats.setViewedSettings();
+ }
+
public LogMaker getLogMaker(long now) {
if (mLogMaker == null) {
// initialize fields that only change on update (so a new record)
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 8783f47..d7e9cf3 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -231,7 +231,11 @@
if (!TextUtils.isEmpty(id) && !TextUtils.isEmpty(channelName)) {
NotificationChannel channel = new NotificationChannel(id,
channelName, channelImportance);
- channel.populateFromXml(parser);
+ if (forRestore) {
+ channel.populateFromXmlForRestore(parser, mContext);
+ } else {
+ channel.populateFromXml(parser);
+ }
r.channels.put(id, channel);
}
}
@@ -394,7 +398,11 @@
}
for (NotificationChannel channel : r.channels.values()) {
- if (!forBackup || (forBackup && !channel.isDeleted())) {
+ if (forBackup) {
+ if (!channel.isDeleted()) {
+ channel.writeXmlForBackup(out, mContext);
+ }
+ } else {
channel.writeXml(out);
}
}
diff --git a/services/core/java/com/android/server/oemlock/OemLockService.java b/services/core/java/com/android/server/oemlock/OemLockService.java
index 40c6639..5b3d1ec 100644
--- a/services/core/java/com/android/server/oemlock/OemLockService.java
+++ b/services/core/java/com/android/server/oemlock/OemLockService.java
@@ -31,6 +31,7 @@
import android.os.UserManagerInternal;
import android.os.UserManagerInternal.UserRestrictionsListener;
import android.service.oemlock.IOemLockService;
+import android.service.persistentdata.PersistentDataBlockManager;
import android.util.Slog;
import com.android.server.LocalServices;
@@ -98,6 +99,7 @@
!newRestrictions.getBoolean(UserManager.DISALLOW_FACTORY_RESET);
if (!unlockAllowedByAdmin) {
mOemLock.setOemUnlockAllowedByDevice(false);
+ setPersistentDataBlockOemUnlockAllowedBit(false);
}
}
}
@@ -158,6 +160,7 @@
}
mOemLock.setOemUnlockAllowedByDevice(allowedByUser);
+ setPersistentDataBlockOemUnlockAllowedBit(allowedByUser);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -202,6 +205,20 @@
}
};
+ /**
+ * Always synchronize the OemUnlockAllowed bit to the FRP partition, which
+ * is used to erase FRP information on a unlockable device.
+ */
+ private void setPersistentDataBlockOemUnlockAllowedBit(boolean allowed) {
+ final PersistentDataBlockManager pdbm = (PersistentDataBlockManager)
+ mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
+ // if mOemLock is PersistentDataBlockLock, then the bit should have already been set
+ if (pdbm != null && !(mOemLock instanceof PersistentDataBlockLock)) {
+ Slog.i(TAG, "Update OEM Unlock bit in pst partition to " + allowed);
+ pdbm.setOemUnlockEnabled(allowed);
+ }
+ }
+
private boolean isOemUnlockAllowedByAdmin() {
return !UserManager.get(mContext)
.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET, UserHandle.SYSTEM);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0c1d4c1..d7329db 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -344,6 +344,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -397,7 +398,7 @@
static final boolean DEBUG_DOMAIN_VERIFICATION = false;
private static final boolean DEBUG_BACKUP = false;
private static final boolean DEBUG_INSTALL = false;
- private static final boolean DEBUG_REMOVE = false;
+ public static final boolean DEBUG_REMOVE = false;
private static final boolean DEBUG_BROADCASTS = false;
private static final boolean DEBUG_SHOW_INFO = false;
private static final boolean DEBUG_PACKAGE_INFO = false;
@@ -405,7 +406,7 @@
public static final boolean DEBUG_PACKAGE_SCANNING = false;
private static final boolean DEBUG_VERIFY = false;
private static final boolean DEBUG_FILTERS = false;
- private static final boolean DEBUG_PERMISSIONS = false;
+ public static final boolean DEBUG_PERMISSIONS = false;
private static final boolean DEBUG_SHARED_LIBRARIES = false;
private static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE;
@@ -953,9 +954,6 @@
final SparseArray<PackageVerificationState> mPendingVerification
= new SparseArray<PackageVerificationState>();
- /** Set of packages associated with each app op permission. */
- final ArrayMap<String, ArraySet<String>> mAppOpPermissionPackages = new ArrayMap<>();
-
final PackageInstallerService mInstallerService;
private final PackageDexOptimizer mPackageDexOptimizer;
@@ -2880,7 +2878,7 @@
+ mSdkVersion + "; regranting permissions for internal storage");
updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
}
- updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
+ updatePermissionsLocked(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
ver.sdkVersion = mSdkVersion;
// If this is the first boot or an update from pre-M, and it is a normal
@@ -3367,7 +3365,9 @@
@Override
public boolean isUpgrade() {
// allow instant applications
- return mIsUpgrade;
+ // The system property allows testing ota flow when upgraded to the same image.
+ return mIsUpgrade || SystemProperties.getBoolean(
+ "persist.pm.mock-upgrade", false /* default */);
}
private @Nullable String getRequiredButNotReallyRequiredVerifierLPr() {
@@ -5181,7 +5181,7 @@
final PermissionsState permissionsState = settingBase.getPermissionsState();
if (permissionsState.hasPermission(permName, userId)) {
if (isUidInstantApp) {
- if (mPermissionManager.isPermissionInstant(permName)) {
+ if (mSettings.mPermissions.isPermissionInstant(permName)) {
return PackageManager.PERMISSION_GRANTED;
}
} else {
@@ -5250,8 +5250,8 @@
}
}
- boolean addPermission(PermissionInfo info, final boolean async) {
- return mPermissionManager.addPermission(
+ private boolean addDynamicPermission(PermissionInfo info, final boolean async) {
+ return mPermissionManager.addDynamicPermission(
info, async, getCallingUid(), new PermissionCallback() {
@Override
public void onPermissionChanged() {
@@ -5267,20 +5267,20 @@
@Override
public boolean addPermission(PermissionInfo info) {
synchronized (mPackages) {
- return addPermission(info, false);
+ return addDynamicPermission(info, false);
}
}
@Override
public boolean addPermissionAsync(PermissionInfo info) {
synchronized (mPackages) {
- return addPermission(info, true);
+ return addDynamicPermission(info, true);
}
}
@Override
public void removePermission(String permName) {
- mPermissionManager.removePermission(permName, getCallingUid(), mPermissionCallback);
+ mPermissionManager.removeDynamicPermission(permName, getCallingUid(), mPermissionCallback);
}
@Override
@@ -5941,17 +5941,8 @@
}
@Override
- public String[] getAppOpPermissionPackages(String permissionName) {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- return null;
- }
- synchronized (mPackages) {
- ArraySet<String> pkgs = mAppOpPermissionPackages.get(permissionName);
- if (pkgs == null) {
- return null;
- }
- return pkgs.toArray(new String[pkgs.size()]);
- }
+ public String[] getAppOpPermissionPackages(String permName) {
+ return mPermissionManager.getAppOpPermissionPackages(permName);
}
@Override
@@ -9732,7 +9723,8 @@
}
}
- private void addSharedLibraryLPr(ArraySet<String> usesLibraryFiles, SharedLibraryEntry file,
+ private void addSharedLibraryLPr(Set<String> usesLibraryFiles,
+ SharedLibraryEntry file,
PackageParser.Package changingLib) {
if (file.path != null) {
usesLibraryFiles.add(file.path);
@@ -9761,7 +9753,10 @@
if (pkg == null) {
return;
}
- ArraySet<String> usesLibraryFiles = null;
+ // The collection used here must maintain the order of addition (so
+ // that libraries are searched in the correct order) and must have no
+ // duplicates.
+ Set<String> usesLibraryFiles = null;
if (pkg.usesLibraries != null) {
usesLibraryFiles = addSharedLibrariesLPw(pkg.usesLibraries,
null, null, pkg.packageName, changingLib, true,
@@ -9785,10 +9780,10 @@
}
}
- private ArraySet<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries,
+ private Set<String> addSharedLibrariesLPw(@NonNull List<String> requestedLibraries,
@Nullable int[] requiredVersions, @Nullable String[][] requiredCertDigests,
@NonNull String packageName, @Nullable PackageParser.Package changingLib,
- boolean required, int targetSdk, @Nullable ArraySet<String> outUsedLibraries)
+ boolean required, int targetSdk, @Nullable Set<String> outUsedLibraries)
throws PackageManagerException {
final int libCount = requestedLibraries.size();
for (int i = 0; i < libCount; i++) {
@@ -9854,7 +9849,9 @@
}
if (outUsedLibraries == null) {
- outUsedLibraries = new ArraySet<>();
+ // Use LinkedHashSet to preserve the order of files added to
+ // usesLibraryFiles while eliminating duplicates.
+ outUsedLibraries = new LinkedHashSet<>();
}
addSharedLibraryLPr(outUsedLibraries, libEntry, changingLib);
}
@@ -10339,7 +10336,7 @@
Slog.i(TAG, "Adopting permissions from " + origName + " to "
+ pkg.packageName);
// SIDE EFFECTS; updates permissions system state; move elsewhere
- mSettings.transferPermissionsLPw(origName, pkg.packageName);
+ mSettings.mPermissions.transferPermissions(origName, pkg.packageName);
}
}
}
@@ -11187,54 +11184,13 @@
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Permission Groups: " + r);
}
- N = pkg.permissions.size();
- r = null;
- for (i=0; i<N; i++) {
- PackageParser.Permission p = pkg.permissions.get(i);
- // Dont allow ephemeral apps to define new permissions.
- if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
- Slog.w(TAG, "Permission " + p.info.name + " from package "
- + p.info.packageName
- + " ignored: instant apps cannot define new permissions.");
- continue;
- }
-
- // Assume by default that we did not install this permission into the system.
- p.info.flags &= ~PermissionInfo.FLAG_INSTALLED;
-
- // Now that permission groups have a special meaning, we ignore permission
- // groups for legacy apps to prevent unexpected behavior. In particular,
- // permissions for one app being granted to someone just because they happen
- // to be in a group defined by another app (before this had no implications).
- if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
- p.group = mPermissionGroups.get(p.info.group);
- // Warn for a permission in an unknown group.
- if (DEBUG_PERMISSIONS && p.info.group != null && p.group == null) {
- Slog.i(TAG, "Permission " + p.info.name + " from package "
- + p.info.packageName + " in an unknown group " + p.info.group);
- }
- }
-
- // TODO Move to PermissionManager once mPermissionTrees moves there.
-// p.tree ? mSettings.mPermissionTrees
-// : mSettings.mPermissions;
-// final BasePermission bp = BasePermission.createOrUpdate(
-// permissionMap.get(p.info.name), p, pkg, mSettings.mPermissionTrees, chatty);
-// permissionMap.put(p.info.name, bp);
- if (p.tree) {
- final ArrayMap<String, BasePermission> permissionMap =
- mSettings.mPermissionTrees;
- final BasePermission bp = BasePermission.createOrUpdate(
- permissionMap.get(p.info.name), p, pkg, mSettings.mPermissionTrees,
- chatty);
- permissionMap.put(p.info.name, bp);
- } else {
- final BasePermission bp = BasePermission.createOrUpdate(
- (BasePermission) mPermissionManager.getPermissionTEMP(p.info.name),
- p, pkg, mSettings.mPermissionTrees, chatty);
- mPermissionManager.putPermissionTEMP(p.info.name, bp);
- }
+ // Dont allow ephemeral apps to define new permissions.
+ if ((scanFlags & SCAN_AS_INSTANT_APP) != 0) {
+ Slog.w(TAG, "Permissions from package " + pkg.packageName
+ + " ignored: instant apps cannot define new permissions.");
+ } else {
+ mPermissionManager.addAllPermissions(pkg, chatty);
}
N = pkg.instrumentation.size();
@@ -11960,53 +11916,7 @@
if (DEBUG_REMOVE) Log.d(TAG, " Activities: " + r);
}
- N = pkg.permissions.size();
- r = null;
- for (i=0; i<N; i++) {
- PackageParser.Permission p = pkg.permissions.get(i);
- BasePermission bp = (BasePermission) mPermissionManager.getPermissionTEMP(p.info.name);
- if (bp == null) {
- bp = mSettings.mPermissionTrees.get(p.info.name);
- }
- if (bp != null && bp.isPermission(p)) {
- bp.setPermission(null);
- if (DEBUG_REMOVE && chatty) {
- if (r == null) {
- r = new StringBuilder(256);
- } else {
- r.append(' ');
- }
- r.append(p.info.name);
- }
- }
- if ((p.info.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
- ArraySet<String> appOpPkgs = mAppOpPermissionPackages.get(p.info.name);
- if (appOpPkgs != null) {
- appOpPkgs.remove(pkg.packageName);
- }
- }
- }
- if (r != null) {
- if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
- }
-
- N = pkg.requestedPermissions.size();
- r = null;
- for (i=0; i<N; i++) {
- String perm = pkg.requestedPermissions.get(i);
- if (mPermissionManager.isPermissionAppOp(perm)) {
- ArraySet<String> appOpPkgs = mAppOpPermissionPackages.get(perm);
- if (appOpPkgs != null) {
- appOpPkgs.remove(pkg.packageName);
- if (appOpPkgs.isEmpty()) {
- mAppOpPermissionPackages.remove(perm);
- }
- }
- }
- }
- if (r != null) {
- if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
- }
+ mPermissionManager.removeAllPermissions(pkg, chatty);
N = pkg.instrumentation.size();
r = null;
@@ -12067,18 +11977,9 @@
}
}
- private static boolean hasPermission(PackageParser.Package pkgInfo, String perm) {
- for (int i=pkgInfo.permissions.size()-1; i>=0; i--) {
- if (pkgInfo.permissions.get(i).info.name.equals(perm)) {
- return true;
- }
- }
- return false;
- }
-
- static final int UPDATE_PERMISSIONS_ALL = 1<<0;
- static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
- static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
+ public static final int UPDATE_PERMISSIONS_ALL = 1<<0;
+ public static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
+ public static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
private void updatePermissionsLPw(PackageParser.Package pkg, int flags) {
// Update the parent permissions
@@ -12094,10 +11995,10 @@
private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo,
int flags) {
final String volumeUuid = (pkgInfo != null) ? getVolumeUuidForPackage(pkgInfo) : null;
- updatePermissionsLPw(changingPkg, pkgInfo, volumeUuid, flags);
+ updatePermissionsLocked(changingPkg, pkgInfo, volumeUuid, flags);
}
- private void updatePermissionsLPw(String changingPkg,
+ private void updatePermissionsLocked(String changingPkg,
PackageParser.Package pkgInfo, String replaceVolumeUuid, int flags) {
// TODO: Most of the methods exposing BasePermission internals [source package name,
// etc..] shouldn't be needed. Instead, when we've parsed a permission that doesn't
@@ -12109,55 +12010,11 @@
// normal permissions. Today, we need two separate loops because these BasePermission
// objects are stored separately.
// Make sure there are no dangling permission trees.
- Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
- while (it.hasNext()) {
- final BasePermission bp = it.next();
- if (bp.getSourcePackageSetting() == null) {
- // We may not yet have parsed the package, so just see if
- // we still know about its settings.
- bp.setSourcePackageSetting(mSettings.mPackages.get(bp.getSourcePackageName()));
- }
- if (bp.getSourcePackageSetting() == null) {
- Slog.w(TAG, "Removing dangling permission tree: " + bp.getName()
- + " from package " + bp.getSourcePackageName());
- it.remove();
- } else if (changingPkg != null && changingPkg.equals(bp.getSourcePackageName())) {
- if (pkgInfo == null || !hasPermission(pkgInfo, bp.getName())) {
- Slog.i(TAG, "Removing old permission tree: " + bp.getName()
- + " from package " + bp.getSourcePackageName());
- flags |= UPDATE_PERMISSIONS_ALL;
- it.remove();
- }
- }
- }
+ flags = mPermissionManager.updatePermissionTrees(changingPkg, pkgInfo, flags);
// Make sure all dynamic permissions have been assigned to a package,
// and make sure there are no dangling permissions.
- final Iterator<BasePermission> permissionIter =
- mPermissionManager.getPermissionIteratorTEMP();
- while (permissionIter.hasNext()) {
- final BasePermission bp = permissionIter.next();
- if (bp.isDynamic()) {
- bp.updateDynamicPermission(mSettings.mPermissionTrees);
- }
- if (bp.getSourcePackageSetting() == null) {
- // We may not yet have parsed the package, so just see if
- // we still know about its settings.
- bp.setSourcePackageSetting(mSettings.mPackages.get(bp.getSourcePackageName()));
- }
- if (bp.getSourcePackageSetting() == null) {
- Slog.w(TAG, "Removing dangling permission: " + bp.getName()
- + " from package " + bp.getSourcePackageName());
- permissionIter.remove();
- } else if (changingPkg != null && changingPkg.equals(bp.getSourcePackageName())) {
- if (pkgInfo == null || !hasPermission(pkgInfo, bp.getName())) {
- Slog.i(TAG, "Removing old permission: " + bp.getName()
- + " from package " + bp.getSourcePackageName());
- flags |= UPDATE_PERMISSIONS_ALL;
- permissionIter.remove();
- }
- }
- }
+ flags = mPermissionManager.updatePermissions(changingPkg, pkgInfo, flags);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "grantPermissions");
// Now update the permissions for all packages, in particular
@@ -12279,12 +12136,7 @@
// Keep track of app op permissions.
if (bp.isAppOp()) {
- ArraySet<String> pkgs = mAppOpPermissionPackages.get(perm);
- if (pkgs == null) {
- pkgs = new ArraySet<>();
- mAppOpPermissionPackages.put(perm, pkgs);
- }
- pkgs.add(pkg.packageName);
+ mSettings.addAppOpPackage(perm, pkg.packageName);
}
if (bp.isNormal()) {
@@ -17698,7 +17550,13 @@
// TODO: Layering violation
BackgroundDexOptService.notifyPackageChanged(pkg.packageName);
- startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
+ if (!instantApp) {
+ startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
+ } else {
+ if (DEBUG_DOMAIN_VERIFICATION) {
+ Slog.d(TAG, "Not verifying instant app install for app links: " + pkgName);
+ }
+ }
try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
"installPackageLI")) {
@@ -21209,11 +21067,9 @@
}
sUserManager.systemReady();
- synchronized(mPackages) {
- // If we upgraded grant all default permissions before kicking off.
- for (int userId : grantPermissionsUserIds) {
- mDefaultPermissionPolicy.grantDefaultPermissions(mPackages.values(), userId);
- }
+ // If we upgraded grant all default permissions before kicking off.
+ for (int userId : grantPermissionsUserIds) {
+ mDefaultPermissionPolicy.grantDefaultPermissions(mPackages.values(), userId);
}
if (grantPermissionsUserIds == EMPTY_INT_ARRAY) {
@@ -21227,7 +21083,7 @@
// permissions, ensure permissions are updated. Beware of dragons if you
// try optimizing this.
synchronized (mPackages) {
- updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL,
+ updatePermissionsLocked(null, null, StorageManager.UUID_PRIVATE_INTERNAL,
UPDATE_PERMISSIONS_ALL);
}
@@ -21278,9 +21134,8 @@
reconcileApps(StorageManager.UUID_PRIVATE_INTERNAL);
if (mPrivappPermissionsViolations != null) {
- Slog.wtf(TAG,"Signature|privileged permissions not in "
+ throw new IllegalStateException("Signature|privileged permissions not in "
+ "privapp-permissions whitelist: " + mPrivappPermissionsViolations);
- mPrivappPermissionsViolations = null;
}
}
@@ -21773,22 +21628,6 @@
if (!checkin && dumpState.isDumping(DumpState.DUMP_PERMISSIONS)) {
mSettings.dumpPermissionsLPr(pw, packageName, permissionNames, dumpState);
- if (packageName == null && permissionNames == null) {
- for (int iperm=0; iperm<mAppOpPermissionPackages.size(); iperm++) {
- if (iperm == 0) {
- if (dumpState.onTitlePrinted())
- pw.println();
- pw.println("AppOp Permissions:");
- }
- pw.print(" AppOp Permission ");
- pw.print(mAppOpPermissionPackages.keyAt(iperm));
- pw.println(":");
- ArraySet<String> pkgs = mAppOpPermissionPackages.valueAt(iperm);
- for (int ipkg=0; ipkg<pkgs.size(); ipkg++) {
- pw.print(" "); pw.println(pkgs.valueAt(ipkg));
- }
- }
- }
}
if (!checkin && dumpState.isDumping(DumpState.DUMP_PROVIDERS)) {
@@ -22292,7 +22131,7 @@
+ mSdkVersion + "; regranting permissions for " + volumeUuid);
updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL;
}
- updatePermissionsLPw(null, null, volumeUuid, updateFlags);
+ updatePermissionsLocked(null, null, volumeUuid, updateFlags);
// Yay, everything is now upgraded
ver.forceCurrent();
@@ -23300,15 +23139,15 @@
void onNewUserCreated(final int userId) {
synchronized(mPackages) {
mDefaultPermissionPolicy.grantDefaultPermissions(mPackages.values(), userId);
- }
- // If permission review for legacy apps is required, we represent
- // dagerous permissions for such apps as always granted runtime
- // permissions to keep per user flag state whether review is needed.
- // Hence, if a new user is added we have to propagate dangerous
- // permission grants for these legacy apps.
- if (mPermissionReviewRequired) {
- updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
- | UPDATE_PERMISSIONS_REPLACE_ALL);
+ // If permission review for legacy apps is required, we represent
+ // dagerous permissions for such apps as always granted runtime
+ // permissions to keep per user flag state whether review is needed.
+ // Hence, if a new user is added we have to propagate dangerous
+ // permission grants for these legacy apps.
+ if (mPermissionReviewRequired) {
+ updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
+ | UPDATE_PERMISSIONS_REPLACE_ALL);
+ }
}
}
@@ -23752,12 +23591,12 @@
}
@Override
- public Object enforcePermissionTreeTEMP(String permName, int callingUid) {
+ public PackageParser.PermissionGroup getPermissionGroupTEMP(String groupName) {
synchronized (mPackages) {
- return BasePermission.enforcePermissionTreeLP(
- mSettings.mPermissionTrees, permName, callingUid);
+ return mPermissionGroups.get(groupName);
}
}
+
@Override
public boolean isInstantApp(String packageName, int userId) {
return PackageManagerService.this.isInstantApp(packageName, userId);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 0084411..56595c9 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -378,10 +378,6 @@
private final ArrayMap<Long, Integer> mKeySetRefs =
new ArrayMap<Long, Integer>();
- // Mapping from permission tree names to info about them.
- final ArrayMap<String, BasePermission> mPermissionTrees =
- new ArrayMap<String, BasePermission>();
-
// Packages that have been uninstalled and still need their external
// storage data deleted.
final ArrayList<PackageCleanItem> mPackagesToBeCleaned = new ArrayList<PackageCleanItem>();
@@ -416,7 +412,7 @@
public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages);
/** Settings and other information about permissions */
- private final PermissionSettings mPermissions;
+ final PermissionSettings mPermissions;
Settings(PermissionSettings permissions, Object lock) {
this(Environment.getDataDirectory(), permissions, lock);
@@ -622,6 +618,10 @@
return null;
}
+ void addAppOpPackage(String permName, String packageName) {
+ mPermissions.addAppOpPackage(permName, packageName);
+ }
+
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
SharedUserSetting s = mSharedUsers.get(name);
if (s != null) {
@@ -666,13 +666,6 @@
}
/**
- * Transfers ownership of permissions from one package to another.
- */
- void transferPermissionsLPw(String origPackageName, String newPackageName) {
- mPermissions.transferPermissions(origPackageName, newPackageName, mPermissionTrees);
- }
-
- /**
* Creates a new {@code PackageSetting} object.
* Use this method instead of the constructor to ensure a settings object is created
* with the correct base.
@@ -2496,9 +2489,7 @@
}
serializer.startTag(null, "permission-trees");
- for (BasePermission bp : mPermissionTrees.values()) {
- writePermissionLPr(serializer, bp);
- }
+ mPermissions.writePermissionTrees(serializer);
serializer.endTag(null, "permission-trees");
serializer.startTag(null, "permissions");
@@ -3042,7 +3033,7 @@
} else if (tagName.equals("permissions")) {
mPermissions.readPermissions(parser);
} else if (tagName.equals("permission-trees")) {
- PermissionSettings.readPermissions(mPermissionTrees, parser);
+ mPermissions.readPermissionTrees(parser);
} else if (tagName.equals("shared-user")) {
readSharedUserLPw(parser);
} else if (tagName.equals("preferred-packages")) {
diff --git a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
index 4f5d156..815f885 100644
--- a/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
+++ b/services/core/java/com/android/server/pm/ShortcutBitmapSaver.java
@@ -21,6 +21,8 @@
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.drawable.Icon;
+import android.os.StrictMode;
+import android.os.StrictMode.ThreadPolicy;
import android.os.SystemClock;
import android.util.Log;
import android.util.Slog;
@@ -165,7 +167,13 @@
// Compress it and enqueue to the requests.
final byte[] bytes;
+ final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
try {
+ // compress() triggers a slow call, but in this case it's needed to save RAM and also
+ // the target bitmap is of an icon size, so let's just permit it.
+ StrictMode.setThreadPolicy(new ThreadPolicy.Builder(oldPolicy)
+ .permitCustomSlowCalls()
+ .build());
final Bitmap shrunk = mService.shrinkBitmap(original, maxDimension);
try {
try (final ByteArrayOutputStream out = new ByteArrayOutputStream(64 * 1024)) {
@@ -184,6 +192,8 @@
} catch (IOException | RuntimeException | OutOfMemoryError e) {
Slog.wtf(ShortcutService.TAG, "Unable to write bitmap to file", e);
return;
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
}
shortcut.addFlags(
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index a6b05d7..c18a71d 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -96,6 +96,7 @@
UserManager.DISALLOW_SMS,
UserManager.DISALLOW_FUN,
UserManager.DISALLOW_CREATE_WINDOWS,
+ UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE,
UserManager.DISALLOW_OUTGOING_BEAM,
UserManager.DISALLOW_WALLPAPER,
@@ -156,6 +157,7 @@
private static final Set<String> GLOBAL_RESTRICTIONS = Sets.newArraySet(
UserManager.DISALLOW_ADJUST_VOLUME,
UserManager.DISALLOW_BLUETOOTH_SHARING,
+ UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
UserManager.DISALLOW_RUN_IN_BACKGROUND,
UserManager.DISALLOW_UNMUTE_MICROPHONE,
UserManager.DISALLOW_UNMUTE_DEVICE
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 09a6e9c..71d3202 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -48,6 +48,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -77,7 +78,7 @@
final String name;
- @PermissionType final int type;
+ final @PermissionType int type;
String sourcePackageName;
@@ -252,12 +253,12 @@
return changed;
}
- public void updateDynamicPermission(Map<String, BasePermission> permissionTrees) {
+ public void updateDynamicPermission(Collection<BasePermission> permissionTrees) {
if (PackageManagerService.DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
+ getName() + " pkg=" + getSourcePackageName()
+ " info=" + pendingPermissionInfo);
if (sourcePackageSetting == null && pendingPermissionInfo != null) {
- final BasePermission tree = findPermissionTreeLP(permissionTrees, name);
+ final BasePermission tree = findPermissionTree(permissionTrees, name);
if (tree != null && tree.perm != null) {
sourcePackageSetting = tree.sourcePackageSetting;
perm = new PackageParser.Permission(tree.perm.owner,
@@ -269,8 +270,8 @@
}
}
- public static BasePermission createOrUpdate(@Nullable BasePermission bp, @NonNull Permission p,
- @NonNull PackageParser.Package pkg, Map<String, BasePermission> permissionTrees,
+ static BasePermission createOrUpdate(@Nullable BasePermission bp, @NonNull Permission p,
+ @NonNull PackageParser.Package pkg, Collection<BasePermission> permissionTrees,
boolean chatty) {
final PackageSettingBase pkgSetting = (PackageSettingBase) pkg.mExtras;
// Allow system apps to redefine non-system permissions
@@ -300,7 +301,7 @@
if (bp.perm == null) {
if (bp.sourcePackageName == null
|| bp.sourcePackageName.equals(p.info.packageName)) {
- final BasePermission tree = findPermissionTreeLP(permissionTrees, p.info.name);
+ final BasePermission tree = findPermissionTree(permissionTrees, p.info.name);
if (tree == null
|| tree.sourcePackageName.equals(p.info.packageName)) {
bp.sourcePackageSetting = pkgSetting;
@@ -345,12 +346,12 @@
return bp;
}
- public static BasePermission enforcePermissionTreeLP(
- Map<String, BasePermission> permissionTrees, String permName, int callingUid) {
+ static BasePermission enforcePermissionTree(
+ Collection<BasePermission> permissionTrees, String permName, int callingUid) {
if (permName != null) {
- BasePermission bp = findPermissionTreeLP(permissionTrees, permName);
+ BasePermission bp = findPermissionTree(permissionTrees, permName);
if (bp != null) {
- if (bp.uid == UserHandle.getAppId(callingUid)) {//UserHandle.getAppId(Binder.getCallingUid())) {
+ if (bp.uid == UserHandle.getAppId(callingUid)) {
return bp;
}
throw new SecurityException("Calling uid " + callingUid
@@ -373,9 +374,9 @@
}
}
- private static BasePermission findPermissionTreeLP(
- Map<String, BasePermission> permissionTrees, String permName) {
- for (BasePermission bp : permissionTrees.values()) {
+ private static BasePermission findPermissionTree(
+ Collection<BasePermission> permissionTrees, String permName) {
+ for (BasePermission bp : permissionTrees) {
if (permName.startsWith(bp.name) &&
permName.length() > bp.name.length() &&
permName.charAt(bp.name.length()) == '.') {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
index 3b20b42..8aac52a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerInternal.java
@@ -31,6 +31,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Internal interfaces to be used by other components within the system server.
@@ -81,11 +82,26 @@
@NonNull int[] allUserIds);
- public abstract boolean addPermission(@NonNull PermissionInfo info, boolean async,
+ /**
+ * Add all permissions in the given package.
+ * <p>
+ * NOTE: argument {@code groupTEMP} is temporary until mPermissionGroups is moved to
+ * the permission settings.
+ */
+ public abstract void addAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
+ public abstract void removeAllPermissions(@NonNull PackageParser.Package pkg, boolean chatty);
+ public abstract boolean addDynamicPermission(@NonNull PermissionInfo info, boolean async,
int callingUid, @Nullable PermissionCallback callback);
- public abstract void removePermission(@NonNull String permName, int callingUid,
+ public abstract void removeDynamicPermission(@NonNull String permName, int callingUid,
@Nullable PermissionCallback callback);
+ public abstract int updatePermissions(@Nullable String changingPkg,
+ @Nullable PackageParser.Package pkgInfo, int flags);
+ public abstract int updatePermissionTrees(@Nullable String changingPkg,
+ @Nullable PackageParser.Package pkgInfo, int flags);
+
+ public abstract @Nullable String[] getAppOpPermissionPackages(@NonNull String permName);
+
public abstract int getPermissionFlags(@NonNull String permName,
@NonNull String packageName, int callingUid, int userId);
/**
@@ -98,8 +114,6 @@
*/
public abstract @Nullable List<PermissionInfo> getPermissionInfoByGroup(@NonNull String group,
@PermissionInfoFlags int flags, int callingUid);
- public abstract boolean isPermissionAppOp(@NonNull String permName);
- public abstract boolean isPermissionInstant(@NonNull String permName);
/**
* Updates the flags associated with a permission by replacing the flags in
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 6c031a6..062aa33 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -69,6 +69,7 @@
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
/**
* Manages all permissions and handles permissions related tasks.
@@ -260,7 +261,7 @@
// }
final ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
- for (BasePermission bp : mSettings.getAllPermissionsLocked()) {
+ for (BasePermission bp : mSettings.mPermissions.values()) {
final PermissionInfo pi = bp.generatePermissionInfo(groupName, flags);
if (pi != null) {
out.add(pi);
@@ -305,7 +306,98 @@
return protectionLevel;
}
- private boolean addPermission(
+ private void addAllPermissions(PackageParser.Package pkg, boolean chatty) {
+ final int N = pkg.permissions.size();
+ for (int i=0; i<N; i++) {
+ PackageParser.Permission p = pkg.permissions.get(i);
+
+ // Assume by default that we did not install this permission into the system.
+ p.info.flags &= ~PermissionInfo.FLAG_INSTALLED;
+
+ // Now that permission groups have a special meaning, we ignore permission
+ // groups for legacy apps to prevent unexpected behavior. In particular,
+ // permissions for one app being granted to someone just because they happen
+ // to be in a group defined by another app (before this had no implications).
+ if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
+ p.group = mPackageManagerInt.getPermissionGroupTEMP(p.info.group);
+ // Warn for a permission in an unknown group.
+ if (PackageManagerService.DEBUG_PERMISSIONS
+ && p.info.group != null && p.group == null) {
+ Slog.i(TAG, "Permission " + p.info.name + " from package "
+ + p.info.packageName + " in an unknown group " + p.info.group);
+ }
+ }
+
+ synchronized (PermissionManagerService.this.mLock) {
+ if (p.tree) {
+ final BasePermission bp = BasePermission.createOrUpdate(
+ mSettings.getPermissionTreeLocked(p.info.name), p, pkg,
+ mSettings.getAllPermissionTreesLocked(), chatty);
+ mSettings.putPermissionTreeLocked(p.info.name, bp);
+ } else {
+ final BasePermission bp = BasePermission.createOrUpdate(
+ mSettings.getPermissionLocked(p.info.name),
+ p, pkg, mSettings.getAllPermissionTreesLocked(), chatty);
+ mSettings.putPermissionLocked(p.info.name, bp);
+ }
+ }
+ }
+ }
+
+ private void removeAllPermissions(PackageParser.Package pkg, boolean chatty) {
+ synchronized (mLock) {
+ int N = pkg.permissions.size();
+ StringBuilder r = null;
+ for (int i=0; i<N; i++) {
+ PackageParser.Permission p = pkg.permissions.get(i);
+ BasePermission bp = (BasePermission) mSettings.mPermissions.get(p.info.name);
+ if (bp == null) {
+ bp = mSettings.mPermissionTrees.get(p.info.name);
+ }
+ if (bp != null && bp.isPermission(p)) {
+ bp.setPermission(null);
+ if (PackageManagerService.DEBUG_REMOVE && chatty) {
+ if (r == null) {
+ r = new StringBuilder(256);
+ } else {
+ r.append(' ');
+ }
+ r.append(p.info.name);
+ }
+ }
+ if (p.isAppOp()) {
+ ArraySet<String> appOpPkgs =
+ mSettings.mAppOpPermissionPackages.get(p.info.name);
+ if (appOpPkgs != null) {
+ appOpPkgs.remove(pkg.packageName);
+ }
+ }
+ }
+ if (r != null) {
+ if (PackageManagerService.DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
+ }
+
+ N = pkg.requestedPermissions.size();
+ r = null;
+ for (int i=0; i<N; i++) {
+ String perm = pkg.requestedPermissions.get(i);
+ if (mSettings.isPermissionAppOp(perm)) {
+ ArraySet<String> appOpPkgs = mSettings.mAppOpPermissionPackages.get(perm);
+ if (appOpPkgs != null) {
+ appOpPkgs.remove(pkg.packageName);
+ if (appOpPkgs.isEmpty()) {
+ mSettings.mAppOpPermissionPackages.remove(perm);
+ }
+ }
+ }
+ }
+ if (r != null) {
+ if (PackageManagerService.DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
+ }
+ }
+ }
+
+ private boolean addDynamicPermission(
PermissionInfo info, int callingUid, PermissionCallback callback) {
if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
throw new SecurityException("Instant apps can't add permissions");
@@ -313,8 +405,7 @@
if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
throw new SecurityException("Label must be specified in permission");
}
- final BasePermission tree = (BasePermission) mPackageManagerInt.enforcePermissionTreeTEMP(
- info.name, callingUid);
+ final BasePermission tree = mSettings.enforcePermissionTree(info.name, callingUid);
final boolean added;
final boolean changed;
synchronized (mLock) {
@@ -341,13 +432,12 @@
return added;
}
- private void removePermission(
+ private void removeDynamicPermission(
String permName, int callingUid, PermissionCallback callback) {
if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
throw new SecurityException("Instant applications don't have access to this method");
}
- final BasePermission tree = (BasePermission) mPackageManagerInt.enforcePermissionTreeTEMP(
- permName, callingUid);
+ final BasePermission tree = mSettings.enforcePermissionTree(permName, callingUid);
synchronized (mLock) {
final BasePermission bp = mSettings.getPermissionLocked(permName);
if (bp == null) {
@@ -713,7 +803,21 @@
return runtimePermissionChangedUserIds;
}
- private int getPermissionFlags(String permName, String packageName, int callingUid, int userId) {
+ private String[] getAppOpPermissionPackages(String permName) {
+ if (mPackageManagerInt.getInstantAppPackageName(Binder.getCallingUid()) != null) {
+ return null;
+ }
+ synchronized (mLock) {
+ final ArraySet<String> pkgs = mSettings.mAppOpPermissionPackages.get(permName);
+ if (pkgs == null) {
+ return null;
+ }
+ return pkgs.toArray(new String[pkgs.size()]);
+ }
+ }
+
+ private int getPermissionFlags(
+ String permName, String packageName, int callingUid, int userId) {
if (!mUserManagerInt.exists(userId)) {
return 0;
}
@@ -741,6 +845,96 @@
return permissionsState.getPermissionFlags(permName, userId);
}
+ private int updatePermissions(String packageName, PackageParser.Package pkgInfo, int flags) {
+ Set<BasePermission> needsUpdate = null;
+ synchronized (mLock) {
+ final Iterator<BasePermission> it = mSettings.mPermissions.values().iterator();
+ while (it.hasNext()) {
+ final BasePermission bp = it.next();
+ if (bp.isDynamic()) {
+ bp.updateDynamicPermission(mSettings.mPermissionTrees.values());
+ }
+ if (bp.getSourcePackageSetting() != null) {
+ if (packageName != null && packageName.equals(bp.getSourcePackageName())
+ && (pkgInfo == null || !hasPermission(pkgInfo, bp.getName()))) {
+ Slog.i(TAG, "Removing old permission tree: " + bp.getName()
+ + " from package " + bp.getSourcePackageName());
+ flags |= PackageManagerService.UPDATE_PERMISSIONS_ALL;
+ it.remove();
+ }
+ continue;
+ }
+ if (needsUpdate == null) {
+ needsUpdate = new ArraySet<>(mSettings.mPermissions.size());
+ }
+ needsUpdate.add(bp);
+ }
+ }
+ if (needsUpdate != null) {
+ for (final BasePermission bp : needsUpdate) {
+ final PackageParser.Package pkg =
+ mPackageManagerInt.getPackage(bp.getSourcePackageName());
+ synchronized (mLock) {
+ if (pkg != null && pkg.mExtras != null) {
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (bp.getSourcePackageSetting() == null) {
+ bp.setSourcePackageSetting(ps);
+ }
+ continue;
+ }
+ Slog.w(TAG, "Removing dangling permission: " + bp.getName()
+ + " from package " + bp.getSourcePackageName());
+ mSettings.removePermissionLocked(bp.getName());
+ }
+ }
+ }
+ return flags;
+ }
+
+ private int updatePermissionTrees(String packageName, PackageParser.Package pkgInfo,
+ int flags) {
+ Set<BasePermission> needsUpdate = null;
+ synchronized (mLock) {
+ final Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
+ while (it.hasNext()) {
+ final BasePermission bp = it.next();
+ if (bp.getSourcePackageSetting() != null) {
+ if (packageName != null && packageName.equals(bp.getSourcePackageName())
+ && (pkgInfo == null || !hasPermission(pkgInfo, bp.getName()))) {
+ Slog.i(TAG, "Removing old permission tree: " + bp.getName()
+ + " from package " + bp.getSourcePackageName());
+ flags |= PackageManagerService.UPDATE_PERMISSIONS_ALL;
+ it.remove();
+ }
+ continue;
+ }
+ if (needsUpdate == null) {
+ needsUpdate = new ArraySet<>(mSettings.mPermissionTrees.size());
+ }
+ needsUpdate.add(bp);
+ }
+ }
+ if (needsUpdate != null) {
+ for (final BasePermission bp : needsUpdate) {
+ final PackageParser.Package pkg =
+ mPackageManagerInt.getPackage(bp.getSourcePackageName());
+ synchronized (mLock) {
+ if (pkg != null && pkg.mExtras != null) {
+ final PackageSetting ps = (PackageSetting) pkg.mExtras;
+ if (bp.getSourcePackageSetting() == null) {
+ bp.setSourcePackageSetting(ps);
+ }
+ continue;
+ }
+ Slog.w(TAG, "Removing dangling permission tree: " + bp.getName()
+ + " from package " + bp.getSourcePackageName());
+ mSettings.removePermissionLocked(bp.getName());
+ }
+ }
+ }
+ return flags;
+ }
+
private void updatePermissionFlags(String permName, String packageName, int flagMask,
int flagValues, int callingUid, int userId, PermissionCallback callback) {
if (!mUserManagerInt.exists(userId)) {
@@ -872,7 +1066,7 @@
private int calculateCurrentPermissionFootprintLocked(BasePermission tree) {
int size = 0;
- for (BasePermission perm : mSettings.getAllPermissionsLocked()) {
+ for (BasePermission perm : mSettings.mPermissions.values()) {
size += tree.calculateFootprint(perm);
}
return size;
@@ -889,6 +1083,15 @@
}
}
+ private static boolean hasPermission(PackageParser.Package pkgInfo, String permName) {
+ for (int i=pkgInfo.permissions.size()-1; i>=0; i--) {
+ if (pkgInfo.permissions.get(i).info.name.equals(permName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Get the first event id for the permission.
*
@@ -951,14 +1154,22 @@
private class PermissionManagerInternalImpl extends PermissionManagerInternal {
@Override
- public boolean addPermission(PermissionInfo info, boolean async, int callingUid,
- PermissionCallback callback) {
- return PermissionManagerService.this.addPermission(info, callingUid, callback);
+ public void addAllPermissions(Package pkg, boolean chatty) {
+ PermissionManagerService.this.addAllPermissions(pkg, chatty);
}
@Override
- public void removePermission(String permName, int callingUid,
+ public void removeAllPermissions(Package pkg, boolean chatty) {
+ PermissionManagerService.this.removeAllPermissions(pkg, chatty);
+ }
+ @Override
+ public boolean addDynamicPermission(PermissionInfo info, boolean async, int callingUid,
PermissionCallback callback) {
- PermissionManagerService.this.removePermission(permName, callingUid, callback);
+ return PermissionManagerService.this.addDynamicPermission(info, callingUid, callback);
+ }
+ @Override
+ public void removeDynamicPermission(String permName, int callingUid,
+ PermissionCallback callback) {
+ PermissionManagerService.this.removeDynamicPermission(permName, callingUid, callback);
}
@Override
public void grantRuntimePermission(String permName, String packageName,
@@ -993,12 +1204,26 @@
(SharedUserSetting) suSetting, allUserIds);
}
@Override
+ public String[] getAppOpPermissionPackages(String permName) {
+ return PermissionManagerService.this.getAppOpPermissionPackages(permName);
+ }
+ @Override
public int getPermissionFlags(String permName, String packageName, int callingUid,
int userId) {
return PermissionManagerService.this.getPermissionFlags(permName, packageName,
callingUid, userId);
}
@Override
+ public int updatePermissions(String packageName,
+ PackageParser.Package pkgInfo, int flags) {
+ return PermissionManagerService.this.updatePermissions(packageName, pkgInfo, flags);
+ }
+ @Override
+ public int updatePermissionTrees(String packageName,
+ PackageParser.Package pkgInfo, int flags) {
+ return PermissionManagerService.this.updatePermissionTrees(packageName, pkgInfo, flags);
+ }
+ @Override
public void updatePermissionFlags(String permName, String packageName, int flagMask,
int flagValues, int callingUid, int userId, PermissionCallback callback) {
PermissionManagerService.this.updatePermissionFlags(
@@ -1038,20 +1263,6 @@
return PermissionManagerService.this.getPermissionInfoByGroup(group, flags, callingUid);
}
@Override
- public boolean isPermissionInstant(String permName) {
- synchronized (PermissionManagerService.this.mLock) {
- final BasePermission bp = mSettings.getPermissionLocked(permName);
- return (bp != null && bp.isInstant());
- }
- }
- @Override
- public boolean isPermissionAppOp(String permName) {
- synchronized (PermissionManagerService.this.mLock) {
- final BasePermission bp = mSettings.getPermissionLocked(permName);
- return (bp != null && bp.isAppOp());
- }
- }
- @Override
public PermissionSettings getPermissionSettings() {
return mSettings;
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionSettings.java b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
index 7a2e5ecc..7d125c9 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionSettings.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionSettings.java
@@ -24,6 +24,7 @@
import android.util.Log;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.XmlUtils;
import com.android.server.pm.DumpState;
import com.android.server.pm.PackageManagerService;
@@ -35,6 +36,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
+import java.util.Set;
/**
* Permissions and other related data. This class is not meant for
@@ -49,8 +51,25 @@
* All of the permissions known to the system. The mapping is from permission
* name to permission object.
*/
- private final ArrayMap<String, BasePermission> mPermissions =
+ @GuardedBy("mLock")
+ final ArrayMap<String, BasePermission> mPermissions =
new ArrayMap<String, BasePermission>();
+
+ /**
+ * All permission trees known to the system. The mapping is from permission tree
+ * name to permission object.
+ */
+ @GuardedBy("mLock")
+ final ArrayMap<String, BasePermission> mPermissionTrees =
+ new ArrayMap<String, BasePermission>();
+
+ /**
+ * Set of packages that request a particular app op. The mapping is from permission
+ * name to package names.
+ */
+ @GuardedBy("mLock")
+ final ArrayMap<String, ArraySet<String>> mAppOpPermissionPackages = new ArrayMap<>();
+
private final Object mLock;
PermissionSettings(@NonNull Context context, @NonNull Object lock) {
@@ -65,15 +84,23 @@
}
}
+ public void addAppOpPackage(String permName, String packageName) {
+ ArraySet<String> pkgs = mAppOpPermissionPackages.get(permName);
+ if (pkgs == null) {
+ pkgs = new ArraySet<>();
+ mAppOpPermissionPackages.put(permName, pkgs);
+ }
+ pkgs.add(packageName);
+ }
+
/**
* Transfers ownership of permissions from one package to another.
*/
- public void transferPermissions(String origPackageName, String newPackageName,
- ArrayMap<String, BasePermission> permissionTrees) {
+ public void transferPermissions(String origPackageName, String newPackageName) {
synchronized (mLock) {
for (int i=0; i<2; i++) {
ArrayMap<String, BasePermission> permissions =
- i == 0 ? permissionTrees : mPermissions;
+ i == 0 ? mPermissionTrees : mPermissions;
for (BasePermission bp : permissions.values()) {
bp.transfer(origPackageName, newPackageName);
}
@@ -94,9 +121,26 @@
}
}
+ public void readPermissionTrees(XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ synchronized (mLock) {
+ readPermissions(mPermissionTrees, parser);
+ }
+ }
+
public void writePermissions(XmlSerializer serializer) throws IOException {
- for (BasePermission bp : mPermissions.values()) {
- bp.writeLPr(serializer);
+ synchronized (mLock) {
+ for (BasePermission bp : mPermissions.values()) {
+ bp.writeLPr(serializer);
+ }
+ }
+ }
+
+ public void writePermissionTrees(XmlSerializer serializer) throws IOException {
+ synchronized (mLock) {
+ for (BasePermission bp : mPermissionTrees.values()) {
+ bp.writeLPr(serializer);
+ }
}
}
@@ -128,6 +172,22 @@
printedSomething = bp.dumpPermissionsLPr(pw, packageName, permissionNames,
externalStorageEnforced, printedSomething, dumpState);
}
+ if (packageName == null && permissionNames == null) {
+ for (int iperm = 0; iperm<mAppOpPermissionPackages.size(); iperm++) {
+ if (iperm == 0) {
+ if (dumpState.onTitlePrinted())
+ pw.println();
+ pw.println("AppOp Permissions:");
+ }
+ pw.print(" AppOp Permission ");
+ pw.print(mAppOpPermissionPackages.keyAt(iperm));
+ pw.println(":");
+ ArraySet<String> pkgs = mAppOpPermissionPackages.valueAt(iperm);
+ for (int ipkg=0; ipkg<pkgs.size(); ipkg++) {
+ pw.print(" "); pw.println(pkgs.valueAt(ipkg));
+ }
+ }
+ }
}
}
@@ -135,15 +195,58 @@
return mPermissions.get(permName);
}
+ @Nullable BasePermission getPermissionTreeLocked(@NonNull String permName) {
+ return mPermissionTrees.get(permName);
+ }
+
void putPermissionLocked(@NonNull String permName, @NonNull BasePermission permission) {
mPermissions.put(permName, permission);
}
+ void putPermissionTreeLocked(@NonNull String permName, @NonNull BasePermission permission) {
+ mPermissionTrees.put(permName, permission);
+ }
+
void removePermissionLocked(@NonNull String permName) {
mPermissions.remove(permName);
}
- Collection<BasePermission> getAllPermissionsLocked() {
+ void removePermissionTreeLocked(@NonNull String permName) {
+ mPermissionTrees.remove(permName);
+ }
+
+ @NonNull Collection<BasePermission> getAllPermissionsLocked() {
return mPermissions.values();
}
+
+ @NonNull Collection<BasePermission> getAllPermissionTreesLocked() {
+ return mPermissionTrees.values();
+ }
+
+ /**
+ * Returns the permission tree for the given permission.
+ * @throws SecurityException If the calling UID is not allowed to add permissions to the
+ * found permission tree.
+ */
+ @Nullable BasePermission enforcePermissionTree(@NonNull String permName, int callingUid) {
+ synchronized (mLock) {
+ return BasePermission.enforcePermissionTree(
+ mPermissionTrees.values(), permName, callingUid);
+ }
+ }
+
+ public boolean isPermissionInstant(String permName) {
+ synchronized (mLock) {
+ final BasePermission bp = mPermissions.get(permName);
+ return (bp != null && bp.isInstant());
+ }
+ }
+
+ boolean isPermissionAppOp(String permName) {
+ synchronized (mLock) {
+ final BasePermission bp = mPermissions.get(permName);
+ return (bp != null && bp.isAppOp());
+ }
+ }
+
}
diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java
index 342ec4b..7a2e630 100644
--- a/services/core/java/com/android/server/policy/GlobalActions.java
+++ b/services/core/java/com/android/server/policy/GlobalActions.java
@@ -58,6 +58,9 @@
public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) {
if (DEBUG) Slog.d(TAG, "showDialog " + keyguardShowing + " " + deviceProvisioned);
+ if (mStatusBarInternal.isGlobalActionsDisabled()) {
+ return;
+ }
mKeyguardShowing = keyguardShowing;
mDeviceProvisioned = deviceProvisioned;
mShowing = true;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index db7817e..d8e8fd3 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -18,13 +18,14 @@
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.AppOpsManager.OP_TOAST_WINDOW;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Context.CONTEXT_RESTRICTED;
import static android.content.Context.DISPLAY_SERVICE;
@@ -2270,7 +2271,11 @@
// Only force the default orientation if the screen is xlarge, at least 960dp x 720dp, per
// http://developer.android.com/guide/practices/screens_support.html#range
- mForceDefaultOrientation = longSizeDp >= 960 && shortSizeDp >= 720 &&
+ // For car, ignore the dp limitation. It's physically impossible to rotate the car's screen
+ // so if the orientation is forced, we need to respect that no matter what.
+ boolean isCar = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE);
+ mForceDefaultOrientation = ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar) &&
res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation) &&
// For debug purposes the next line turns this feature off with:
// $ adb shell setprop config.override_forced_orient true
@@ -2844,7 +2849,7 @@
boolean keyguardLocked = isKeyguardLocked();
boolean hideDockDivider = attrs.type == TYPE_DOCK_DIVIDER
- && !mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID);
+ && !mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
return (keyguardLocked && !allowWhenLocked && win.getDisplayId() == DEFAULT_DISPLAY)
|| hideDockDivider;
}
@@ -5445,7 +5450,10 @@
boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
&& attrs.type < FIRST_SYSTEM_WINDOW;
- final int stackId = win.getStackId();
+ final int windowingMode = win.getWindowingMode();
+ final boolean inFullScreenOrSplitScreenSecondaryWindowingMode =
+ windowingMode == WINDOWING_MODE_FULLSCREEN
+ || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
if (mTopFullscreenOpaqueWindowState == null && affectsSystemUi) {
if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
mForceStatusBar = true;
@@ -5464,7 +5472,7 @@
// represent should be hidden or if we should hide the lockscreen. For attached app
// windows we defer the decision to the window it is attached to.
if (appWindow && attached == null) {
- if (attrs.isFullscreen() && StackId.normallyFullscreenWindows(stackId)) {
+ if (attrs.isFullscreen() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
if (DEBUG_LAYOUT) Slog.v(TAG, "Fullscreen window: " + win);
mTopFullscreenOpaqueWindowState = win;
if (mTopFullscreenOpaqueOrDimmingWindowState == null) {
@@ -5495,7 +5503,7 @@
// Keep track of the window if it's dimming but not necessarily fullscreen.
if (mTopFullscreenOpaqueOrDimmingWindowState == null && affectsSystemUi
- && win.isDimming() && StackId.normallyFullscreenWindows(stackId)) {
+ && win.isDimming() && inFullScreenOrSplitScreenSecondaryWindowingMode) {
mTopFullscreenOpaqueOrDimmingWindowState = win;
}
@@ -5503,7 +5511,7 @@
// separately, because both the "real fullscreen" opaque window and the one for the docked
// stack can control View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
if (mTopDockedOpaqueWindowState == null && affectsSystemUi && appWindow && attached == null
- && attrs.isFullscreen() && stackId == DOCKED_STACK_ID) {
+ && attrs.isFullscreen() && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
mTopDockedOpaqueWindowState = win;
if (mTopDockedOpaqueOrDimmingWindowState == null) {
mTopDockedOpaqueOrDimmingWindowState = win;
@@ -5513,7 +5521,7 @@
// Also keep track of any windows that are dimming but not necessarily fullscreen in the
// docked stack.
if (mTopDockedOpaqueOrDimmingWindowState == null && affectsSystemUi && win.isDimming()
- && stackId == DOCKED_STACK_ID) {
+ && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
mTopDockedOpaqueOrDimmingWindowState = win;
}
@@ -5608,8 +5616,9 @@
changes |= FINISH_LAYOUT_REDO_LAYOUT;
}
} else if (topIsFullscreen
- && !mWindowManagerInternal.isStackVisible(FREEFORM_WORKSPACE_STACK_ID)
- && !mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID)) {
+ && !mWindowManagerInternal.isStackVisible(WINDOWING_MODE_FREEFORM)
+ && !mWindowManagerInternal.isStackVisible(
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) {
if (DEBUG_LAYOUT) Slog.v(TAG, "** HIDING status bar");
if (mStatusBarController.setBarShowingLw(false)) {
changes |= FINISH_LAYOUT_REDO_LAYOUT;
@@ -8009,9 +8018,10 @@
}
private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
- final boolean dockedStackVisible = mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID);
+ final boolean dockedStackVisible =
+ mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final boolean freeformStackVisible =
- mWindowManagerInternal.isStackVisible(FREEFORM_WORKSPACE_STACK_ID);
+ mWindowManagerInternal.isStackVisible(WINDOWING_MODE_FREEFORM);
final boolean resizing = mWindowManagerInternal.isDockedDividerResizing();
// We need to force system bars when the docked stack is visible, when the freeform stack
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 853e1b2..515fa39 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -457,8 +457,7 @@
// First send the high-level shut down broadcast.
mActionDone = false;
Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
- intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
- | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendOrderedBroadcastAsUser(intent,
UserHandle.ALL, null, br, mHandler, 0, null, null);
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 05fd248..6a5ecfaa 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -20,15 +20,25 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.content.IntentFilter;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.IStatsCompanionService;
import android.os.IStatsManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.util.Slog;
+import java.util.ArrayList;
+import java.util.List;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.KernelWakelockReader;
import com.android.internal.os.KernelWakelockStats;
@@ -53,6 +63,7 @@
private final PendingIntent mAnomalyAlarmIntent;
private final PendingIntent mPollingAlarmIntent;
+ private final BroadcastReceiver mAppUpdateReceiver;
public StatsCompanionService(Context context) {
super();
@@ -63,8 +74,85 @@
new Intent(mContext, AnomalyAlarmReceiver.class), 0);
mPollingAlarmIntent = PendingIntent.getBroadcast(mContext, 0,
new Intent(mContext, PollingAlarmReceiver.class), 0);
+ mAppUpdateReceiver = new AppUpdateReceiver();
+ Slog.w(TAG, "Registered receiver for ACTION_PACKAGE_REPLACE AND ADDED.");
}
+ private final static int[] toIntArray(List<Integer> list){
+ int[] ret = new int[list.size()];
+ for(int i = 0;i < ret.length;i++) {
+ ret[i] = list.get(i);
+ }
+ return ret;
+ }
+
+ // Assumes that sStatsdLock is held.
+ private final void informAllUidsLocked(Context context) throws RemoteException {
+ UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ PackageManager pm = context.getPackageManager();
+ final List<UserInfo> users = um.getUsers(true);
+ if (DEBUG) {
+ Slog.w(TAG, "Iterating over "+users.size() + " profiles.");
+ }
+
+ List<Integer> uids = new ArrayList();
+ List<Integer> versions = new ArrayList();
+ List<String> apps = new ArrayList();
+
+ // Add in all the apps for every user/profile.
+ for (UserInfo profile : users) {
+ List<PackageInfo> pi = pm.getInstalledPackagesAsUser(0, profile.id);
+ for (int j = 0; j < pi.size(); j++) {
+ if (pi.get(j).applicationInfo != null) {
+ uids.add(pi.get(j).applicationInfo.uid);
+ versions.add(pi.get(j).versionCode);
+ apps.add(pi.get(j).packageName);
+ }
+ }
+ }
+ sStatsd.informAllUidData(toIntArray(uids), toIntArray(versions), apps.toArray(new
+ String[apps.size()]));
+ if (DEBUG) {
+ Slog.w(TAG, "Sent data for "+uids.size() +" apps");
+ }
+ }
+
+ public final static class AppUpdateReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Slog.i(TAG, "StatsCompanionService noticed an app was updated.");
+ synchronized (sStatsdLock) {
+ if (sStatsd == null) {
+ Slog.w(TAG, "Could not access statsd to inform it of anomaly alarm firing");
+ return;
+ }
+ try {
+ if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {
+ Bundle b = intent.getExtras();
+ int uid = b.getInt(Intent.EXTRA_UID);
+ boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+ if (!replacing) {
+ // Don't bother sending an update if we're right about to get another
+ // intent for the new version that's added.
+ PackageManager pm = context.getPackageManager();
+ String app = intent.getData().getSchemeSpecificPart();
+ sStatsd.informOnePackageRemoved(app, uid);
+ }
+ } else {
+ PackageManager pm = context.getPackageManager();
+ Bundle b = intent.getExtras();
+ int uid = b.getInt(Intent.EXTRA_UID);
+ String app = intent.getData().getSchemeSpecificPart();
+ PackageInfo pi = pm.getPackageInfo(app, PackageManager.MATCH_ANY_USER);
+ sStatsd.informOnePackage(app, uid, pi.versionCode);
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed to inform statsd of an app update", e);
+ }
+ }
+ }
+ };
+
public final static class AnomalyAlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -161,7 +249,7 @@
}
}
- // These values must be kept in sync with cmd/statsd/StatsPuller.h.
+ // These values must be kept in sync with cmd/statsd/StatsPullerManager.h.
// TODO: pull the constant from stats_events.proto instead
private static final int PULL_CODE_KERNEL_WAKELOCKS = 20;
@@ -275,6 +363,15 @@
Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
forgetEverything();
}
+ // Setup broadcast receiver for updates
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED);
+ filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addDataScheme("package");
+ mContext.registerReceiverAsUser(mAppUpdateReceiver, UserHandle.ALL, filter, null,
+ null);
+ // Pull the latest state of UID->app name, version mapping when statsd starts.
+ informAllUidsLocked(mContext);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
forgetEverything();
@@ -293,6 +390,7 @@
private void forgetEverything() {
synchronized (sStatsdLock) {
sStatsd = null;
+ mContext.unregisterReceiver(mAppUpdateReceiver);
cancelAnomalyAlarm();
cancelPollingAlarms();
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 0884678..b07fe98 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -77,6 +77,7 @@
void setCurrentUser(int newUserId);
+ boolean isGlobalActionsDisabled();
void setGlobalActionsListener(GlobalActionsListener listener);
void showGlobalActions();
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 83fd549..c78a340 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -16,6 +16,8 @@
package com.android.server.statusbar;
+import static android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS;
+
import android.app.ActivityThread;
import android.app.StatusBarManager;
import android.content.ComponentName;
@@ -31,6 +33,7 @@
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
+import android.service.notification.NotificationStats;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
@@ -362,6 +365,11 @@
}
@Override
+ public boolean isGlobalActionsDisabled() {
+ return (mDisabled2 & DISABLE2_GLOBAL_ACTIONS) != 0;
+ }
+
+ @Override
public void setGlobalActionsListener(GlobalActionsListener listener) {
mGlobalActionListener = listener;
mGlobalActionListener.onStatusBarConnectedChanged(mBar != null);
@@ -943,13 +951,15 @@
}
@Override
- public void onNotificationClear(String pkg, String tag, int id, int userId) {
+ public void onNotificationClear(String pkg, String tag, int id, int userId, String key,
+ @NotificationStats.DismissalSurface int dismissalSurface) {
enforceStatusBarService();
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
long identity = Binder.clearCallingIdentity();
try {
- mNotificationDelegate.onNotificationClear(callingUid, callingPid, pkg, tag, id, userId);
+ mNotificationDelegate.onNotificationClear(
+ callingUid, callingPid, pkg, tag, id, userId, key, dismissalSurface);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -983,6 +993,28 @@
}
@Override
+ public void onNotificationDirectReplied(String key) throws RemoteException {
+ enforceStatusBarService();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mNotificationDelegate.onNotificationDirectReplied(key);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void onNotificationSettingsViewed(String key) throws RemoteException {
+ enforceStatusBarService();
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mNotificationDelegate.onNotificationSettingsViewed(key);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void onClearAllNotifications(int userId) {
enforceStatusBarService();
final int callingUid = Binder.getCallingUid();
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index 6824a59..52b49ba 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -47,6 +47,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import libcore.icu.ICU;
+import libcore.util.TimeZoneFinder;
import libcore.util.ZoneInfoDB;
import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED;
@@ -479,9 +480,10 @@
case 'a': {
// Report the active rules version (i.e. the rules in use by the current
// process).
- pw.println("Active rules version (ICU, libcore): "
+ pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): "
+ ICU.getTZDataVersion() + ","
- + ZoneInfoDB.getInstance().getVersion());
+ + ZoneInfoDB.getInstance().getVersion() + ","
+ + TimeZoneFinder.getInstance().getIanaVersion());
break;
}
default: {
@@ -494,8 +496,10 @@
}
pw.println("RulesManagerService state: " + toString());
- pw.println("Active rules version (ICU, libcore): " + ICU.getTZDataVersion() + ","
- + ZoneInfoDB.getInstance().getVersion());
+ pw.println("Active rules version (ICU, ZoneInfoDB, TimeZoneFinder): "
+ + ICU.getTZDataVersion() + ","
+ + ZoneInfoDB.getInstance().getVersion() + ","
+ + TimeZoneFinder.getInstance().getIanaVersion());
pw.println("Distro state: " + rulesState.toString());
mPackageTracker.dump(pw);
}
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 6117da7..c1607e9 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -1022,20 +1022,6 @@
}
}
- @Override
- public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
- synchronized (mImplLock) {
- if (mReleased) {
- throw new IllegalStateException("Device already released.");
- }
- }
- if (mInfo.getType() != TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
- return false;
- }
- // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
- return false;
- }
-
private boolean startCapture(Surface surface, TvStreamConfig config) {
synchronized (mImplLock) {
if (mReleased) {
diff --git a/services/core/java/com/android/server/vr/Vr2dDisplay.java b/services/core/java/com/android/server/vr/Vr2dDisplay.java
index 5721415..95d03d4 100644
--- a/services/core/java/com/android/server/vr/Vr2dDisplay.java
+++ b/services/core/java/com/android/server/vr/Vr2dDisplay.java
@@ -296,6 +296,7 @@
flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT;
flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
+ flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
mVirtualDisplay = mDisplayManager.createVirtualDisplay(null /* projection */,
DISPLAY_NAME, mVirtualDisplayWidth, mVirtualDisplayHeight, mVirtualDisplayDpi,
null /* surface */, flags, null /* callback */, null /* handler */,
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index bdd9de0..7b1e12e 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -74,6 +74,13 @@
public abstract void onScreenStateChanged(boolean isScreenOn);
/**
+ * Set whether the keyguard is currently active/showing.
+ *
+ * @param isShowing is {@code true} if the keyguard is active/showing.
+ */
+ public abstract void onKeyguardStateChanged(boolean isShowing);
+
+ /**
* Return NO_ERROR if the given package is installed on the device and enabled as a
* VrListenerService for the given current user, or a negative error code indicating a failure.
*
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index f06f76d..e7e4efc 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -115,11 +115,13 @@
/** Null set of sleep sleep flags. */
private static final int FLAG_NONE = 0;
/** Flag set when the device is not sleeping. */
- private static final int FLAG_AWAKE = 1;
+ private static final int FLAG_AWAKE = 1 << 0;
/** Flag set when the screen has been turned on. */
- private static final int FLAG_SCREEN_ON = 2;
+ private static final int FLAG_SCREEN_ON = 1 << 1;
+ /** Flag set when the keyguard is not active. */
+ private static final int FLAG_KEYGUARD_UNLOCKED = 1 << 2;
/** Flag indicating that all system sleep flags have been set.*/
- private static final int FLAG_ALL = FLAG_AWAKE | FLAG_SCREEN_ON;
+ private static final int FLAG_ALL = FLAG_AWAKE | FLAG_SCREEN_ON | FLAG_KEYGUARD_UNLOCKED;
private static native void initializeNative();
private static native void setVrModeNative(boolean enabled);
@@ -155,10 +157,11 @@
private final NotificationAccessManager mNotifAccessManager = new NotificationAccessManager();
private INotificationManager mNotificationManager;
/** Tracks the state of the screen and keyguard UI.*/
- private int mSystemSleepFlags = FLAG_AWAKE;
+ private int mSystemSleepFlags = FLAG_AWAKE | FLAG_KEYGUARD_UNLOCKED;
/**
* Set when ACTION_USER_UNLOCKED is fired. We shouldn't try to bind to the
- * vr service before then.
+ * vr service before then. This gets set only once the first time the user unlocks the device
+ * and stays true thereafter.
*/
private boolean mUserUnlocked;
private Vr2dDisplay mVr2dDisplay;
@@ -208,7 +211,6 @@
if (mBootsToVr) {
setPersistentVrModeEnabled(true);
}
- consumeAndApplyPendingStateLocked();
if (mBootsToVr && !mVrModeEnabled) {
setVrMode(true, mDefaultVrService, 0, -1, null);
}
@@ -230,29 +232,40 @@
}
private void setSleepState(boolean isAsleep) {
- synchronized(mLock) {
-
- if (!isAsleep) {
- mSystemSleepFlags |= FLAG_AWAKE;
- } else {
- mSystemSleepFlags &= ~FLAG_AWAKE;
- }
-
- updateVrModeAllowedLocked();
- }
+ setSystemState(FLAG_AWAKE, !isAsleep);
}
private void setScreenOn(boolean isScreenOn) {
+ setSystemState(FLAG_SCREEN_ON, isScreenOn);
+ }
+
+ private void setKeyguardShowing(boolean isShowing) {
+ setSystemState(FLAG_KEYGUARD_UNLOCKED, !isShowing);
+ }
+
+ private void setSystemState(int flags, boolean isOn) {
synchronized(mLock) {
- if (isScreenOn) {
- mSystemSleepFlags |= FLAG_SCREEN_ON;
+ int oldState = mSystemSleepFlags;
+ if (isOn) {
+ mSystemSleepFlags |= flags;
} else {
- mSystemSleepFlags &= ~FLAG_SCREEN_ON;
+ mSystemSleepFlags &= ~flags;
}
- updateVrModeAllowedLocked();
+ if (oldState != mSystemSleepFlags) {
+ if (DBG) Slog.d(TAG, "System state: " + getStateAsString());
+ updateVrModeAllowedLocked();
+ }
}
}
+ private String getStateAsString() {
+ return new StringBuilder()
+ .append((mSystemSleepFlags & FLAG_AWAKE) != 0 ? "awake, " : "")
+ .append((mSystemSleepFlags & FLAG_SCREEN_ON) != 0 ? "screen_on, " : "")
+ .append((mSystemSleepFlags & FLAG_KEYGUARD_UNLOCKED) != 0 ? "keyguard_off" : "")
+ .toString();
+ }
+
private void setUserUnlocked() {
synchronized(mLock) {
mUserUnlocked = true;
@@ -583,7 +596,7 @@
((mPersistentVrModeEnabled) ? "enabled" : "disabled"));
pw.println("Currently bound VR listener service: "
+ ((mCurrentVrService == null)
- ? "None" : mCurrentVrCompositorService.getComponent().flattenToString()));
+ ? "None" : mCurrentVrService.getComponent().flattenToString()));
pw.println("Currently bound VR compositor service: "
+ ((mCurrentVrCompositorService == null)
? "None" : mCurrentVrCompositorService.getComponent().flattenToString()));
@@ -672,6 +685,11 @@
}
@Override
+ public void onKeyguardStateChanged(boolean isShowing) {
+ VrManagerService.this.setKeyguardShowing(isShowing);
+ }
+
+ @Override
public boolean isCurrentVrListener(String packageName, int userId) {
return VrManagerService.this.isCurrentVrListener(packageName, userId);
}
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index c76b905..5365e27 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -16,10 +16,10 @@
package com.android.server.wm;
-import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
import static com.android.server.wm.WindowManagerDebugConfig.SHOW_TRANSACTIONS;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -78,11 +78,8 @@
// requires that the duration of the two animations are the same.
SurfaceControl thumbnail;
int thumbnailTransactionSeq;
- // TODO(b/62029108): combine both members into a private one. Create a member function to set
- // the thumbnail layer to +1 to the highest layer position and replace all setter instances
- // with this function. Remove all unnecessary calls to both variables in other classes.
- int thumbnailLayer;
- int thumbnailForceAboveLayer;
+ private int mThumbnailLayer;
+
Animation thumbnailAnimation;
final Transformation thumbnailTransformation = new Transformation();
// This flag indicates that the destruction of the thumbnail surface is synchronized with
@@ -256,7 +253,7 @@
private void updateLayers() {
mAppToken.getDisplayContent().assignWindowLayers(false /* relayoutNeeded */);
- thumbnailLayer = mAppToken.getHighestAnimLayer();
+ updateThumbnailLayer();
}
private void stepThumbnailAnimation(long currentTime) {
@@ -280,26 +277,35 @@
thumbnail.setPosition(tmpFloats[Matrix.MTRANS_X], tmpFloats[Matrix.MTRANS_Y]);
if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(thumbnail,
"thumbnail", "alpha=" + thumbnailTransformation.getAlpha()
- + " layer=" + thumbnailLayer
+ + " layer=" + mThumbnailLayer
+ " matrix=[" + tmpFloats[Matrix.MSCALE_X]
+ "," + tmpFloats[Matrix.MSKEW_Y]
+ "][" + tmpFloats[Matrix.MSKEW_X]
+ "," + tmpFloats[Matrix.MSCALE_Y] + "]");
thumbnail.setAlpha(thumbnailTransformation.getAlpha());
- if (thumbnailForceAboveLayer > 0) {
- thumbnail.setLayer(thumbnailForceAboveLayer + 1);
- } else {
- // The thumbnail is layered below the window immediately above this
- // token's anim layer.
- thumbnail.setLayer(thumbnailLayer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
- - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
- }
+ updateThumbnailLayer();
thumbnail.setMatrix(tmpFloats[Matrix.MSCALE_X], tmpFloats[Matrix.MSKEW_Y],
tmpFloats[Matrix.MSKEW_X], tmpFloats[Matrix.MSCALE_Y]);
thumbnail.setWindowCrop(thumbnailTransformation.getClipRect());
}
/**
+ * Updates the thumbnail layer z order to just above the highest animation layer if changed
+ */
+ void updateThumbnailLayer() {
+ if (thumbnail != null) {
+ final int layer = mAppToken.getHighestAnimLayer();
+ if (layer != mThumbnailLayer) {
+ if (DEBUG_LAYERS) Slog.v(TAG,
+ "Setting thumbnail layer " + mAppToken + ": layer=" + layer);
+ thumbnail.setLayer(layer + WindowManagerService.WINDOW_LAYER_MULTIPLIER
+ - WindowManagerService.LAYER_OFFSET_THUMBNAIL);
+ mThumbnailLayer = layer;
+ }
+ }
+ }
+
+ /**
* Sometimes we need to synchronize the first frame of animation with some external event, e.g.
* Recents hiding some of its content. To achieve this, we prolong the start of the animaiton
* and keep producing the first frame of the animation.
@@ -473,7 +479,7 @@
}
if (thumbnail != null) {
pw.print(prefix); pw.print("thumbnail="); pw.print(thumbnail);
- pw.print(" layer="); pw.println(thumbnailLayer);
+ pw.print(" layer="); pw.println(mThumbnailLayer);
pw.print(prefix); pw.print("thumbnailAnimation="); pw.println(thumbnailAnimation);
pw.print(prefix); pw.print("thumbnailTransformation=");
pw.println(thumbnailTransformation.toShortString());
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 78f8157..1b463c7 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -823,7 +823,7 @@
// For freeform windows, we can't freeze the bounds at the moment because this would make
// the resizing unresponsive.
- if (task == null || task.inFreeformWorkspace()) {
+ if (task == null || task.inFreeformWindowingMode()) {
return false;
}
@@ -1170,7 +1170,6 @@
wAppAnimator.thumbnail.destroy();
}
wAppAnimator.thumbnail = tAppAnimator.thumbnail;
- wAppAnimator.thumbnailLayer = tAppAnimator.thumbnailLayer;
wAppAnimator.thumbnailAnimation = tAppAnimator.thumbnailAnimation;
tAppAnimator.thumbnail = null;
}
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 9e028d3..5bfea98 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -21,7 +21,9 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -182,6 +184,11 @@
return windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
}
+ public boolean inSplitScreenPrimaryWindowingMode() {
+ return mFullConfiguration.windowConfiguration.getWindowingMode()
+ == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+ }
+
/**
* Returns true if this container can be put in either
* {@link WindowConfiguration#WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} or
@@ -192,6 +199,14 @@
return mFullConfiguration.windowConfiguration.supportSplitScreenWindowingMode();
}
+ public boolean inPinnedWindowingMode() {
+ return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED;
+ }
+
+ public boolean inFreeformWindowingMode() {
+ return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM;
+ }
+
/** Returns the activity type associated with the the configuration container. */
/*@WindowConfiguration.ActivityType*/
public int getActivityType() {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8fc9dda..05afd55 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -16,10 +16,8 @@
package com.android.server.wm;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -119,6 +117,7 @@
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.app.ActivityManager.StackId;
+import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
@@ -392,10 +391,6 @@
!= mTmpWindowAnimator.mAnimTransactionSequence) {
appAnimator.thumbnailTransactionSeq =
mTmpWindowAnimator.mAnimTransactionSequence;
- appAnimator.thumbnailLayer = 0;
- }
- if (appAnimator.thumbnailLayer < winAnimator.mAnimLayer) {
- appAnimator.thumbnailLayer = winAnimator.mAnimLayer;
}
}
};
@@ -983,8 +978,8 @@
}
// In the presence of the PINNED stack or System Alert
- // windows we unforuntately can not seamlessly rotate.
- if (getStackById(PINNED_STACK_ID) != null) {
+ // windows we unfortunately can not seamlessly rotate.
+ if (getStack(WINDOWING_MODE_PINNED) != null) {
mayRotateSeamlessly = false;
}
for (int i = 0; i < mService.mSessions.size(); i++) {
@@ -1777,7 +1772,7 @@
mTaskStackContainers.addStackToDisplay(stack, onTop);
}
- if (stackId == DOCKED_STACK_ID) {
+ if (stack.inSplitScreenPrimaryWindowingMode()) {
mDividerControllerLocked.notifyDockedStackExistsChanged(true);
}
return stack;
@@ -2041,7 +2036,7 @@
final WindowState imeWin = mService.mInputMethodWindow;
final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
&& !mDividerControllerLocked.isImeHideRequested();
- final boolean dockVisible = isStackVisible(DOCKED_STACK_ID);
+ final boolean dockVisible = isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
final TaskStack imeTargetStack = mService.getImeFocusStackLocked();
final int imeDockSide = (dockVisible && imeTargetStack != null) ?
imeTargetStack.getDockSide() : DOCKED_INVALID;
@@ -2264,10 +2259,10 @@
return "Display " + mDisplayId + " name=\"" + mDisplayInfo.name + "\"";
}
- /** Checks if stack with provided id is visible on this display. */
- boolean isStackVisible(int stackId) {
- final TaskStack stack = getStackById(stackId);
- return (stack != null && stack.isVisible());
+ /** Returns true if the stack in the windowing mode is visible. */
+ boolean isStackVisible(int windowingMode) {
+ final TaskStack stack = getStack(windowingMode);
+ return stack != null && stack.isVisible();
}
/**
@@ -3560,7 +3555,8 @@
@Override
int getOrientation() {
- if (isStackVisible(DOCKED_STACK_ID) || isStackVisible(FREEFORM_WORKSPACE_STACK_ID)) {
+ if (isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
+ || isStackVisible(WINDOWING_MODE_FREEFORM)) {
// Apps and their containers are not allowed to specify an orientation while the
// docked or freeform stack is visible...except for the home stack/task if the
// docked stack is minimized and it actually set something.
@@ -3575,6 +3571,16 @@
}
final int orientation = super.getOrientation();
+ boolean isCar = mService.mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE);
+ if (isCar) {
+ // In a car, you cannot physically rotate the screen, so it doesn't make sense to
+ // allow anything but the default orientation.
+ if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
+ "Forcing UNSPECIFIED orientation in car. Ignoring " + orientation);
+ return SCREEN_ORIENTATION_UNSPECIFIED;
+ }
+
if (orientation != SCREEN_ORIENTATION_UNSET
&& orientation != SCREEN_ORIENTATION_BEHIND) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 6f441b9..629af66 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -16,8 +16,6 @@
package com.android.server.wm;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -588,7 +586,7 @@
private boolean containsAppInDockedStack(ArraySet<AppWindowToken> apps) {
for (int i = apps.size() - 1; i >= 0; i--) {
final AppWindowToken token = apps.valueAt(i);
- if (token.getTask() != null && token.getTask().mStack.mStackId == DOCKED_STACK_ID) {
+ if (token.getTask() != null && token.inSplitScreenPrimaryWindowingMode()) {
return true;
}
}
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 5057f63..238cb9f 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.INPUT_CONSUMER_NAVIGATION;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
@@ -650,7 +649,7 @@
final boolean hasFocus = w == mInputFocus;
final boolean isVisible = w.isVisibleLw();
- if (w.getStackId() == PINNED_STACK_ID) {
+ if (w.inPinnedWindowingMode()) {
if (mAddPipInputConsumerHandle
&& (inputWindowHandle.layer <= pipInputConsumer.mWindowHandle.layer)) {
// Update the bounds of the Pip input consumer to match the Pinned stack
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index 2215f20..41f076d 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -37,8 +37,8 @@
private Rect mTmpToBounds = new Rect();
public PinnedStackWindowController(int stackId, PinnedStackWindowListener listener,
- int displayId, boolean onTop, Rect outBounds) {
- super(stackId, listener, displayId, onTop, outBounds, WindowManagerService.getInstance());
+ int displayId, boolean onTop, Rect outBounds, WindowManagerService service) {
+ super(stackId, listener, displayId, onTop, outBounds, service);
}
/**
diff --git a/services/core/java/com/android/server/wm/StackWindowController.java b/services/core/java/com/android/server/wm/StackWindowController.java
index c0a4cb7..c0e5fd4 100644
--- a/services/core/java/com/android/server/wm/StackWindowController.java
+++ b/services/core/java/com/android/server/wm/StackWindowController.java
@@ -16,8 +16,6 @@
package com.android.server.wm;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Handler;
@@ -280,8 +278,9 @@
if (stack.getWindowConfiguration().tasksAreFloating()) {
// Floating tasks should not be resized to the screen's bounds.
- if (mStackId == PINNED_STACK_ID && bounds.width() == mTmpDisplayBounds.width() &&
- bounds.height() == mTmpDisplayBounds.height()) {
+ if (stack.inPinnedWindowingMode()
+ && bounds.width() == mTmpDisplayBounds.width()
+ && bounds.height() == mTmpDisplayBounds.height()) {
// If the bounds we are animating is the same as the fullscreen stack
// dimensions, then apply the same inset calculations that we normally do for
// the fullscreen stack, without intersecting it with the display bounds
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7e8d130..6c6934f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -17,8 +17,6 @@
package com.android.server.wm;
import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
@@ -213,7 +211,7 @@
// then we want to preserve our insets so that there will not
// be a jump in the area covered by system decorations. We rely
// on the pinned animation to later unset this value.
- if (stack.mStackId == PINNED_STACK_ID) {
+ if (stack.inPinnedWindowingMode()) {
mPreserveNonFloatingState = true;
} else {
mPreserveNonFloatingState = false;
@@ -492,7 +490,7 @@
final boolean dockedResizing = displayContent != null
&& displayContent.mDividerControllerLocked.isResizing();
if (useCurrentBounds()) {
- if (inFreeformWorkspace() && getMaxVisibleBounds(out)) {
+ if (inFreeformWindowingMode() && getMaxVisibleBounds(out)) {
return;
}
@@ -598,14 +596,6 @@
return (tokensCount != 0) && mChildren.get(tokensCount - 1).mShowForAllUsers;
}
- boolean inFreeformWorkspace() {
- return mStack != null && mStack.mStackId == FREEFORM_WORKSPACE_STACK_ID;
- }
-
- boolean inPinnedWorkspace() {
- return mStack != null && mStack.mStackId == PINNED_STACK_ID;
- }
-
/**
* When we are in a floating stack (Freeform, Pinned, ...) we calculate
* insets differently. However if we are animating to the fullscreen stack
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index c58212c..87de151 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -20,7 +20,6 @@
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.RESIZE_MODE_USER;
import static android.app.ActivityManager.RESIZE_MODE_USER_FORCED;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
@@ -649,7 +648,7 @@
* shouldn't be shown.
*/
private int getDimSide(int x) {
- if (mTask.mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
+ if (!mTask.mStack.inFreeformWindowingMode()
|| !mTask.mStack.fillsParent()
|| mTask.mStack.getConfiguration().orientation != ORIENTATION_LANDSCAPE) {
return CTRL_NONE;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 65278837..3780d19 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -18,8 +18,6 @@
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
@@ -409,7 +407,7 @@
return false;
}
- if (mStackId == PINNED_STACK_ID) {
+ if (inPinnedWindowingMode()) {
getAnimationOrCurrentBounds(mTmpRect2);
boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
mTmpRect2, mTmpRect3);
@@ -443,21 +441,19 @@
mTmpRect2.set(mBounds);
mDisplayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
- switch (mStackId) {
- case DOCKED_STACK_ID:
- repositionDockedStackAfterRotation(mTmpRect2);
- snapDockedStackAfterRotation(mTmpRect2);
- final int newDockSide = getDockSide(mTmpRect2);
+ if (inSplitScreenPrimaryWindowingMode()) {
+ repositionDockedStackAfterRotation(mTmpRect2);
+ snapDockedStackAfterRotation(mTmpRect2);
+ final int newDockSide = getDockSide(mTmpRect2);
- // Update the dock create mode and clear the dock create bounds, these
- // might change after a rotation and the original values will be invalid.
- mService.setDockedStackCreateStateLocked(
- (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
- ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
- : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT,
- null);
- mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
- break;
+ // Update the dock create mode and clear the dock create bounds, these
+ // might change after a rotation and the original values will be invalid.
+ mService.setDockedStackCreateStateLocked(
+ (newDockSide == DOCKED_LEFT || newDockSide == DOCKED_TOP)
+ ? DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT
+ : DOCKED_STACK_CREATE_MODE_BOTTOM_OR_RIGHT,
+ null);
+ mDisplayContent.getDockedDividerController().notifyDockSideChanged(newDockSide);
}
mBoundsAfterRotation.set(mTmpRect2);
@@ -688,7 +684,7 @@
Rect bounds = null;
final TaskStack dockedStack = dc.getDockedStackIgnoringVisibility();
- if (mStackId == DOCKED_STACK_ID
+ if (inSplitScreenPrimaryWindowingMode()
|| (dockedStack != null && inSplitScreenSecondaryWindowingMode()
&& !dockedStack.fillsParent())) {
// The existence of a docked stack affects the size of other static stack created since
@@ -703,10 +699,10 @@
}
final boolean dockedOnTopOrLeft = mService.mDockedStackCreateMode
== DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
- getStackDockedModeBounds(mTmpRect, bounds, mStackId, mTmpRect2,
+ getStackDockedModeBounds(mTmpRect, bounds, mTmpRect2,
mDisplayContent.mDividerControllerLocked.getContentWidth(),
dockedOnTopOrLeft);
- } else if (mStackId == PINNED_STACK_ID) {
+ } else if (inPinnedWindowingMode()) {
// Update the bounds based on any changes to the display info
getAnimationOrCurrentBounds(mTmpRect2);
boolean updated = mDisplayContent.mPinnedStackControllerLocked.onTaskStackBoundsChanged(
@@ -791,7 +787,7 @@
mDisplayContent.getLogicalDisplayRect(mTmpRect);
dockedStack.getRawBounds(mTmpRect2);
final boolean dockedOnTopOrLeft = dockedSide == DOCKED_TOP || dockedSide == DOCKED_LEFT;
- getStackDockedModeBounds(mTmpRect, outStackBounds, mStackId, mTmpRect2,
+ getStackDockedModeBounds(mTmpRect, outStackBounds, mTmpRect2,
mDisplayContent.mDividerControllerLocked.getContentWidth(), dockedOnTopOrLeft);
}
@@ -800,16 +796,15 @@
* Outputs the bounds a stack should be given the presence of a docked stack on the display.
* @param displayRect The bounds of the display the docked stack is on.
* @param outBounds Output bounds that should be used for the stack.
- * @param stackId Id of stack we are calculating the bounds for.
* @param dockedBounds Bounds of the docked stack.
* @param dockDividerWidth We need to know the width of the divider make to the output bounds
* close to the side of the dock.
* @param dockOnTopOrLeft If the docked stack is on the top or left side of the screen.
*/
private void getStackDockedModeBounds(
- Rect displayRect, Rect outBounds, int stackId, Rect dockedBounds, int dockDividerWidth,
+ Rect displayRect, Rect outBounds, Rect dockedBounds, int dockDividerWidth,
boolean dockOnTopOrLeft) {
- final boolean dockedStack = stackId == DOCKED_STACK_ID;
+ final boolean dockedStack = inSplitScreenPrimaryWindowingMode();
final boolean splitHorizontally = displayRect.width() > displayRect.height();
outBounds.set(displayRect);
@@ -866,7 +861,7 @@
}
void resetDockedStackToMiddle() {
- if (mStackId != DOCKED_STACK_ID) {
+ if (inSplitScreenPrimaryWindowingMode()) {
throw new IllegalStateException("Not a docked stack=" + this);
}
@@ -913,7 +908,7 @@
mAnimationBackgroundSurface = null;
}
- if (mStackId == DOCKED_STACK_ID) {
+ if (inSplitScreenPrimaryWindowingMode()) {
mDisplayContent.mDividerControllerLocked.notifyDockedStackExistsChanged(false);
}
@@ -1035,8 +1030,8 @@
}
boolean shouldIgnoreInput() {
- return isAdjustedForMinimizedDockedStack() || mStackId == DOCKED_STACK_ID &&
- isMinimizedDockAndHomeStackResizable();
+ return isAdjustedForMinimizedDockedStack() ||
+ (inSplitScreenPrimaryWindowingMode() && isMinimizedDockAndHomeStackResizable());
}
/**
@@ -1471,7 +1466,7 @@
postExclude.set(mTmpRect);
}
- final boolean isFreeformed = task.inFreeformWorkspace();
+ final boolean isFreeformed = task.inFreeformWindowingMode();
if (task != focusedTask || isFreeformed) {
if (isFreeformed) {
// If the task is freeformed, enlarge the area to account for outside
@@ -1529,7 +1524,7 @@
}
}
- if (mStackId == PINNED_STACK_ID) {
+ if (inPinnedWindowingMode()) {
try {
mService.mActivityManager.notifyPinnedStackAnimationStarted();
} catch (RemoteException e) {
@@ -1561,7 +1556,7 @@
mService.requestTraversal();
}
- if (mStackId == PINNED_STACK_ID) {
+ if (inPinnedWindowingMode()) {
// Update to the final bounds if requested. This is done here instead of in the bounds
// animator to allow us to coordinate this after we notify the PiP mode changed
@@ -1595,7 +1590,7 @@
* bounds and we have a deferred PiP mode changed callback set with the animation.
*/
public boolean deferScheduleMultiWindowModeChanged() {
- if (mStackId == PINNED_STACK_ID) {
+ if (inPinnedWindowingMode()) {
return (mBoundsAnimatingRequested || mBoundsAnimating);
}
return false;
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 7213c95..629cc86 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -18,7 +18,7 @@
import com.android.internal.util.ToBooleanFunction;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
@@ -447,7 +447,7 @@
private void findWallpaperTarget(DisplayContent dc) {
mFindResults.reset();
- if (dc.isStackVisible(FREEFORM_WORKSPACE_STACK_ID)) {
+ if (dc.isStackVisible(WINDOWING_MODE_FREEFORM)) {
// In freeform mode we set the wallpaper as its own target, so we don't need an
// additional window to make it visible.
mFindResults.setUseTopWallpaperAsTarget(true);
diff --git a/services/core/java/com/android/server/wm/WindowLayersController.java b/services/core/java/com/android/server/wm/WindowLayersController.java
index fdd2ade..7caf2fe 100644
--- a/services/core/java/com/android/server/wm/WindowLayersController.java
+++ b/services/core/java/com/android/server/wm/WindowLayersController.java
@@ -266,20 +266,8 @@
w.mLayer = layer;
w.mWinAnimator.mAnimLayer = w.getAnimLayerAdjustment()
+ w.getSpecialWindowAnimLayerAdjustment();
- if (w.mAppToken != null && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer > 0) {
- if (w.mWinAnimator.mAnimLayer > w.mAppToken.mAppAnimator.thumbnailForceAboveLayer) {
- w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = w.mWinAnimator.mAnimLayer;
- }
- // TODO(b/62029108): the entire contents of the if statement should call the refactored
- // function to set the thumbnail layer for w.AppToken
- int highestLayer = w.mAppToken.getHighestAnimLayer();
- if (highestLayer > 0) {
- if (w.mAppToken.mAppAnimator.thumbnail != null
- && w.mAppToken.mAppAnimator.thumbnailForceAboveLayer != highestLayer) {
- w.mAppToken.mAppAnimator.thumbnailForceAboveLayer = highestLayer;
- w.mAppToken.mAppAnimator.thumbnail.setLayer(highestLayer + 1);
- }
- }
+ if (w.mAppToken != null) {
+ w.mAppToken.mAppAnimator.updateThumbnailLayer();
}
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1fb2188..4279d2e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2384,7 +2384,7 @@
final Rect insets = new Rect();
final Rect stableInsets = new Rect();
Rect surfaceInsets = null;
- final boolean freeform = win != null && win.inFreeformWorkspace();
+ final boolean freeform = win != null && win.inFreeformWindowingMode();
if (win != null) {
// Containing frame will usually cover the whole screen, including dialog windows.
// For freeform workspace windows it will not cover the whole screen and it also
@@ -2794,7 +2794,7 @@
for (final WindowState win : mWindowMap.values()) {
final Task task = win.getTask();
if (task != null && mTmpTaskIds.get(task.mTaskId, -1) != -1
- && task.inFreeformWorkspace()) {
+ && task.inFreeformWindowingMode()) {
final AppWindowToken appToken = win.mAppToken;
if (appToken != null && appToken.mAppAnimator != null) {
appToken.mAppAnimator.startProlongAnimation(scaleUp ?
@@ -7604,10 +7604,10 @@
}
@Override
- public boolean isStackVisible(int stackId) {
+ public boolean isStackVisible(int windowingMode) {
synchronized (mWindowMap) {
final DisplayContent dc = getDefaultDisplayContentLocked();
- return dc.isStackVisible(stackId);
+ return dc.isStackVisible(windowingMode);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4ff0f39..4cb2a9d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -16,10 +16,7 @@
package com.android.server.wm;
-import static android.app.ActivityManager.StackId;
-import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
@@ -815,13 +812,12 @@
final WindowState imeWin = mService.mInputMethodWindow;
// IME is up and obscuring this window. Adjust the window position so it is visible.
if (imeWin != null && imeWin.isVisibleNow() && mService.mInputMethodTarget == this) {
- final int stackId = getStackId();
- if (stackId == FREEFORM_WORKSPACE_STACK_ID
+ if (inFreeformWindowingMode()
&& mContainingFrame.bottom > contentFrame.bottom) {
// In freeform we want to move the top up directly.
// TODO: Investigate why this is contentFrame not parentFrame.
mContainingFrame.top -= mContainingFrame.bottom - contentFrame.bottom;
- } else if (stackId != PINNED_STACK_ID
+ } else if (!inPinnedWindowingMode()
&& mContainingFrame.bottom > parentFrame.bottom) {
// But in docked we want to behave like fullscreen and behave as if the task
// were given smaller bounds for the purposes of layout. Skip adjustments for
@@ -898,7 +894,7 @@
// For pinned workspace the frame isn't limited in any particular
// way since SystemUI controls the bounds. For freeform however
// we want to keep things inside the content frame.
- final Rect limitFrame = task.inPinnedWorkspace() ? mFrame : mContentFrame;
+ final Rect limitFrame = task.inPinnedWindowingMode() ? mFrame : mContentFrame;
// Keep the frame out of the blocked system area, limit it in size to the content area
// and make sure that there is always a minimum visible so that the user can drag it
// into a usable area..
@@ -1662,9 +1658,9 @@
//
// Anyway we don't need to synchronize position and content updates for these
// windows since they aren't at the base layer and could be moved around anyway.
- if (!computeDragResizing() && mAttrs.type == TYPE_BASE_APPLICATION &&
- !mWinAnimator.isForceScaled() && !isGoneForLayoutLw() &&
- !getTask().inPinnedWorkspace()) {
+ if (!computeDragResizing() && mAttrs.type == TYPE_BASE_APPLICATION
+ && !mWinAnimator.isForceScaled() && !isGoneForLayoutLw()
+ && !getTask().inPinnedWindowingMode()) {
setResizedWhileNotDragResizing(true);
}
}
@@ -2196,12 +2192,6 @@
}
}
- // TODO: Strange usage of word workspace here and above.
- boolean inPinnedWorkspace() {
- final Task task = getTask();
- return task != null && task.inPinnedWorkspace();
- }
-
void applyAdjustForImeIfNeeded() {
final Task task = getTask();
if (task != null && task.mStack != null && task.mStack.isAdjustedForIme()) {
@@ -2235,7 +2225,7 @@
} else {
getVisibleBounds(mTmpRect);
}
- if (inFreeformWorkspace()) {
+ if (inFreeformWindowingMode()) {
// For freeform windows we the touch region to include the whole surface for the
// shadows.
final DisplayMetrics displayMetrics = getDisplayContent().getDisplayMetrics();
@@ -2938,8 +2928,7 @@
return mTmpRect;
}
- @Override
- public int getStackId() {
+ private int getStackId() {
final TaskStack stack = getStack();
if (stack == null) {
return INVALID_STACK_ID;
@@ -2983,11 +2972,6 @@
}
}
- boolean inFreeformWorkspace() {
- final Task task = getTask();
- return task != null && task.inFreeformWorkspace();
- }
-
@Override
public boolean isInMultiWindowMode() {
final Task task = getTask();
@@ -3105,7 +3089,7 @@
// background.
return (getDisplayContent().mDividerControllerLocked.isResizing()
|| mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) &&
- !task.inFreeformWorkspace() && !isGoneForLayoutLw();
+ !task.inFreeformWindowingMode() && !isGoneForLayoutLw();
}
@@ -3756,7 +3740,7 @@
windowInfo.accessibilityIdOfAnchor = mAttrs.accessibilityIdOfAnchor;
windowInfo.focused = isFocused();
Task task = getTask();
- windowInfo.inPictureInPicture = (task != null) && task.inPinnedWorkspace();
+ windowInfo.inPictureInPicture = (task != null) && task.inPinnedWindowingMode();
if (mIsChildWindow) {
windowInfo.parentToken = getParentWindow().mClient.asBinder();
@@ -4219,7 +4203,7 @@
// If a freeform window is animating from a position where it would be cutoff, it would be
// cutoff during the animation. We don't want that, so for the duration of the animation
// we ignore the decor cropping and depend on layering to position windows correctly.
- final boolean cropToDecor = !(inFreeformWorkspace() && isAnimatingLw());
+ final boolean cropToDecor = !(inFreeformWindowingMode() && isAnimatingLw());
if (cropToDecor) {
// Intersect with the decor rect, offsetted by window position.
systemDecorRect.intersect(decorRect.left - left, decorRect.top - top,
@@ -4303,7 +4287,7 @@
// scale for the animation using the source hint rect
// (see WindowStateAnimator#setSurfaceBoundariesLocked()).
if (isDragResizeChanged() || isResizedWhileNotDragResizing()
- || (surfaceInsetsChanging() && !inPinnedWorkspace())) {
+ || (surfaceInsetsChanging() && !inPinnedWindowingMode())) {
mLastSurfaceInsets.set(mAttrs.surfaceInsets);
setDragResizing();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 1b7e527..f544321 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1112,7 +1112,7 @@
*/
private boolean useFinalClipRect() {
return (isAnimationSet() && resolveStackClip() == STACK_CLIP_AFTER_ANIM)
- || mDestroyPreservedSurfaceUponRedraw || mWin.inPinnedWorkspace();
+ || mDestroyPreservedSurfaceUponRedraw || mWin.inPinnedWindowingMode();
}
/**
@@ -1177,7 +1177,7 @@
return false;
}
- if (w.inPinnedWorkspace()) {
+ if (w.inPinnedWindowingMode()) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 88625d3..af1fa2f 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -342,10 +342,7 @@
mTmpLayerAndToken.token = null;
handleClosingApps(transit, animLp, voiceInteraction, mTmpLayerAndToken);
final AppWindowToken topClosingApp = mTmpLayerAndToken.token;
- final int topClosingLayer = mTmpLayerAndToken.layer;
-
- final AppWindowToken topOpeningApp = handleOpeningApps(transit,
- animLp, voiceInteraction, topClosingLayer);
+ final AppWindowToken topOpeningApp = handleOpeningApps(transit, animLp, voiceInteraction);
mService.mAppTransition.setLastAppTransition(transit, topOpeningApp, topClosingApp);
@@ -387,8 +384,9 @@
}
private AppWindowToken handleOpeningApps(int transit, LayoutParams animLp,
- boolean voiceInteraction, int topClosingLayer) {
+ boolean voiceInteraction) {
AppWindowToken topOpeningApp = null;
+ int topOpeningLayer = Integer.MIN_VALUE;
final int appsCount = mService.mOpeningApps.size();
for (int i = 0; i < appsCount; i++) {
AppWindowToken wtoken = mService.mOpeningApps.valueAt(i);
@@ -422,7 +420,6 @@
}
mService.mAnimator.mAppWindowAnimating |= appAnimator.isAnimating();
- int topOpeningLayer = 0;
if (animLp != null) {
final int layer = wtoken.getHighestAnimLayer();
if (topOpeningApp == null || layer > topOpeningLayer) {
@@ -431,7 +428,7 @@
}
}
if (mService.mAppTransition.isNextAppTransitionThumbnailUp()) {
- createThumbnailAppAnimator(transit, wtoken, topOpeningLayer, topClosingLayer);
+ createThumbnailAppAnimator(transit, wtoken);
}
}
return topOpeningApp;
@@ -473,7 +470,7 @@
}
}
if (mService.mAppTransition.isNextAppTransitionThumbnailDown()) {
- createThumbnailAppAnimator(transit, wtoken, 0, layerAndToken.layer);
+ createThumbnailAppAnimator(transit, wtoken);
}
}
}
@@ -666,8 +663,7 @@
}
}
- private void createThumbnailAppAnimator(int transit, AppWindowToken appToken,
- int openingLayer, int closingLayer) {
+ private void createThumbnailAppAnimator(int transit, AppWindowToken appToken) {
AppWindowAnimator openingAppAnimator = (appToken == null) ? null : appToken.mAppAnimator;
if (openingAppAnimator == null || openingAppAnimator.animation == null) {
return;
@@ -724,7 +720,6 @@
anim = mService.mAppTransition.createThumbnailAspectScaleAnimationLocked(appRect,
insets, thumbnailHeader, taskId, displayConfig.uiMode,
displayConfig.orientation);
- openingAppAnimator.thumbnailForceAboveLayer = Math.max(openingLayer, closingLayer);
openingAppAnimator.deferThumbnailDestruction =
!mService.mAppTransition.isNextThumbnailTransitionScaleUp();
} else {
@@ -734,8 +729,8 @@
anim.restrictDuration(MAX_ANIMATION_DURATION);
anim.scaleCurrentDuration(mService.getTransitionAnimationScaleLocked());
+ openingAppAnimator.updateThumbnailLayer();
openingAppAnimator.thumbnail = surfaceControl;
- openingAppAnimator.thumbnailLayer = openingLayer;
openingAppAnimator.thumbnailAnimation = anim;
mService.mAppTransition.getNextAppTransitionStartRect(taskId, mTmpStartRect);
} catch (Surface.OutOfResourcesException e) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 92cbd3d..ff45070 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1640,11 +1640,7 @@
traceEnd();
traceBeginAndSlog("MakePackageManagerServiceReady");
- try {
- mPackageManagerService.systemReady();
- } catch (Throwable e) {
- reportWtf("making Package Manager Service ready", e);
- }
+ mPackageManagerService.systemReady();
traceEnd();
traceBeginAndSlog("MakeDisplayManagerServiceReady");
diff --git a/services/net/java/android/net/ip/ConnectivityPacketTracker.java b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
index 0230f36..1925c39 100644
--- a/services/net/java/android/net/ip/ConnectivityPacketTracker.java
+++ b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
@@ -25,6 +25,7 @@
import android.system.ErrnoException;
import android.system.Os;
import android.system.PacketSocketAddress;
+import android.text.TextUtils;
import android.util.Log;
import android.util.LocalLog;
@@ -59,11 +60,14 @@
private static final boolean DBG = false;
private static final String MARK_START = "--- START ---";
private static final String MARK_STOP = "--- STOP ---";
+ private static final String MARK_NAMED_START = "--- START (%s) ---";
+ private static final String MARK_NAMED_STOP = "--- STOP (%s) ---";
private final String mTag;
private final LocalLog mLog;
private final BlockingSocketReader mPacketListener;
private boolean mRunning;
+ private String mDisplayName;
public ConnectivityPacketTracker(Handler h, NetworkInterface netif, LocalLog log) {
final String ifname;
@@ -85,14 +89,16 @@
mPacketListener = new PacketListener(h, ifindex, hwaddr, mtu);
}
- public void start() {
+ public void start(String displayName) {
mRunning = true;
+ mDisplayName = displayName;
mPacketListener.start();
}
public void stop() {
mPacketListener.stop();
mRunning = false;
+ mDisplayName = null;
}
private final class PacketListener extends BlockingSocketReader {
@@ -133,16 +139,19 @@
@Override
protected void onStart() {
- mLog.log(MARK_START);
+ final String msg = TextUtils.isEmpty(mDisplayName)
+ ? MARK_START
+ : String.format(MARK_NAMED_START, mDisplayName);
+ mLog.log(msg);
}
@Override
protected void onStop() {
- if (mRunning) {
- mLog.log(MARK_STOP);
- } else {
- mLog.log(MARK_STOP + " (packet listener stopped unexpectedly)");
- }
+ String msg = TextUtils.isEmpty(mDisplayName)
+ ? MARK_STOP
+ : String.format(MARK_NAMED_STOP, mDisplayName);
+ if (!mRunning) msg += " (packet listener stopped unexpectedly)";
+ mLog.log(msg);
}
@Override
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index bc07b81..e33f6c9 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -26,6 +26,7 @@
import android.net.LinkAddress;
import android.net.LinkProperties.ProvisioningChange;
import android.net.LinkProperties;
+import android.net.Network;
import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.StaticIpConfiguration;
@@ -348,6 +349,16 @@
return this;
}
+ public Builder withNetwork(Network network) {
+ mConfig.mNetwork = network;
+ return this;
+ }
+
+ public Builder withDisplayName(String displayName) {
+ mConfig.mDisplayName = displayName;
+ return this;
+ }
+
public ProvisioningConfiguration build() {
return new ProvisioningConfiguration(mConfig);
}
@@ -362,6 +373,8 @@
/* package */ ApfCapabilities mApfCapabilities;
/* package */ int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS;
/* package */ int mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY;
+ /* package */ Network mNetwork = null;
+ /* package */ String mDisplayName = null;
public ProvisioningConfiguration() {} // used by Builder
@@ -374,6 +387,9 @@
mStaticIpConfig = other.mStaticIpConfig;
mApfCapabilities = other.mApfCapabilities;
mProvisioningTimeoutMs = other.mProvisioningTimeoutMs;
+ mIPv6AddrGenMode = other.mIPv6AddrGenMode;
+ mNetwork = other.mNetwork;
+ mDisplayName = other.mDisplayName;
}
@Override
@@ -388,6 +404,8 @@
.add("mApfCapabilities: " + mApfCapabilities)
.add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs)
.add("mIPv6AddrGenMode: " + mIPv6AddrGenMode)
+ .add("mNetwork: " + mNetwork)
+ .add("mDisplayName: " + mDisplayName)
.toString();
}
@@ -1441,10 +1459,10 @@
@Override
public void enter() {
// Get the Configuration for ApfFilter from Context
- boolean filter802_3Frames =
+ final boolean filter802_3Frames =
mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
- int[] ethTypeBlackList = mContext.getResources().getIntArray(
+ final int[] ethTypeBlackList = mContext.getResources().getIntArray(
R.array.config_apfEthTypeBlackList);
mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface,
@@ -1456,7 +1474,7 @@
}
mPacketTracker = createPacketTracker();
- if (mPacketTracker != null) mPacketTracker.start();
+ if (mPacketTracker != null) mPacketTracker.start(mConfiguration.mDisplayName);
if (mConfiguration.mEnableIPv6 && !startIPv6()) {
doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6);
@@ -1470,7 +1488,7 @@
return;
}
- InitialConfiguration config = mConfiguration.mInitialConfig;
+ final InitialConfiguration config = mConfiguration.mInitialConfig;
if ((config != null) && !applyInitialConfig(config)) {
// TODO introduce a new IpManagerEvent constant to distinguish this error case.
doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
diff --git a/services/net/java/android/net/util/NetworkConstants.java b/services/net/java/android/net/util/NetworkConstants.java
index 6065268..5a3a8be 100644
--- a/services/net/java/android/net/util/NetworkConstants.java
+++ b/services/net/java/android/net/util/NetworkConstants.java
@@ -107,6 +107,20 @@
public static final int RFC6177_MIN_PREFIX_LENGTH = 48;
/**
+ * ICMP common (v4/v6) constants.
+ *
+ * See also:
+ * - https://tools.ietf.org/html/rfc792
+ * - https://tools.ietf.org/html/rfc4443
+ */
+ public static final int ICMP_HEADER_TYPE_OFFSET = 0;
+ public static final int ICMP_HEADER_CODE_OFFSET = 1;
+ public static final int ICMP_HEADER_CHECKSUM_OFFSET = 2;
+ public static final int ICMP_ECHO_IDENTIFIER_OFFSET = 4;
+ public static final int ICMP_ECHO_SEQUENCE_NUMBER_OFFSET = 6;
+ public static final int ICMP_ECHO_DATA_OFFSET = 8;
+
+ /**
* ICMPv6 constants.
*
* See also:
diff --git a/services/net/java/android/net/util/SharedLog.java b/services/net/java/android/net/util/SharedLog.java
index 343d237..bbd3d13 100644
--- a/services/net/java/android/net/util/SharedLog.java
+++ b/services/net/java/android/net/util/SharedLog.java
@@ -106,6 +106,10 @@
record(Category.NONE, msg);
}
+ public void logf(String fmt, Object... args) {
+ log(String.format(fmt, args));
+ }
+
public void mark(String msg) {
record(Category.MARK, msg);
}
diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 9fa1d68..0b4d61f 100644
--- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -19,6 +19,7 @@
import static android.app.Notification.GROUP_ALERT_CHILDREN;
import static android.app.Notification.GROUP_ALERT_SUMMARY;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
@@ -57,7 +58,13 @@
import android.service.notification.StatusBarNotification;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Slog;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManager;
+import android.view.accessibility.IAccessibilityManagerClient;
+import com.android.internal.util.IntPair;
import com.android.server.lights.Light;
import org.junit.Before;
@@ -67,6 +74,8 @@
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -80,6 +89,8 @@
NotificationManagerService.WorkerHandler mHandler;
@Mock
NotificationUsageStats mUsageStats;
+ @Mock
+ IAccessibilityManager mAccessibilityService;
private NotificationManagerService mService;
private String mPkg = "com.android.server.notification";
@@ -111,17 +122,25 @@
private static final int MAX_VIBRATION_DELAY = 1000;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mAudioManager.isAudioFocusExclusive()).thenReturn(false);
when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer);
when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10);
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
-
when(mUsageStats.isAlertRateLimited(any())).thenReturn(false);
- mService = new NotificationManagerService(getContext());
+ long serviceReturnValue = IntPair.of(
+ AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED,
+ AccessibilityEvent.TYPES_ALL_MASK);
+ when(mAccessibilityService.addClient(any(), anyInt())).thenReturn(serviceReturnValue);
+ AccessibilityManager accessibilityManager =
+ new AccessibilityManager(Handler.getMain(), mAccessibilityService, 0);
+ verify(mAccessibilityService).addClient(any(IAccessibilityManagerClient.class), anyInt());
+ assertTrue(accessibilityManager.isEnabled());
+
+ mService = spy(new NotificationManagerService(getContext()));
mService.setAudioManager(mAudioManager);
mService.setVibrator(mVibrator);
mService.setSystemReady(true);
@@ -130,6 +149,7 @@
mService.setScreenOn(false);
mService.setFallbackVibrationPattern(FALLBACK_VIBRATION_PATTERN);
mService.setUsageStats(mUsageStats);
+ mService.setAccessibilityManager(accessibilityManager);
}
//
@@ -381,6 +401,7 @@
verifyBeepLooped();
verifyNeverVibrate();
+ verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
}
@Test
@@ -435,6 +456,7 @@
r.isUpdate = true;
mService.buzzBeepBlinkLocked(r);
verifyBeepLooped();
+ verify(mAccessibilityService, times(2)).sendAccessibilityEvent(any(), anyInt());
}
@Test
@@ -450,6 +472,7 @@
// update should not beep
mService.buzzBeepBlinkLocked(s);
verifyNeverBeep();
+ verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
}
@Test
@@ -547,7 +570,7 @@
mService.mInCall = true;
mService.buzzBeepBlinkLocked(r);
- //verify(mService, times(1)).playInCallNotification();
+ verify(mService, times(1)).playInCallNotification();
verifyNeverBeep(); // doesn't play normal beep
}
@@ -842,7 +865,6 @@
mService.addNotification(r);
mService.buzzBeepBlinkLocked(r);
-
verifyNeverBeep();
}
@@ -870,7 +892,6 @@
summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY;
mService.buzzBeepBlinkLocked(summary);
-
verify(mUsageStats, never()).isAlertRateLimited(any());
}
@@ -889,6 +910,30 @@
verifyNeverBeep();
}
+ @Test
+ public void testA11yMinInitialPost() throws Exception {
+ NotificationRecord r = getQuietNotification();
+ r.setImportance(IMPORTANCE_MIN, "");
+ mService.buzzBeepBlinkLocked(r);
+ verify(mAccessibilityService, never()).sendAccessibilityEvent(any(), anyInt());
+ }
+
+ @Test
+ public void testA11yQuietInitialPost() throws Exception {
+ NotificationRecord r = getQuietNotification();
+ mService.buzzBeepBlinkLocked(r);
+ verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
+ }
+
+ @Test
+ public void testA11yQuietUpdate() throws Exception {
+ NotificationRecord r = getQuietNotification();
+ mService.buzzBeepBlinkLocked(r);
+ r.isUpdate = true;
+ mService.buzzBeepBlinkLocked(r);
+ verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
+ }
+
static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> {
private final int mRepeatIndex;
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
index 725e8f2..d767ba2 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -16,6 +16,13 @@
package com.android.server.notification;
+import static android.service.notification.NotificationListenerService.Ranking
+ .USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking
+ .USER_SENTIMENT_NEUTRAL;
+import static android.service.notification.NotificationListenerService.Ranking
+ .USER_SENTIMENT_POSITIVE;
+
import static org.junit.Assert.assertEquals;
import android.app.NotificationChannel;
@@ -60,6 +67,7 @@
assertEquals(getPeople(key, i), ranking.getAdditionalPeople());
assertEquals(getSnoozeCriteria(key, i), ranking.getSnoozeCriteria());
assertEquals(getShowBadge(i), ranking.canShowBadge());
+ assertEquals(getUserSentiment(i), ranking.getUserSentiment());
}
}
@@ -74,6 +82,7 @@
Bundle snoozeCriteria = new Bundle();
Bundle showBadge = new Bundle();
int[] importance = new int[mKeys.length];
+ Bundle userSentiment = new Bundle();
for (int i = 0; i < mKeys.length; i++) {
String key = mKeys[i];
@@ -89,11 +98,12 @@
overridePeople.putStringArrayList(key, getPeople(key, i));
snoozeCriteria.putParcelableArrayList(key, getSnoozeCriteria(key, i));
showBadge.putBoolean(key, getShowBadge(i));
+ userSentiment.putInt(key, getUserSentiment(i));
}
NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys,
interceptedKeys.toArray(new String[0]), visibilityOverrides,
suppressedVisualEffects, importance, explanation, overrideGroupKeys,
- channels, overridePeople, snoozeCriteria, showBadge);
+ channels, overridePeople, snoozeCriteria, showBadge, userSentiment);
return update;
}
@@ -129,6 +139,18 @@
return index % 3 == 0;
}
+ private int getUserSentiment(int index) {
+ switch(index % 3) {
+ case 0:
+ return USER_SENTIMENT_NEGATIVE;
+ case 1:
+ return USER_SENTIMENT_NEUTRAL;
+ case 2:
+ return USER_SENTIMENT_POSITIVE;
+ }
+ return USER_SENTIMENT_NEUTRAL;
+ }
+
private ArrayList<String> getPeople(String key, int index) {
ArrayList<String> people = new ArrayList<>();
for (int i = 0; i < index; i++) {
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index 71a024c..9f5f856 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -38,6 +38,8 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -60,10 +62,13 @@
import android.media.AudioManager;
import android.os.Binder;
import android.os.Build;
+import android.os.Bundle;
import android.os.Process;
import android.os.UserHandle;
import android.provider.Settings.Secure;
+import android.service.notification.Adjustment;
import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -72,6 +77,7 @@
import android.util.ArrayMap;
import android.util.AtomicFile;
+import com.android.internal.statusbar.NotificationVisibility;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import com.android.server.notification.NotificationManagerService.NotificationAssistants;
@@ -81,8 +87,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.*;
import org.mockito.stubbing.Answer;
import java.io.BufferedInputStream;
@@ -101,7 +106,7 @@
public class NotificationManagerServiceTest extends NotificationTestCase {
private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
private final int mUid = Binder.getCallingUid();
- private NotificationManagerService mNotificationManagerService;
+ private NotificationManagerService mService;
private INotificationManager mBinderService;
private NotificationManagerInternal mInternalService;
@Mock
@@ -163,11 +168,11 @@
Secure.NOTIFICATION_BADGING, 1,
UserHandle.getUserHandleForUid(mUid).getIdentifier());
- mNotificationManagerService = new TestableNotificationManagerService(mContext);
+ mService = new TestableNotificationManagerService(mContext);
// Use this testable looper.
mTestableLooper = TestableLooper.get(this);
- mHandler = mNotificationManagerService.new WorkerHandler(mTestableLooper.getLooper());
+ mHandler = mService.new WorkerHandler(mTestableLooper.getLooper());
// MockPackageManager - default returns ApplicationInfo with matching calling UID
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.uid = mUid;
@@ -204,7 +209,7 @@
when(mConditionProviders.getConfig()).thenReturn(dndConfig);
try {
- mNotificationManagerService.init(mTestableLooper.getLooper(),
+ mService.init(mTestableLooper.getLooper(),
mPackageManager, mPackageManagerClient, mockLightsManager,
mListeners, mAssistants, mConditionProviders,
mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
@@ -214,11 +219,11 @@
throw e;
}
}
- mNotificationManagerService.setAudioManager(mAudioManager);
+ mService.setAudioManager(mAudioManager);
// Tests call directly into the Binder.
- mBinderService = mNotificationManagerService.getBinderService();
- mInternalService = mNotificationManagerService.getInternalService();
+ mBinderService = mService.getBinderService();
+ mInternalService = mService.getInternalService();
mBinderService.createNotificationChannels(
PKG, new ParceledListSlice(Arrays.asList(mTestNotificationChannel)));
@@ -417,7 +422,7 @@
NotificationChannel channel = new NotificationChannel("id", "name",
IMPORTANCE_HIGH);
NotificationRecord r = generateNotificationRecord(channel);
- assertTrue(mNotificationManagerService.isBlocked(r, mUsageStats));
+ assertTrue(mService.isBlocked(r, mUsageStats));
verify(mUsageStats, times(1)).registerSuspendedByAdmin(eq(r));
}
@@ -428,7 +433,7 @@
NotificationChannel channel = new NotificationChannel("id", "name",
NotificationManager.IMPORTANCE_NONE);
NotificationRecord r = generateNotificationRecord(channel);
- assertTrue(mNotificationManagerService.isBlocked(r, mUsageStats));
+ assertTrue(mService.isBlocked(r, mUsageStats));
verify(mUsageStats, times(1)).registerBlocked(eq(r));
mBinderService.createNotificationChannels(
@@ -457,7 +462,7 @@
waitForIdle();
assertEquals(1, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
assertEquals(IMPORTANCE_LOW,
- mNotificationManagerService.getNotificationRecord(sbn.getKey()).getImportance());
+ mService.getNotificationRecord(sbn.getKey()).getImportance());
assertEquals(IMPORTANCE_LOW,
mBinderService.getNotificationChannel(PKG, channel.getId()).getImportance());
}
@@ -485,7 +490,7 @@
sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
- assertNull(mNotificationManagerService.getNotificationRecord(sbn.getKey()));
+ assertNull(mService.getNotificationRecord(sbn.getKey()));
assertEquals(IMPORTANCE_NONE,
mBinderService.getNotificationChannel(PKG, channel.getId()).getImportance());
}
@@ -493,14 +498,14 @@
@Test
public void testBlockedNotifications_blockedChannelGroup() throws Exception {
when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
- mNotificationManagerService.setRankingHelper(mRankingHelper);
+ mService.setRankingHelper(mRankingHelper);
when(mRankingHelper.isGroupBlocked(anyString(), anyInt(), anyString())).thenReturn(true);
NotificationChannel channel = new NotificationChannel("id", "name",
NotificationManager.IMPORTANCE_HIGH);
channel.setGroup("something");
NotificationRecord r = generateNotificationRecord(channel);
- assertTrue(mNotificationManagerService.isBlocked(r, mUsageStats));
+ assertTrue(mService.isBlocked(r, mUsageStats));
verify(mUsageStats, times(1)).registerBlocked(eq(r));
}
@@ -529,7 +534,7 @@
sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
- assertNull(mNotificationManagerService.getNotificationRecord(sbn.getKey()));
+ assertNull(mService.getNotificationRecord(sbn.getKey()));
}
@Test
@@ -539,7 +544,7 @@
waitForIdle();
StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
assertEquals(1, notifs.length);
- assertEquals(1, mNotificationManagerService.getNotificationRecordCount());
+ assertEquals(1, mService.getNotificationRecordCount());
}
@Test
@@ -551,7 +556,7 @@
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(PKG);
assertEquals(0, notifs.length);
- assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+ assertEquals(0, mService.getNotificationRecordCount());
}
@Test
@@ -566,12 +571,16 @@
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(PKG);
assertEquals(0, notifs.length);
- assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+ assertEquals(0, mService.getNotificationRecordCount());
+ ArgumentCaptor<NotificationStats> captor = ArgumentCaptor.forClass(NotificationStats.class);
+ verify(mListeners, times(1)).notifyRemovedLocked(any(), anyInt(), captor.capture());
+ assertEquals(NotificationStats.DISMISSAL_OTHER, captor.getValue().getDismissalSurface());
}
@Test
public void testCancelNotificationsFromListenerImmediatelyAfterEnqueue() throws Exception {
- final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ NotificationRecord r = generateNotificationRecord(null);
+ final StatusBarNotification sbn = r.sbn;
mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
mBinderService.cancelNotificationsFromListener(null, null);
@@ -579,7 +588,7 @@
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(sbn.getPackageName());
assertEquals(0, notifs.length);
- assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+ assertEquals(0, mService.getNotificationRecordCount());
}
@Test
@@ -592,7 +601,7 @@
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(sbn.getPackageName());
assertEquals(0, notifs.length);
- assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+ assertEquals(0, mService.getNotificationRecordCount());
}
@Test
@@ -604,13 +613,16 @@
n.sbn.getId(), n.sbn.getNotification(), n.sbn.getUserId());
waitForIdle();
- mNotificationManagerService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+ mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
n.getUserId());
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(n.sbn.getPackageName());
assertEquals(0, notifs.length);
- assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+ assertEquals(0, mService.getNotificationRecordCount());
+ ArgumentCaptor<NotificationStats> captor = ArgumentCaptor.forClass(NotificationStats.class);
+ verify(mListeners, times(1)).notifyRemovedLocked(any(), anyInt(), captor.capture());
+ assertEquals(NotificationStats.DISMISSAL_OTHER, captor.getValue().getDismissalSurface());
}
@Test
@@ -628,7 +640,7 @@
mBinderService.cancelAllNotifications(PKG, parent.sbn.getUserId());
waitForIdle();
- assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+ assertEquals(0, mService.getNotificationRecordCount());
}
@Test
@@ -641,7 +653,7 @@
mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
waitForIdle();
- assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+ assertEquals(0, mService.getNotificationRecordCount());
}
@Test
@@ -669,7 +681,7 @@
parentAsChild.sbn.getUserId());
waitForIdle();
- assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+ assertEquals(0, mService.getNotificationRecordCount());
}
@Test
@@ -683,7 +695,7 @@
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(sbn.getPackageName());
assertEquals(1, notifs.length);
- assertEquals(1, mNotificationManagerService.getNotificationRecordCount());
+ assertEquals(1, mService.getNotificationRecordCount());
}
@Test
@@ -697,7 +709,7 @@
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(sbn.getPackageName());
assertEquals(1, notifs.length);
- assertEquals(1, mNotificationManagerService.getNotificationRecordCount());
+ assertEquals(1, mService.getNotificationRecordCount());
}
@Test
@@ -710,7 +722,7 @@
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(sbn.getPackageName());
assertEquals(0, notifs.length);
- assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+ assertEquals(0, mService.getNotificationRecordCount());
}
@Test
@@ -724,7 +736,7 @@
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(sbn.getPackageName());
assertEquals(1, notifs.length);
- assertEquals(1, mNotificationManagerService.getNotificationRecordCount());
+ assertEquals(1, mService.getNotificationRecordCount());
}
@Test
@@ -745,8 +757,8 @@
final NotificationRecord notif = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
notif.getNotification().flags |= Notification.FLAG_NO_CLEAR;
- mNotificationManagerService.addNotification(notif);
- mNotificationManagerService.cancelAllNotificationsInt(mUid, 0, PKG, null, 0, 0, true,
+ mService.addNotification(notif);
+ mService.cancelAllNotificationsInt(mUid, 0, PKG, null, 0, 0, true,
notif.getUserId(), 0, null);
waitForIdle();
StatusBarNotification[] notifs =
@@ -759,9 +771,9 @@
final NotificationRecord notif = generateNotificationRecord(
mTestNotificationChannel, 1, "group", true);
notif.getNotification().flags |= Notification.FLAG_NO_CLEAR;
- mNotificationManagerService.addNotification(notif);
+ mService.addNotification(notif);
- mNotificationManagerService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+ mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
notif.getUserId());
waitForIdle();
StatusBarNotification[] notifs =
@@ -780,11 +792,11 @@
child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
final NotificationRecord newGroup = generateNotificationRecord(
mTestNotificationChannel, 4, "group2", false);
- mNotificationManagerService.addNotification(parent);
- mNotificationManagerService.addNotification(child);
- mNotificationManagerService.addNotification(child2);
- mNotificationManagerService.addNotification(newGroup);
- mNotificationManagerService.getBinderService().cancelNotificationsFromListener(null, null);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.getBinderService().cancelNotificationsFromListener(null, null);
waitForIdle();
StatusBarNotification[] notifs =
mBinderService.getActiveNotifications(parent.sbn.getPackageName());
@@ -802,11 +814,11 @@
child2.getNotification().flags |= Notification.FLAG_NO_CLEAR;
final NotificationRecord newGroup = generateNotificationRecord(
mTestNotificationChannel, 4, "group2", false);
- mNotificationManagerService.addNotification(parent);
- mNotificationManagerService.addNotification(child);
- mNotificationManagerService.addNotification(child2);
- mNotificationManagerService.addNotification(newGroup);
- mNotificationManagerService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
+ mService.addNotification(newGroup);
+ mService.mNotificationDelegate.onClearAll(mUid, Binder.getCallingPid(),
parent.getUserId());
waitForIdle();
StatusBarNotification[] notifs =
@@ -841,7 +853,7 @@
mBinderService.cancelNotificationWithTag(PKG, "tag", sbn.getId(), sbn.getUserId());
waitForIdle();
assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
- assertEquals(0, mNotificationManagerService.getNotificationRecordCount());
+ assertEquals(0, mService.getNotificationRecordCount());
}
@Test
@@ -849,8 +861,8 @@
// make sure the same notification can be found in both lists and returned
final NotificationRecord group1 = generateNotificationRecord(
mTestNotificationChannel, 1, "group1", true);
- mNotificationManagerService.addEnqueuedNotification(group1);
- mNotificationManagerService.addNotification(group1);
+ mService.addEnqueuedNotification(group1);
+ mService.addNotification(group1);
// should not be returned
final NotificationRecord group2 = generateNotificationRecord(
@@ -874,7 +886,7 @@
waitForIdle();
List<NotificationRecord> inGroup1 =
- mNotificationManagerService.findGroupNotificationsLocked(PKG, group1.getGroupKey(),
+ mService.findGroupNotificationsLocked(PKG, group1.getGroupKey(),
group1.sbn.getUserId());
assertEquals(3, inGroup1.size());
for (NotificationRecord record : inGroup1) {
@@ -885,8 +897,8 @@
@Test
public void testTvExtenderChannelOverride_onTv() throws Exception {
- mNotificationManagerService.setIsTelevision(true);
- mNotificationManagerService.setRankingHelper(mRankingHelper);
+ mService.setIsTelevision(true);
+ mService.setRankingHelper(mRankingHelper);
when(mRankingHelper.getNotificationChannel(
anyString(), anyInt(), eq("foo"), anyBoolean())).thenReturn(
new NotificationChannel("foo", "foo", IMPORTANCE_HIGH));
@@ -900,8 +912,8 @@
@Test
public void testTvExtenderChannelOverride_notOnTv() throws Exception {
- mNotificationManagerService.setIsTelevision(false);
- mNotificationManagerService.setRankingHelper(mRankingHelper);
+ mService.setIsTelevision(false);
+ mService.setRankingHelper(mRankingHelper);
when(mRankingHelper.getNotificationChannel(
anyString(), anyInt(), anyString(), anyBoolean())).thenReturn(
mTestNotificationChannel);
@@ -918,7 +930,7 @@
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
- mNotificationManagerService.setRankingHelper(mRankingHelper);
+ mService.setRankingHelper(mRankingHelper);
when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
.thenReturn(mTestNotificationChannel);
@@ -943,7 +955,7 @@
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
- mNotificationManagerService.setRankingHelper(mRankingHelper);
+ mService.setRankingHelper(mRankingHelper);
NotificationChannelGroup group1 = new NotificationChannelGroup("a", "b");
NotificationChannelGroup group2 = new NotificationChannelGroup("n", "m");
@@ -963,7 +975,7 @@
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
- mNotificationManagerService.setRankingHelper(mRankingHelper);
+ mService.setRankingHelper(mRankingHelper);
mTestNotificationChannel.setLightColor(Color.CYAN);
when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
@@ -981,7 +993,7 @@
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
- mNotificationManagerService.setRankingHelper(mRankingHelper);
+ mService.setRankingHelper(mRankingHelper);
when(mRankingHelper.getNotificationChannel(eq(PKG), anyInt(),
eq(mTestNotificationChannel.getId()), anyBoolean()))
.thenReturn(mTestNotificationChannel);
@@ -998,7 +1010,7 @@
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
NotificationChannelGroup ncg = new NotificationChannelGroup("a", "b/c");
- mNotificationManagerService.setRankingHelper(mRankingHelper);
+ mService.setRankingHelper(mRankingHelper);
when(mRankingHelper.getNotificationChannelGroup(eq(ncg.getId()), eq(PKG), anyInt()))
.thenReturn(ncg);
reset(mListeners);
@@ -1010,7 +1022,7 @@
@Test
public void testUpdateNotificationChannelFromPrivilegedListener_success() throws Exception {
- mNotificationManagerService.setRankingHelper(mRankingHelper);
+ mService.setRankingHelper(mRankingHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1028,7 +1040,7 @@
@Test
public void testUpdateNotificationChannelFromPrivilegedListener_noAccess() throws Exception {
- mNotificationManagerService.setRankingHelper(mRankingHelper);
+ mService.setRankingHelper(mRankingHelper);
List<String> associations = new ArrayList<>();
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1050,7 +1062,7 @@
@Test
public void testUpdateNotificationChannelFromPrivilegedListener_badUser() throws Exception {
- mNotificationManagerService.setRankingHelper(mRankingHelper);
+ mService.setRankingHelper(mRankingHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1077,7 +1089,7 @@
@Test
public void testGetNotificationChannelFromPrivilegedListener_success() throws Exception {
- mNotificationManagerService.setRankingHelper(mRankingHelper);
+ mService.setRankingHelper(mRankingHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1091,7 +1103,7 @@
@Test
public void testGetNotificationChannelFromPrivilegedListener_noAccess() throws Exception {
- mNotificationManagerService.setRankingHelper(mRankingHelper);
+ mService.setRankingHelper(mRankingHelper);
List<String> associations = new ArrayList<>();
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1109,7 +1121,7 @@
@Test
public void testGetNotificationChannelFromPrivilegedListener_badUser() throws Exception {
- mNotificationManagerService.setRankingHelper(mRankingHelper);
+ mService.setRankingHelper(mRankingHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1131,7 +1143,7 @@
@Test
public void testGetNotificationChannelGroupsFromPrivilegedListener_success() throws Exception {
- mNotificationManagerService.setRankingHelper(mRankingHelper);
+ mService.setRankingHelper(mRankingHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1144,7 +1156,7 @@
@Test
public void testGetNotificationChannelGroupsFromPrivilegedListener_noAccess() throws Exception {
- mNotificationManagerService.setRankingHelper(mRankingHelper);
+ mService.setRankingHelper(mRankingHelper);
List<String> associations = new ArrayList<>();
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
@@ -1161,7 +1173,7 @@
@Test
public void testGetNotificationChannelGroupsFromPrivilegedListener_badUser() throws Exception {
- mNotificationManagerService.setRankingHelper(mRankingHelper);
+ mService.setRankingHelper(mRankingHelper);
List<String> associations = new ArrayList<>();
when(mCompanionMgr.getAssociations(PKG, mUid)).thenReturn(associations);
mListener = mock(ManagedServices.ManagedServiceInfo.class);
@@ -1183,14 +1195,14 @@
public void testHasCompanionDevice_failure() throws Exception {
when(mCompanionMgr.getAssociations(anyString(), anyInt())).thenThrow(
new IllegalArgumentException());
- mNotificationManagerService.hasCompanionDevice(mListener);
+ mService.hasCompanionDevice(mListener);
}
@Test
public void testHasCompanionDevice_noService() throws Exception {
- mNotificationManagerService = new TestableNotificationManagerService(mContext);
+ mService = new TestableNotificationManagerService(mContext);
- assertFalse(mNotificationManagerService.hasCompanionDevice(mListener));
+ assertFalse(mService.hasCompanionDevice(mListener));
}
@Test
@@ -1199,16 +1211,17 @@
mTestNotificationChannel, 1, null, false);
final NotificationRecord grouped = generateNotificationRecord(
mTestNotificationChannel, 2, "group", false);
- mNotificationManagerService.addNotification(grouped);
- mNotificationManagerService.addNotification(nonGrouped);
+ mService.addNotification(grouped);
+ mService.addNotification(nonGrouped);
NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
- mNotificationManagerService.new SnoozeNotificationRunnable(
+ mService.new SnoozeNotificationRunnable(
nonGrouped.getKey(), 100, null);
snoozeNotificationRunnable.run();
// only snooze the one notification
verify(mSnoozeHelper, times(1)).snooze(any(NotificationRecord.class), anyLong());
+ assertTrue(nonGrouped.getStats().hasSnoozed());
}
@Test
@@ -1219,12 +1232,12 @@
mTestNotificationChannel, 2, "group", false);
final NotificationRecord child2 = generateNotificationRecord(
mTestNotificationChannel, 3, "group", false);
- mNotificationManagerService.addNotification(parent);
- mNotificationManagerService.addNotification(child);
- mNotificationManagerService.addNotification(child2);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
- mNotificationManagerService.new SnoozeNotificationRunnable(
+ mService.new SnoozeNotificationRunnable(
parent.getKey(), 100, null);
snoozeNotificationRunnable.run();
@@ -1240,12 +1253,12 @@
mTestNotificationChannel, 2, "group", false);
final NotificationRecord child2 = generateNotificationRecord(
mTestNotificationChannel, 3, "group", false);
- mNotificationManagerService.addNotification(parent);
- mNotificationManagerService.addNotification(child);
- mNotificationManagerService.addNotification(child2);
+ mService.addNotification(parent);
+ mService.addNotification(child);
+ mService.addNotification(child2);
NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
- mNotificationManagerService.new SnoozeNotificationRunnable(
+ mService.new SnoozeNotificationRunnable(
child2.getKey(), 100, null);
snoozeNotificationRunnable.run();
@@ -1260,11 +1273,11 @@
assertTrue(parent.sbn.getNotification().isGroupSummary());
final NotificationRecord child = generateNotificationRecord(
mTestNotificationChannel, 2, "group", false);
- mNotificationManagerService.addNotification(parent);
- mNotificationManagerService.addNotification(child);
+ mService.addNotification(parent);
+ mService.addNotification(child);
NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
- mNotificationManagerService.new SnoozeNotificationRunnable(
+ mService.new SnoozeNotificationRunnable(
child.getKey(), 100, null);
snoozeNotificationRunnable.run();
@@ -1276,10 +1289,10 @@
public void testSnoozeRunnable_snoozeGroupChild_noOthersInGroup() throws Exception {
final NotificationRecord child = generateNotificationRecord(
mTestNotificationChannel, 2, "group", false);
- mNotificationManagerService.addNotification(child);
+ mService.addNotification(child);
NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
- mNotificationManagerService.new SnoozeNotificationRunnable(
+ mService.new SnoozeNotificationRunnable(
child.getKey(), 100, null);
snoozeNotificationRunnable.run();
@@ -1427,9 +1440,9 @@
@Test
public void testOnlyAutogroupIfGroupChanged_noPriorNoti_autogroups() throws Exception {
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, null, false);
- mNotificationManagerService.addEnqueuedNotification(r);
+ mService.addEnqueuedNotification(r);
NotificationManagerService.PostNotificationRunnable runnable =
- mNotificationManagerService.new PostNotificationRunnable(r.getKey());
+ mService.new PostNotificationRunnable(r.getKey());
runnable.run();
waitForIdle();
@@ -1441,12 +1454,12 @@
throws Exception {
NotificationRecord r =
generateNotificationRecord(mTestNotificationChannel, 0, "group", false);
- mNotificationManagerService.addNotification(r);
+ mService.addNotification(r);
r = generateNotificationRecord(mTestNotificationChannel, 0, null, false);
- mNotificationManagerService.addEnqueuedNotification(r);
+ mService.addEnqueuedNotification(r);
NotificationManagerService.PostNotificationRunnable runnable =
- mNotificationManagerService.new PostNotificationRunnable(r.getKey());
+ mService.new PostNotificationRunnable(r.getKey());
runnable.run();
waitForIdle();
@@ -1458,11 +1471,11 @@
throws Exception {
NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, "group",
false);
- mNotificationManagerService.addNotification(r);
- mNotificationManagerService.addEnqueuedNotification(r);
+ mService.addNotification(r);
+ mService.addEnqueuedNotification(r);
NotificationManagerService.PostNotificationRunnable runnable =
- mNotificationManagerService.new PostNotificationRunnable(r.getKey());
+ mService.new PostNotificationRunnable(r.getKey());
runnable.run();
waitForIdle();
@@ -1486,7 +1499,7 @@
nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
waitForIdle();
- NotificationRecord posted = mNotificationManagerService.findNotificationLocked(
+ NotificationRecord posted = mService.findNotificationLocked(
PKG, null, nr.sbn.getId(), nr.sbn.getUserId());
assertFalse(posted.getNotification().isColorized());
@@ -1497,12 +1510,12 @@
for (int i = 0; i < 20; i++) {
NotificationRecord r =
generateNotificationRecord(mTestNotificationChannel, i, null, false);
- mNotificationManagerService.addEnqueuedNotification(r);
+ mService.addEnqueuedNotification(r);
}
for (int i = 0; i < 20; i++) {
NotificationRecord r =
generateNotificationRecord(mTestNotificationChannel, i, null, false);
- mNotificationManagerService.addNotification(r);
+ mService.addNotification(r);
}
// another package
@@ -1515,31 +1528,31 @@
n, new UserHandle(mUid), null, 0);
NotificationRecord otherPackage =
new NotificationRecord(mContext, sbn, mTestNotificationChannel);
- mNotificationManagerService.addEnqueuedNotification(otherPackage);
- mNotificationManagerService.addNotification(otherPackage);
+ mService.addEnqueuedNotification(otherPackage);
+ mService.addNotification(otherPackage);
// Same notifications are enqueued as posted, everything counts b/c id and tag don't match
int userId = new UserHandle(mUid).getIdentifier();
assertEquals(40,
- mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, null));
+ mService.getNotificationCountLocked(PKG, userId, 0, null));
assertEquals(40,
- mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, "tag2"));
+ mService.getNotificationCountLocked(PKG, userId, 0, "tag2"));
assertEquals(2,
- mNotificationManagerService.getNotificationCountLocked("a", userId, 0, "banana"));
+ mService.getNotificationCountLocked("a", userId, 0, "banana"));
// exclude a known notification - it's excluded from only the posted list, not enqueued
assertEquals(39,
- mNotificationManagerService.getNotificationCountLocked(PKG, userId, 0, "tag"));
+ mService.getNotificationCountLocked(PKG, userId, 0, "tag"));
}
@Test
public void testAddAutogroup_requestsSort() throws Exception {
RankingHandler rh = mock(RankingHandler.class);
- mNotificationManagerService.setRankingHandler(rh);
+ mService.setRankingHandler(rh);
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
- mNotificationManagerService.addNotification(r);
- mNotificationManagerService.addAutogroupKeyLocked(r.getKey());
+ mService.addNotification(r);
+ mService.addAutogroupKeyLocked(r.getKey());
verify(rh, times(1)).requestSort();
}
@@ -1547,12 +1560,12 @@
@Test
public void testRemoveAutogroup_requestsSort() throws Exception {
RankingHandler rh = mock(RankingHandler.class);
- mNotificationManagerService.setRankingHandler(rh);
+ mService.setRankingHandler(rh);
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
r.setOverrideGroupKey("TEST");
- mNotificationManagerService.addNotification(r);
- mNotificationManagerService.removeAutogroupKeyLocked(r.getKey());
+ mService.addNotification(r);
+ mService.removeAutogroupKeyLocked(r.getKey());
verify(rh, times(1)).requestSort();
}
@@ -1560,47 +1573,47 @@
@Test
public void testReaddAutogroup_noSort() throws Exception {
RankingHandler rh = mock(RankingHandler.class);
- mNotificationManagerService.setRankingHandler(rh);
+ mService.setRankingHandler(rh);
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
r.setOverrideGroupKey("TEST");
- mNotificationManagerService.addNotification(r);
- mNotificationManagerService.addAutogroupKeyLocked(r.getKey());
+ mService.addNotification(r);
+ mService.addAutogroupKeyLocked(r.getKey());
verify(rh, never()).requestSort();
}
@Test
public void testHandleRankingSort_sendsUpdateOnSignalExtractorChange() throws Exception {
- mNotificationManagerService.setRankingHelper(mRankingHelper);
+ mService.setRankingHelper(mRankingHelper);
NotificationManagerService.WorkerHandler handler = mock(
NotificationManagerService.WorkerHandler.class);
- mNotificationManagerService.setHandler(handler);
+ mService.setHandler(handler);
Map<String, Answer> answers = getSignalExtractorSideEffects();
for (String message : answers.keySet()) {
- mNotificationManagerService.clearNotifications();
+ mService.clearNotifications();
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
- mNotificationManagerService.addNotification(r);
+ mService.addNotification(r);
doAnswer(answers.get(message)).when(mRankingHelper).extractSignals(r);
- mNotificationManagerService.handleRankingSort();
+ mService.handleRankingSort();
}
verify(handler, times(answers.size())).scheduleSendRankingUpdate();
}
@Test
public void testHandleRankingSort_noUpdateWhenNoSignalChange() throws Exception {
- mNotificationManagerService.setRankingHelper(mRankingHelper);
+ mService.setRankingHelper(mRankingHelper);
NotificationManagerService.WorkerHandler handler = mock(
NotificationManagerService.WorkerHandler.class);
- mNotificationManagerService.setHandler(handler);
+ mService.setHandler(handler);
final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
- mNotificationManagerService.addNotification(r);
+ mService.addNotification(r);
- mNotificationManagerService.handleRankingSort();
+ mService.handleRankingSort();
verify(handler, never()).scheduleSendRankingUpdate();
}
@@ -1619,7 +1632,7 @@
+ "<service_listing approved=\"test\" user=\"0\" primary=\"true\" />"
+ "</dnd_apps>"
+ "</notification-policy>";
- mNotificationManagerService.readPolicyXml(
+ mService.readPolicyXml(
new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())), false);
verify(mListeners, times(1)).readXml(any());
verify(mConditionProviders, times(1)).readXml(any());
@@ -1637,7 +1650,7 @@
+ "<zen></zen>"
+ "<ranking></ranking>"
+ "</notification-policy>";
- mNotificationManagerService.readPolicyXml(
+ mService.readPolicyXml(
new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())), false);
verify(mListeners, never()).readXml(any());
verify(mConditionProviders, never()).readXml(any());
@@ -1653,8 +1666,8 @@
@Test
public void testLocaleChangedCallsUpdateDefaultZenModeRules() throws Exception {
ZenModeHelper mZenModeHelper = mock(ZenModeHelper.class);
- mNotificationManagerService.mZenModeHelper = mZenModeHelper;
- mNotificationManagerService.mLocaleChangeReceiver.onReceive(mContext,
+ mService.mZenModeHelper = mZenModeHelper;
+ mService.mLocaleChangeReceiver.onReceive(mContext,
new Intent(Intent.ACTION_LOCALE_CHANGED));
verify(mZenModeHelper, times(1)).updateDefaultZenRules();
@@ -1685,7 +1698,7 @@
sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
assertEquals(IMPORTANCE_LOW,
- mNotificationManagerService.getNotificationRecord(sbn.getKey()).getImportance());
+ mService.getNotificationRecord(sbn.getKey()).getImportance());
nb = new Notification.Builder(mContext)
.setContentTitle("foo")
@@ -1700,10 +1713,85 @@
sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
assertEquals(IMPORTANCE_LOW,
- mNotificationManagerService.getNotificationRecord(sbn.getKey()).getImportance());
+ mService.getNotificationRecord(sbn.getKey()).getImportance());
NotificationChannel defaultChannel = mBinderService.getNotificationChannel(
preOPkg, NotificationChannel.DEFAULT_CHANNEL_ID);
assertEquals(IMPORTANCE_UNSPECIFIED, defaultChannel.getImportance());
}
+
+ @Test
+ public void testStats_updatedOnDirectReply() throws Exception {
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
+ assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasDirectReplied());
+ }
+
+ @Test
+ public void testStats_updatedOnExpansion() throws Exception {
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, true);
+ assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+ mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, false);
+ assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
+ }
+
+ @Test
+ public void testStats_updatedOnViewSettings() throws Exception {
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationSettingsViewed(r.getKey());
+ assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasViewedSettings());
+ }
+
+ @Test
+ public void testStats_updatedOnVisibilityChanged() throws Exception {
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+
+ NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 1, true);
+ mService.mNotificationDelegate.onNotificationVisibilityChanged(
+ new NotificationVisibility[] {nv}, new NotificationVisibility[]{});
+ assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasSeen());
+ mService.mNotificationDelegate.onNotificationVisibilityChanged(
+ new NotificationVisibility[] {}, new NotificationVisibility[]{nv});
+ assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasSeen());
+ }
+
+ @Test
+ public void testStats_dismissalSurface() throws Exception {
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+
+ mService.mNotificationDelegate.onNotificationClear(mUid, 0, PKG, r.sbn.getTag(),
+ r.sbn.getId(), r.getUserId(), r.getKey(), NotificationStats.DISMISSAL_AOD);
+ waitForIdle();
+
+ assertEquals(NotificationStats.DISMISSAL_AOD, r.getStats().getDismissalSurface());
+ }
+
+ @Test
+ public void testUserSentimentChangeTriggersUpdate() throws Exception {
+ final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
+ mService.addNotification(r);
+ NotificationManagerService.WorkerHandler handler = mock(
+ NotificationManagerService.WorkerHandler.class);
+ mService.setHandler(handler);
+
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_USER_SENTIMENT,
+ NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
+ Adjustment adjustment = new Adjustment(
+ r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
+ mBinderService.applyAdjustmentFromAssistant(null, adjustment);
+
+ waitForIdle();
+
+ verify(handler, timeout(300).times(1)).scheduleSendRankingUpdate();
+ }
}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
index 5a6225a..ef26705a 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
@@ -15,6 +15,11 @@
*/
package com.android.server.notification;
+import static android.service.notification.NotificationListenerService.Ranking
+ .USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking
+ .USER_SENTIMENT_NEUTRAL;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
@@ -38,8 +43,10 @@
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.Build;
+import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
+import android.service.notification.Adjustment;
import android.service.notification.StatusBarNotification;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -481,5 +488,64 @@
record.getLogMaker().getTaggedData(MetricsEvent.FIELD_NOTIFICATION_GROUP_ID));
}
+ @Test
+ public void testNotificationStats() throws Exception {
+ StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+ true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+ false /* lights */, false /* defaultLights */, groupId /* group */);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+ assertFalse(record.getStats().hasSeen());
+ assertFalse(record.isSeen());
+ assertFalse(record.getStats().hasDirectReplied());
+ assertFalse(record.getStats().hasExpanded());
+ assertFalse(record.getStats().hasInteracted());
+ assertFalse(record.getStats().hasViewedSettings());
+ assertFalse(record.getStats().hasSnoozed());
+
+ record.setSeen();
+ assertTrue(record.getStats().hasSeen());
+ assertTrue(record.isSeen());
+ assertFalse(record.getStats().hasDirectReplied());
+ assertFalse(record.getStats().hasExpanded());
+ assertFalse(record.getStats().hasInteracted());
+ assertFalse(record.getStats().hasViewedSettings());
+ assertFalse(record.getStats().hasSnoozed());
+
+ record.recordViewedSettings();
+ assertFalse(record.getStats().hasDirectReplied());
+ assertFalse(record.getStats().hasExpanded());
+ assertTrue(record.getStats().hasViewedSettings());
+ assertFalse(record.getStats().hasSnoozed());
+
+ record.recordSnoozed();
+ assertFalse(record.getStats().hasDirectReplied());
+ assertFalse(record.getStats().hasExpanded());
+ assertTrue(record.getStats().hasSnoozed());
+
+ record.recordExpanded();
+ assertFalse(record.getStats().hasDirectReplied());
+ assertTrue(record.getStats().hasExpanded());
+
+ record.recordDirectReplied();
+ assertTrue(record.getStats().hasDirectReplied());
+ }
+
+ @Test
+ public void testUserSentiment() throws Exception {
+ StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
+ true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+ false /* lights */, false /* defaultLights */, groupId /* group */);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
+
+ assertEquals(USER_SENTIMENT_NEUTRAL, record.getUserSentiment());
+
+ Bundle signals = new Bundle();
+ signals.putInt(Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE);
+ record.addAdjustment(new Adjustment(pkg, record.getKey(), signals, null, sbn.getUserId()));
+
+ record.applyAdjustments();
+
+ assertEquals(USER_SENTIMENT_NEGATIVE, record.getUserSentiment());
+ }
}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationStatsTest.java b/services/tests/notification/src/com/android/server/notification/NotificationStatsTest.java
new file mode 100644
index 0000000..fec2811
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/NotificationStatsTest.java
@@ -0,0 +1,93 @@
+package com.android.server.notification;
+
+import static android.service.notification.NotificationStats.DISMISSAL_PEEK;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.service.notification.NotificationStats;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationStatsTest extends NotificationTestCase {
+
+ @Test
+ public void testConstructor() {
+ NotificationStats stats = new NotificationStats();
+
+ assertFalse(stats.hasSeen());
+ assertFalse(stats.hasDirectReplied());
+ assertFalse(stats.hasExpanded());
+ assertFalse(stats.hasInteracted());
+ assertFalse(stats.hasViewedSettings());
+ assertFalse(stats.hasSnoozed());
+ assertEquals(NotificationStats.DISMISSAL_NOT_DISMISSED, stats.getDismissalSurface());
+ }
+
+ @Test
+ public void testSeen() {
+ NotificationStats stats = new NotificationStats();
+ stats.setSeen();
+ assertTrue(stats.hasSeen());
+ assertFalse(stats.hasInteracted());
+ }
+
+ @Test
+ public void testDirectReplied() {
+ NotificationStats stats = new NotificationStats();
+ stats.setDirectReplied();
+ assertTrue(stats.hasDirectReplied());
+ assertTrue(stats.hasInteracted());
+ }
+
+ @Test
+ public void testExpanded() {
+ NotificationStats stats = new NotificationStats();
+ stats.setExpanded();
+ assertTrue(stats.hasExpanded());
+ assertTrue(stats.hasInteracted());
+ }
+
+ @Test
+ public void testSnoozed() {
+ NotificationStats stats = new NotificationStats();
+ stats.setSnoozed();
+ assertTrue(stats.hasSnoozed());
+ assertTrue(stats.hasInteracted());
+ }
+
+ @Test
+ public void testViewedSettings() {
+ NotificationStats stats = new NotificationStats();
+ stats.setViewedSettings();
+ assertTrue(stats.hasViewedSettings());
+ assertTrue(stats.hasInteracted());
+ }
+
+ @Test
+ public void testDismissalSurface() {
+ NotificationStats stats = new NotificationStats();
+ stats.setDismissalSurface(DISMISSAL_PEEK);
+ assertEquals(DISMISSAL_PEEK, stats.getDismissalSurface());
+ assertFalse(stats.hasInteracted());
+ }
+
+ @Test
+ public void testWriteToParcel() {
+ NotificationStats stats = new NotificationStats();
+ stats.setViewedSettings();
+ stats.setDismissalSurface(NotificationStats.DISMISSAL_AOD);
+ Parcel parcel = Parcel.obtain();
+ stats.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ NotificationStats stats1 = NotificationStats.CREATOR.createFromParcel(parcel);
+ assertEquals(stats, stats1);
+ }
+}
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 2249ff0..df989f7 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -25,25 +25,13 @@
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.fail;
-import org.json.JSONArray;
-import org.json.JSONObject;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import com.android.internal.util.FastXmlSerializer;
-
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlSerializer;
-
import android.app.Notification;
-import android.app.NotificationChannelGroup;
-import android.content.Context;
import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
+import android.content.ContentProvider;
+import android.content.Context;
+import android.content.IContentProvider;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -52,14 +40,28 @@
import android.net.Uri;
import android.os.Build;
import android.os.UserHandle;
+import android.provider.Settings;
import android.provider.Settings.Secure;
import android.service.notification.StatusBarNotification;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.TestableContentResolver;
import android.util.ArrayMap;
import android.util.Xml;
+import com.android.internal.util.FastXmlSerializer;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlSerializer;
+
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
@@ -76,6 +78,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
@@ -95,10 +98,17 @@
private static final int UID2 = 1111;
private static final UserHandle USER2 = UserHandle.of(10);
private static final String TEST_CHANNEL_ID = "test_channel_id";
+ private static final String TEST_AUTHORITY = "test";
+ private static final Uri SOUND_URI =
+ Uri.parse("content://" + TEST_AUTHORITY + "/internal/audio/media/10");
+ private static final Uri CANONICAL_SOUND_URI =
+ Uri.parse("content://" + TEST_AUTHORITY
+ + "/internal/audio/media/10?title=Test&canonical=1");
@Mock NotificationUsageStats mUsageStats;
@Mock RankingHandler mHandler;
@Mock PackageManager mPm;
+ @Mock IContentProvider mTestIContentProvider;
@Mock Context mContext;
private Notification mNotiGroupGSortA;
@@ -134,9 +144,22 @@
when(mContext.getPackageManager()).thenReturn(mPm);
when(mContext.getApplicationInfo()).thenReturn(legacy);
// most tests assume badging is enabled
- Secure.putIntForUser(getContext().getContentResolver(),
+ TestableContentResolver contentResolver = getContext().getContentResolver();
+ contentResolver.setFallbackToExisting(false);
+ Secure.putIntForUser(contentResolver,
Secure.NOTIFICATION_BADGING, 1, UserHandle.getUserId(UID));
+ ContentProvider testContentProvider = mock(ContentProvider.class);
+ when(testContentProvider.getIContentProvider()).thenReturn(mTestIContentProvider);
+ contentResolver.addProvider(TEST_AUTHORITY, testContentProvider);
+
+ when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI)))
+ .thenReturn(CANONICAL_SOUND_URI);
+ when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ .thenReturn(CANONICAL_SOUND_URI);
+ when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ .thenReturn(SOUND_URI);
+
mHelper = new RankingHelper(getContext(), mPm, mHandler, mUsageStats,
new String[] {ImportanceExtractor.class.getName()});
@@ -214,9 +237,12 @@
}
private void loadStreamXml(ByteArrayOutputStream stream, boolean forRestore) throws Exception {
+ loadByteArrayXml(stream.toByteArray(), forRestore);
+ }
+
+ private void loadByteArrayXml(byte[] byteArray, boolean forRestore) throws Exception {
XmlPullParser parser = Xml.newPullParser();
- parser.setInput(new BufferedInputStream(new ByteArrayInputStream(stream.toByteArray())),
- null);
+ parser.setInput(new BufferedInputStream(new ByteArrayInputStream(byteArray)), null);
parser.nextTag();
mHelper.readXml(parser, forRestore);
}
@@ -377,7 +403,7 @@
NotificationChannel channel2 =
new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
channel2.setDescription("descriptions for all");
- channel2.setSound(new Uri.Builder().scheme("test").build(), mAudioAttributes);
+ channel2.setSound(SOUND_URI, mAudioAttributes);
channel2.enableLights(true);
channel2.setBypassDnd(true);
channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
@@ -439,6 +465,109 @@
}
@Test
+ public void testBackupXml_backupCanonicalizedSoundUri() throws Exception {
+ NotificationChannel channel =
+ new NotificationChannel("id", "name", IMPORTANCE_LOW);
+ channel.setSound(SOUND_URI, mAudioAttributes);
+ mHelper.createNotificationChannel(PKG, UID, channel, true);
+
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+
+ // Testing that in restore we are given the canonical version
+ loadStreamXml(baos, true);
+ verify(mTestIContentProvider).uncanonicalize(any(), eq(CANONICAL_SOUND_URI));
+ }
+
+ @Test
+ public void testRestoreXml_withExistentCanonicalizedSoundUri() throws Exception {
+ Uri localUri = Uri.parse("content://" + TEST_AUTHORITY + "/local/url");
+ Uri canonicalBasedOnLocal = localUri.buildUpon()
+ .appendQueryParameter("title", "Test")
+ .appendQueryParameter("canonical", "1")
+ .build();
+ when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ .thenReturn(canonicalBasedOnLocal);
+ when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ .thenReturn(localUri);
+ when(mTestIContentProvider.uncanonicalize(any(), eq(canonicalBasedOnLocal)))
+ .thenReturn(localUri);
+
+ NotificationChannel channel =
+ new NotificationChannel("id", "name", IMPORTANCE_LOW);
+ channel.setSound(SOUND_URI, mAudioAttributes);
+ mHelper.createNotificationChannel(PKG, UID, channel, true);
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+
+ loadStreamXml(baos, true);
+
+ NotificationChannel actualChannel = mHelper.getNotificationChannel(
+ PKG, UID, channel.getId(), false);
+ assertEquals(localUri, actualChannel.getSound());
+ }
+
+ @Test
+ public void testRestoreXml_withNonExistentCanonicalizedSoundUri() throws Exception {
+ Thread.sleep(3000);
+ when(mTestIContentProvider.canonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ .thenReturn(null);
+ when(mTestIContentProvider.uncanonicalize(any(), eq(CANONICAL_SOUND_URI)))
+ .thenReturn(null);
+
+ NotificationChannel channel =
+ new NotificationChannel("id", "name", IMPORTANCE_LOW);
+ channel.setSound(SOUND_URI, mAudioAttributes);
+ mHelper.createNotificationChannel(PKG, UID, channel, true);
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+
+ loadStreamXml(baos, true);
+
+ NotificationChannel actualChannel = mHelper.getNotificationChannel(
+ PKG, UID, channel.getId(), false);
+ assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
+ }
+
+
+ /**
+ * Although we don't make backups with uncanonicalized uris anymore, we used to, so we have to
+ * handle its restore properly.
+ */
+ @Test
+ public void testRestoreXml_withUncanonicalizedNonLocalSoundUri() throws Exception {
+ // Not a local uncanonicalized uri, simulating that it fails to exist locally
+ when(mTestIContentProvider.canonicalize(any(), eq(SOUND_URI))).thenReturn(null);
+ String id = "id";
+ String backupWithUncanonicalizedSoundUri = "<ranking version=\"1\">\n"
+ + "<package name=\"com.android.server.notification\" show_badge=\"true\">\n"
+ + "<channel id=\"" + id + "\" name=\"name\" importance=\"2\" "
+ + "sound=\"" + SOUND_URI + "\" "
+ + "usage=\"6\" content_type=\"0\" flags=\"1\" show_badge=\"true\" />\n"
+ + "<channel id=\"miscellaneous\" name=\"Uncategorized\" usage=\"5\" "
+ + "content_type=\"4\" flags=\"0\" show_badge=\"true\" />\n"
+ + "</package>\n"
+ + "</ranking>\n";
+
+ loadByteArrayXml(backupWithUncanonicalizedSoundUri.getBytes(), true);
+
+ NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG, UID, id, false);
+ assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound());
+ }
+
+ @Test
+ public void testBackupRestoreXml_withNullSoundUri() throws Exception {
+ NotificationChannel channel =
+ new NotificationChannel("id", "name", IMPORTANCE_LOW);
+ channel.setSound(null, mAudioAttributes);
+ mHelper.createNotificationChannel(PKG, UID, channel, true);
+ ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
+
+ loadStreamXml(baos, true);
+
+ NotificationChannel actualChannel = mHelper.getNotificationChannel(
+ PKG, UID, channel.getId(), false);
+ assertEquals(null, actualChannel.getSound());
+ }
+
+ @Test
public void testChannelXml_backup() throws Exception {
NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye");
NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello");
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index fd83223..6d7d4cb 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -62,6 +62,8 @@
LOCAL_STATIC_JAVA_LIBRARIES += ub-uiautomator
+LOCAL_PROGUARD_ENABLED := disabled
+
include $(BUILD_PACKAGE)
include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
new file mode 100644
index 0000000..e1623b0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -0,0 +1,96 @@
+/*
+ * 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.server.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
+import android.content.ComponentName;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+import static com.android.server.am.ActivityManagerService.ANIMATE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.times;
+
+/**
+ * Tests for the {@link ActivityStack} class.
+ *
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.am.ActivityStarterTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ActivityStarterTests extends ActivityTestsBase {
+ private static final ComponentName testActivityComponent =
+ ComponentName.unflattenFromString("com.foo/.BarActivity");
+
+ private ActivityManagerService mService;
+ private ActivityStarter mStarter;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mService = createActivityManagerService();
+ mStarter = new ActivityStarter(mService, mService.mStackSupervisor);
+ }
+
+ @Test
+ public void testUpdateLaunchBounds() throws Exception {
+ // When in a non-resizeable stack, the task bounds should be updated.
+ final TaskRecord task = createTask(mService.mStackSupervisor, testActivityComponent,
+ mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */));
+ final Rect bounds = new Rect(10, 10, 100, 100);
+
+ mStarter.updateBounds(task, bounds);
+ assertEquals(task.mBounds, bounds);
+ assertEquals(task.getStack().mBounds, null);
+
+ // When in a resizeable stack, the stack bounds should be updated as well.
+ final TaskRecord task2 = createTask(mService.mStackSupervisor, testActivityComponent,
+ mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */));
+ assertTrue(task2.getStack() instanceof PinnedActivityStack);
+ mStarter.updateBounds(task2, bounds);
+
+ verify(mService, times(1)).resizeStack(eq(task2.getStack().mStackId),
+ eq(bounds), anyBoolean(), anyBoolean(), anyBoolean(), anyInt());
+
+ // In the case of no animation, the stack and task bounds should be set immediately.
+ if (!ANIMATE) {
+ assertEquals(task2.getStack().mBounds, bounds);
+ assertEquals(task2.mBounds, bounds);
+ } else {
+ assertEquals(task2.mBounds, null);
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index b4bfa62..cc8bd69 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -22,6 +22,7 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
import org.mockito.invocation.InvocationOnMock;
@@ -38,8 +39,8 @@
import android.support.test.InstrumentationRegistry;
import com.android.server.AttributeCache;
import com.android.server.wm.AppWindowContainerController;
+import com.android.server.wm.PinnedStackWindowController;
import com.android.server.wm.StackWindowController;
-
import com.android.server.wm.TaskWindowContainerController;
import com.android.server.wm.WindowManagerService;
import com.android.server.wm.WindowTestUtils;
@@ -54,10 +55,6 @@
private final Context mContext = InstrumentationRegistry.getContext();
private HandlerThread mHandlerThread;
- // Grabbing an instance of {@link WindowManagerService} creates it if not present so this must
- // be called at before any tests.
- private final WindowManagerService mWms = WindowTestUtils.getWindowManagerService(mContext);
-
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -71,8 +68,8 @@
}
protected ActivityManagerService createActivityManagerService() {
- final ActivityManagerService service = new TestActivityManagerService(mContext);
- service.mWindowManager = WindowTestUtils.getMockWindowManagerService();
+ final ActivityManagerService service = spy(new TestActivityManagerService(mContext));
+ service.mWindowManager = prepareMockWindowManager();
return service;
}
@@ -135,7 +132,7 @@
mSupportsSplitScreenMultiWindow = true;
mSupportsFreeformWindowManagement = true;
mSupportsPictureInPicture = true;
- mWindowManager = WindowTestUtils.getWindowManagerService(context);
+ mWindowManager = WindowTestUtils.getMockWindowManagerService();
}
@Override
@@ -237,6 +234,12 @@
Rect getDefaultPictureInPictureBounds(float aspectRatio) {
return new Rect(50, 50, 100, 100);
}
+
+ @Override
+ PinnedStackWindowController createStackWindowController(int displayId,
+ boolean onTop, Rect outBounds) {
+ return mock(PinnedStackWindowController.class);
+ }
};
} else {
return (T) new TestActivityStack(
@@ -246,7 +249,7 @@
}
private static WindowManagerService prepareMockWindowManager() {
- final WindowManagerService service = mock(WindowManagerService.class);
+ final WindowManagerService service = WindowTestUtils.getMockWindowManagerService();
doAnswer((InvocationOnMock invocationOnMock) -> {
final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
@@ -264,7 +267,7 @@
}
/**
- * Override of {@link ActivityStack} that tracks test metrics, such as the number of times a
+ * Overrided of {@link ActivityStack} that tracks test metrics, such as the number of times a
* method is called. Note that its functionality depends on the implementations of the
* construction arguments.
*/
diff --git a/services/tests/servicestests/src/com/android/server/am/LaunchBoundsTests.java b/services/tests/servicestests/src/com/android/server/am/LaunchBoundsTests.java
new file mode 100644
index 0000000..e6d6831
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/LaunchBoundsTests.java
@@ -0,0 +1,245 @@
+/*
+ * 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.server.am;
+
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ActivityInfo.WindowLayout;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import android.view.Display;
+import android.view.Gravity;
+import org.junit.runner.RunWith;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.mockito.invocation.InvocationOnMock;
+
+import java.util.ArrayList;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.doAnswer;
+import static org.junit.Assert.assertEquals;
+
+
+/**
+ * Tests for exercising resizing bounds.
+ *
+ * Build/Install/Run:
+ * bit FrameworksServicesTests:com.android.server.am.LaunchBoundsTests
+ */
+@MediumTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class LaunchBoundsTests extends ActivityTestsBase {
+ private final ComponentName testActivityComponent =
+ ComponentName.unflattenFromString("com.foo/.BarActivity");
+ private final ComponentName testActivityComponent2 =
+ ComponentName.unflattenFromString("com.foo/.BarActivity2");
+
+ private final static int STACK_WIDTH = 100;
+ private final static int STACK_HEIGHT = 200;
+
+ private final static Rect STACK_BOUNDS = new Rect(0, 0, STACK_WIDTH, STACK_HEIGHT);
+
+ private ActivityManagerService mService;
+ private ActivityStack mStack;
+ private TaskRecord mTask;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mService = createActivityManagerService();
+ mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+ WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ mStack.requestResize(STACK_BOUNDS);
+
+ // We must create the task after resizing to make sure it does not inherit the stack
+ // dimensions on resize.
+ mTask = createTask(mService.mStackSupervisor, testActivityComponent, mStack);
+ }
+
+ /**
+ * Ensures that the setup bounds are set as expected with the stack bounds set and the task
+ * bounds still {@code null}.
+ * @throws Exception
+ */
+ @Test
+ public void testInitialBounds() throws Exception {
+ assertEquals(mStack.mBounds, STACK_BOUNDS);
+ assertEquals(mTask.mBounds, null);
+ }
+
+ /**
+ * Ensures that a task positioned with no {@link WindowLayout} receives the default launch
+ * position.
+ * @throws Exception
+ */
+ @Test
+ public void testLaunchNoWindowLayout() throws Exception {
+ final Rect expectedTaskBounds = getDefaultBounds(Gravity.NO_GRAVITY);
+
+ mStack.layoutTaskInStack(mTask, null);
+
+ // We expect the task to be placed in the middle of the screen with margins applied.
+ assertEquals(mTask.mBounds, expectedTaskBounds);
+ }
+
+ /**
+ * Ensures that a task positioned with an empty {@link WindowLayout} receives the default launch
+ * position.
+ * @throws Exception
+ */
+ @Test
+ public void testlaunchEmptyWindowLayout() throws Exception {
+ final Rect expectedTaskBounds = getDefaultBounds(Gravity.NO_GRAVITY);
+
+ WindowLayout layout = new WindowLayout(0, 0, 0, 0, 0, 0, 0);
+ mStack.layoutTaskInStack(mTask, layout);
+ assertEquals(mTask.mBounds, expectedTaskBounds);
+ }
+
+ /**
+ * Ensures that a task positioned with a {@link WindowLayout} gravity specified is positioned
+ * according to specification.
+ * @throws Exception
+ */
+ @Test
+ public void testlaunchWindowLayoutGravity() throws Exception {
+ // Unspecified gravity should be ignored
+ testGravity(Gravity.NO_GRAVITY);
+
+ // Unsupported gravity should be ignored
+ testGravity(Gravity.LEFT);
+ testGravity(Gravity.RIGHT);
+
+ // Test defaults for vertical gravities
+ testGravity(Gravity.TOP);
+ testGravity(Gravity.BOTTOM);
+
+ // Test corners
+ testGravity(Gravity.TOP | Gravity.LEFT);
+ testGravity(Gravity.TOP | Gravity.RIGHT);
+ testGravity(Gravity.BOTTOM | Gravity.LEFT);
+ testGravity(Gravity.BOTTOM | Gravity.RIGHT);
+ }
+
+ private void testGravity(int gravity) {
+ final WindowLayout gravityLayout = new WindowLayout(0, 0, 0, 0, gravity, 0, 0);
+ mStack.layoutTaskInStack(mTask, gravityLayout);
+ assertEquals(mTask.mBounds, getDefaultBounds(gravity));
+ }
+
+ /**
+ * Ensures that a task which causes a conflict with another task when positioned is adjusted as
+ * expected.
+ * @throws Exception
+ */
+ @Test
+ public void testLaunchWindowCenterConflict() throws Exception {
+ testConflict(Gravity.NO_GRAVITY);
+ testConflict(Gravity.TOP);
+ testConflict(Gravity.BOTTOM);
+ testConflict(Gravity.TOP | Gravity.LEFT);
+ testConflict(Gravity.TOP | Gravity.RIGHT);
+ testConflict(Gravity.BOTTOM | Gravity.LEFT);
+ testConflict(Gravity.BOTTOM | Gravity.RIGHT);
+ }
+
+ private void testConflict(int gravity) {
+ final WindowLayout layout = new WindowLayout(0, 0, 0, 0, gravity, 0, 0);
+
+ // layout first task
+ mStack.layoutTaskInStack(mTask, layout /*windowLayout*/);
+
+ // Second task will be laid out on top of the first so starting bounds is the same.
+ final Rect expectedBounds = new Rect(mTask.mBounds);
+
+ ActivityRecord activity = null;
+ TaskRecord secondTask = null;
+
+ // wrap with try/finally to ensure cleanup of activity/stack.
+ try {
+ // empty tasks are ignored in conflicts
+ activity = createActivity(mService, testActivityComponent, mTask);
+
+ // Create secondary task
+ secondTask = createTask(mService.mStackSupervisor, testActivityComponent,
+ mStack);
+
+ // layout second task
+ mStack.layoutTaskInStack(secondTask, layout /*windowLayout*/);
+
+ if ((gravity & (Gravity.TOP | Gravity.RIGHT)) == (Gravity.TOP | Gravity.RIGHT)
+ || (gravity & (Gravity.BOTTOM | Gravity.RIGHT))
+ == (Gravity.BOTTOM | Gravity.RIGHT)) {
+ expectedBounds.offset(-LaunchingTaskPositioner.getHorizontalStep(mStack.mBounds),
+ 0);
+ } else if ((gravity & Gravity.TOP) == Gravity.TOP
+ || (gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
+ expectedBounds.offset(LaunchingTaskPositioner.getHorizontalStep(mStack.mBounds), 0);
+ } else {
+ expectedBounds.offset(LaunchingTaskPositioner.getHorizontalStep(mStack.mBounds),
+ LaunchingTaskPositioner.getVerticalStep(mStack.mBounds));
+ }
+
+ assertEquals(secondTask.mBounds, expectedBounds);
+ } finally {
+ // Remove task and activity to prevent influencing future tests
+ if (activity != null) {
+ mTask.removeActivity(activity);
+ }
+
+ if (secondTask != null) {
+ mStack.removeTask(secondTask, "cleanup", ActivityStack.REMOVE_TASK_MODE_DESTROYING);
+ }
+ }
+ }
+
+ private Rect getDefaultBounds(int gravity) {
+ final Rect bounds = new Rect();
+ bounds.set(mStack.mBounds);
+
+ final int verticalInset = LaunchingTaskPositioner.getFreeformStartTop(mStack.mBounds);
+ final int horizontalInset = LaunchingTaskPositioner.getFreeformStartLeft(mStack.mBounds);
+
+ bounds.inset(horizontalInset, verticalInset);
+
+ if ((gravity & (Gravity.TOP | Gravity.RIGHT)) == (Gravity.TOP | Gravity.RIGHT)) {
+ bounds.offsetTo(horizontalInset * 2, 0);
+ } else if ((gravity & Gravity.TOP) == Gravity.TOP) {
+ bounds.offsetTo(0, 0);
+ } else if ((gravity & (Gravity.BOTTOM | Gravity.RIGHT))
+ == (Gravity.BOTTOM | Gravity.RIGHT)) {
+ bounds.offsetTo(horizontalInset * 2, verticalInset * 2);
+ } else if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) {
+ bounds.offsetTo(0, verticalInset * 2);
+ }
+
+ return bounds;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
index 536a504..71bcae7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -16,19 +16,7 @@
package com.android.server.wm;
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-
-import android.content.res.Configuration;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.Before;
-import org.junit.After;
-
-import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -36,6 +24,15 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.Before;
+import org.junit.After;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
/**
* Tests for the {@link DisplayContent.TaskStackContainers} container in {@link DisplayContent}.
*
@@ -52,12 +49,8 @@
@Before
public void setUp() throws Exception {
super.setUp();
- final Configuration overrideConfig = new Configuration();
- overrideConfig.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
- mPinnedStack = new StackWindowController(PINNED_STACK_ID, null,
- mDisplayContent.getDisplayId(), true /* onTop */, new Rect(), sWm).mContainer;
- mPinnedStack.onOverrideConfigurationChanged(overrideConfig);
-
+ mPinnedStack = createStackControllerOnStackOnDisplay(
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mDisplayContent).mContainer;
// Stack should contain visible app window to be considered visible.
final Task pinnedTask = createTaskInStack(mPinnedStack, 0 /* userId */);
assertFalse(mPinnedStack.isVisible());
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 4e810f2..56a3fb0 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -36,8 +36,6 @@
import android.view.IWindow;
import android.view.WindowManager;
-import static android.app.ActivityManager.StackId.FIRST_DYNAMIC_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.app.AppOpsManager.OP_NONE;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
@@ -70,7 +68,7 @@
// make sure we don't collide with any existing display. If we run into no other display, the
// added display should be treated as default. This cannot be the default display
private static int sNextDisplayId = DEFAULT_DISPLAY + 1;
- private static int sNextStackId = FIRST_DYNAMIC_STACK_ID;
+ static int sNextStackId = 1000;
private static boolean sOneTimeSetupDone = false;
DisplayContent mDisplayContent;
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
new file mode 100644
index 0000000..b2446ba
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -0,0 +1,987 @@
+/**
+ * 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.server.usage;
+
+import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
+import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
+import static com.android.server.usage.UsageStatsService.MSG_REPORT_EVENT;
+
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.app.admin.DevicePolicyManager;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
+import android.appwidget.AppWidgetManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ParceledListSlice;
+import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
+import android.net.NetworkScoreManager;
+import android.os.BatteryManager;
+import android.os.BatteryStats;
+import android.os.Handler;
+import android.os.IDeviceIdleController;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+import android.util.SparseIntArray;
+import android.util.TimeUtils;
+import android.view.Display;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.LocalServices;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages the standby state of an app, listening to various events.
+ */
+public class AppStandbyController {
+
+ private static final String TAG = "AppStandbyController";
+ private static final boolean DEBUG = false;
+
+ static final boolean COMPRESS_TIME = false;
+ private static final long ONE_MINUTE = 60 * 1000;
+
+ // To name the lock for stack traces
+ static class Lock {}
+
+ /** Lock to protect the app's standby state. Required for calls into AppIdleHistory */
+ private final Object mAppIdleLock = new Lock();
+
+ /** Keeps the history and state for each app. */
+ @GuardedBy("mAppIdleLock")
+ private AppIdleHistory mAppIdleHistory;
+
+ @GuardedBy("mAppIdleLock")
+ private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
+ mPackageAccessListeners = new ArrayList<>();
+
+ /** Whether we've queried the list of carrier privileged apps. */
+ @GuardedBy("mAppIdleLock")
+ private boolean mHaveCarrierPrivilegedApps;
+
+ /** List of carrier-privileged apps that should be excluded from standby */
+ @GuardedBy("mAppIdleLock")
+ private List<String> mCarrierPrivilegedApps;
+
+ // Messages for the handler
+ static final int MSG_INFORM_LISTENERS = 3;
+ static final int MSG_FORCE_IDLE_STATE = 4;
+ static final int MSG_CHECK_IDLE_STATES = 5;
+ static final int MSG_CHECK_PAROLE_TIMEOUT = 6;
+ static final int MSG_PAROLE_END_TIMEOUT = 7;
+ static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
+ static final int MSG_PAROLE_STATE_CHANGED = 9;
+ static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
+
+ long mAppIdleScreenThresholdMillis;
+ long mCheckIdleIntervalMillis;
+ long mAppIdleWallclockThresholdMillis;
+ long mAppIdleParoleIntervalMillis;
+ long mAppIdleParoleDurationMillis;
+ boolean mAppIdleEnabled;
+ boolean mAppIdleTempParoled;
+ boolean mCharging;
+ private long mLastAppIdleParoledTime;
+ private boolean mSystemServicesReady = false;
+
+ private volatile boolean mPendingOneTimeCheckIdleStates;
+
+ private final Handler mHandler;
+ private final Context mContext;
+
+ private DisplayManager mDisplayManager;
+ private IDeviceIdleController mDeviceIdleController;
+ private AppWidgetManager mAppWidgetManager;
+ private IBatteryStats mBatteryStats;
+ private PowerManager mPowerManager;
+ private PackageManager mPackageManager;
+ private PackageManagerInternal mPackageManagerInternal;
+
+ AppStandbyController(Context context, Looper looper) {
+ mContext = context;
+ mHandler = new AppStandbyHandler(looper);
+ mPackageManager = mContext.getPackageManager();
+ mAppIdleEnabled = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_enableAutoPowerModes);
+ if (mAppIdleEnabled) {
+ IntentFilter deviceStates = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
+ deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
+ mContext.registerReceiver(new DeviceStateReceiver(), deviceStates);
+ }
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime());
+ }
+
+ IntentFilter packageFilter = new IntentFilter();
+ packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ packageFilter.addDataScheme("package");
+
+ mContext.registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, packageFilter,
+ null, mHandler);
+ }
+
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_SYSTEM_SERVICES_READY) {
+ // Observe changes to the threshold
+ SettingsObserver settingsObserver = new SettingsObserver(mHandler);
+ settingsObserver.registerObserver();
+ settingsObserver.updateSettings();
+
+ mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class);
+ mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
+ mBatteryStats = IBatteryStats.Stub.asInterface(
+ ServiceManager.getService(BatteryStats.SERVICE_NAME));
+ mDisplayManager = (DisplayManager) mContext.getSystemService(
+ Context.DISPLAY_SERVICE);
+ mPowerManager = mContext.getSystemService(PowerManager.class);
+ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+
+ mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.updateDisplay(isDisplayOn(), SystemClock.elapsedRealtime());
+ }
+
+ if (mPendingOneTimeCheckIdleStates) {
+ postOneTimeCheckIdleStates();
+ }
+
+ mSystemServicesReady = true;
+ } else if (phase == PHASE_BOOT_COMPLETED) {
+ setChargingState(mContext.getSystemService(BatteryManager.class).isCharging());
+ }
+ }
+
+ void reportContentProviderUsage(String authority, String providerPkgName, int userId) {
+ // Get sync adapters for the authority
+ String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
+ authority, userId);
+ for (String packageName: packages) {
+ // Only force the sync adapters to active if the provider is not in the same package and
+ // the sync adapter is a system package.
+ try {
+ PackageInfo pi = mPackageManager.getPackageInfoAsUser(
+ packageName, PackageManager.MATCH_SYSTEM_ONLY, userId);
+ if (pi == null || pi.applicationInfo == null) {
+ continue;
+ }
+ if (!packageName.equals(providerPkgName)) {
+ setAppIdleAsync(packageName, false, userId);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Shouldn't happen
+ }
+ }
+ }
+
+ void setChargingState(boolean charging) {
+ synchronized (mAppIdleLock) {
+ if (mCharging != charging) {
+ mCharging = charging;
+ postParoleStateChanged();
+ }
+ }
+ }
+
+ /** Paroled here means temporary pardon from being inactive */
+ void setAppIdleParoled(boolean paroled) {
+ synchronized (mAppIdleLock) {
+ final long now = System.currentTimeMillis();
+ if (mAppIdleTempParoled != paroled) {
+ mAppIdleTempParoled = paroled;
+ if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleTempParoled);
+ if (paroled) {
+ postParoleEndTimeout();
+ } else {
+ mLastAppIdleParoledTime = now;
+ postNextParoleTimeout(now);
+ }
+ postParoleStateChanged();
+ }
+ }
+ }
+
+ boolean isParoledOrCharging() {
+ synchronized (mAppIdleLock) {
+ return mAppIdleTempParoled || mCharging;
+ }
+ }
+
+ private void postNextParoleTimeout(long now) {
+ if (DEBUG) Slog.d(TAG, "Posting MSG_CHECK_PAROLE_TIMEOUT");
+ mHandler.removeMessages(MSG_CHECK_PAROLE_TIMEOUT);
+ // Compute when the next parole needs to happen. We check more frequently than necessary
+ // since the message handler delays are based on elapsedRealTime and not wallclock time.
+ // The comparison is done in wallclock time.
+ long timeLeft = (mLastAppIdleParoledTime + mAppIdleParoleIntervalMillis) - now;
+ if (timeLeft < 0) {
+ timeLeft = 0;
+ }
+ mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft);
+ }
+
+ private void postParoleEndTimeout() {
+ if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_END_TIMEOUT");
+ mHandler.removeMessages(MSG_PAROLE_END_TIMEOUT);
+ mHandler.sendEmptyMessageDelayed(MSG_PAROLE_END_TIMEOUT, mAppIdleParoleDurationMillis);
+ }
+
+ private void postParoleStateChanged() {
+ if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED");
+ mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED);
+ mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED);
+ }
+
+ void postCheckIdleStates(int userId) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
+ }
+
+ /**
+ * We send a different message to check idle states once, otherwise we would end up
+ * scheduling a series of repeating checkIdleStates each time we fired off one.
+ */
+ void postOneTimeCheckIdleStates() {
+ if (mDeviceIdleController == null) {
+ // Not booted yet; wait for it!
+ mPendingOneTimeCheckIdleStates = true;
+ } else {
+ mHandler.sendEmptyMessage(MSG_ONE_TIME_CHECK_IDLE_STATES);
+ mPendingOneTimeCheckIdleStates = false;
+ }
+ }
+
+ /**
+ * Check all running users' or specified user's apps to see if they enter an idle state.
+ * @return Returns whether checking should continue periodically.
+ */
+ boolean checkIdleStates(int checkUserId) {
+ if (!mAppIdleEnabled) {
+ return false;
+ }
+
+ final int[] runningUserIds;
+ try {
+ runningUserIds = ActivityManager.getService().getRunningUserIds();
+ if (checkUserId != UserHandle.USER_ALL
+ && !ArrayUtils.contains(runningUserIds, checkUserId)) {
+ return false;
+ }
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ for (int i = 0; i < runningUserIds.length; i++) {
+ final int userId = runningUserIds[i];
+ if (checkUserId != UserHandle.USER_ALL && checkUserId != userId) {
+ continue;
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Checking idle state for user " + userId);
+ }
+ List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
+ PackageManager.MATCH_DISABLED_COMPONENTS,
+ userId);
+ final int packageCount = packages.size();
+ for (int p = 0; p < packageCount; p++) {
+ final PackageInfo pi = packages.get(p);
+ final String packageName = pi.packageName;
+ final boolean isIdle = isAppIdleFiltered(packageName,
+ UserHandle.getAppId(pi.applicationInfo.uid),
+ userId, elapsedRealtime);
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
+ userId, isIdle ? 1 : 0, packageName));
+ if (isIdle) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.setIdle(packageName, userId, elapsedRealtime);
+ }
+ }
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "checkIdleStates took "
+ + (SystemClock.elapsedRealtime() - elapsedRealtime));
+ }
+ return true;
+ }
+
+ /** Check if it's been a while since last parole and let idle apps do some work */
+ void checkParoleTimeout() {
+ boolean setParoled = false;
+ synchronized (mAppIdleLock) {
+ final long now = System.currentTimeMillis();
+ if (!mAppIdleTempParoled) {
+ final long timeSinceLastParole = now - mLastAppIdleParoledTime;
+ if (timeSinceLastParole > mAppIdleParoleIntervalMillis) {
+ if (DEBUG) Slog.d(TAG, "Crossed default parole interval");
+ setParoled = true;
+ } else {
+ if (DEBUG) Slog.d(TAG, "Not long enough to go to parole");
+ postNextParoleTimeout(now);
+ }
+ }
+ }
+ if (setParoled) {
+ setAppIdleParoled(true);
+ }
+ }
+
+ private void notifyBatteryStats(String packageName, int userId, boolean idle) {
+ try {
+ final int uid = mPackageManager.getPackageUidAsUser(packageName,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+ if (idle) {
+ mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE,
+ packageName, uid);
+ } else {
+ mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE,
+ packageName, uid);
+ }
+ } catch (PackageManager.NameNotFoundException | RemoteException e) {
+ }
+ }
+
+ void onDeviceIdleModeChanged() {
+ final boolean deviceIdle = mPowerManager.isDeviceIdleMode();
+ if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
+ boolean paroled = false;
+ synchronized (mAppIdleLock) {
+ final long timeSinceLastParole = System.currentTimeMillis() - mLastAppIdleParoledTime;
+ if (!deviceIdle
+ && timeSinceLastParole >= mAppIdleParoleIntervalMillis) {
+ if (DEBUG) {
+ Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false");
+ }
+ paroled = true;
+ } else if (deviceIdle) {
+ if (DEBUG) Slog.i(TAG, "Device idle, back to prison");
+ paroled = false;
+ } else {
+ return;
+ }
+ }
+ setAppIdleParoled(paroled);
+ }
+
+ void reportEvent(UsageEvents.Event event, long elapsedRealtime, int userId) {
+ synchronized (mAppIdleLock) {
+ // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
+ // about apps that are on some kind of whitelist anyway.
+ final boolean previouslyIdle = mAppIdleHistory.isIdle(
+ event.mPackage, userId, elapsedRealtime);
+ // Inform listeners if necessary
+ if ((event.mEventType == UsageEvents.Event.MOVE_TO_FOREGROUND
+ || event.mEventType == UsageEvents.Event.MOVE_TO_BACKGROUND
+ || event.mEventType == UsageEvents.Event.SYSTEM_INTERACTION
+ || event.mEventType == UsageEvents.Event.USER_INTERACTION)) {
+ mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
+ if (previouslyIdle) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
+ /* idle = */ 0, event.mPackage));
+ notifyBatteryStats(event.mPackage, userId, false);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle,
+ * then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind
+ * the threshold for idle.
+ *
+ * This method is always called from the handler thread, so not much synchronization is
+ * required.
+ */
+ void forceIdleState(String packageName, int userId, boolean idle) {
+ final int appId = getAppId(packageName);
+ if (appId < 0) return;
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+
+ final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
+ userId, elapsedRealtime);
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.setIdle(packageName, userId, idle, elapsedRealtime);
+ }
+ final boolean stillIdle = isAppIdleFiltered(packageName, appId,
+ userId, elapsedRealtime);
+ // Inform listeners if necessary
+ if (previouslyIdle != stillIdle) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
+ /* idle = */ stillIdle ? 1 : 0, packageName));
+ if (!stillIdle) {
+ notifyBatteryStats(packageName, userId, idle);
+ }
+ }
+ }
+
+ public void onUserRemoved(int userId) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.onUserRemoved(userId);
+ }
+ }
+
+ private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
+ synchronized (mAppIdleLock) {
+ return mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
+ }
+ }
+
+ void addListener(UsageStatsManagerInternal.AppIdleStateChangeListener listener) {
+ synchronized (mAppIdleLock) {
+ if (!mPackageAccessListeners.contains(listener)) {
+ mPackageAccessListeners.add(listener);
+ }
+ }
+ }
+
+ void removeListener(UsageStatsManagerInternal.AppIdleStateChangeListener listener) {
+ synchronized (mAppIdleLock) {
+ mPackageAccessListeners.remove(listener);
+ }
+ }
+
+ int getAppId(String packageName) {
+ try {
+ ApplicationInfo ai = mPackageManager.getApplicationInfo(packageName,
+ PackageManager.MATCH_ANY_USER
+ | PackageManager.MATCH_DISABLED_COMPONENTS);
+ return ai.uid;
+ } catch (PackageManager.NameNotFoundException re) {
+ return -1;
+ }
+ }
+
+ boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime,
+ boolean shouldObfuscateInstantApps) {
+ if (isParoledOrCharging()) {
+ return false;
+ }
+ if (shouldObfuscateInstantApps &&
+ mPackageManagerInternal.isPackageEphemeral(userId, packageName)) {
+ return false;
+ }
+ return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
+ }
+
+ /**
+ * Checks if an app has been idle for a while and filters out apps that are excluded.
+ * It returns false if the current system state allows all apps to be considered active.
+ * This happens if the device is plugged in or temporarily allowed to make exceptions.
+ * Called by interface impls.
+ */
+ boolean isAppIdleFiltered(String packageName, int appId, int userId,
+ long elapsedRealtime) {
+ if (packageName == null) return false;
+ // If not enabled at all, of course nobody is ever idle.
+ if (!mAppIdleEnabled) {
+ return false;
+ }
+ if (appId < Process.FIRST_APPLICATION_UID) {
+ // System uids never go idle.
+ return false;
+ }
+ if (packageName.equals("android")) {
+ // Nor does the framework (which should be redundant with the above, but for MR1 we will
+ // retain this for safety).
+ return false;
+ }
+ if (mSystemServicesReady) {
+ try {
+ // We allow all whitelisted apps, including those that don't want to be whitelisted
+ // for idle mode, because app idle (aka app standby) is really not as big an issue
+ // for controlling who participates vs. doze mode.
+ if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) {
+ return false;
+ }
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+
+ if (isActiveDeviceAdmin(packageName, userId)) {
+ return false;
+ }
+
+ if (isActiveNetworkScorer(packageName)) {
+ return false;
+ }
+
+ if (mAppWidgetManager != null
+ && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {
+ return false;
+ }
+
+ if (isDeviceProvisioningPackage(packageName)) {
+ return false;
+ }
+ }
+
+ if (!isAppIdleUnfiltered(packageName, userId, elapsedRealtime)) {
+ return false;
+ }
+
+ // Check this last, as it is the most expensive check
+ // TODO: Optimize this by fetching the carrier privileged apps ahead of time
+ if (isCarrierApp(packageName)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ int[] getIdleUidsForUser(int userId) {
+ if (!mAppIdleEnabled) {
+ return new int[0];
+ }
+
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+
+ List<ApplicationInfo> apps;
+ try {
+ ParceledListSlice<ApplicationInfo> slice = AppGlobals.getPackageManager()
+ .getInstalledApplications(/* flags= */ 0, userId);
+ if (slice == null) {
+ return new int[0];
+ }
+ apps = slice.getList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ // State of each uid. Key is the uid. Value lower 16 bits is the number of apps
+ // associated with that uid, upper 16 bits is the number of those apps that is idle.
+ SparseIntArray uidStates = new SparseIntArray();
+
+ // Now resolve all app state. Iterating over all apps, keeping track of how many
+ // we find for each uid and how many of those are idle.
+ for (int i = apps.size() - 1; i >= 0; i--) {
+ ApplicationInfo ai = apps.get(i);
+
+ // Check whether this app is idle.
+ boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
+ userId, elapsedRealtime);
+
+ int index = uidStates.indexOfKey(ai.uid);
+ if (index < 0) {
+ uidStates.put(ai.uid, 1 + (idle ? 1<<16 : 0));
+ } else {
+ int value = uidStates.valueAt(index);
+ uidStates.setValueAt(index, value + 1 + (idle ? 1<<16 : 0));
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "getIdleUids took " + (SystemClock.elapsedRealtime() - elapsedRealtime));
+ }
+ int numIdle = 0;
+ for (int i = uidStates.size() - 1; i >= 0; i--) {
+ int value = uidStates.valueAt(i);
+ if ((value&0x7fff) == (value>>16)) {
+ numIdle++;
+ }
+ }
+
+ int[] res = new int[numIdle];
+ numIdle = 0;
+ for (int i = uidStates.size() - 1; i >= 0; i--) {
+ int value = uidStates.valueAt(i);
+ if ((value&0x7fff) == (value>>16)) {
+ res[numIdle] = uidStates.keyAt(i);
+ numIdle++;
+ }
+ }
+
+ return res;
+ }
+
+ void setAppIdleAsync(String packageName, boolean idle, int userId) {
+ if (packageName == null) return;
+
+ mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
+ .sendToTarget();
+ }
+
+ private boolean isActiveDeviceAdmin(String packageName, int userId) {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ if (dpm == null) return false;
+ return dpm.packageHasActiveAdmins(packageName, userId);
+ }
+
+ /**
+ * Returns {@code true} if the supplied package is the device provisioning app. Otherwise,
+ * returns {@code false}.
+ */
+ private boolean isDeviceProvisioningPackage(String packageName) {
+ String deviceProvisioningPackage = mContext.getResources().getString(
+ com.android.internal.R.string.config_deviceProvisioningPackage);
+ return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
+ }
+
+ private boolean isCarrierApp(String packageName) {
+ synchronized (mAppIdleLock) {
+ if (!mHaveCarrierPrivilegedApps) {
+ fetchCarrierPrivilegedAppsLA();
+ }
+ if (mCarrierPrivilegedApps != null) {
+ return mCarrierPrivilegedApps.contains(packageName);
+ }
+ return false;
+ }
+ }
+
+ void clearCarrierPrivilegedApps() {
+ if (DEBUG) {
+ Slog.i(TAG, "Clearing carrier privileged apps list");
+ }
+ synchronized (mAppIdleLock) {
+ mHaveCarrierPrivilegedApps = false;
+ mCarrierPrivilegedApps = null; // Need to be refetched.
+ }
+ }
+
+ @GuardedBy("mAppIdleLock")
+ private void fetchCarrierPrivilegedAppsLA() {
+ TelephonyManager telephonyManager =
+ mContext.getSystemService(TelephonyManager.class);
+ mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivileges();
+ mHaveCarrierPrivilegedApps = true;
+ if (DEBUG) {
+ Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
+ }
+ }
+
+ private boolean isActiveNetworkScorer(String packageName) {
+ NetworkScoreManager nsm = (NetworkScoreManager) mContext.getSystemService(
+ Context.NETWORK_SCORE_SERVICE);
+ return packageName != null && packageName.equals(nsm.getActiveScorerPackage());
+ }
+
+ void informListeners(String packageName, int userId, boolean isIdle) {
+ for (UsageStatsManagerInternal.AppIdleStateChangeListener listener : mPackageAccessListeners) {
+ listener.onAppIdleStateChanged(packageName, userId, isIdle);
+ }
+ }
+
+ void informParoleStateChanged() {
+ final boolean paroled = isParoledOrCharging();
+ for (UsageStatsManagerInternal.AppIdleStateChangeListener listener : mPackageAccessListeners) {
+ listener.onParoleStateChanged(paroled);
+ }
+ }
+
+ void flushToDisk(int userId) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.writeAppIdleTimes(userId);
+ }
+ }
+
+ void flushDurationsToDisk() {
+ // Persist elapsed and screen on time. If this fails for whatever reason, the apps will be
+ // considered not-idle, which is the safest outcome in such an event.
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.writeAppIdleDurations();
+ }
+ }
+
+ boolean isDisplayOn() {
+ return mDisplayManager
+ .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
+ }
+
+ void clearAppIdleForPackage(String packageName, int userId) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.clearUsage(packageName, userId);
+ }
+ }
+
+ private class PackageReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_PACKAGE_ADDED.equals(action)
+ || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+ clearCarrierPrivilegedApps();
+ }
+ if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
+ Intent.ACTION_PACKAGE_ADDED.equals(action))
+ && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ clearAppIdleForPackage(intent.getData().getSchemeSpecificPart(),
+ getSendingUserId());
+ }
+ }
+ }
+
+ void initializeDefaultsForSystemApps(int userId) {
+ Slog.d(TAG, "Initializing defaults for system apps on user " + userId);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
+ PackageManager.MATCH_DISABLED_COMPONENTS,
+ userId);
+ final int packageCount = packages.size();
+ synchronized (mAppIdleLock) {
+ for (int i = 0; i < packageCount; i++) {
+ final PackageInfo pi = packages.get(i);
+ String packageName = pi.packageName;
+ if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
+ mAppIdleHistory.reportUsage(packageName, userId, elapsedRealtime);
+ }
+ }
+ }
+ }
+
+ void postReportContentProviderUsage(String name, String packageName, int userId) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = name;
+ args.arg2 = packageName;
+ args.arg3 = userId;
+ mHandler.obtainMessage(MSG_REPORT_CONTENT_PROVIDER_USAGE, args)
+ .sendToTarget();
+ }
+
+ void dumpHistory(IndentingPrintWriter idpw, int userId) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.dumpHistory(idpw, userId);
+ }
+ }
+
+ void dumpUser(IndentingPrintWriter idpw, int userId) {
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.dump(idpw, userId);
+ }
+ }
+
+ void dumpState(String[] args, PrintWriter pw) {
+ synchronized (mAppIdleLock) {
+ pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
+ + "): " + mCarrierPrivilegedApps);
+ }
+
+ pw.println();
+ pw.println("Settings:");
+
+ pw.print(" mAppIdleDurationMillis=");
+ TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw);
+ pw.println();
+
+ pw.print(" mAppIdleWallclockThresholdMillis=");
+ TimeUtils.formatDuration(mAppIdleWallclockThresholdMillis, pw);
+ pw.println();
+
+ pw.print(" mCheckIdleIntervalMillis=");
+ TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
+ pw.println();
+
+ pw.print(" mAppIdleParoleIntervalMillis=");
+ TimeUtils.formatDuration(mAppIdleParoleIntervalMillis, pw);
+ pw.println();
+
+ pw.print(" mAppIdleParoleDurationMillis=");
+ TimeUtils.formatDuration(mAppIdleParoleDurationMillis, pw);
+ pw.println();
+
+ pw.println();
+ pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
+ pw.print(" mAppIdleTempParoled="); pw.print(mAppIdleTempParoled);
+ pw.print(" mCharging="); pw.print(mCharging);
+ pw.print(" mLastAppIdleParoledTime=");
+ TimeUtils.formatDuration(mLastAppIdleParoledTime, pw);
+ pw.println();
+ }
+
+ class AppStandbyHandler extends Handler {
+
+ AppStandbyHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_FORCE_IDLE_STATE:
+ forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1);
+ break;
+
+ case MSG_CHECK_IDLE_STATES:
+ if (checkIdleStates(msg.arg1)) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ MSG_CHECK_IDLE_STATES, msg.arg1, 0),
+ mCheckIdleIntervalMillis);
+ }
+ break;
+
+ case MSG_ONE_TIME_CHECK_IDLE_STATES:
+ mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
+ checkIdleStates(UserHandle.USER_ALL);
+ break;
+
+ case MSG_CHECK_PAROLE_TIMEOUT:
+ checkParoleTimeout();
+ break;
+
+ case MSG_PAROLE_END_TIMEOUT:
+ if (DEBUG) Slog.d(TAG, "Ending parole");
+ setAppIdleParoled(false);
+ break;
+
+ case MSG_REPORT_CONTENT_PROVIDER_USAGE:
+ SomeArgs args = (SomeArgs) msg.obj;
+ reportContentProviderUsage((String) args.arg1, // authority name
+ (String) args.arg2, // package name
+ (int) args.arg3); // userId
+ args.recycle();
+ break;
+
+ case MSG_PAROLE_STATE_CHANGED:
+ if (DEBUG) Slog.d(TAG, "Parole state: " + mAppIdleTempParoled
+ + ", Charging state:" + mCharging);
+ informParoleStateChanged();
+ break;
+ default:
+ super.handleMessage(msg);
+ break;
+
+ }
+ }
+ };
+
+ private class DeviceStateReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
+ setChargingState(intent.getIntExtra("plugged", 0) != 0);
+ } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
+ onDeviceIdleModeChanged();
+ }
+ }
+ }
+
+ private final DisplayManager.DisplayListener mDisplayListener
+ = new DisplayManager.DisplayListener() {
+
+ @Override public void onDisplayAdded(int displayId) {
+ }
+
+ @Override public void onDisplayRemoved(int displayId) {
+ }
+
+ @Override public void onDisplayChanged(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ final boolean displayOn = isDisplayOn();
+ synchronized (mAppIdleLock) {
+ mAppIdleHistory.updateDisplay(displayOn, SystemClock.elapsedRealtime());
+ }
+ }
+ }
+ };
+
+ /**
+ * Observe settings changes for {@link Settings.Global#APP_IDLE_CONSTANTS}.
+ */
+ private class SettingsObserver extends ContentObserver {
+ /**
+ * This flag has been used to disable app idle on older builds with bug b/26355386.
+ */
+ @Deprecated
+ private static final String KEY_IDLE_DURATION_OLD = "idle_duration";
+
+ private static final String KEY_IDLE_DURATION = "idle_duration2";
+ private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
+ private static final String KEY_PAROLE_INTERVAL = "parole_interval";
+ private static final String KEY_PAROLE_DURATION = "parole_duration";
+
+ private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ void registerObserver() {
+ mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.APP_IDLE_CONSTANTS), false, this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ updateSettings();
+ postOneTimeCheckIdleStates();
+ }
+
+ void updateSettings() {
+ synchronized (mAppIdleLock) {
+ // Look at global settings for this.
+ // TODO: Maybe apply different thresholds for different users.
+ try {
+ mParser.setString(Settings.Global.getString(mContext.getContentResolver(),
+ Settings.Global.APP_IDLE_CONSTANTS));
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
+ // fallthrough, mParser is empty and all defaults will be returned.
+ }
+
+ // Default: 12 hours of screen-on time sans dream-time
+ mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION,
+ COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
+
+ mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
+ COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
+
+ mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
+ COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours
+
+ // Default: 24 hours between paroles
+ mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
+ COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);
+
+ mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
+ COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
+ mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
+ mAppIdleScreenThresholdMillis);
+ }
+ }
+ }
+
+}
+
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 25e471c..afafea1 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -18,37 +18,24 @@
import android.Manifest;
import android.app.ActivityManager;
-import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.IUidObserver;
-import android.app.admin.DevicePolicyManager;
import android.app.usage.ConfigurationStats;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManagerInternal;
-import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
-import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
-import android.database.ContentObserver;
-import android.hardware.display.DisplayManager;
-import android.net.NetworkScoreManager;
-import android.os.BatteryManager;
-import android.os.BatteryStats;
import android.os.Binder;
import android.os.Environment;
import android.os.FileUtils;
@@ -56,7 +43,6 @@
import android.os.IDeviceIdleController;
import android.os.Looper;
import android.os.Message;
-import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -64,21 +50,12 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.Settings;
-import android.telephony.TelephonyManager;
import android.util.ArraySet;
-import android.util.KeyValueListParser;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
-import android.util.TimeUtils;
-import android.view.Display;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.os.SomeArgs;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
@@ -88,7 +65,6 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -107,7 +83,6 @@
static final boolean COMPRESS_TIME = false;
private static final long TEN_SECONDS = 10 * 1000;
- private static final long ONE_MINUTE = 60 * 1000;
private static final long TWENTY_MINUTES = 20 * 60 * 1000;
private static final long FLUSH_INTERVAL = COMPRESS_TIME ? TEN_SECONDS : TWENTY_MINUTES;
private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
@@ -115,24 +90,10 @@
private static final boolean ENABLE_KERNEL_UPDATES = true;
private static final File KERNEL_COUNTER_FILE = new File("/proc/uid_procstat/set");
- long mAppIdleScreenThresholdMillis;
- long mCheckIdleIntervalMillis;
- long mAppIdleWallclockThresholdMillis;
- long mAppIdleParoleIntervalMillis;
- long mAppIdleParoleDurationMillis;
-
// Handler message types.
static final int MSG_REPORT_EVENT = 0;
static final int MSG_FLUSH_TO_DISK = 1;
static final int MSG_REMOVE_USER = 2;
- static final int MSG_INFORM_LISTENERS = 3;
- static final int MSG_FORCE_IDLE_STATE = 4;
- static final int MSG_CHECK_IDLE_STATES = 5;
- static final int MSG_CHECK_PAROLE_TIMEOUT = 6;
- static final int MSG_PAROLE_END_TIMEOUT = 7;
- static final int MSG_REPORT_CONTENT_PROVIDER_USAGE = 8;
- static final int MSG_PAROLE_STATE_CHANGED = 9;
- static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
private final Object mLock = new Object();
Handler mHandler;
@@ -140,11 +101,7 @@
UserManager mUserManager;
PackageManager mPackageManager;
PackageManagerInternal mPackageManagerInternal;
- AppWidgetManager mAppWidgetManager;
IDeviceIdleController mDeviceIdleController;
- private DisplayManager mDisplayManager;
- private PowerManager mPowerManager;
- private IBatteryStats mBatteryStats;
private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>();
private final SparseIntArray mUidToKernelCounter = new SparseIntArray();
@@ -152,26 +109,7 @@
long mRealTimeSnapshot;
long mSystemTimeSnapshot;
- boolean mAppIdleEnabled;
- boolean mAppIdleTempParoled;
- boolean mCharging;
- private long mLastAppIdleParoledTime;
-
- private volatile boolean mPendingOneTimeCheckIdleStates;
- private boolean mSystemServicesReady = false;
-
- private final Object mAppIdleLock = new Object();
- @GuardedBy("mAppIdleLock")
- private AppIdleHistory mAppIdleHistory;
-
- @GuardedBy("mAppIdleLock")
- private ArrayList<UsageStatsManagerInternal.AppIdleStateChangeListener>
- mPackageAccessListeners = new ArrayList<>();
-
- @GuardedBy("mAppIdleLock")
- private boolean mHaveCarrierPrivilegedApps;
- @GuardedBy("mAppIdleLock")
- private List<String> mCarrierPrivilegedApps;
+ AppStandbyController mAppStandby;
public UsageStatsService(Context context) {
super(context);
@@ -185,6 +123,8 @@
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mHandler = new H(BackgroundThread.get().getLooper());
+ mAppStandby = new AppStandbyController(getContext(), BackgroundThread.get().getLooper());
+
File systemDataDir = new File(Environment.getDataDirectory(), "system");
mUsageStatsDir = new File(systemDataDir, "usagestats");
mUsageStatsDir.mkdirs();
@@ -198,30 +138,9 @@
getContext().registerReceiverAsUser(new UserActionsReceiver(), UserHandle.ALL, filter,
null, mHandler);
- IntentFilter packageFilter = new IntentFilter();
- packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- packageFilter.addDataScheme("package");
-
- getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, packageFilter,
- null, mHandler);
-
- mAppIdleEnabled = getContext().getResources().getBoolean(
- com.android.internal.R.bool.config_enableAutoPowerModes);
- if (mAppIdleEnabled) {
- IntentFilter deviceStates = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
- deviceStates.addAction(BatteryManager.ACTION_DISCHARGING);
- deviceStates.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
- getContext().registerReceiver(new DeviceStateReceiver(), deviceStates);
- }
-
synchronized (mLock) {
cleanUpRemovedUsersLocked();
}
- synchronized (mAppIdleLock) {
- mAppIdleHistory = new AppIdleHistory(SystemClock.elapsedRealtime());
- }
mRealTimeSnapshot = SystemClock.elapsedRealtime();
mSystemTimeSnapshot = System.currentTimeMillis();
@@ -233,28 +152,10 @@
@Override
public void onBootPhase(int phase) {
if (phase == PHASE_SYSTEM_SERVICES_READY) {
- // Observe changes to the threshold
- SettingsObserver settingsObserver = new SettingsObserver(mHandler);
- settingsObserver.registerObserver();
- settingsObserver.updateSettings();
+ mAppStandby.onBootPhase(phase);
- mAppWidgetManager = getContext().getSystemService(AppWidgetManager.class);
mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
- mBatteryStats = IBatteryStats.Stub.asInterface(
- ServiceManager.getService(BatteryStats.SERVICE_NAME));
- mDisplayManager = (DisplayManager) getContext().getSystemService(
- Context.DISPLAY_SERVICE);
- mPowerManager = getContext().getSystemService(PowerManager.class);
-
- mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
- synchronized (mAppIdleLock) {
- mAppIdleHistory.updateDisplay(isDisplayOn(), SystemClock.elapsedRealtime());
- }
-
- if (mPendingOneTimeCheckIdleStates) {
- postOneTimeCheckIdleStates();
- }
if (ENABLE_KERNEL_UPDATES && KERNEL_COUNTER_FILE.exists()) {
try {
@@ -268,18 +169,9 @@
} else {
Slog.w(TAG, "Missing procfs interface: " + KERNEL_COUNTER_FILE);
}
-
- mSystemServicesReady = true;
- } else if (phase == PHASE_BOOT_COMPLETED) {
- setChargingState(getContext().getSystemService(BatteryManager.class).isCharging());
}
}
- private boolean isDisplayOn() {
- return mDisplayManager
- .getDisplay(Display.DEFAULT_DISPLAY).getState() == Display.STATE_ON;
- }
-
private class UserActionsReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -291,60 +183,12 @@
}
} else if (Intent.ACTION_USER_STARTED.equals(action)) {
if (userId >=0) {
- postCheckIdleStates(userId);
+ mAppStandby.postCheckIdleStates(userId);
}
}
}
}
- private class PackageReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_PACKAGE_ADDED.equals(action)
- || Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
- clearCarrierPrivilegedApps();
- }
- if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
- Intent.ACTION_PACKAGE_ADDED.equals(action))
- && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
- clearAppIdleForPackage(intent.getData().getSchemeSpecificPart(),
- getSendingUserId());
- }
- }
- }
-
- private class DeviceStateReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
- setChargingState(intent.getIntExtra("plugged", 0) != 0);
- } else if (PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
- onDeviceIdleModeChanged();
- }
- }
- }
-
- private final DisplayManager.DisplayListener mDisplayListener
- = new DisplayManager.DisplayListener() {
-
- @Override public void onDisplayAdded(int displayId) {
- }
-
- @Override public void onDisplayRemoved(int displayId) {
- }
-
- @Override public void onDisplayChanged(int displayId) {
- if (displayId == Display.DEFAULT_DISPLAY) {
- final boolean displayOn = isDisplayOn();
- synchronized (UsageStatsService.this.mAppIdleLock) {
- mAppIdleHistory.updateDisplay(displayOn, SystemClock.elapsedRealtime());
- }
- }
- }
- };
-
private final IUidObserver mUidObserver = new IUidObserver.Stub() {
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq) {
@@ -388,42 +232,18 @@
@Override
public void onStatsReloaded() {
- postOneTimeCheckIdleStates();
+ mAppStandby.postOneTimeCheckIdleStates();
}
@Override
public void onNewUpdate(int userId) {
- initializeDefaultsForSystemApps(userId);
- }
-
- private void initializeDefaultsForSystemApps(int userId) {
- Slog.d(TAG, "Initializing defaults for system apps on user " + userId);
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
- PackageManager.MATCH_DISABLED_COMPONENTS,
- userId);
- final int packageCount = packages.size();
- synchronized (mAppIdleLock) {
- for (int i = 0; i < packageCount; i++) {
- final PackageInfo pi = packages.get(i);
- String packageName = pi.packageName;
- if (pi.applicationInfo != null && pi.applicationInfo.isSystemApp()) {
- mAppIdleHistory.reportUsage(packageName, userId, elapsedRealtime);
- }
- }
- }
+ mAppStandby.initializeDefaultsForSystemApps(userId);
}
private boolean shouldObfuscateInstantAppsForCaller(int callingUid, int userId) {
return !mPackageManagerInternal.canAccessInstantApps(callingUid, userId);
}
- void clearAppIdleForPackage(String packageName, int userId) {
- synchronized (mAppIdleLock) {
- mAppIdleHistory.clearUsage(packageName, userId);
- }
- }
-
private void cleanUpRemovedUsersLocked() {
final List<UserInfo> users = mUserManager.getUsers(true);
if (users == null || users.size() == 0) {
@@ -451,195 +271,6 @@
}
}
- void setChargingState(boolean charging) {
- synchronized (mAppIdleLock) {
- if (mCharging != charging) {
- mCharging = charging;
- postParoleStateChanged();
- }
- }
- }
-
- /** Paroled here means temporary pardon from being inactive */
- void setAppIdleParoled(boolean paroled) {
- synchronized (mAppIdleLock) {
- final long now = System.currentTimeMillis();
- if (mAppIdleTempParoled != paroled) {
- mAppIdleTempParoled = paroled;
- if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleTempParoled);
- if (paroled) {
- postParoleEndTimeout();
- } else {
- mLastAppIdleParoledTime = now;
- postNextParoleTimeout(now);
- }
- postParoleStateChanged();
- }
- }
- }
-
- boolean isParoledOrCharging() {
- synchronized (mAppIdleLock) {
- return mAppIdleTempParoled || mCharging;
- }
- }
-
- private void postNextParoleTimeout(long now) {
- if (DEBUG) Slog.d(TAG, "Posting MSG_CHECK_PAROLE_TIMEOUT");
- mHandler.removeMessages(MSG_CHECK_PAROLE_TIMEOUT);
- // Compute when the next parole needs to happen. We check more frequently than necessary
- // since the message handler delays are based on elapsedRealTime and not wallclock time.
- // The comparison is done in wallclock time.
- long timeLeft = (mLastAppIdleParoledTime + mAppIdleParoleIntervalMillis) - now;
- if (timeLeft < 0) {
- timeLeft = 0;
- }
- mHandler.sendEmptyMessageDelayed(MSG_CHECK_PAROLE_TIMEOUT, timeLeft);
- }
-
- private void postParoleEndTimeout() {
- if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_END_TIMEOUT");
- mHandler.removeMessages(MSG_PAROLE_END_TIMEOUT);
- mHandler.sendEmptyMessageDelayed(MSG_PAROLE_END_TIMEOUT, mAppIdleParoleDurationMillis);
- }
-
- private void postParoleStateChanged() {
- if (DEBUG) Slog.d(TAG, "Posting MSG_PAROLE_STATE_CHANGED");
- mHandler.removeMessages(MSG_PAROLE_STATE_CHANGED);
- mHandler.sendEmptyMessage(MSG_PAROLE_STATE_CHANGED);
- }
-
- void postCheckIdleStates(int userId) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
- }
-
- /**
- * We send a different message to check idle states once, otherwise we would end up
- * scheduling a series of repeating checkIdleStates each time we fired off one.
- */
- void postOneTimeCheckIdleStates() {
- if (mDeviceIdleController == null) {
- // Not booted yet; wait for it!
- mPendingOneTimeCheckIdleStates = true;
- } else {
- mHandler.sendEmptyMessage(MSG_ONE_TIME_CHECK_IDLE_STATES);
- mPendingOneTimeCheckIdleStates = false;
- }
- }
-
- /**
- * Check all running users' or specified user's apps to see if they enter an idle state.
- * @return Returns whether checking should continue periodically.
- */
- boolean checkIdleStates(int checkUserId) {
- if (!mAppIdleEnabled) {
- return false;
- }
-
- final int[] runningUserIds;
- try {
- runningUserIds = ActivityManager.getService().getRunningUserIds();
- if (checkUserId != UserHandle.USER_ALL
- && !ArrayUtils.contains(runningUserIds, checkUserId)) {
- return false;
- }
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
-
- final long elapsedRealtime = SystemClock.elapsedRealtime();
- for (int i = 0; i < runningUserIds.length; i++) {
- final int userId = runningUserIds[i];
- if (checkUserId != UserHandle.USER_ALL && checkUserId != userId) {
- continue;
- }
- if (DEBUG) {
- Slog.d(TAG, "Checking idle state for user " + userId);
- }
- List<PackageInfo> packages = mPackageManager.getInstalledPackagesAsUser(
- PackageManager.MATCH_DISABLED_COMPONENTS,
- userId);
- final int packageCount = packages.size();
- for (int p = 0; p < packageCount; p++) {
- final PackageInfo pi = packages.get(p);
- final String packageName = pi.packageName;
- final boolean isIdle = isAppIdleFiltered(packageName,
- UserHandle.getAppId(pi.applicationInfo.uid),
- userId, elapsedRealtime);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS,
- userId, isIdle ? 1 : 0, packageName));
- if (isIdle) {
- synchronized (mAppIdleLock) {
- mAppIdleHistory.setIdle(packageName, userId, elapsedRealtime);
- }
- }
- }
- }
- if (DEBUG) {
- Slog.d(TAG, "checkIdleStates took "
- + (SystemClock.elapsedRealtime() - elapsedRealtime));
- }
- return true;
- }
-
- /** Check if it's been a while since last parole and let idle apps do some work */
- void checkParoleTimeout() {
- boolean setParoled = false;
- synchronized (mAppIdleLock) {
- final long now = System.currentTimeMillis();
- if (!mAppIdleTempParoled) {
- final long timeSinceLastParole = now - mLastAppIdleParoledTime;
- if (timeSinceLastParole > mAppIdleParoleIntervalMillis) {
- if (DEBUG) Slog.d(TAG, "Crossed default parole interval");
- setParoled = true;
- } else {
- if (DEBUG) Slog.d(TAG, "Not long enough to go to parole");
- postNextParoleTimeout(now);
- }
- }
- }
- if (setParoled) {
- setAppIdleParoled(true);
- }
- }
-
- private void notifyBatteryStats(String packageName, int userId, boolean idle) {
- try {
- final int uid = mPackageManager.getPackageUidAsUser(packageName,
- PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
- if (idle) {
- mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_INACTIVE,
- packageName, uid);
- } else {
- mBatteryStats.noteEvent(BatteryStats.HistoryItem.EVENT_PACKAGE_ACTIVE,
- packageName, uid);
- }
- } catch (NameNotFoundException | RemoteException e) {
- }
- }
-
- void onDeviceIdleModeChanged() {
- final boolean deviceIdle = mPowerManager.isDeviceIdleMode();
- if (DEBUG) Slog.i(TAG, "DeviceIdleMode changed to " + deviceIdle);
- boolean paroled = false;
- synchronized (mAppIdleLock) {
- final long timeSinceLastParole = System.currentTimeMillis() - mLastAppIdleParoledTime;
- if (!deviceIdle
- && timeSinceLastParole >= mAppIdleParoleIntervalMillis) {
- if (DEBUG) {
- Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false");
- }
- paroled = true;
- } else if (deviceIdle) {
- if (DEBUG) Slog.i(TAG, "Device idle, back to prison");
- paroled = false;
- } else {
- return;
- }
- }
- setAppIdleParoled(paroled);
- }
-
private static void deleteRecursively(File f) {
File[] files = f.listFiles();
if (files != null) {
@@ -724,76 +355,7 @@
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
service.reportEvent(event);
- synchronized (mAppIdleLock) {
- // TODO: Ideally this should call isAppIdleFiltered() to avoid calling back
- // about apps that are on some kind of whitelist anyway.
- final boolean previouslyIdle = mAppIdleHistory.isIdle(
- event.mPackage, userId, elapsedRealtime);
- // Inform listeners if necessary
- if ((event.mEventType == Event.MOVE_TO_FOREGROUND
- || event.mEventType == Event.MOVE_TO_BACKGROUND
- || event.mEventType == Event.SYSTEM_INTERACTION
- || event.mEventType == Event.USER_INTERACTION)) {
- mAppIdleHistory.reportUsage(event.mPackage, userId, elapsedRealtime);
- if (previouslyIdle) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
- /* idle = */ 0, event.mPackage));
- notifyBatteryStats(event.mPackage, userId, false);
- }
- }
- }
- }
- }
-
- void reportContentProviderUsage(String authority, String providerPkgName, int userId) {
- // Get sync adapters for the authority
- String[] packages = ContentResolver.getSyncAdapterPackagesForAuthorityAsUser(
- authority, userId);
- for (String packageName: packages) {
- // Only force the sync adapters to active if the provider is not in the same package and
- // the sync adapter is a system package.
- try {
- PackageInfo pi = mPackageManager.getPackageInfoAsUser(
- packageName, PackageManager.MATCH_SYSTEM_ONLY, userId);
- if (pi == null || pi.applicationInfo == null) {
- continue;
- }
- if (!packageName.equals(providerPkgName)) {
- setAppIdleAsync(packageName, false, userId);
- }
- } catch (NameNotFoundException e) {
- // Shouldn't happen
- }
- }
- }
-
- /**
- * Forces the app's beginIdleTime and lastUsedTime to reflect idle or active. If idle,
- * then it rolls back the beginIdleTime and lastUsedTime to a point in time that's behind
- * the threshold for idle.
- *
- * This method is always called from the handler thread, so not much synchronization is
- * required.
- */
- void forceIdleState(String packageName, int userId, boolean idle) {
- final int appId = getAppId(packageName);
- if (appId < 0) return;
- final long elapsedRealtime = SystemClock.elapsedRealtime();
-
- final boolean previouslyIdle = isAppIdleFiltered(packageName, appId,
- userId, elapsedRealtime);
- synchronized (mAppIdleLock) {
- mAppIdleHistory.setIdle(packageName, userId, idle, elapsedRealtime);
- }
- final boolean stillIdle = isAppIdleFiltered(packageName, appId,
- userId, elapsedRealtime);
- // Inform listeners if necessary
- if (previouslyIdle != stillIdle) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId,
- /* idle = */ stillIdle ? 1 : 0, packageName));
- if (!stillIdle) {
- notifyBatteryStats(packageName, userId, idle);
- }
+ mAppStandby.reportEvent(event, elapsedRealtime, userId);
}
}
@@ -813,9 +375,7 @@
synchronized (mLock) {
Slog.i(TAG, "Removing user " + userId + " and all data.");
mUserState.remove(userId);
- synchronized (mAppIdleLock) {
- mAppIdleHistory.onUserRemoved(userId);
- }
+ mAppStandby.onUserRemoved(userId);
cleanUpRemovedUsersLocked();
}
}
@@ -887,253 +447,6 @@
}
}
- private boolean isAppIdleUnfiltered(String packageName, int userId, long elapsedRealtime) {
- synchronized (mAppIdleLock) {
- return mAppIdleHistory.isIdle(packageName, userId, elapsedRealtime);
- }
- }
-
- void addListener(AppIdleStateChangeListener listener) {
- synchronized (mAppIdleLock) {
- if (!mPackageAccessListeners.contains(listener)) {
- mPackageAccessListeners.add(listener);
- }
- }
- }
-
- void removeListener(AppIdleStateChangeListener listener) {
- synchronized (mAppIdleLock) {
- mPackageAccessListeners.remove(listener);
- }
- }
-
- int getAppId(String packageName) {
- try {
- ApplicationInfo ai = mPackageManager.getApplicationInfo(packageName,
- PackageManager.MATCH_ANY_USER
- | PackageManager.MATCH_DISABLED_COMPONENTS);
- return ai.uid;
- } catch (NameNotFoundException re) {
- return -1;
- }
- }
-
- boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime,
- boolean shouldObfuscateInstantApps) {
- if (isParoledOrCharging()) {
- return false;
- }
- if (shouldObfuscateInstantApps &&
- mPackageManagerInternal.isPackageEphemeral(userId, packageName)) {
- return false;
- }
- return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime);
- }
-
- /**
- * Checks if an app has been idle for a while and filters out apps that are excluded.
- * It returns false if the current system state allows all apps to be considered active.
- * This happens if the device is plugged in or temporarily allowed to make exceptions.
- * Called by interface impls.
- */
- private boolean isAppIdleFiltered(String packageName, int appId, int userId,
- long elapsedRealtime) {
- if (packageName == null) return false;
- // If not enabled at all, of course nobody is ever idle.
- if (!mAppIdleEnabled) {
- return false;
- }
- if (appId < Process.FIRST_APPLICATION_UID) {
- // System uids never go idle.
- return false;
- }
- if (packageName.equals("android")) {
- // Nor does the framework (which should be redundant with the above, but for MR1 we will
- // retain this for safety).
- return false;
- }
- if (mSystemServicesReady) {
- try {
- // We allow all whitelisted apps, including those that don't want to be whitelisted
- // for idle mode, because app idle (aka app standby) is really not as big an issue
- // for controlling who participates vs. doze mode.
- if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) {
- return false;
- }
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
-
- if (isActiveDeviceAdmin(packageName, userId)) {
- return false;
- }
-
- if (isActiveNetworkScorer(packageName)) {
- return false;
- }
-
- if (mAppWidgetManager != null
- && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {
- return false;
- }
-
- if (isDeviceProvisioningPackage(packageName)) {
- return false;
- }
- }
-
- if (!isAppIdleUnfiltered(packageName, userId, elapsedRealtime)) {
- return false;
- }
-
- // Check this last, as it is the most expensive check
- // TODO: Optimize this by fetching the carrier privileged apps ahead of time
- if (isCarrierApp(packageName)) {
- return false;
- }
-
- return true;
- }
-
- int[] getIdleUidsForUser(int userId) {
- if (!mAppIdleEnabled) {
- return new int[0];
- }
-
- final long elapsedRealtime = SystemClock.elapsedRealtime();
-
- List<ApplicationInfo> apps;
- try {
- ParceledListSlice<ApplicationInfo> slice = AppGlobals.getPackageManager()
- .getInstalledApplications(/* flags= */ 0, userId);
- if (slice == null) {
- return new int[0];
- }
- apps = slice.getList();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
-
- // State of each uid. Key is the uid. Value lower 16 bits is the number of apps
- // associated with that uid, upper 16 bits is the number of those apps that is idle.
- SparseIntArray uidStates = new SparseIntArray();
-
- // Now resolve all app state. Iterating over all apps, keeping track of how many
- // we find for each uid and how many of those are idle.
- for (int i = apps.size() - 1; i >= 0; i--) {
- ApplicationInfo ai = apps.get(i);
-
- // Check whether this app is idle.
- boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid),
- userId, elapsedRealtime);
-
- int index = uidStates.indexOfKey(ai.uid);
- if (index < 0) {
- uidStates.put(ai.uid, 1 + (idle ? 1<<16 : 0));
- } else {
- int value = uidStates.valueAt(index);
- uidStates.setValueAt(index, value + 1 + (idle ? 1<<16 : 0));
- }
- }
- if (DEBUG) {
- Slog.d(TAG, "getIdleUids took " + (SystemClock.elapsedRealtime() - elapsedRealtime));
- }
- int numIdle = 0;
- for (int i = uidStates.size() - 1; i >= 0; i--) {
- int value = uidStates.valueAt(i);
- if ((value&0x7fff) == (value>>16)) {
- numIdle++;
- }
- }
-
- int[] res = new int[numIdle];
- numIdle = 0;
- for (int i = uidStates.size() - 1; i >= 0; i--) {
- int value = uidStates.valueAt(i);
- if ((value&0x7fff) == (value>>16)) {
- res[numIdle] = uidStates.keyAt(i);
- numIdle++;
- }
- }
-
- return res;
- }
-
- void setAppIdleAsync(String packageName, boolean idle, int userId) {
- if (packageName == null) return;
-
- mHandler.obtainMessage(MSG_FORCE_IDLE_STATE, userId, idle ? 1 : 0, packageName)
- .sendToTarget();
- }
-
- private boolean isActiveDeviceAdmin(String packageName, int userId) {
- DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
- if (dpm == null) return false;
- return dpm.packageHasActiveAdmins(packageName, userId);
- }
-
- /**
- * Returns {@code true} if the supplied package is the device provisioning app. Otherwise,
- * returns {@code false}.
- */
- private boolean isDeviceProvisioningPackage(String packageName) {
- String deviceProvisioningPackage = getContext().getResources().getString(
- com.android.internal.R.string.config_deviceProvisioningPackage);
- return deviceProvisioningPackage != null && deviceProvisioningPackage.equals(packageName);
- }
-
- private boolean isCarrierApp(String packageName) {
- synchronized (mAppIdleLock) {
- if (!mHaveCarrierPrivilegedApps) {
- fetchCarrierPrivilegedAppsLA();
- }
- if (mCarrierPrivilegedApps != null) {
- return mCarrierPrivilegedApps.contains(packageName);
- }
- return false;
- }
- }
-
- void clearCarrierPrivilegedApps() {
- if (DEBUG) {
- Slog.i(TAG, "Clearing carrier privileged apps list");
- }
- synchronized (mAppIdleLock) {
- mHaveCarrierPrivilegedApps = false;
- mCarrierPrivilegedApps = null; // Need to be refetched.
- }
- }
-
- @GuardedBy("mAppIdleLock")
- private void fetchCarrierPrivilegedAppsLA() {
- TelephonyManager telephonyManager =
- getContext().getSystemService(TelephonyManager.class);
- mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivileges();
- mHaveCarrierPrivilegedApps = true;
- if (DEBUG) {
- Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
- }
- }
-
- private boolean isActiveNetworkScorer(String packageName) {
- NetworkScoreManager nsm = (NetworkScoreManager) getContext().getSystemService(
- Context.NETWORK_SCORE_SERVICE);
- return packageName != null && packageName.equals(nsm.getActiveScorerPackage());
- }
-
- void informListeners(String packageName, int userId, boolean isIdle) {
- for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
- listener.onAppIdleStateChanged(packageName, userId, isIdle);
- }
- }
-
- void informParoleStateChanged() {
- final boolean paroled = isParoledOrCharging();
- for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
- listener.onParoleStateChanged(paroled);
- }
- }
-
private static boolean validRange(long currentTime, long beginTime, long endTime) {
return beginTime <= currentTime && beginTime < endTime;
}
@@ -1143,15 +456,10 @@
for (int i = 0; i < userCount; i++) {
UserUsageStatsService service = mUserState.valueAt(i);
service.persistActiveStats();
- synchronized (mAppIdleLock) {
- mAppIdleHistory.writeAppIdleTimes(mUserState.keyAt(i));
- }
+ mAppStandby.flushToDisk(mUserState.keyAt(i));
}
- // Persist elapsed and screen on time. If this fails for whatever reason, the apps will be
- // considered not-idle, which is the safest outcome in such an event.
- synchronized (mAppIdleLock) {
- mAppIdleHistory.writeAppIdleDurations();
- }
+ mAppStandby.flushDurationsToDisk();
+
mHandler.removeMessages(MSG_FLUSH_TO_DISK);
}
@@ -1166,7 +474,8 @@
final int userCount = mUserState.size();
for (int i = 0; i < userCount; i++) {
- idpw.printPair("user", mUserState.keyAt(i));
+ int userId = mUserState.keyAt(i);
+ idpw.printPair("user", userId);
idpw.println();
idpw.increaseIndent();
if (argSet.contains("--checkin")) {
@@ -1176,57 +485,19 @@
idpw.println();
if (args.length > 0) {
if ("history".equals(args[0])) {
- synchronized (mAppIdleLock) {
- mAppIdleHistory.dumpHistory(idpw, mUserState.keyAt(i));
- }
+ mAppStandby.dumpHistory(idpw, userId);
} else if ("flush".equals(args[0])) {
- UsageStatsService.this.flushToDiskLocked();
+ flushToDiskLocked();
pw.println("Flushed stats to disk");
}
}
}
- synchronized (mAppIdleLock) {
- mAppIdleHistory.dump(idpw, mUserState.keyAt(i));
- }
+ mAppStandby.dumpUser(idpw, userId);
idpw.decreaseIndent();
}
pw.println();
- synchronized (mAppIdleLock) {
- pw.println("Carrier privileged apps (have=" + mHaveCarrierPrivilegedApps
- + "): " + mCarrierPrivilegedApps);
- }
-
- pw.println();
- pw.println("Settings:");
-
- pw.print(" mAppIdleDurationMillis=");
- TimeUtils.formatDuration(mAppIdleScreenThresholdMillis, pw);
- pw.println();
-
- pw.print(" mAppIdleWallclockThresholdMillis=");
- TimeUtils.formatDuration(mAppIdleWallclockThresholdMillis, pw);
- pw.println();
-
- pw.print(" mCheckIdleIntervalMillis=");
- TimeUtils.formatDuration(mCheckIdleIntervalMillis, pw);
- pw.println();
-
- pw.print(" mAppIdleParoleIntervalMillis=");
- TimeUtils.formatDuration(mAppIdleParoleIntervalMillis, pw);
- pw.println();
-
- pw.print(" mAppIdleParoleDurationMillis=");
- TimeUtils.formatDuration(mAppIdleParoleDurationMillis, pw);
- pw.println();
-
- pw.println();
- pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
- pw.print(" mAppIdleTempParoled="); pw.print(mAppIdleTempParoled);
- pw.print(" mCharging="); pw.print(mCharging);
- pw.print(" mLastAppIdleParoledTime=");
- TimeUtils.formatDuration(mLastAppIdleParoledTime, pw);
- pw.println();
+ mAppStandby.dumpState(args, pw);
}
}
@@ -1250,50 +521,6 @@
onUserRemoved(msg.arg1);
break;
- case MSG_INFORM_LISTENERS:
- informListeners((String) msg.obj, msg.arg1, msg.arg2 == 1);
- break;
-
- case MSG_FORCE_IDLE_STATE:
- forceIdleState((String) msg.obj, msg.arg1, msg.arg2 == 1);
- break;
-
- case MSG_CHECK_IDLE_STATES:
- if (checkIdleStates(msg.arg1)) {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(
- MSG_CHECK_IDLE_STATES, msg.arg1, 0),
- mCheckIdleIntervalMillis);
- }
- break;
-
- case MSG_ONE_TIME_CHECK_IDLE_STATES:
- mHandler.removeMessages(MSG_ONE_TIME_CHECK_IDLE_STATES);
- checkIdleStates(UserHandle.USER_ALL);
- break;
-
- case MSG_CHECK_PAROLE_TIMEOUT:
- checkParoleTimeout();
- break;
-
- case MSG_PAROLE_END_TIMEOUT:
- if (DEBUG) Slog.d(TAG, "Ending parole");
- setAppIdleParoled(false);
- break;
-
- case MSG_REPORT_CONTENT_PROVIDER_USAGE:
- SomeArgs args = (SomeArgs) msg.obj;
- reportContentProviderUsage((String) args.arg1, // authority name
- (String) args.arg2, // package name
- (int) args.arg3); // userId
- args.recycle();
- break;
-
- case MSG_PAROLE_STATE_CHANGED:
- if (DEBUG) Slog.d(TAG, "Parole state: " + mAppIdleTempParoled
- + ", Charging state:" + mCharging);
- informParoleStateChanged();
- break;
-
default:
super.handleMessage(msg);
break;
@@ -1301,72 +528,6 @@
}
}
- /**
- * Observe settings changes for {@link Settings.Global#APP_IDLE_CONSTANTS}.
- */
- private class SettingsObserver extends ContentObserver {
- /**
- * This flag has been used to disable app idle on older builds with bug b/26355386.
- */
- @Deprecated
- private static final String KEY_IDLE_DURATION_OLD = "idle_duration";
-
- private static final String KEY_IDLE_DURATION = "idle_duration2";
- private static final String KEY_WALLCLOCK_THRESHOLD = "wallclock_threshold";
- private static final String KEY_PAROLE_INTERVAL = "parole_interval";
- private static final String KEY_PAROLE_DURATION = "parole_duration";
-
- private final KeyValueListParser mParser = new KeyValueListParser(',');
-
- SettingsObserver(Handler handler) {
- super(handler);
- }
-
- void registerObserver() {
- getContext().getContentResolver().registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.APP_IDLE_CONSTANTS), false, this);
- }
-
- @Override
- public void onChange(boolean selfChange) {
- updateSettings();
- postOneTimeCheckIdleStates();
- }
-
- void updateSettings() {
- synchronized (mAppIdleLock) {
- // Look at global settings for this.
- // TODO: Maybe apply different thresholds for different users.
- try {
- mParser.setString(Settings.Global.getString(getContext().getContentResolver(),
- Settings.Global.APP_IDLE_CONSTANTS));
- } catch (IllegalArgumentException e) {
- Slog.e(TAG, "Bad value for app idle settings: " + e.getMessage());
- // fallthrough, mParser is empty and all defaults will be returned.
- }
-
- // Default: 12 hours of screen-on time sans dream-time
- mAppIdleScreenThresholdMillis = mParser.getLong(KEY_IDLE_DURATION,
- COMPRESS_TIME ? ONE_MINUTE * 4 : 12 * 60 * ONE_MINUTE);
-
- mAppIdleWallclockThresholdMillis = mParser.getLong(KEY_WALLCLOCK_THRESHOLD,
- COMPRESS_TIME ? ONE_MINUTE * 8 : 2L * 24 * 60 * ONE_MINUTE); // 2 days
-
- mCheckIdleIntervalMillis = Math.min(mAppIdleScreenThresholdMillis / 4,
- COMPRESS_TIME ? ONE_MINUTE : 8 * 60 * ONE_MINUTE); // 8 hours
-
- // Default: 24 hours between paroles
- mAppIdleParoleIntervalMillis = mParser.getLong(KEY_PAROLE_INTERVAL,
- COMPRESS_TIME ? ONE_MINUTE * 10 : 24 * 60 * ONE_MINUTE);
-
- mAppIdleParoleDurationMillis = mParser.getLong(KEY_PAROLE_DURATION,
- COMPRESS_TIME ? ONE_MINUTE : 10 * ONE_MINUTE); // 10 minutes
- mAppIdleHistory.setThresholds(mAppIdleWallclockThresholdMillis,
- mAppIdleScreenThresholdMillis);
- }
- }
- }
-
private final class BinderService extends IUsageStatsManager.Stub {
private boolean hasPermission(String callingPackage) {
@@ -1462,7 +623,8 @@
Binder.getCallingUid(), userId);
final long token = Binder.clearCallingIdentity();
try {
- return UsageStatsService.this.isAppIdleFilteredOrParoled(packageName, userId,
+ return mAppStandby.isAppIdleFilteredOrParoled(
+ packageName, userId,
SystemClock.elapsedRealtime(), obfuscateInstantApps);
} finally {
Binder.restoreCallingIdentity(token);
@@ -1483,9 +645,9 @@
"No permission to change app idle state");
final long token = Binder.clearCallingIdentity();
try {
- final int appId = getAppId(packageName);
+ final int appId = mAppStandby.getAppId(packageName);
if (appId < 0) return;
- UsageStatsService.this.setAppIdleAsync(packageName, idle, userId);
+ mAppStandby.setAppIdleAsync(packageName, idle, userId);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -1509,7 +671,7 @@
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.BIND_CARRIER_SERVICES,
"onCarrierPrivilegedAppsChanged can only be called by privileged apps.");
- UsageStatsService.this.clearCarrierPrivilegedApps();
+ mAppStandby.clearCarrierPrivilegedApps();
}
@Override
@@ -1624,28 +786,23 @@
@Override
public void reportContentProviderUsage(String name, String packageName, int userId) {
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = name;
- args.arg2 = packageName;
- args.arg3 = userId;
- mHandler.obtainMessage(MSG_REPORT_CONTENT_PROVIDER_USAGE, args)
- .sendToTarget();
+ mAppStandby.postReportContentProviderUsage(name, packageName, userId);
}
@Override
public boolean isAppIdle(String packageName, int uidForAppId, int userId) {
- return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId,
- SystemClock.elapsedRealtime());
+ return mAppStandby.isAppIdleFiltered(packageName, uidForAppId,
+ userId, SystemClock.elapsedRealtime());
}
@Override
public int[] getIdleUidsForUser(int userId) {
- return UsageStatsService.this.getIdleUidsForUser(userId);
+ return mAppStandby.getIdleUidsForUser(userId);
}
@Override
public boolean isAppIdleParoleOn() {
- return isParoledOrCharging();
+ return mAppStandby.isParoledOrCharging();
}
@Override
@@ -1658,20 +815,20 @@
@Override
public void addAppIdleStateChangeListener(AppIdleStateChangeListener listener) {
- UsageStatsService.this.addListener(listener);
+ mAppStandby.addListener(listener);
listener.onParoleStateChanged(isAppIdleParoleOn());
}
@Override
public void removeAppIdleStateChangeListener(
AppIdleStateChangeListener listener) {
- UsageStatsService.this.removeListener(listener);
+ mAppStandby.removeListener(listener);
}
@Override
public byte[] getBackupPayload(int user, String key) {
// Check to ensure that only user 0's data is b/r for now
- synchronized (UsageStatsService.this.mLock) {
+ synchronized (mLock) {
if (user == UserHandle.USER_SYSTEM) {
final UserUsageStatsService userStats =
getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
@@ -1684,7 +841,7 @@
@Override
public void applyRestoredPayload(int user, String key, byte[] payload) {
- synchronized (UsageStatsService.this.mLock) {
+ synchronized (mLock) {
if (user == UserHandle.USER_SYSTEM) {
final UserUsageStatsService userStats =
getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 68c1d5f..acc27be 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -314,7 +314,11 @@
return null;
}
- mDevicesParser.scan();
+ if (!mDevicesParser.scan()) {
+ Slog.e(TAG, "Error parsing ALSA devices file.");
+ return null;
+ }
+
int device = mDevicesParser.getDefaultDeviceNum(card);
boolean hasPlayback = mDevicesParser.hasPlaybackDevices(card);
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index 764b7b2..9a9877a 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -77,8 +77,9 @@
* Integer extra that Android will attach to the intent supplied via
* {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}
* Indicates the result code of the download. One of
- * {@link #RESULT_SUCCESSFUL}, {@link #RESULT_EXPIRED}, {@link #RESULT_CANCELLED}, or
- * {@link #RESULT_IO_ERROR}.
+ * {@link #RESULT_SUCCESSFUL}, {@link #RESULT_EXPIRED}, {@link #RESULT_CANCELLED},
+ * {@link #RESULT_IO_ERROR}, {@link #RESULT_DOWNLOAD_FAILURE}, {@link #RESULT_OUT_OF_STORAGE},
+ * {@link #RESULT_SERVICE_ID_NOT_DEFINED}, or {@link #RESULT_FILE_ROOT_UNREACHABLE}.
*
* This extra may also be used by the middleware when it is sending intents to the app.
*/
@@ -142,11 +143,41 @@
/**
* Indicates that the download will not be completed due to an I/O error incurred while
- * writing to temp files. This commonly indicates that the device is out of storage space,
- * but may indicate other conditions as well (such as an SD card being removed).
+ * writing to temp files.
+ *
+ * This is likely a transient error and another {@link DownloadRequest} should be sent to try
+ * the download again.
*/
public static final int RESULT_IO_ERROR = 4;
- // TODO - more results!
+
+ /**
+ * Indicates that the Service ID specified in the {@link DownloadRequest} is incorrect due to
+ * the Id being incorrect, stale, expired, or similar.
+ */
+ public static final int RESULT_SERVICE_ID_NOT_DEFINED = 5;
+
+ /**
+ * Indicates that there was an error while processing downloaded files, such as a file repair or
+ * file decoding error and is not due to a file I/O error.
+ *
+ * This is likely a transient error and another {@link DownloadRequest} should be sent to try
+ * the download again.
+ */
+ public static final int RESULT_DOWNLOAD_FAILURE = 6;
+
+ /**
+ * Indicates that the file system is full and the {@link DownloadRequest} can not complete.
+ * Either space must be made on the current file system or the temp file root location must be
+ * changed to a location that is not full to download the temp files.
+ */
+ public static final int RESULT_OUT_OF_STORAGE = 7;
+
+ /**
+ * Indicates that the file root that was set is currently unreachable. This can happen if the
+ * temp files are set to be stored on external storage and the SD card was removed, for example.
+ * The temp file root should be changed before sending another DownloadRequest.
+ */
+ public static final int RESULT_FILE_ROOT_UNREACHABLE = 8;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java
index 5a57f32..f0d60b6 100644
--- a/telephony/java/android/telephony/mbms/DownloadRequest.java
+++ b/telephony/java/android/telephony/mbms/DownloadRequest.java
@@ -16,6 +16,7 @@
package android.telephony.mbms;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.content.Intent;
import android.net.Uri;
@@ -26,7 +27,6 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
-import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
@@ -71,6 +71,19 @@
private String appIntent;
private int version = CURRENT_VERSION;
+
+ /**
+ * Builds a new DownloadRequest.
+ * @param sourceUri the source URI for the DownloadRequest to be built. This URI should
+ * never be null.
+ */
+ public Builder(@NonNull Uri sourceUri) {
+ if (sourceUri == null) {
+ throw new IllegalArgumentException("Source URI must be non-null.");
+ }
+ source = sourceUri;
+ }
+
/**
* Sets the service from which the download request to be built will download from.
* @param serviceInfo
@@ -92,15 +105,6 @@
}
/**
- * Sets the source URI for the download request to be built.
- * @param source
- */
- public Builder setSource(Uri source) {
- this.source = source;
- return this;
- }
-
- /**
* Set the subscription ID on which the file(s) should be downloaded.
* @param subscriptionId
*/
@@ -316,9 +320,11 @@
throw new RuntimeException("Could not get sha256 hash object");
}
if (version >= 1) {
- // Hash the source URI, destination URI, and the app intent
+ // Hash the source URI and the app intent
digest.update(sourceUri.toString().getBytes(StandardCharsets.UTF_8));
- digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8));
+ if (serializedResultIntentForApp != null) {
+ digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8));
+ }
}
// Add updates for future versions here
return Base64.encodeToString(digest.digest(), Base64.URL_SAFE | Base64.NO_WRAP);
diff --git a/tests/CantSaveState1/Android.mk b/tests/CantSaveState1/Android.mk
new file mode 100644
index 0000000..6e9db6e
--- /dev/null
+++ b/tests/CantSaveState1/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := CantSaveState1
+
+include $(BUILD_PACKAGE)
diff --git a/tests/CantSaveState1/AndroidManifest.xml b/tests/CantSaveState1/AndroidManifest.xml
new file mode 100644
index 0000000..fadcaeb
--- /dev/null
+++ b/tests/CantSaveState1/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.cantsavestate1">
+ <application android:label="Can't Save 1" android:cantSaveState="true">
+ <activity android:name="CantSave1Activity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/CantSaveState1/res/layout/cant_save_1_activity.xml b/tests/CantSaveState1/res/layout/cant_save_1_activity.xml
new file mode 100644
index 0000000..c5bf657
--- /dev/null
+++ b/tests/CantSaveState1/res/layout/cant_save_1_activity.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="25dp"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:text="This app #1 can't save its state"
+ />
+
+</LinearLayout>
diff --git a/cmds/statsd/src/parse_util.h b/tests/CantSaveState1/src/com/android/test/cantsavestate2/CantSave1Activity.java
similarity index 64%
copy from cmds/statsd/src/parse_util.h
copy to tests/CantSaveState1/src/com/android/test/cantsavestate2/CantSave1Activity.java
index 8b82e7b..8879ed0 100644
--- a/cmds/statsd/src/parse_util.h
+++ b/tests/CantSaveState1/src/com/android/test/cantsavestate2/CantSave1Activity.java
@@ -13,24 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef PARSE_UTIL_H
-#define PARSE_UTIL_H
-#include "DropboxWriter.h"
-#include "LogReader.h"
+package com.android.test.cantsavestate1;
-#include <log/logprint.h>
+import android.app.Activity;
+import android.os.Bundle;
-namespace android {
-namespace os {
-namespace statsd {
-
-EventMetricData parse(log_msg msg);
-
-int getTagId(log_msg msg);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#endif // PARSE_UTIL_H
+public class CantSave1Activity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.cant_save_1_activity);
+ }
+}
diff --git a/tests/CantSaveState2/Android.mk b/tests/CantSaveState2/Android.mk
new file mode 100644
index 0000000..add9214
--- /dev/null
+++ b/tests/CantSaveState2/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := CantSaveState2
+
+include $(BUILD_PACKAGE)
diff --git a/tests/CantSaveState2/AndroidManifest.xml b/tests/CantSaveState2/AndroidManifest.xml
new file mode 100644
index 0000000..8f4f01d
--- /dev/null
+++ b/tests/CantSaveState2/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.cantsavestate2">
+ <application android:label="Can't Save 2" android:cantSaveState="true">
+ <activity android:name="CantSave2Activity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/CantSaveState2/res/layout/cant_save_2_activity.xml b/tests/CantSaveState2/res/layout/cant_save_2_activity.xml
new file mode 100644
index 0000000..c5b8e3d
--- /dev/null
+++ b/tests/CantSaveState2/res/layout/cant_save_2_activity.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="25dp"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:text="This app #2 can't save its state"
+ />
+
+</LinearLayout>
diff --git a/cmds/statsd/src/parse_util.h b/tests/CantSaveState2/src/com/android/test/cantsavestate2/CantSave2Activity.java
similarity index 64%
copy from cmds/statsd/src/parse_util.h
copy to tests/CantSaveState2/src/com/android/test/cantsavestate2/CantSave2Activity.java
index 8b82e7b..3ce63c7 100644
--- a/cmds/statsd/src/parse_util.h
+++ b/tests/CantSaveState2/src/com/android/test/cantsavestate2/CantSave2Activity.java
@@ -13,24 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef PARSE_UTIL_H
-#define PARSE_UTIL_H
-#include "DropboxWriter.h"
-#include "LogReader.h"
+package com.android.test.cantsavestate2;
-#include <log/logprint.h>
+import android.app.Activity;
+import android.os.Bundle;
-namespace android {
-namespace os {
-namespace statsd {
-
-EventMetricData parse(log_msg msg);
-
-int getTagId(log_msg msg);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#endif // PARSE_UTIL_H
+public class CantSave2Activity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.cant_save_2_activity);
+ }
+}
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 8210403..163250d 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -296,6 +296,7 @@
Notification n = new Notification.Builder(NotificationTestList.this, "min")
.setSmallIcon(R.drawable.icon2)
.setContentTitle("Min priority")
+ .setTicker("Min priority")
.build();
mNM.notify("min", 7000, n);
}
@@ -306,6 +307,7 @@
Notification n = new Notification.Builder(NotificationTestList.this, "low")
.setSmallIcon(R.drawable.icon2)
.setContentTitle("Low priority")
+ .setTicker("Low priority")
.build();
mNM.notify("low", 7002, n);
}
@@ -326,6 +328,7 @@
Notification n = new Notification.Builder(NotificationTestList.this, "high")
.setSmallIcon(R.drawable.icon2)
.setContentTitle("High priority")
+ .setTicker("High priority")
.build();
mNM.notify("high", 7006, n);
}
diff --git a/tests/net/java/com/android/internal/util/BitUtilsTest.java b/tests/net/java/com/android/internal/util/BitUtilsTest.java
index 0ad8a21..f4dc12a 100644
--- a/tests/net/java/com/android/internal/util/BitUtilsTest.java
+++ b/tests/net/java/com/android/internal/util/BitUtilsTest.java
@@ -56,6 +56,25 @@
}
@Test
+ public void testUnsignedShortComposition() {
+ byte b0 = 0;
+ byte b1 = 1;
+ byte b2 = 2;
+ byte b10 = 10;
+ byte b16 = 16;
+ byte b128 = -128;
+ byte b224 = -32;
+ byte b255 = -1;
+ assertEquals(0x0000, uint16(b0, b0));
+ assertEquals(0xffff, uint16(b255, b255));
+ assertEquals(0x0a01, uint16(b10, b1));
+ assertEquals(0x8002, uint16(b128, b2));
+ assertEquals(0x01ff, uint16(b1, b255));
+ assertEquals(0x80ff, uint16(b128, b255));
+ assertEquals(0xe010, uint16(b224, b16));
+ }
+
+ @Test
public void testUnsignedIntWideningConversions() {
assertEquals(0, uint32(0));
assertEquals(1, uint32(1));
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 4aeae70..9c4da1f 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -24,7 +24,6 @@
import android.view.IWindowManager;
import junit.framework.TestCase;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.view.Display.DEFAULT_DISPLAY;
/**
diff --git a/tools/incident_report/generic_message.h b/tools/incident_report/generic_message.h
index df3f7b2..7c4ad34 100644
--- a/tools/incident_report/generic_message.h
+++ b/tools/incident_report/generic_message.h
@@ -25,7 +25,7 @@
/**
* Class to represent a protobuf Message, where we don't actually
* know what any of the fields are, just their type codes. In other
- * words, this loslessly stores a parsed protobuf object without
+ * words, this losslessly stores a parsed protobuf object without
* having the .proto file that generated it.
*/
class GenericMessage
diff --git a/tools/incident_report/main.cpp b/tools/incident_report/main.cpp
index 250d118..d4ad340 100644
--- a/tools/incident_report/main.cpp
+++ b/tools/incident_report/main.cpp
@@ -138,7 +138,6 @@
static void
print_value(Out* out, FieldDescriptor const* field, GenericMessage::Node const& node)
{
- uint32_t val32;
FieldDescriptor::Type type = field->type();
switch (node.type) {
@@ -160,23 +159,25 @@
break;
case GenericMessage::TYPE_VALUE64:
switch (type) {
- case FieldDescriptor::TYPE_FIXED64:
- case FieldDescriptor::TYPE_SFIXED64:
case FieldDescriptor::TYPE_DOUBLE:
out->printf("%f", *(double*)&node.value64);
break;
+ // Int32s here were added with addInt64 from a WIRETYPE_VARINT,
+ // even if the definition is for a 32 bit int.
case FieldDescriptor::TYPE_SINT32:
case FieldDescriptor::TYPE_INT32:
- val32 = (uint32_t)node.value32;
- out->printf("%d", val32);
+ out->printf("%d", node.value64);
break;
case FieldDescriptor::TYPE_INT64:
- case FieldDescriptor::TYPE_UINT32:
- val32 = (uint32_t)node.value32;
- out->printf("%u", val32);
- break;
- case FieldDescriptor::TYPE_UINT64:
case FieldDescriptor::TYPE_SINT64:
+ case FieldDescriptor::TYPE_SFIXED64:
+ out->printf("%lld", node.value64);
+ break;
+ case FieldDescriptor::TYPE_UINT32:
+ case FieldDescriptor::TYPE_UINT64:
+ case FieldDescriptor::TYPE_FIXED64:
+ out->printf("%u", node.value64);
+ break;
case FieldDescriptor::TYPE_BOOL:
if (node.value64) {
out->printf("true");
@@ -185,11 +186,15 @@
}
break;
case FieldDescriptor::TYPE_ENUM:
- out->printf("%s", field->enum_type()->FindValueByNumber((int)node.value64)
+ if (field->enum_type()->FindValueByNumber((int)node.value64) == NULL) {
+ out->printf("%lld", (int) node.value64);
+ } else {
+ out->printf("%s", field->enum_type()->FindValueByNumber((int)node.value64)
->name().c_str());
+ }
break;
default:
- out->printf("(unexpected value64 %ld (0x%x))", node.value64, node.value64);
+ out->printf("(unexpected value64 %lld (0x%x))", node.value64, node.value64);
break;
}
break;
@@ -224,22 +229,13 @@
out->printf("%s=", field->name().c_str());
if (repeated) {
if (it.first != it.second) {
- out->printf("[");
- if (type == FieldDescriptor::TYPE_MESSAGE
- || type == FieldDescriptor::TYPE_STRING
- || type == FieldDescriptor::TYPE_BYTES) {
- out->printf("\n");
- }
+ out->printf("[\n");
out->indent();
for (GenericMessage::const_iterator_pair it = message->find(fieldId);
it.first != it.second; it.first++) {
print_value(out, field, it.first->second);
- if (type == FieldDescriptor::TYPE_MESSAGE
- || type == FieldDescriptor::TYPE_STRING
- || type == FieldDescriptor::TYPE_BYTES) {
- out->printf("\n");
- }
+ out->printf("\n");
}
out->dedent();
@@ -335,7 +331,7 @@
}
id = field->number();
}
-
+
int pfd[2];
if (pipe(pfd) != 0) {
fprintf(stderr, "pipe failed: %s\n", strerror(errno));
diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
index 24068e9..756549c 100644
--- a/tools/streaming_proto/Android.bp
+++ b/tools/streaming_proto/Android.bp
@@ -17,13 +17,31 @@
// ==========================================================
// Build the host executable: protoc-gen-javastream
// ==========================================================
-cc_binary_host {
- name: "protoc-gen-javastream",
+cc_defaults {
+ name: "protoc-gen-stream-defaults",
srcs: [
"Errors.cpp",
"string_utils.cpp",
- "main.cpp",
+ ],
+}
+
+
+cc_binary_host {
+ name: "protoc-gen-javastream",
+ srcs: [
+ "java/main.cpp",
],
+ defaults: ["protoc-gen-stream-defaults"],
+ shared_libs: ["libprotoc"],
+}
+
+cc_binary_host {
+ name: "protoc-gen-cppstream",
+ srcs: [
+ "cpp/main.cpp",
+ ],
+
+ defaults: ["protoc-gen-stream-defaults"],
shared_libs: ["libprotoc"],
}
diff --git a/tools/streaming_proto/Errors.cpp b/tools/streaming_proto/Errors.cpp
index 91c6b92..0cd9037 100644
--- a/tools/streaming_proto/Errors.cpp
+++ b/tools/streaming_proto/Errors.cpp
@@ -3,7 +3,7 @@
#include <stdlib.h>
namespace android {
-namespace javastream_proto {
+namespace stream_proto {
Errors ERRORS;
@@ -82,6 +82,6 @@
return m_errors.size() > 0;
}
-} // namespace javastream_proto
+} // namespace stream_proto
} // namespace android
diff --git a/tools/streaming_proto/Errors.h b/tools/streaming_proto/Errors.h
index 109195a..f14bbfd 100644
--- a/tools/streaming_proto/Errors.h
+++ b/tools/streaming_proto/Errors.h
@@ -4,7 +4,7 @@
#include <vector>
namespace android {
-namespace javastream_proto {
+namespace stream_proto {
using namespace std;
@@ -44,5 +44,5 @@
extern const int UNKNOWN_LINE;
-} // namespace javastream_proto
+} // namespace stream_proto
} // namespace android
diff --git a/tools/streaming_proto/cpp/main.cpp b/tools/streaming_proto/cpp/main.cpp
new file mode 100644
index 0000000..d4e1b7a
--- /dev/null
+++ b/tools/streaming_proto/cpp/main.cpp
@@ -0,0 +1,273 @@
+#include "Errors.h"
+#include "string_utils.h"
+
+#include "google/protobuf/compiler/plugin.pb.h"
+#include "google/protobuf/io/zero_copy_stream_impl.h"
+#include "google/protobuf/text_format.h"
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+using namespace android::stream_proto;
+using namespace google::protobuf;
+using namespace google::protobuf::compiler;
+using namespace google::protobuf::io;
+using namespace std;
+
+/**
+ * Position of the field type in a (long long) fieldId.
+ */
+const uint64_t FIELD_TYPE_SHIFT = 32;
+
+//
+// FieldId flags for whether the field is single, repeated or packed.
+// TODO: packed is not supported yet.
+//
+const uint64_t FIELD_COUNT_SHIFT = 40;
+const uint64_t FIELD_COUNT_MASK = 0x0fULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_UNKNOWN = 0;
+const uint64_t FIELD_COUNT_SINGLE = 1ULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_REPEATED = 2ULL << FIELD_COUNT_SHIFT;
+const uint64_t FIELD_COUNT_PACKED = 4ULL << FIELD_COUNT_SHIFT;
+
+// Indent
+const string INDENT = " ";
+
+/**
+ * See if this is the file for this request, and not one of the imported ones.
+ */
+static bool
+should_generate_for_file(const CodeGeneratorRequest& request, const string& file)
+{
+ const int N = request.file_to_generate_size();
+ for (int i=0; i<N; i++) {
+ if (request.file_to_generate(i) == file) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static string
+make_filename(const FileDescriptorProto& file_descriptor)
+{
+ return file_descriptor.name() + ".h";
+}
+
+static string
+get_proto_type(const FieldDescriptorProto& field)
+{
+ switch (field.type()) {
+ case FieldDescriptorProto::TYPE_DOUBLE:
+ return "double";
+ case FieldDescriptorProto::TYPE_FLOAT:
+ return "float";
+ case FieldDescriptorProto::TYPE_INT64:
+ return "int64";
+ case FieldDescriptorProto::TYPE_UINT64:
+ return "uint64";
+ case FieldDescriptorProto::TYPE_INT32:
+ return "int32";
+ case FieldDescriptorProto::TYPE_FIXED64:
+ return "fixed64";
+ case FieldDescriptorProto::TYPE_FIXED32:
+ return "fixed32";
+ case FieldDescriptorProto::TYPE_BOOL:
+ return "bool";
+ case FieldDescriptorProto::TYPE_STRING:
+ return "string";
+ case FieldDescriptorProto::TYPE_GROUP:
+ return "group<unsupported!>";
+ case FieldDescriptorProto::TYPE_MESSAGE:
+ return field.type_name();
+ case FieldDescriptorProto::TYPE_BYTES:
+ return "bytes";
+ case FieldDescriptorProto::TYPE_UINT32:
+ return "uint32";
+ case FieldDescriptorProto::TYPE_ENUM:
+ return field.type_name();
+ case FieldDescriptorProto::TYPE_SFIXED32:
+ return "sfixed32";
+ case FieldDescriptorProto::TYPE_SFIXED64:
+ return "sfixed64";
+ case FieldDescriptorProto::TYPE_SINT32:
+ return "sint32";
+ case FieldDescriptorProto::TYPE_SINT64:
+ return "sint64";
+ default:
+ // won't happen
+ return "void";
+ }
+}
+
+static void
+write_enum(stringstream& text, const EnumDescriptorProto& enu, const string& indent)
+{
+ const int N = enu.value_size();
+ text << indent << "// enum " << enu.name() << endl;
+ for (int i=0; i<N; i++) {
+ const EnumValueDescriptorProto& value = enu.value(i);
+ text << indent << "const uint32_t "
+ << make_constant_name(value.name())
+ << " = " << value.number() << ";" << endl;
+ }
+ text << endl;
+}
+
+static uint64_t
+get_field_id(const FieldDescriptorProto& field)
+{
+ // Number
+ uint64_t result = (uint64_t)field.number();
+
+ // Type
+ result |= (uint64_t)field.type() << FIELD_TYPE_SHIFT;
+
+ // Count
+ if (field.options().packed()) {
+ result |= FIELD_COUNT_PACKED;
+ } else if (field.label() == FieldDescriptorProto::LABEL_REPEATED) {
+ result |= FIELD_COUNT_REPEATED;
+ } else {
+ result |= FIELD_COUNT_SINGLE;
+ }
+
+ return result;
+}
+
+static void
+write_field(stringstream& text, const FieldDescriptorProto& field, const string& indent)
+{
+ string optional_comment = field.label() == FieldDescriptorProto::LABEL_OPTIONAL
+ ? "optional " : "";
+ string repeated_comment = field.label() == FieldDescriptorProto::LABEL_REPEATED
+ ? "repeated " : "";
+ string proto_type = get_proto_type(field);
+ string packed_comment = field.options().packed()
+ ? " [packed=true]" : "";
+ text << indent << "// " << optional_comment << repeated_comment << proto_type << ' '
+ << field.name() << " = " << field.number() << packed_comment << ';' << endl;
+
+ text << indent << "const uint64_t " << make_constant_name(field.name()) << " = 0x";
+
+ ios::fmtflags fmt(text.flags());
+ text << setfill('0') << setw(16) << hex << get_field_id(field);
+ text.flags(fmt);
+
+ text << "LL;" << endl;
+
+ text << endl;
+}
+
+static void
+write_message(stringstream& text, const DescriptorProto& message, const string& indent)
+{
+ int N;
+ const string indented = indent + INDENT;
+
+ text << indent << "// message " << message.name() << endl;
+ text << indent << "class " << message.name() << " {" << endl;
+ text << indent << "public:" << endl;
+
+ // Enums
+ N = message.enum_type_size();
+ for (int i=0; i<N; i++) {
+ write_enum(text, message.enum_type(i), indented);
+ }
+
+ // Nested classes
+ N = message.nested_type_size();
+ for (int i=0; i<N; i++) {
+ write_message(text, message.nested_type(i), indented);
+ }
+
+ // Fields
+ N = message.field_size();
+ for (int i=0; i<N; i++) {
+ write_field(text, message.field(i), indented);
+ }
+
+ text << indent << "};" << endl;
+ text << endl;
+}
+
+static void
+write_cpp_file(CodeGeneratorResponse* response, const FileDescriptorProto& file_descriptor)
+{
+ stringstream text;
+
+ text << "// Generated by protoc-gen-cppstream. DO NOT MODIFY." << endl;
+ text << "// source: " << file_descriptor.name() << endl << endl;
+
+ string header = "ANDROID_" + replace_string(file_descriptor.name(), '/', '_');
+ header = replace_string(header, '.', '_') + "_stream_h";
+ header = make_constant_name(header);
+
+ text << "#ifndef " << header << endl;
+ text << "#define " << header << endl;
+ text << endl;
+
+ vector<string> namespaces = split(file_descriptor.package(), '.');
+ for (vector<string>::iterator it = namespaces.begin(); it != namespaces.end(); it++) {
+ text << "namespace " << *it << " {" << endl;
+ }
+ text << endl;
+
+ size_t N;
+ N = file_descriptor.enum_type_size();
+ for (size_t i=0; i<N; i++) {
+ write_enum(text, file_descriptor.enum_type(i), "");
+ }
+
+ N = file_descriptor.message_type_size();
+ for (size_t i=0; i<N; i++) {
+ write_message(text, file_descriptor.message_type(i), "");
+ }
+
+ for (vector<string>::iterator it = namespaces.begin(); it != namespaces.end(); it++) {
+ text << "} // " << *it << endl;
+ }
+
+ text << endl;
+ text << "#endif // " << header << endl;
+
+ CodeGeneratorResponse::File* file_response = response->add_file();
+ file_response->set_name(make_filename(file_descriptor));
+ file_response->set_content(text.str());
+}
+
+int main(int argc, char const *argv[])
+{
+ (void)argc;
+ (void)argv;
+
+ GOOGLE_PROTOBUF_VERIFY_VERSION;
+
+ CodeGeneratorRequest request;
+ CodeGeneratorResponse response;
+
+ // Read the request
+ request.ParseFromIstream(&cin);
+
+ // Build the files we need.
+ const int N = request.proto_file_size();
+ for (int i=0; i<N; i++) {
+ const FileDescriptorProto& file_descriptor = request.proto_file(i);
+ if (should_generate_for_file(request, file_descriptor.name())) {
+ write_cpp_file(&response, file_descriptor);
+ }
+ }
+
+ // If we had errors, don't write the response. Print the errors and exit.
+ if (ERRORS.HasErrors()) {
+ ERRORS.Print();
+ return 1;
+ }
+
+ // If we didn't have errors, write the response and exit happily.
+ response.SerializeToOstream(&cout);
+
+ /* code */
+ return 0;
+}
\ No newline at end of file
diff --git a/tools/streaming_proto/main.cpp b/tools/streaming_proto/java/main.cpp
similarity index 99%
rename from tools/streaming_proto/main.cpp
rename to tools/streaming_proto/java/main.cpp
index 5b4ba04..b7d594b 100644
--- a/tools/streaming_proto/main.cpp
+++ b/tools/streaming_proto/java/main.cpp
@@ -12,7 +12,7 @@
#include <sstream>
#include <map>
-using namespace android::javastream_proto;
+using namespace android::stream_proto;
using namespace google::protobuf;
using namespace google::protobuf::compiler;
using namespace google::protobuf::io;
diff --git a/tools/streaming_proto/string_utils.cpp b/tools/streaming_proto/string_utils.cpp
index cc738c4..bd34ab7 100644
--- a/tools/streaming_proto/string_utils.cpp
+++ b/tools/streaming_proto/string_utils.cpp
@@ -3,7 +3,7 @@
#include <iostream>
namespace android {
-namespace javastream_proto {
+namespace stream_proto {
using namespace std;
@@ -89,7 +89,26 @@
return result;
}
-} // namespace javastream_proto
+vector<string>
+split(const string& str, const char delimiter)
+{
+ vector<string> result;
+ size_t base = 0, found = 0;
+ while (true) {
+ found = str.find_first_of(delimiter, base);
+ if (found != base) {
+ string part = str.substr(base, found - base);
+ if (!part.empty()) {
+ result.push_back(part);
+ }
+ }
+ if (found == str.npos) break;
+ base = found + 1;
+ }
+ return result;
+}
+
+} // namespace stream_proto
} // namespace android
diff --git a/tools/streaming_proto/string_utils.h b/tools/streaming_proto/string_utils.h
index ffe83ca..03284d1 100644
--- a/tools/streaming_proto/string_utils.h
+++ b/tools/streaming_proto/string_utils.h
@@ -1,7 +1,8 @@
#include <string>
+#include <vector>
namespace android {
-namespace javastream_proto {
+namespace stream_proto {
using namespace std;
@@ -26,7 +27,11 @@
*/
string replace_string(const string& str, const char replace, const char with);
+/**
+ * Split a string to parts by delimiter.
+ */
+vector<string> split(const string& str, const char delimiter);
-} // namespace javastream_proto
+} // namespace stream_proto
} // namespace android
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index f47d5ca..e3752ac 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -1105,8 +1105,6 @@
private static final int BASE = Protocol.BASE_WIFI_SCANNER;
/** @hide */
- public static final int CMD_SCAN = BASE + 0;
- /** @hide */
public static final int CMD_START_BACKGROUND_SCAN = BASE + 2;
/** @hide */
public static final int CMD_STOP_BACKGROUND_SCAN = BASE + 3;
@@ -1115,20 +1113,10 @@
/** @hide */
public static final int CMD_SCAN_RESULT = BASE + 5;
/** @hide */
- public static final int CMD_AP_FOUND = BASE + 9;
- /** @hide */
- public static final int CMD_AP_LOST = BASE + 10;
- /** @hide */
- public static final int CMD_WIFI_CHANGE_DETECTED = BASE + 15;
- /** @hide */
- public static final int CMD_WIFI_CHANGES_STABILIZED = BASE + 16;
- /** @hide */
public static final int CMD_OP_SUCCEEDED = BASE + 17;
/** @hide */
public static final int CMD_OP_FAILED = BASE + 18;
/** @hide */
- public static final int CMD_PERIOD_CHANGED = BASE + 19;
- /** @hide */
public static final int CMD_FULL_SCAN_RESULT = BASE + 20;
/** @hide */
public static final int CMD_START_SINGLE_SCAN = BASE + 21;
@@ -1359,25 +1347,6 @@
ScanResult result = (ScanResult) msg.obj;
((ScanListener) listener).onFullResult(result);
return;
- case CMD_PERIOD_CHANGED:
- ((ScanListener) listener).onPeriodChanged(msg.arg1);
- return;
- case CMD_AP_FOUND:
- ((BssidListener) listener).onFound(
- ((ParcelableScanResults) msg.obj).getResults());
- return;
- case CMD_AP_LOST:
- ((BssidListener) listener).onLost(
- ((ParcelableScanResults) msg.obj).getResults());
- return;
- case CMD_WIFI_CHANGE_DETECTED:
- ((WifiChangeListener) listener).onChanging(
- ((ParcelableScanResults) msg.obj).getResults());
- return;
- case CMD_WIFI_CHANGES_STABILIZED:
- ((WifiChangeListener) listener).onQuiescence(
- ((ParcelableScanResults) msg.obj).getResults());
- return;
case CMD_SINGLE_SCAN_COMPLETED:
if (DBG) Log.d(TAG, "removing listener for single scan");
removeListener(msg.arg2);
diff --git a/cmds/statsd/src/parse_util.h b/wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl
similarity index 64%
copy from cmds/statsd/src/parse_util.h
copy to wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl
index 8b82e7b..0e7289c 100644
--- a/cmds/statsd/src/parse_util.h
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareMacAddressProvider.aidl
@@ -13,24 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-#ifndef PARSE_UTIL_H
-#define PARSE_UTIL_H
-#include "DropboxWriter.h"
-#include "LogReader.h"
+package android.net.wifi.aware;
-#include <log/logprint.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-EventMetricData parse(log_msg msg);
-
-int getTagId(log_msg msg);
-
-} // namespace statsd
-} // namespace os
-} // namespace android
-
-#endif // PARSE_UTIL_H
+/**
+ * Callback for IWifiAwareManager.getMacAddressFromPeerHandle
+ *
+ * @hide
+ */
+oneway interface IWifiAwareMacAddressProvider
+{
+ void macAddress(in Map peerIdToMacMap);
+}
diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
index f33424b..bad5ce2 100644
--- a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
@@ -21,6 +21,7 @@
import android.net.wifi.aware.ConfigRequest;
import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
import android.net.wifi.aware.IWifiAwareEventCallback;
+import android.net.wifi.aware.IWifiAwareMacAddressProvider;
import android.net.wifi.aware.PublishConfig;
import android.net.wifi.aware.SubscribeConfig;
import android.net.wifi.aware.Characteristics;
@@ -52,4 +53,7 @@
void sendMessage(int clientId, int discoverySessionId, int peerId, in byte[] message,
int messageId, int retryCount);
void terminateSession(int clientId, int discoverySessionId);
+
+ // internal APIs: intended to be used between System Services (restricted permissions)
+ void requestMacAddresses(int uid, in List peerIds, in IWifiAwareMacAddressProvider callback);
}
diff --git a/wifi/java/android/net/wifi/aware/PeerHandle.java b/wifi/java/android/net/wifi/aware/PeerHandle.java
index cd45c52..1b0aba1 100644
--- a/wifi/java/android/net/wifi/aware/PeerHandle.java
+++ b/wifi/java/android/net/wifi/aware/PeerHandle.java
@@ -32,4 +32,24 @@
/** @hide */
public int peerId;
+
+ /** @hide RTT_API */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof PeerHandle)) {
+ return false;
+ }
+
+ return peerId == ((PeerHandle) o).peerId;
+ }
+
+ /** @hide RTT_API */
+ @Override
+ public int hashCode() {
+ return peerId;
+ }
}