Merge "Add experiment flag namespace for Multi-Gen LRU"
diff --git a/Android.bp b/Android.bp
index 8c5a855..ceb35bd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -65,7 +65,6 @@
// Java/AIDL sources under frameworks/base
":framework-annotations",
":framework-blobstore-sources",
- ":framework-connectivity-tiramisu-sources",
":framework-core-sources",
":framework-drm-sources",
":framework-graphics-nonupdatable-sources",
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 2efeab6..39f055b 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -226,7 +226,7 @@
"android.whichdoc offline",
],
compat_config: ":global-compat-config",
- proofread_file: "offline-sdk-docs-proofrerad.txt",
+ proofread_file: "offline-sdk-docs-proofread.txt",
args: framework_docs_only_args + " -offlinemode -title \"Android SDK\"",
static_doc_index_redirect: "docs/docs-preview-index.html",
}
@@ -243,7 +243,7 @@
hdf: [
"android.whichdoc offline",
],
- proofread_file: "offline-sdk-referenceonly-docs-proofrerad.txt",
+ proofread_file: "offline-sdk-referenceonly-docs-proofread.txt",
args: framework_docs_only_args + " -offlinemode -title \"Android SDK\" -referenceonly",
static_doc_index_redirect: "docs/docs-documentation-redirect.html",
static_doc_properties: "docs/source.properties",
@@ -261,7 +261,7 @@
hdf: [
"android.whichdoc offline",
],
- proofread_file: "offline-system-sdk-referenceonly-docs-proofrerad.txt",
+ proofread_file: "offline-system-sdk-referenceonly-docs-proofread.txt",
args: framework_docs_only_args + " -hide 101 -hide 104 -hide 108" +
" -offlinemode -title \"Android System SDK\" -referenceonly",
static_doc_index_redirect: "docs/docs-documentation-redirect.html",
@@ -278,7 +278,7 @@
"android.whichdoc online",
"android.hasSamples true",
],
- proofread_file: "online-sdk-docs-proofrerad.txt",
+ proofread_file: "online-sdk-docs-proofread.txt",
args: framework_docs_only_args +
" -toroot / -samplegroup Admin " +
" -samplegroup Background " +
@@ -307,7 +307,7 @@
"android.whichdoc online",
"android.hasSamples true",
],
- proofread_file: "online-system-api-sdk-docs-proofrerad.txt",
+ proofread_file: "online-system-api-sdk-docs-proofread.txt",
args: framework_docs_only_args +
" -referenceonly " +
" -title \"Android SDK - Including system APIs.\" " +
@@ -342,7 +342,7 @@
"android.whichdoc online",
"android.hasSamples true",
],
- proofread_file: "ds-docs-proofrerad.txt",
+ proofread_file: "ds-docs-proofread.txt",
args: framework_docs_only_args +
" -toroot / -yamlV2 -metalavaApiSince -samplegroup Admin " +
" -samplegroup Background " +
@@ -453,7 +453,7 @@
"android.whichdoc online",
"android.hasSamples true",
],
- proofread_file: "online-sdk-dev-docs-proofrerad.txt",
+ proofread_file: "online-sdk-dev-docs-proofread.txt",
args: framework_docs_only_args +
" -toroot / -samplegroup Admin " +
" -samplegroup Background " +
@@ -478,7 +478,7 @@
srcs: [
":framework-doc-stubs",
],
- proofread_file: "hidden-docs-proofrerad.txt",
+ proofread_file: "hidden-docs-proofread.txt",
args: framework_docs_only_args +
" -referenceonly " +
" -title \"Android SDK - Including hidden APIs.\"",
diff --git a/OWNERS b/OWNERS
index 47d8c83..d4d1936 100644
--- a/OWNERS
+++ b/OWNERS
@@ -31,5 +31,6 @@
per-file *.bp = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION}
per-file Android.mk = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION}
+per-file framework-jarjar-rules.txt = file:platform/build/soong:/OWNERS #{LAST_RESORT_SUGGESTION}
per-file TestProtoLibraries.bp = file:platform/platform_testing:/libraries/health/OWNERS
per-file TestProtoLibraries.bp = file:platform/tools/tradefederation:/OWNERS
diff --git a/TEST_MAPPING b/TEST_MAPPING
index c5c6012..3a6b347 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -17,6 +17,14 @@
],
"presubmit": [
{
+ "name": "ManagedProvisioningTests",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
+ },
+ {
"name": "FrameworksUiServicesTests",
"options": [
{
diff --git a/TRACE_OWNERS b/TRACE_OWNERS
new file mode 100644
index 0000000..f746a16
--- /dev/null
+++ b/TRACE_OWNERS
@@ -0,0 +1,5 @@
+# OWNERS of Trace-related files
+primiano@google.com
+timmurray@google.com
+jjaggi@google.com
+jreck@google.com
diff --git a/apct-tests/perftests/core/src/android/libcore/AdditionPerfTest.java b/apct-tests/perftests/core/src/android/libcore/AdditionPerfTest.java
new file mode 100644
index 0000000..aa47e0a
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/AdditionPerfTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * What do various kinds of addition cost?
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class AdditionPerfTest {
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void timeAddConstantToLocalInt() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ int result = 0;
+ while (state.keepRunning()) {
+ result += 123;
+ }
+ }
+ @Test
+ public void timeAddTwoLocalInts() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ int result = 0;
+ int constant = 123;
+ while (state.keepRunning()) {
+ result += constant;
+ }
+ }
+ @Test
+ public void timeAddConstantToLocalLong() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ long result = 0;
+ while (state.keepRunning()) {
+ result += 123L;
+ }
+ }
+ @Test
+ public void timeAddTwoLocalLongs() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ long result = 0;
+ long constant = 123L;
+ while (state.keepRunning()) {
+ result += constant;
+ }
+ }
+ @Test
+ public void timeAddConstantToLocalFloat() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ float result = 0.0f;
+ while (state.keepRunning()) {
+ result += 123.0f;
+ }
+ }
+ @Test
+ public void timeAddTwoLocalFloats() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ float result = 0.0f;
+ float constant = 123.0f;
+ while (state.keepRunning()) {
+ result += constant;
+ }
+ }
+ @Test
+ public void timeAddConstantToLocalDouble() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ double result = 0.0;
+ while (state.keepRunning()) {
+ result += 123.0;
+ }
+ }
+ @Test
+ public void timeAddTwoLocalDoubles() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ double result = 0.0;
+ double constant = 123.0;
+ while (state.keepRunning()) {
+ result += constant;
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ArrayCopyPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ArrayCopyPerfTest.java
new file mode 100644
index 0000000..97ab6c7
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ArrayCopyPerfTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ArrayCopyPerfTest {
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void timeManualArrayCopy() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ char[] src = new char[8192];
+ while (state.keepRunning()) {
+ char[] dst = new char[8192];
+ for (int i = 0; i < 8192; ++i) {
+ dst[i] = src[i];
+ }
+ }
+ }
+
+ @Test
+ public void time_System_arrayCopy() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ char[] src = new char[8192];
+ while (state.keepRunning()) {
+ char[] dst = new char[8192];
+ System.arraycopy(src, 0, dst, 0, 8192);
+ }
+ }
+
+ @Test
+ public void time_Arrays_copyOf() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ char[] src = new char[8192];
+ while (state.keepRunning()) {
+ char[] dst = Arrays.copyOf(src, 8192);
+ }
+ }
+
+ @Test
+ public void time_Arrays_copyOfRange() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ char[] src = new char[8192];
+ while (state.keepRunning()) {
+ char[] dst = Arrays.copyOfRange(src, 0, 8192);
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ArrayIterationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ArrayIterationPerfTest.java
new file mode 100644
index 0000000..bb452d3
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ArrayIterationPerfTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * How do various ways of iterating through an array compare?
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ArrayIterationPerfTest {
+
+ public class Foo {
+ int mSplat;
+ }
+
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ Foo[] mArray = new Foo[27];
+ {
+ for (int i = 0; i < mArray.length; ++i) mArray[i] = new Foo();
+ }
+ @Test
+ public void timeArrayIteration() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ int sum = 0;
+ for (int i = 0; i < mArray.length; i++) {
+ sum += mArray[i].mSplat;
+ }
+ }
+ }
+ @Test
+ public void timeArrayIterationCached() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ int sum = 0;
+ Foo[] localArray = mArray;
+ int len = localArray.length;
+
+ for (int i = 0; i < len; i++) {
+ sum += localArray[i].mSplat;
+ }
+ }
+ }
+ @Test
+ public void timeArrayIterationForEach() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ int sum = 0;
+ for (Foo a: mArray) {
+ sum += a.mSplat;
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ArrayListIterationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ArrayListIterationPerfTest.java
new file mode 100644
index 0000000..ff6d46f
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ArrayListIterationPerfTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+/**
+ * Is a hand-coded counted loop through an ArrayList cheaper than enhanced for?
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ArrayListIterationPerfTest {
+
+ public class Foo {
+ int mSplat;
+ }
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ ArrayList<Foo> mList = new ArrayList<Foo>();
+ {
+ for (int i = 0; i < 27; ++i) mList.add(new Foo());
+ }
+ @Test
+ public void timeArrayListIterationIndexed() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ int sum = 0;
+ ArrayList<Foo> list = mList;
+ int len = list.size();
+ for (int i = 0; i < len; ++i) {
+ sum += list.get(i).mSplat;
+ }
+ }
+ }
+ @Test
+ public void timeArrayListIterationForEach() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ int sum = 0;
+ for (Foo a : mList) {
+ sum += a.mSplat;
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java b/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java
new file mode 100644
index 0000000..e0c12dd
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/BigIntegerPerfTest.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2020 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.math.BigInteger;
+
+/**
+ * Tries to measure important BigInteger operations across a variety of BigInteger sizes. Note that
+ * BigInteger implementations commonly need to use wildly different algorithms for different sizes,
+ * so relative performance may change substantially depending on the size of the integer. This is
+ * not structured as a proper benchmark; just run main(), e.g. with vogar
+ * libcore/benchmarks/src/benchmarks/BigIntegerBenchmark.java.
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class BigIntegerPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ // A simple sum of products computation, mostly so we can check timing in the
+ // absence of any division. Computes the sum from 1 to n of ((10^prec) << 30) + 1)^2,
+ // repeating the multiplication, but not addition of 1, each time through the loop.
+ // Check the last few bits of the result as we go. Assumes n < 2^30.
+ // Note that we're actually squaring values in computing the product.
+ // That affects the algorithm used by some implementations.
+ private static void inner(int n, int prec) {
+ BigInteger big = BigInteger.TEN.pow(prec).shiftLeft(30).add(BigInteger.ONE);
+ BigInteger sum = BigInteger.ZERO;
+ for (int i = 0; i < n; ++i) {
+ sum = sum.add(big.multiply(big));
+ }
+ if (sum.and(BigInteger.valueOf(0x3fffffff)).intValue() != n) {
+ throw new AssertionError(
+ "inner() got " + sum.and(BigInteger.valueOf(0x3fffffff)) + " instead of " + n);
+ }
+ }
+
+ // Execute the above rep times, optionally timing it.
+ @Test
+ public void repeatInner() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 10; i <= 10_000; i *= 10) {
+ inner(100, i);
+ }
+ }
+ }
+
+ // Approximate the sum of the first 1000 terms of the harmonic series (sum of 1/m as m
+ // goes from 1 to n) to about prec digits. The result has an implicit decimal point
+ // prec digits from the right.
+ private static BigInteger harmonic1000(int prec) {
+ BigInteger scaledOne = BigInteger.TEN.pow(prec);
+ BigInteger sum = BigInteger.ZERO;
+ for (int i = 1; i <= 1000; ++i) {
+ sum = sum.add(scaledOne.divide(BigInteger.valueOf(i)));
+ }
+ return sum;
+ }
+
+ // Execute the above rep times, optionally timing it.
+ // Check results for equality, and print one, to compaare against reference.
+ @Test
+ public void repeatHarmonic1000() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 5; i <= 5_000; i *= 10) {
+ BigInteger refRes = harmonic1000(i);
+ BigInteger newRes = harmonic1000(i);
+ if (!newRes.equals(refRes)) {
+ throw new AssertionError(newRes + " != " + refRes);
+ }
+ if (i >= 50
+ && !refRes.toString()
+ .startsWith("748547086055034491265651820433390017652167916970")) {
+ throw new AssertionError("harmanic(" + i + ") incorrectly produced " + refRes);
+ }
+ }
+ }
+ }
+
+ // Repeatedly execute just the base conversion from the last test, allowing
+ // us to time and check it for consistency as well.
+ @Test
+ public void repeatToString() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 5; i <= 5_000; i *= 10) {
+ BigInteger refRes = harmonic1000(i);
+ String refString = refRes.toString();
+ // Disguise refRes to avoid compiler optimization issues.
+ BigInteger newRes = refRes.shiftLeft(30).add(BigInteger.valueOf(i)).shiftRight(30);
+ // The time-consuming part:
+ String newString = newRes.toString();
+ }
+ }
+ }
+
+ // Compute base^exp, where base and result are scaled/multiplied by scaleBy to make them
+ // integers. exp >= 0 .
+ private static BigInteger myPow(BigInteger base, int exp, BigInteger scaleBy) {
+ if (exp == 0) {
+ return scaleBy; // Return one.
+ } else if ((exp & 1) != 0) {
+ BigInteger tmp = myPow(base, exp - 1, scaleBy);
+ return tmp.multiply(base).divide(scaleBy);
+ } else {
+ BigInteger tmp = myPow(base, exp / 2, scaleBy);
+ return tmp.multiply(tmp).divide(scaleBy);
+ }
+ }
+
+ // Approximate e by computing (1 + 1/n)^n to prec decimal digits.
+ // This isn't necessarily a very good approximation to e.
+ // Return the result, scaled by 10^prec.
+ private static BigInteger eApprox(int n, int prec) {
+ BigInteger scaledOne = BigInteger.TEN.pow(prec);
+ BigInteger base = scaledOne.add(scaledOne.divide(BigInteger.valueOf(n)));
+ return myPow(base, n, scaledOne);
+ }
+
+ // Repeatedly execute and check the above, printing one of the results
+ // to compare to reference.
+ @Test
+ public void repeatEApprox() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 10; i <= 10_000; i *= 10) {
+ BigInteger refRes = eApprox(100_000, i);
+ BigInteger newRes = eApprox(100_000, i);
+ if (!newRes.equals(refRes)) {
+ throw new AssertionError(newRes + " != " + refRes);
+ }
+ if (i >= 10 && !refRes.toString().startsWith("271826")) {
+ throw new AssertionError(
+ "eApprox(" + 100_000 + "," + i + ") incorrectly produced " + refRes);
+ }
+ }
+ }
+ }
+
+ // Test / time modPow()
+ @Test
+ public void repeatModPow() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 5; i <= 500; i *= 10) {
+ BigInteger odd1 = BigInteger.TEN.pow(i / 2).add(BigInteger.ONE);
+ BigInteger odd2 = BigInteger.TEN.pow(i / 2).add(BigInteger.valueOf(17));
+ BigInteger product = odd1.multiply(odd2);
+ BigInteger exponent = BigInteger.TEN.pow(i / 2 - 1);
+ BigInteger base = BigInteger.TEN.pow(i / 4);
+ BigInteger newRes = base.modPow(exponent, product);
+ if (!newRes.mod(odd1).equals(base.modPow(exponent, odd1))) {
+ throw new AssertionError(
+ "ModPow() result incorrect mod odd1:"
+ + odd1
+ + "; lastRes.mod(odd1)="
+ + newRes.mod(odd1)
+ + " vs. "
+ + "base.modPow(exponent, odd1)="
+ + base.modPow(exponent, odd1)
+ + " base="
+ + base
+ + " exponent="
+ + exponent);
+ }
+ if (!newRes.mod(odd2).equals(base.modPow(exponent, odd2))) {
+ throw new AssertionError("ModPow() result incorrect mod odd2");
+ }
+ }
+ }
+ }
+
+ // Test / time modInverse()
+ @Test
+ public void repeatModInverse() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 10; i <= 10_000; i *= 10) {
+ BigInteger odd1 = BigInteger.TEN.pow(i / 2).add(BigInteger.ONE);
+ BigInteger odd2 = BigInteger.TEN.pow(i / 2).add(BigInteger.valueOf(17));
+ BigInteger product = odd1.multiply(odd2);
+ BigInteger arg = BigInteger.ONE.shiftLeft(i / 4);
+ BigInteger lastRes = null;
+ BigInteger newRes = arg.modInverse(product);
+ lastRes = newRes;
+ if (!lastRes.mod(odd1).equals(arg.modInverse(odd1))) {
+ throw new AssertionError("ModInverse() result incorrect mod odd1");
+ }
+ if (!lastRes.mod(odd2).equals(arg.modInverse(odd2))) {
+ throw new AssertionError("ModInverse() result incorrect mod odd2");
+ }
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java
new file mode 100644
index 0000000..04ef09e4
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/BufferedZipFilePerfTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2022 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.util.Random;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public final class BufferedZipFilePerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ int[] mReadSize = new int[] {4, 32, 128};
+ int[] mCompressedSize = new int[] {128, 1024, 8192, 65536};
+ private File mFile;
+
+ @Before
+ public void setUp() throws Exception {
+ mFile = File.createTempFile("BufferedZipFilePerfTest", ".zip");
+ mFile.deleteOnExit();
+ Random random = new Random(0);
+ ZipOutputStream out = new ZipOutputStream(new FileOutputStream(mFile));
+ for (int i = 0; i < mCompressedSize.length; i++) {
+ byte[] data = new byte[8192];
+ out.putNextEntry(new ZipEntry("entry.data" + mCompressedSize[i]));
+ int written = 0;
+ while (written < mCompressedSize[i]) {
+ random.nextBytes(data);
+ int toWrite = Math.min(mCompressedSize[i] - written, data.length);
+ out.write(data, 0, toWrite);
+ written += toWrite;
+ }
+ }
+ out.close();
+ }
+
+ @Test
+ public void timeUnbufferedRead() throws Exception {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 0; i < mCompressedSize.length; i++) {
+ for (int j = 0; j < mReadSize.length; j++) {
+ ZipFile zipFile = new ZipFile(mFile);
+ ZipEntry entry = zipFile.getEntry("entry.data" + mCompressedSize[i]);
+ InputStream in = zipFile.getInputStream(entry);
+ byte[] buffer = new byte[mReadSize[j]];
+ while (in.read(buffer) != -1) {
+ // Keep reading
+ }
+ in.close();
+ zipFile.close();
+ }
+ }
+ }
+ }
+
+ @Test
+ public void timeBufferedRead() throws Exception {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ for (int i = 0; i < mCompressedSize.length; i++) {
+ for (int j = 0; j < mReadSize.length; j++) {
+ ZipFile zipFile = new ZipFile(mFile);
+ ZipEntry entry = zipFile.getEntry("entry.data" + mCompressedSize[i]);
+ InputStream in = new BufferedInputStream(zipFile.getInputStream(entry));
+ byte[] buffer = new byte[mReadSize[j]];
+ while (in.read(buffer) != -1) {
+ // Keep reading
+ }
+ in.close();
+ zipFile.close();
+ }
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java
new file mode 100644
index 0000000..4ae88b8
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ClassLoaderResourcePerfTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2022 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ClassLoaderResourcePerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private static final String EXISTENT_RESOURCE = "java/util/logging/logging.properties";
+ private static final String MISSING_RESOURCE = "missing_entry";
+
+ @Test
+ public void timeGetBootResource_hit() {
+ ClassLoader currentClassLoader = getClass().getClassLoader();
+ Assert.assertNotNull(currentClassLoader.getResource(EXISTENT_RESOURCE));
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ currentClassLoader.getResource(EXISTENT_RESOURCE);
+ }
+ }
+
+ @Test
+ public void timeGetBootResource_miss() {
+ ClassLoader currentClassLoader = getClass().getClassLoader();
+ Assert.assertNull(currentClassLoader.getResource(MISSING_RESOURCE));
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ currentClassLoader.getResource(MISSING_RESOURCE);
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java
new file mode 100644
index 0000000..5e73916
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ClonePerfTest.java
@@ -0,0 +1,1197 @@
+/*
+ * Copyright (C) 2022 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ClonePerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ static class CloneableObject implements Cloneable {
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+ }
+
+ static class CloneableManyFieldObject implements Cloneable {
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+
+ Object mO1 = new Object();
+ Object mO2 = new Object();
+ Object mO3 = new Object();
+ Object mO4 = new Object();
+ Object mO5 = new Object();
+ Object mO6 = new Object();
+ Object mO7 = new Object();
+ Object mO8 = new Object();
+ Object mO9 = new Object();
+ Object mO10 = new Object();
+ Object mO11 = new Object();
+ Object mO12 = new Object();
+ Object mO13 = new Object();
+ Object mO14 = new Object();
+ Object mO15 = new Object();
+ Object mO16 = new Object();
+ Object mO17 = new Object();
+ Object mO18 = new Object();
+ Object mO19 = new Object();
+ Object mO20 = new Object();
+ Object mO21 = new Object();
+ Object mO22 = new Object();
+ Object mO23 = new Object();
+ Object mO24 = new Object();
+ Object mO25 = new Object();
+ Object mO26 = new Object();
+ Object mO27 = new Object();
+ Object mO28 = new Object();
+ Object mO29 = new Object();
+ Object mO30 = new Object();
+ Object mO31 = new Object();
+ Object mO32 = new Object();
+ Object mO33 = new Object();
+ Object mO34 = new Object();
+ Object mO35 = new Object();
+ Object mO36 = new Object();
+ Object mO37 = new Object();
+ Object mO38 = new Object();
+ Object mO39 = new Object();
+ Object mO40 = new Object();
+ Object mO41 = new Object();
+ Object mO42 = new Object();
+ Object mO43 = new Object();
+ Object mO44 = new Object();
+ Object mO45 = new Object();
+ Object mO46 = new Object();
+ Object mO47 = new Object();
+ Object mO48 = new Object();
+ Object mO49 = new Object();
+ Object mO50 = new Object();
+ Object mO51 = new Object();
+ Object mO52 = new Object();
+ Object mO53 = new Object();
+ Object mO54 = new Object();
+ Object mO55 = new Object();
+ Object mO56 = new Object();
+ Object mO57 = new Object();
+ Object mO58 = new Object();
+ Object mO59 = new Object();
+ Object mO60 = new Object();
+ Object mO61 = new Object();
+ Object mO62 = new Object();
+ Object mO63 = new Object();
+ Object mO64 = new Object();
+ Object mO65 = new Object();
+ Object mO66 = new Object();
+ Object mO67 = new Object();
+ Object mO68 = new Object();
+ Object mO69 = new Object();
+ Object mO70 = new Object();
+ Object mO71 = new Object();
+ Object mO72 = new Object();
+ Object mO73 = new Object();
+ Object mO74 = new Object();
+ Object mO75 = new Object();
+ Object mO76 = new Object();
+ Object mO77 = new Object();
+ Object mO78 = new Object();
+ Object mO79 = new Object();
+ Object mO80 = new Object();
+ Object mO81 = new Object();
+ Object mO82 = new Object();
+ Object mO83 = new Object();
+ Object mO84 = new Object();
+ Object mO85 = new Object();
+ Object mO86 = new Object();
+ Object mO87 = new Object();
+ Object mO88 = new Object();
+ Object mO89 = new Object();
+ Object mO90 = new Object();
+ Object mO91 = new Object();
+ Object mO92 = new Object();
+ Object mO93 = new Object();
+ Object mO94 = new Object();
+ Object mO95 = new Object();
+ Object mO96 = new Object();
+ Object mO97 = new Object();
+ Object mO98 = new Object();
+ Object mO99 = new Object();
+ Object mO100 = new Object();
+ Object mO101 = new Object();
+ Object mO102 = new Object();
+ Object mO103 = new Object();
+ Object mO104 = new Object();
+ Object mO105 = new Object();
+ Object mO106 = new Object();
+ Object mO107 = new Object();
+ Object mO108 = new Object();
+ Object mO109 = new Object();
+ Object mO110 = new Object();
+ Object mO111 = new Object();
+ Object mO112 = new Object();
+ Object mO113 = new Object();
+ Object mO114 = new Object();
+ Object mO115 = new Object();
+ Object mO116 = new Object();
+ Object mO117 = new Object();
+ Object mO118 = new Object();
+ Object mO119 = new Object();
+ Object mO120 = new Object();
+ Object mO121 = new Object();
+ Object mO122 = new Object();
+ Object mO123 = new Object();
+ Object mO124 = new Object();
+ Object mO125 = new Object();
+ Object mO126 = new Object();
+ Object mO127 = new Object();
+ Object mO128 = new Object();
+ Object mO129 = new Object();
+ Object mO130 = new Object();
+ Object mO131 = new Object();
+ Object mO132 = new Object();
+ Object mO133 = new Object();
+ Object mO134 = new Object();
+ Object mO135 = new Object();
+ Object mO136 = new Object();
+ Object mO137 = new Object();
+ Object mO138 = new Object();
+ Object mO139 = new Object();
+ Object mO140 = new Object();
+ Object mO141 = new Object();
+ Object mO142 = new Object();
+ Object mO143 = new Object();
+ Object mO144 = new Object();
+ Object mO145 = new Object();
+ Object mO146 = new Object();
+ Object mO147 = new Object();
+ Object mO148 = new Object();
+ Object mO149 = new Object();
+ Object mO150 = new Object();
+ Object mO151 = new Object();
+ Object mO152 = new Object();
+ Object mO153 = new Object();
+ Object mO154 = new Object();
+ Object mO155 = new Object();
+ Object mO156 = new Object();
+ Object mO157 = new Object();
+ Object mO158 = new Object();
+ Object mO159 = new Object();
+ Object mO160 = new Object();
+ Object mO161 = new Object();
+ Object mO162 = new Object();
+ Object mO163 = new Object();
+ Object mO164 = new Object();
+ Object mO165 = new Object();
+ Object mO166 = new Object();
+ Object mO167 = new Object();
+ Object mO168 = new Object();
+ Object mO169 = new Object();
+ Object mO170 = new Object();
+ Object mO171 = new Object();
+ Object mO172 = new Object();
+ Object mO173 = new Object();
+ Object mO174 = new Object();
+ Object mO175 = new Object();
+ Object mO176 = new Object();
+ Object mO177 = new Object();
+ Object mO178 = new Object();
+ Object mO179 = new Object();
+ Object mO180 = new Object();
+ Object mO181 = new Object();
+ Object mO182 = new Object();
+ Object mO183 = new Object();
+ Object mO184 = new Object();
+ Object mO185 = new Object();
+ Object mO186 = new Object();
+ Object mO187 = new Object();
+ Object mO188 = new Object();
+ Object mO189 = new Object();
+ Object mO190 = new Object();
+ Object mO191 = new Object();
+ Object mO192 = new Object();
+ Object mO193 = new Object();
+ Object mO194 = new Object();
+ Object mO195 = new Object();
+ Object mO196 = new Object();
+ Object mO197 = new Object();
+ Object mO198 = new Object();
+ Object mO199 = new Object();
+ Object mO200 = new Object();
+ Object mO201 = new Object();
+ Object mO202 = new Object();
+ Object mO203 = new Object();
+ Object mO204 = new Object();
+ Object mO205 = new Object();
+ Object mO206 = new Object();
+ Object mO207 = new Object();
+ Object mO208 = new Object();
+ Object mO209 = new Object();
+ Object mO210 = new Object();
+ Object mO211 = new Object();
+ Object mO212 = new Object();
+ Object mO213 = new Object();
+ Object mO214 = new Object();
+ Object mO215 = new Object();
+ Object mO216 = new Object();
+ Object mO217 = new Object();
+ Object mO218 = new Object();
+ Object mO219 = new Object();
+ Object mO220 = new Object();
+ Object mO221 = new Object();
+ Object mO222 = new Object();
+ Object mO223 = new Object();
+ Object mO224 = new Object();
+ Object mO225 = new Object();
+ Object mO226 = new Object();
+ Object mO227 = new Object();
+ Object mO228 = new Object();
+ Object mO229 = new Object();
+ Object mO230 = new Object();
+ Object mO231 = new Object();
+ Object mO232 = new Object();
+ Object mO233 = new Object();
+ Object mO234 = new Object();
+ Object mO235 = new Object();
+ Object mO236 = new Object();
+ Object mO237 = new Object();
+ Object mO238 = new Object();
+ Object mO239 = new Object();
+ Object mO240 = new Object();
+ Object mO241 = new Object();
+ Object mO242 = new Object();
+ Object mO243 = new Object();
+ Object mO244 = new Object();
+ Object mO245 = new Object();
+ Object mO246 = new Object();
+ Object mO247 = new Object();
+ Object mO248 = new Object();
+ Object mO249 = new Object();
+ Object mO250 = new Object();
+ Object mO251 = new Object();
+ Object mO252 = new Object();
+ Object mO253 = new Object();
+ Object mO254 = new Object();
+ Object mO255 = new Object();
+ Object mO256 = new Object();
+ Object mO257 = new Object();
+ Object mO258 = new Object();
+ Object mO259 = new Object();
+ Object mO260 = new Object();
+ Object mO261 = new Object();
+ Object mO262 = new Object();
+ Object mO263 = new Object();
+ Object mO264 = new Object();
+ Object mO265 = new Object();
+ Object mO266 = new Object();
+ Object mO267 = new Object();
+ Object mO268 = new Object();
+ Object mO269 = new Object();
+ Object mO270 = new Object();
+ Object mO271 = new Object();
+ Object mO272 = new Object();
+ Object mO273 = new Object();
+ Object mO274 = new Object();
+ Object mO275 = new Object();
+ Object mO276 = new Object();
+ Object mO277 = new Object();
+ Object mO278 = new Object();
+ Object mO279 = new Object();
+ Object mO280 = new Object();
+ Object mO281 = new Object();
+ Object mO282 = new Object();
+ Object mO283 = new Object();
+ Object mO284 = new Object();
+ Object mO285 = new Object();
+ Object mO286 = new Object();
+ Object mO287 = new Object();
+ Object mO288 = new Object();
+ Object mO289 = new Object();
+ Object mO290 = new Object();
+ Object mO291 = new Object();
+ Object mO292 = new Object();
+ Object mO293 = new Object();
+ Object mO294 = new Object();
+ Object mO295 = new Object();
+ Object mO296 = new Object();
+ Object mO297 = new Object();
+ Object mO298 = new Object();
+ Object mO299 = new Object();
+ Object mO300 = new Object();
+ Object mO301 = new Object();
+ Object mO302 = new Object();
+ Object mO303 = new Object();
+ Object mO304 = new Object();
+ Object mO305 = new Object();
+ Object mO306 = new Object();
+ Object mO307 = new Object();
+ Object mO308 = new Object();
+ Object mO309 = new Object();
+ Object mO310 = new Object();
+ Object mO311 = new Object();
+ Object mO312 = new Object();
+ Object mO313 = new Object();
+ Object mO314 = new Object();
+ Object mO315 = new Object();
+ Object mO316 = new Object();
+ Object mO317 = new Object();
+ Object mO318 = new Object();
+ Object mO319 = new Object();
+ Object mO320 = new Object();
+ Object mO321 = new Object();
+ Object mO322 = new Object();
+ Object mO323 = new Object();
+ Object mO324 = new Object();
+ Object mO325 = new Object();
+ Object mO326 = new Object();
+ Object mO327 = new Object();
+ Object mO328 = new Object();
+ Object mO329 = new Object();
+ Object mO330 = new Object();
+ Object mO331 = new Object();
+ Object mO332 = new Object();
+ Object mO333 = new Object();
+ Object mO334 = new Object();
+ Object mO335 = new Object();
+ Object mO336 = new Object();
+ Object mO337 = new Object();
+ Object mO338 = new Object();
+ Object mO339 = new Object();
+ Object mO340 = new Object();
+ Object mO341 = new Object();
+ Object mO342 = new Object();
+ Object mO343 = new Object();
+ Object mO344 = new Object();
+ Object mO345 = new Object();
+ Object mO346 = new Object();
+ Object mO347 = new Object();
+ Object mO348 = new Object();
+ Object mO349 = new Object();
+ Object mO350 = new Object();
+ Object mO351 = new Object();
+ Object mO352 = new Object();
+ Object mO353 = new Object();
+ Object mO354 = new Object();
+ Object mO355 = new Object();
+ Object mO356 = new Object();
+ Object mO357 = new Object();
+ Object mO358 = new Object();
+ Object mO359 = new Object();
+ Object mO360 = new Object();
+ Object mO361 = new Object();
+ Object mO362 = new Object();
+ Object mO363 = new Object();
+ Object mO364 = new Object();
+ Object mO365 = new Object();
+ Object mO366 = new Object();
+ Object mO367 = new Object();
+ Object mO368 = new Object();
+ Object mO369 = new Object();
+ Object mO370 = new Object();
+ Object mO371 = new Object();
+ Object mO372 = new Object();
+ Object mO373 = new Object();
+ Object mO374 = new Object();
+ Object mO375 = new Object();
+ Object mO376 = new Object();
+ Object mO377 = new Object();
+ Object mO378 = new Object();
+ Object mO379 = new Object();
+ Object mO380 = new Object();
+ Object mO381 = new Object();
+ Object mO382 = new Object();
+ Object mO383 = new Object();
+ Object mO384 = new Object();
+ Object mO385 = new Object();
+ Object mO386 = new Object();
+ Object mO387 = new Object();
+ Object mO388 = new Object();
+ Object mO389 = new Object();
+ Object mO390 = new Object();
+ Object mO391 = new Object();
+ Object mO392 = new Object();
+ Object mO393 = new Object();
+ Object mO394 = new Object();
+ Object mO395 = new Object();
+ Object mO396 = new Object();
+ Object mO397 = new Object();
+ Object mO398 = new Object();
+ Object mO399 = new Object();
+ Object mO400 = new Object();
+ Object mO401 = new Object();
+ Object mO402 = new Object();
+ Object mO403 = new Object();
+ Object mO404 = new Object();
+ Object mO405 = new Object();
+ Object mO406 = new Object();
+ Object mO407 = new Object();
+ Object mO408 = new Object();
+ Object mO409 = new Object();
+ Object mO410 = new Object();
+ Object mO411 = new Object();
+ Object mO412 = new Object();
+ Object mO413 = new Object();
+ Object mO414 = new Object();
+ Object mO415 = new Object();
+ Object mO416 = new Object();
+ Object mO417 = new Object();
+ Object mO418 = new Object();
+ Object mO419 = new Object();
+ Object mO420 = new Object();
+ Object mO421 = new Object();
+ Object mO422 = new Object();
+ Object mO423 = new Object();
+ Object mO424 = new Object();
+ Object mO425 = new Object();
+ Object mO426 = new Object();
+ Object mO427 = new Object();
+ Object mO428 = new Object();
+ Object mO429 = new Object();
+ Object mO430 = new Object();
+ Object mO431 = new Object();
+ Object mO432 = new Object();
+ Object mO433 = new Object();
+ Object mO434 = new Object();
+ Object mO435 = new Object();
+ Object mO436 = new Object();
+ Object mO437 = new Object();
+ Object mO438 = new Object();
+ Object mO439 = new Object();
+ Object mO440 = new Object();
+ Object mO441 = new Object();
+ Object mO442 = new Object();
+ Object mO460 = new Object();
+ Object mO461 = new Object();
+ Object mO462 = new Object();
+ Object mO463 = new Object();
+ Object mO464 = new Object();
+ Object mO465 = new Object();
+ Object mO466 = new Object();
+ Object mO467 = new Object();
+ Object mO468 = new Object();
+ Object mO469 = new Object();
+ Object mO470 = new Object();
+ Object mO471 = new Object();
+ Object mO472 = new Object();
+ Object mO473 = new Object();
+ Object mO474 = new Object();
+ Object mO475 = new Object();
+ Object mO476 = new Object();
+ Object mO477 = new Object();
+ Object mO478 = new Object();
+ Object mO479 = new Object();
+ Object mO480 = new Object();
+ Object mO481 = new Object();
+ Object mO482 = new Object();
+ Object mO483 = new Object();
+ Object mO484 = new Object();
+ Object mO485 = new Object();
+ Object mO486 = new Object();
+ Object mO487 = new Object();
+ Object mO488 = new Object();
+ Object mO489 = new Object();
+ Object mO490 = new Object();
+ Object mO491 = new Object();
+ Object mO492 = new Object();
+ Object mO493 = new Object();
+ Object mO494 = new Object();
+ Object mO495 = new Object();
+ Object mO496 = new Object();
+ Object mO497 = new Object();
+ Object mO498 = new Object();
+ Object mO499 = new Object();
+ Object mO500 = new Object();
+ Object mO501 = new Object();
+ Object mO502 = new Object();
+ Object mO503 = new Object();
+ Object mO504 = new Object();
+ Object mO505 = new Object();
+ Object mO506 = new Object();
+ Object mO507 = new Object();
+ Object mO508 = new Object();
+ Object mO509 = new Object();
+ Object mO510 = new Object();
+ Object mO511 = new Object();
+ Object mO512 = new Object();
+ Object mO513 = new Object();
+ Object mO514 = new Object();
+ Object mO515 = new Object();
+ Object mO516 = new Object();
+ Object mO517 = new Object();
+ Object mO518 = new Object();
+ Object mO519 = new Object();
+ Object mO520 = new Object();
+ Object mO521 = new Object();
+ Object mO522 = new Object();
+ Object mO523 = new Object();
+ Object mO556 = new Object();
+ Object mO557 = new Object();
+ Object mO558 = new Object();
+ Object mO559 = new Object();
+ Object mO560 = new Object();
+ Object mO561 = new Object();
+ Object mO562 = new Object();
+ Object mO563 = new Object();
+ Object mO564 = new Object();
+ Object mO565 = new Object();
+ Object mO566 = new Object();
+ Object mO567 = new Object();
+ Object mO568 = new Object();
+ Object mO569 = new Object();
+ Object mO570 = new Object();
+ Object mO571 = new Object();
+ Object mO572 = new Object();
+ Object mO573 = new Object();
+ Object mO574 = new Object();
+ Object mO575 = new Object();
+ Object mO576 = new Object();
+ Object mO577 = new Object();
+ Object mO578 = new Object();
+ Object mO579 = new Object();
+ Object mO580 = new Object();
+ Object mO581 = new Object();
+ Object mO582 = new Object();
+ Object mO583 = new Object();
+ Object mO584 = new Object();
+ Object mO585 = new Object();
+ Object mO586 = new Object();
+ Object mO587 = new Object();
+ Object mO588 = new Object();
+ Object mO589 = new Object();
+ Object mO590 = new Object();
+ Object mO591 = new Object();
+ Object mO592 = new Object();
+ Object mO593 = new Object();
+ Object mO594 = new Object();
+ Object mO595 = new Object();
+ Object mO596 = new Object();
+ Object mO597 = new Object();
+ Object mO598 = new Object();
+ Object mO599 = new Object();
+ Object mO600 = new Object();
+ Object mO601 = new Object();
+ Object mO602 = new Object();
+ Object mO603 = new Object();
+ Object mO604 = new Object();
+ Object mO605 = new Object();
+ Object mO606 = new Object();
+ Object mO607 = new Object();
+ Object mO608 = new Object();
+ Object mO609 = new Object();
+ Object mO610 = new Object();
+ Object mO611 = new Object();
+ Object mO612 = new Object();
+ Object mO613 = new Object();
+ Object mO614 = new Object();
+ Object mO615 = new Object();
+ Object mO616 = new Object();
+ Object mO617 = new Object();
+ Object mO618 = new Object();
+ Object mO619 = new Object();
+ Object mO620 = new Object();
+ Object mO621 = new Object();
+ Object mO622 = new Object();
+ Object mO623 = new Object();
+ Object mO624 = new Object();
+ Object mO625 = new Object();
+ Object mO626 = new Object();
+ Object mO627 = new Object();
+ Object mO628 = new Object();
+ Object mO629 = new Object();
+ Object mO630 = new Object();
+ Object mO631 = new Object();
+ Object mO632 = new Object();
+ Object mO633 = new Object();
+ Object mO634 = new Object();
+ Object mO635 = new Object();
+ Object mO636 = new Object();
+ Object mO637 = new Object();
+ Object mO638 = new Object();
+ Object mO639 = new Object();
+ Object mO640 = new Object();
+ Object mO641 = new Object();
+ Object mO642 = new Object();
+ Object mO643 = new Object();
+ Object mO644 = new Object();
+ Object mO645 = new Object();
+ Object mO646 = new Object();
+ Object mO647 = new Object();
+ Object mO648 = new Object();
+ Object mO649 = new Object();
+ Object mO650 = new Object();
+ Object mO651 = new Object();
+ Object mO652 = new Object();
+ Object mO653 = new Object();
+ Object mO654 = new Object();
+ Object mO655 = new Object();
+ Object mO656 = new Object();
+ Object mO657 = new Object();
+ Object mO658 = new Object();
+ Object mO659 = new Object();
+ Object mO660 = new Object();
+ Object mO661 = new Object();
+ Object mO662 = new Object();
+ Object mO663 = new Object();
+ Object mO664 = new Object();
+ Object mO665 = new Object();
+ Object mO666 = new Object();
+ Object mO667 = new Object();
+ Object mO668 = new Object();
+ Object mO669 = new Object();
+ Object mO670 = new Object();
+ Object mO671 = new Object();
+ Object mO672 = new Object();
+ Object mO673 = new Object();
+ Object mO674 = new Object();
+ Object mO675 = new Object();
+ Object mO676 = new Object();
+ Object mO677 = new Object();
+ Object mO678 = new Object();
+ Object mO679 = new Object();
+ Object mO680 = new Object();
+ Object mO681 = new Object();
+ Object mO682 = new Object();
+ Object mO683 = new Object();
+ Object mO684 = new Object();
+ Object mO685 = new Object();
+ Object mO686 = new Object();
+ Object mO687 = new Object();
+ Object mO688 = new Object();
+ Object mO734 = new Object();
+ Object mO735 = new Object();
+ Object mO736 = new Object();
+ Object mO737 = new Object();
+ Object mO738 = new Object();
+ Object mO739 = new Object();
+ Object mO740 = new Object();
+ Object mO741 = new Object();
+ Object mO742 = new Object();
+ Object mO743 = new Object();
+ Object mO744 = new Object();
+ Object mO745 = new Object();
+ Object mO746 = new Object();
+ Object mO747 = new Object();
+ Object mO748 = new Object();
+ Object mO749 = new Object();
+ Object mO750 = new Object();
+ Object mO751 = new Object();
+ Object mO752 = new Object();
+ Object mO753 = new Object();
+ Object mO754 = new Object();
+ Object mO755 = new Object();
+ Object mO756 = new Object();
+ Object mO757 = new Object();
+ Object mO758 = new Object();
+ Object mO759 = new Object();
+ Object mO760 = new Object();
+ Object mO761 = new Object();
+ Object mO762 = new Object();
+ Object mO763 = new Object();
+ Object mO764 = new Object();
+ Object mO765 = new Object();
+ Object mO766 = new Object();
+ Object mO767 = new Object();
+ Object mO768 = new Object();
+ Object mO769 = new Object();
+ Object mO770 = new Object();
+ Object mO771 = new Object();
+ Object mO772 = new Object();
+ Object mO773 = new Object();
+ Object mO774 = new Object();
+ Object mO775 = new Object();
+ Object mO776 = new Object();
+ Object mO777 = new Object();
+ Object mO778 = new Object();
+ Object mO779 = new Object();
+ Object mO780 = new Object();
+ Object mO781 = new Object();
+ Object mO782 = new Object();
+ Object mO783 = new Object();
+ Object mO784 = new Object();
+ Object mO785 = new Object();
+ Object mO786 = new Object();
+ Object mO787 = new Object();
+ Object mO788 = new Object();
+ Object mO789 = new Object();
+ Object mO790 = new Object();
+ Object mO791 = new Object();
+ Object mO792 = new Object();
+ Object mO793 = new Object();
+ Object mO794 = new Object();
+ Object mO795 = new Object();
+ Object mO796 = new Object();
+ Object mO797 = new Object();
+ Object mO798 = new Object();
+ Object mO799 = new Object();
+ Object mO800 = new Object();
+ Object mO801 = new Object();
+ Object mO802 = new Object();
+ Object mO803 = new Object();
+ Object mO804 = new Object();
+ Object mO805 = new Object();
+ Object mO806 = new Object();
+ Object mO807 = new Object();
+ Object mO808 = new Object();
+ Object mO809 = new Object();
+ Object mO810 = new Object();
+ Object mO811 = new Object();
+ Object mO812 = new Object();
+ Object mO813 = new Object();
+ Object mO848 = new Object();
+ Object mO849 = new Object();
+ Object mO850 = new Object();
+ Object mO851 = new Object();
+ Object mO852 = new Object();
+ Object mO853 = new Object();
+ Object mO854 = new Object();
+ Object mO855 = new Object();
+ Object mO856 = new Object();
+ Object mO857 = new Object();
+ Object mO858 = new Object();
+ Object mO859 = new Object();
+ Object mO860 = new Object();
+ Object mO861 = new Object();
+ Object mO862 = new Object();
+ Object mO863 = new Object();
+ Object mO864 = new Object();
+ Object mO865 = new Object();
+ Object mO866 = new Object();
+ Object mO867 = new Object();
+ Object mO868 = new Object();
+ Object mO869 = new Object();
+ Object mO870 = new Object();
+ Object mO871 = new Object();
+ Object mO872 = new Object();
+ Object mO873 = new Object();
+ Object mO874 = new Object();
+ Object mO875 = new Object();
+ Object mO876 = new Object();
+ Object mO877 = new Object();
+ Object mO878 = new Object();
+ Object mO879 = new Object();
+ Object mO880 = new Object();
+ Object mO881 = new Object();
+ Object mO882 = new Object();
+ Object mO883 = new Object();
+ Object mO884 = new Object();
+ Object mO885 = new Object();
+ Object mO886 = new Object();
+ Object mO887 = new Object();
+ Object mO888 = new Object();
+ Object mO889 = new Object();
+ Object mO890 = new Object();
+ Object mO891 = new Object();
+ Object mO892 = new Object();
+ Object mO893 = new Object();
+ Object mO894 = new Object();
+ Object mO895 = new Object();
+ Object mO896 = new Object();
+ Object mO897 = new Object();
+ Object mO898 = new Object();
+ Object mO899 = new Object();
+ Object mO900 = new Object();
+ Object mO901 = new Object();
+ Object mO902 = new Object();
+ Object mO903 = new Object();
+ Object mO904 = new Object();
+ Object mO905 = new Object();
+ Object mO906 = new Object();
+ Object mO907 = new Object();
+ Object mO908 = new Object();
+ Object mO909 = new Object();
+ Object mO910 = new Object();
+ Object mO911 = new Object();
+ Object mO912 = new Object();
+ Object mO913 = new Object();
+ Object mO914 = new Object();
+ Object mO915 = new Object();
+ Object mO916 = new Object();
+ Object mO917 = new Object();
+ Object mO918 = new Object();
+ Object mO919 = new Object();
+ Object mO920 = new Object();
+ Object mO921 = new Object();
+ Object mO922 = new Object();
+ Object mO923 = new Object();
+ Object mO924 = new Object();
+ Object mO925 = new Object();
+ Object mO926 = new Object();
+ Object mO927 = new Object();
+ Object mO928 = new Object();
+ Object mO929 = new Object();
+ Object mO930 = new Object();
+ Object mO931 = new Object();
+ Object mO932 = new Object();
+ Object mO933 = new Object();
+ Object mO934 = new Object();
+ Object mO935 = new Object();
+ Object mO936 = new Object();
+ Object mO937 = new Object();
+ Object mO938 = new Object();
+ Object mO939 = new Object();
+ Object mO940 = new Object();
+ Object mO941 = new Object();
+ Object mO942 = new Object();
+ Object mO943 = new Object();
+ Object mO944 = new Object();
+ Object mO945 = new Object();
+ Object mO946 = new Object();
+ Object mO947 = new Object();
+ Object mO948 = new Object();
+ Object mO949 = new Object();
+ Object mO950 = new Object();
+ Object mO951 = new Object();
+ Object mO952 = new Object();
+ Object mO953 = new Object();
+ Object mO954 = new Object();
+ Object mO955 = new Object();
+ Object mO956 = new Object();
+ Object mO957 = new Object();
+ Object mO958 = new Object();
+ Object mO959 = new Object();
+ Object mO960 = new Object();
+ Object mO961 = new Object();
+ Object mO962 = new Object();
+ Object mO963 = new Object();
+ Object mO964 = new Object();
+ Object mO965 = new Object();
+ Object mO966 = new Object();
+ Object mO967 = new Object();
+ Object mO968 = new Object();
+ Object mO969 = new Object();
+ Object mO970 = new Object();
+ Object mO971 = new Object();
+ Object mO972 = new Object();
+ Object mO973 = new Object();
+ Object mO974 = new Object();
+ Object mO975 = new Object();
+ Object mO976 = new Object();
+ Object mO977 = new Object();
+ Object mO978 = new Object();
+ Object mO979 = new Object();
+ Object mO980 = new Object();
+ Object mO981 = new Object();
+ Object mO982 = new Object();
+ Object mO983 = new Object();
+ Object mO984 = new Object();
+ Object mO985 = new Object();
+ Object mO986 = new Object();
+ Object mO987 = new Object();
+ Object mO988 = new Object();
+ Object mO989 = new Object();
+ Object mO990 = new Object();
+ Object mO991 = new Object();
+ Object mO992 = new Object();
+ Object mO993 = new Object();
+ Object mO994 = new Object();
+ Object mO995 = new Object();
+ Object mO996 = new Object();
+ Object mO997 = new Object();
+ Object mO998 = new Object();
+ Object mO999 = new Object();
+ }
+
+ static class Deep0 {}
+
+ static class Deep1 extends Deep0 {}
+
+ static class Deep2 extends Deep1 {}
+
+ static class Deep3 extends Deep2 {}
+
+ static class Deep4 extends Deep3 {}
+
+ static class Deep5 extends Deep4 {}
+
+ static class Deep6 extends Deep5 {}
+
+ static class Deep7 extends Deep6 {}
+
+ static class Deep8 extends Deep7 {}
+
+ static class Deep9 extends Deep8 {}
+
+ static class Deep10 extends Deep9 {}
+
+ static class Deep11 extends Deep10 {}
+
+ static class Deep12 extends Deep11 {}
+
+ static class Deep13 extends Deep12 {}
+
+ static class Deep14 extends Deep13 {}
+
+ static class Deep15 extends Deep14 {}
+
+ static class Deep16 extends Deep15 {}
+
+ static class Deep17 extends Deep16 {}
+
+ static class Deep18 extends Deep17 {}
+
+ static class Deep19 extends Deep18 {}
+
+ static class Deep20 extends Deep19 {}
+
+ static class Deep21 extends Deep20 {}
+
+ static class Deep22 extends Deep21 {}
+
+ static class Deep23 extends Deep22 {}
+
+ static class Deep24 extends Deep23 {}
+
+ static class Deep25 extends Deep24 {}
+
+ static class Deep26 extends Deep25 {}
+
+ static class Deep27 extends Deep26 {}
+
+ static class Deep28 extends Deep27 {}
+
+ static class Deep29 extends Deep28 {}
+
+ static class Deep30 extends Deep29 {}
+
+ static class Deep31 extends Deep30 {}
+
+ static class Deep32 extends Deep31 {}
+
+ static class Deep33 extends Deep32 {}
+
+ static class Deep34 extends Deep33 {}
+
+ static class Deep35 extends Deep34 {}
+
+ static class Deep36 extends Deep35 {}
+
+ static class Deep37 extends Deep36 {}
+
+ static class Deep38 extends Deep37 {}
+
+ static class Deep39 extends Deep38 {}
+
+ static class Deep40 extends Deep39 {}
+
+ static class Deep41 extends Deep40 {}
+
+ static class Deep42 extends Deep41 {}
+
+ static class Deep43 extends Deep42 {}
+
+ static class Deep44 extends Deep43 {}
+
+ static class Deep45 extends Deep44 {}
+
+ static class Deep46 extends Deep45 {}
+
+ static class Deep47 extends Deep46 {}
+
+ static class Deep48 extends Deep47 {}
+
+ static class Deep49 extends Deep48 {}
+
+ static class Deep50 extends Deep49 {}
+
+ static class Deep51 extends Deep50 {}
+
+ static class Deep52 extends Deep51 {}
+
+ static class Deep53 extends Deep52 {}
+
+ static class Deep54 extends Deep53 {}
+
+ static class Deep55 extends Deep54 {}
+
+ static class Deep56 extends Deep55 {}
+
+ static class Deep57 extends Deep56 {}
+
+ static class Deep58 extends Deep57 {}
+
+ static class Deep59 extends Deep58 {}
+
+ static class Deep60 extends Deep59 {}
+
+ static class Deep61 extends Deep60 {}
+
+ static class Deep62 extends Deep61 {}
+
+ static class Deep63 extends Deep62 {}
+
+ static class Deep64 extends Deep63 {}
+
+ static class Deep65 extends Deep64 {}
+
+ static class Deep66 extends Deep65 {}
+
+ static class Deep67 extends Deep66 {}
+
+ static class Deep68 extends Deep67 {}
+
+ static class Deep69 extends Deep68 {}
+
+ static class Deep70 extends Deep69 {}
+
+ static class Deep71 extends Deep70 {}
+
+ static class Deep72 extends Deep71 {}
+
+ static class Deep73 extends Deep72 {}
+
+ static class Deep74 extends Deep73 {}
+
+ static class Deep75 extends Deep74 {}
+
+ static class Deep76 extends Deep75 {}
+
+ static class Deep77 extends Deep76 {}
+
+ static class Deep78 extends Deep77 {}
+
+ static class Deep79 extends Deep78 {}
+
+ static class Deep80 extends Deep79 {}
+
+ static class Deep81 extends Deep80 {}
+
+ static class Deep82 extends Deep81 {}
+
+ static class Deep83 extends Deep82 {}
+
+ static class Deep84 extends Deep83 {}
+
+ static class Deep85 extends Deep84 {}
+
+ static class Deep86 extends Deep85 {}
+
+ static class Deep87 extends Deep86 {}
+
+ static class Deep88 extends Deep87 {}
+
+ static class Deep89 extends Deep88 {}
+
+ static class Deep90 extends Deep89 {}
+
+ static class Deep91 extends Deep90 {}
+
+ static class Deep92 extends Deep91 {}
+
+ static class Deep93 extends Deep92 {}
+
+ static class Deep94 extends Deep93 {}
+
+ static class Deep95 extends Deep94 {}
+
+ static class Deep96 extends Deep95 {}
+
+ static class Deep97 extends Deep96 {}
+
+ static class Deep98 extends Deep97 {}
+
+ static class Deep99 extends Deep98 {}
+
+ static class Deep100 extends Deep99 {}
+
+ static class DeepCloneable extends Deep100 implements Cloneable {
+ public Object clone() throws CloneNotSupportedException {
+ return super.clone();
+ }
+ }
+
+ @Test
+ public void time_Object_clone() {
+ try {
+ CloneableObject o = new CloneableObject();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ o.clone();
+ }
+ } catch (Exception e) {
+ throw new AssertionError(e.getMessage());
+ }
+ }
+
+ @Test
+ public void time_Object_manyFieldClone() {
+ try {
+ CloneableManyFieldObject o = new CloneableManyFieldObject();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ o.clone();
+ }
+ } catch (Exception e) {
+ throw new AssertionError(e.getMessage());
+ }
+ }
+
+ @Test
+ public void time_Object_deepClone() {
+ try {
+ DeepCloneable o = new DeepCloneable();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ o.clone();
+ }
+ } catch (Exception e) {
+ throw new AssertionError(e.getMessage());
+ }
+ }
+
+ @Test
+ public void time_Array_clone() {
+ int[] o = new int[32];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ o.clone();
+ }
+ }
+
+ @Test
+ public void time_ObjectArray_smallClone() {
+ Object[] o = new Object[32];
+ for (int i = 0; i < o.length / 2; ++i) {
+ o[i] = new Object();
+ }
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ o.clone();
+ }
+ }
+
+ @Test
+ public void time_ObjectArray_largeClone() {
+ Object[] o = new Object[2048];
+ for (int i = 0; i < o.length / 2; ++i) {
+ o[i] = new Object();
+ }
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ o.clone();
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java
new file mode 100644
index 0000000..3f4f6af
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/DeepArrayOpsPerfTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2013 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class DeepArrayOpsPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private Object[] mArray;
+ private Object[] mArray2;
+
+ @Parameterized.Parameter(0)
+ public int mArrayLength;
+
+ @Parameterized.Parameters(name = "mArrayLength({0})")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] {{1}, {4}, {16}, {32}, {2048}});
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mArray = new Object[mArrayLength * 14];
+ mArray2 = new Object[mArrayLength * 14];
+ for (int i = 0; i < mArrayLength; i += 14) {
+ mArray[i] = new IntWrapper(i);
+ mArray2[i] = new IntWrapper(i);
+
+ mArray[i + 1] = new16ElementObjectmArray();
+ mArray2[i + 1] = new16ElementObjectmArray();
+
+ mArray[i + 2] = new boolean[16];
+ mArray2[i + 2] = new boolean[16];
+
+ mArray[i + 3] = new byte[16];
+ mArray2[i + 3] = new byte[16];
+
+ mArray[i + 4] = new char[16];
+ mArray2[i + 4] = new char[16];
+
+ mArray[i + 5] = new short[16];
+ mArray2[i + 5] = new short[16];
+
+ mArray[i + 6] = new float[16];
+ mArray2[i + 6] = new float[16];
+
+ mArray[i + 7] = new long[16];
+ mArray2[i + 7] = new long[16];
+
+ mArray[i + 8] = new int[16];
+ mArray2[i + 8] = new int[16];
+
+ mArray[i + 9] = new double[16];
+ mArray2[i + 9] = new double[16];
+
+ // SubmArray types are concrete objects.
+ mArray[i + 10] = new16ElementArray(String.class, String.class);
+ mArray2[i + 10] = new16ElementArray(String.class, String.class);
+
+ mArray[i + 11] = new16ElementArray(Integer.class, Integer.class);
+ mArray2[i + 11] = new16ElementArray(Integer.class, Integer.class);
+
+ // SubmArray types is an interface.
+ mArray[i + 12] = new16ElementArray(CharSequence.class, String.class);
+ mArray2[i + 12] = new16ElementArray(CharSequence.class, String.class);
+
+ mArray[i + 13] = null;
+ mArray2[i + 13] = null;
+ }
+ }
+
+ @Test
+ public void deepHashCode() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Arrays.deepHashCode(mArray);
+ }
+ }
+
+ @Test
+ public void deepEquals() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Arrays.deepEquals(mArray, mArray2);
+ }
+ }
+
+ private static Object[] new16ElementObjectmArray() {
+ Object[] array = new Object[16];
+ for (int i = 0; i < 16; ++i) {
+ array[i] = new IntWrapper(i);
+ }
+
+ return array;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T, V> T[] new16ElementArray(Class<T> mArrayType, Class<V> type)
+ throws Exception {
+ T[] array = (T[]) Array.newInstance(type, 16);
+ if (!mArrayType.isAssignableFrom(type)) {
+ throw new IllegalArgumentException(mArrayType + " is not assignable from " + type);
+ }
+
+ Constructor<V> constructor = type.getDeclaredConstructor(String.class);
+ for (int i = 0; i < 16; ++i) {
+ array[i] = (T) constructor.newInstance(String.valueOf(i + 1000));
+ }
+
+ return array;
+ }
+
+ /**
+ * A class that provides very basic equals() and hashCode() operations and doesn't resort to
+ * memoization tricks like {@link java.lang.Integer}.
+ *
+ * <p>Useful for providing equal objects that aren't the same (a.equals(b) but a != b).
+ */
+ public static final class IntWrapper {
+ private final int mWrapped;
+
+ public IntWrapper(int wrap) {
+ mWrapped = wrap;
+ }
+
+ @Override
+ public int hashCode() {
+ return mWrapped;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof IntWrapper)) {
+ return false;
+ }
+
+ return ((IntWrapper) o).mWrapped == this.mWrapped;
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java b/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java
new file mode 100644
index 0000000..da94ae1
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/FieldAccessPerfTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2022 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** What does field access cost? */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class FieldAccessPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private static class Inner {
+ public int mPublicInnerIntVal;
+ protected int mProtectedInnerIntVal;
+ private int mPrivateInnerIntVal;
+ int mPackageInnerIntVal;
+ }
+
+ int mIntVal = 42;
+ final int mFinalIntVal = 42;
+ static int sStaticIntVal = 42;
+ static final int FINAL_INT_VAL = 42;
+
+ @Test
+ public void timeField() {
+ int result = 0;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = mIntVal;
+ }
+ }
+
+ @Test
+ public void timeFieldFinal() {
+ int result = 0;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = mFinalIntVal;
+ }
+ }
+
+ @Test
+ public void timeFieldStatic() {
+ int result = 0;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = sStaticIntVal;
+ }
+ }
+
+ @Test
+ public void timeFieldStaticFinal() {
+ int result = 0;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = FINAL_INT_VAL;
+ }
+ }
+
+ @Test
+ public void timeFieldCached() {
+ int result = 0;
+ int cachedIntVal = this.mIntVal;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = cachedIntVal;
+ }
+ }
+
+ @Test
+ public void timeFieldPrivateInnerClassPublicField() {
+ int result = 0;
+ Inner inner = new Inner();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = inner.mPublicInnerIntVal;
+ }
+ }
+
+ @Test
+ public void timeFieldPrivateInnerClassProtectedField() {
+ int result = 0;
+ Inner inner = new Inner();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = inner.mProtectedInnerIntVal;
+ }
+ }
+
+ @Test
+ public void timeFieldPrivateInnerClassPrivateField() {
+ int result = 0;
+ Inner inner = new Inner();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = inner.mPrivateInnerIntVal;
+ }
+ }
+
+ @Test
+ public void timeFieldPrivateInnerClassPackageField() {
+ int result = 0;
+ Inner inner = new Inner();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = inner.mPackageInnerIntVal;
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java
new file mode 100644
index 0000000..9446d99c
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/HashedCollectionsPerfTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2022 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.LinkedHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+/** How do the various hash maps compare? */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class HashedCollectionsPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void timeHashMapGet() {
+ HashMap<String, String> map = new HashMap<String, String>();
+ map.put("hello", "world");
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ map.get("hello");
+ }
+ }
+
+ @Test
+ public void timeHashMapGet_Synchronized() {
+ HashMap<String, String> map = new HashMap<String, String>();
+ synchronized (map) {
+ map.put("hello", "world");
+ }
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ synchronized (map) {
+ map.get("hello");
+ }
+ }
+ }
+
+ @Test
+ public void timeHashtableGet() {
+ Hashtable<String, String> map = new Hashtable<String, String>();
+ map.put("hello", "world");
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ map.get("hello");
+ }
+ }
+
+ @Test
+ public void timeLinkedHashMapGet() {
+ LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
+ map.put("hello", "world");
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ map.get("hello");
+ }
+ }
+
+ @Test
+ public void timeConcurrentHashMapGet() {
+ ConcurrentHashMap<String, String> map = new ConcurrentHashMap<String, String>();
+ map.put("hello", "world");
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ map.get("hello");
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java
new file mode 100644
index 0000000..be2a7e9
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTest.java
@@ -0,0 +1,1818 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * This file is script-generated by ImtConflictPerfTestGen.py. It measures the performance impact of
+ * conflicts in interface method tables. Run `python ImtConflictPerfTestGen.py >
+ * ImtConflictPerfTest.java` to regenerate.
+ *
+ * <p>Each interface has 64 methods, which is the current size of an IMT. C0 implements one
+ * interface, C1 implements two, C2 implements three, and so on. The intent is that C0 has no
+ * conflicts in its IMT, C1 has depth-2 conflicts in its IMT, C2 has depth-3 conflicts, etc. This is
+ * currently guaranteed by the fact that we hash interface methods by taking their method index
+ * modulo 64. (Note that a "conflict depth" of 1 means no conflict at all.)
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ImtConflictPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Before
+ public void setup() {
+ C0 c0 = new C0();
+ callF0(c0);
+ C1 c1 = new C1();
+ callF0(c1);
+ callF19(c1);
+ C2 c2 = new C2();
+ callF0(c2);
+ callF19(c2);
+ callF38(c2);
+ C3 c3 = new C3();
+ callF0(c3);
+ callF19(c3);
+ callF38(c3);
+ callF57(c3);
+ C4 c4 = new C4();
+ callF0(c4);
+ callF19(c4);
+ callF38(c4);
+ callF57(c4);
+ callF76(c4);
+ C5 c5 = new C5();
+ callF0(c5);
+ callF19(c5);
+ callF38(c5);
+ callF57(c5);
+ callF76(c5);
+ callF95(c5);
+ C6 c6 = new C6();
+ callF0(c6);
+ callF19(c6);
+ callF38(c6);
+ callF57(c6);
+ callF76(c6);
+ callF95(c6);
+ callF114(c6);
+ C7 c7 = new C7();
+ callF0(c7);
+ callF19(c7);
+ callF38(c7);
+ callF57(c7);
+ callF76(c7);
+ callF95(c7);
+ callF114(c7);
+ callF133(c7);
+ C8 c8 = new C8();
+ callF0(c8);
+ callF19(c8);
+ callF38(c8);
+ callF57(c8);
+ callF76(c8);
+ callF95(c8);
+ callF114(c8);
+ callF133(c8);
+ callF152(c8);
+ C9 c9 = new C9();
+ callF0(c9);
+ callF19(c9);
+ callF38(c9);
+ callF57(c9);
+ callF76(c9);
+ callF95(c9);
+ callF114(c9);
+ callF133(c9);
+ callF152(c9);
+ callF171(c9);
+ C10 c10 = new C10();
+ callF0(c10);
+ callF19(c10);
+ callF38(c10);
+ callF57(c10);
+ callF76(c10);
+ callF95(c10);
+ callF114(c10);
+ callF133(c10);
+ callF152(c10);
+ callF171(c10);
+ callF190(c10);
+ C11 c11 = new C11();
+ callF0(c11);
+ callF19(c11);
+ callF38(c11);
+ callF57(c11);
+ callF76(c11);
+ callF95(c11);
+ callF114(c11);
+ callF133(c11);
+ callF152(c11);
+ callF171(c11);
+ callF190(c11);
+ callF209(c11);
+ C12 c12 = new C12();
+ callF0(c12);
+ callF19(c12);
+ callF38(c12);
+ callF57(c12);
+ callF76(c12);
+ callF95(c12);
+ callF114(c12);
+ callF133(c12);
+ callF152(c12);
+ callF171(c12);
+ callF190(c12);
+ callF209(c12);
+ callF228(c12);
+ C13 c13 = new C13();
+ callF0(c13);
+ callF19(c13);
+ callF38(c13);
+ callF57(c13);
+ callF76(c13);
+ callF95(c13);
+ callF114(c13);
+ callF133(c13);
+ callF152(c13);
+ callF171(c13);
+ callF190(c13);
+ callF209(c13);
+ callF228(c13);
+ callF247(c13);
+ C14 c14 = new C14();
+ callF0(c14);
+ callF19(c14);
+ callF38(c14);
+ callF57(c14);
+ callF76(c14);
+ callF95(c14);
+ callF114(c14);
+ callF133(c14);
+ callF152(c14);
+ callF171(c14);
+ callF190(c14);
+ callF209(c14);
+ callF228(c14);
+ callF247(c14);
+ callF266(c14);
+ C15 c15 = new C15();
+ callF0(c15);
+ callF19(c15);
+ callF38(c15);
+ callF57(c15);
+ callF76(c15);
+ callF95(c15);
+ callF114(c15);
+ callF133(c15);
+ callF152(c15);
+ callF171(c15);
+ callF190(c15);
+ callF209(c15);
+ callF228(c15);
+ callF247(c15);
+ callF266(c15);
+ callF285(c15);
+ C16 c16 = new C16();
+ callF0(c16);
+ callF19(c16);
+ callF38(c16);
+ callF57(c16);
+ callF76(c16);
+ callF95(c16);
+ callF114(c16);
+ callF133(c16);
+ callF152(c16);
+ callF171(c16);
+ callF190(c16);
+ callF209(c16);
+ callF228(c16);
+ callF247(c16);
+ callF266(c16);
+ callF285(c16);
+ callF304(c16);
+ C17 c17 = new C17();
+ callF0(c17);
+ callF19(c17);
+ callF38(c17);
+ callF57(c17);
+ callF76(c17);
+ callF95(c17);
+ callF114(c17);
+ callF133(c17);
+ callF152(c17);
+ callF171(c17);
+ callF190(c17);
+ callF209(c17);
+ callF228(c17);
+ callF247(c17);
+ callF266(c17);
+ callF285(c17);
+ callF304(c17);
+ callF323(c17);
+ C18 c18 = new C18();
+ callF0(c18);
+ callF19(c18);
+ callF38(c18);
+ callF57(c18);
+ callF76(c18);
+ callF95(c18);
+ callF114(c18);
+ callF133(c18);
+ callF152(c18);
+ callF171(c18);
+ callF190(c18);
+ callF209(c18);
+ callF228(c18);
+ callF247(c18);
+ callF266(c18);
+ callF285(c18);
+ callF304(c18);
+ callF323(c18);
+ callF342(c18);
+ C19 c19 = new C19();
+ callF0(c19);
+ callF19(c19);
+ callF38(c19);
+ callF57(c19);
+ callF76(c19);
+ callF95(c19);
+ callF114(c19);
+ callF133(c19);
+ callF152(c19);
+ callF171(c19);
+ callF190(c19);
+ callF209(c19);
+ callF228(c19);
+ callF247(c19);
+ callF266(c19);
+ callF285(c19);
+ callF304(c19);
+ callF323(c19);
+ callF342(c19);
+ callF361(c19);
+ }
+
+ @Test
+ public void timeConflictDepth01() {
+ C0 c0 = new C0();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ callF0(c0);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth02() {
+ C1 c1 = new C1();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c1);
+ callF19(c1);
+ callF0(c1);
+ callF19(c1);
+ callF0(c1);
+ callF19(c1);
+ callF0(c1);
+ callF19(c1);
+ callF0(c1);
+ callF19(c1);
+ callF0(c1);
+ callF19(c1);
+ callF0(c1);
+ callF19(c1);
+ callF0(c1);
+ callF19(c1);
+ callF0(c1);
+ callF19(c1);
+ callF0(c1);
+ callF19(c1);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth03() {
+ C2 c2 = new C2();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c2);
+ callF19(c2);
+ callF38(c2);
+ callF0(c2);
+ callF19(c2);
+ callF38(c2);
+ callF0(c2);
+ callF19(c2);
+ callF38(c2);
+ callF0(c2);
+ callF19(c2);
+ callF38(c2);
+ callF0(c2);
+ callF19(c2);
+ callF38(c2);
+ callF0(c2);
+ callF19(c2);
+ callF38(c2);
+ callF0(c2);
+ callF19(c2);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth04() {
+ C3 c3 = new C3();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c3);
+ callF19(c3);
+ callF38(c3);
+ callF57(c3);
+ callF0(c3);
+ callF19(c3);
+ callF38(c3);
+ callF57(c3);
+ callF0(c3);
+ callF19(c3);
+ callF38(c3);
+ callF57(c3);
+ callF0(c3);
+ callF19(c3);
+ callF38(c3);
+ callF57(c3);
+ callF0(c3);
+ callF19(c3);
+ callF38(c3);
+ callF57(c3);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth05() {
+ C4 c4 = new C4();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c4);
+ callF19(c4);
+ callF38(c4);
+ callF57(c4);
+ callF76(c4);
+ callF0(c4);
+ callF19(c4);
+ callF38(c4);
+ callF57(c4);
+ callF76(c4);
+ callF0(c4);
+ callF19(c4);
+ callF38(c4);
+ callF57(c4);
+ callF76(c4);
+ callF0(c4);
+ callF19(c4);
+ callF38(c4);
+ callF57(c4);
+ callF76(c4);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth06() {
+ C5 c5 = new C5();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c5);
+ callF19(c5);
+ callF38(c5);
+ callF57(c5);
+ callF76(c5);
+ callF95(c5);
+ callF0(c5);
+ callF19(c5);
+ callF38(c5);
+ callF57(c5);
+ callF76(c5);
+ callF95(c5);
+ callF0(c5);
+ callF19(c5);
+ callF38(c5);
+ callF57(c5);
+ callF76(c5);
+ callF95(c5);
+ callF0(c5);
+ callF19(c5);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth07() {
+ C6 c6 = new C6();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c6);
+ callF19(c6);
+ callF38(c6);
+ callF57(c6);
+ callF76(c6);
+ callF95(c6);
+ callF114(c6);
+ callF0(c6);
+ callF19(c6);
+ callF38(c6);
+ callF57(c6);
+ callF76(c6);
+ callF95(c6);
+ callF114(c6);
+ callF0(c6);
+ callF19(c6);
+ callF38(c6);
+ callF57(c6);
+ callF76(c6);
+ callF95(c6);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth08() {
+ C7 c7 = new C7();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c7);
+ callF19(c7);
+ callF38(c7);
+ callF57(c7);
+ callF76(c7);
+ callF95(c7);
+ callF114(c7);
+ callF133(c7);
+ callF0(c7);
+ callF19(c7);
+ callF38(c7);
+ callF57(c7);
+ callF76(c7);
+ callF95(c7);
+ callF114(c7);
+ callF133(c7);
+ callF0(c7);
+ callF19(c7);
+ callF38(c7);
+ callF57(c7);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth09() {
+ C8 c8 = new C8();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c8);
+ callF19(c8);
+ callF38(c8);
+ callF57(c8);
+ callF76(c8);
+ callF95(c8);
+ callF114(c8);
+ callF133(c8);
+ callF152(c8);
+ callF0(c8);
+ callF19(c8);
+ callF38(c8);
+ callF57(c8);
+ callF76(c8);
+ callF95(c8);
+ callF114(c8);
+ callF133(c8);
+ callF152(c8);
+ callF0(c8);
+ callF19(c8);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth10() {
+ C9 c9 = new C9();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c9);
+ callF19(c9);
+ callF38(c9);
+ callF57(c9);
+ callF76(c9);
+ callF95(c9);
+ callF114(c9);
+ callF133(c9);
+ callF152(c9);
+ callF171(c9);
+ callF0(c9);
+ callF19(c9);
+ callF38(c9);
+ callF57(c9);
+ callF76(c9);
+ callF95(c9);
+ callF114(c9);
+ callF133(c9);
+ callF152(c9);
+ callF171(c9);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth11() {
+ C10 c10 = new C10();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c10);
+ callF19(c10);
+ callF38(c10);
+ callF57(c10);
+ callF76(c10);
+ callF95(c10);
+ callF114(c10);
+ callF133(c10);
+ callF152(c10);
+ callF171(c10);
+ callF190(c10);
+ callF0(c10);
+ callF19(c10);
+ callF38(c10);
+ callF57(c10);
+ callF76(c10);
+ callF95(c10);
+ callF114(c10);
+ callF133(c10);
+ callF152(c10);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth12() {
+ C11 c11 = new C11();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c11);
+ callF19(c11);
+ callF38(c11);
+ callF57(c11);
+ callF76(c11);
+ callF95(c11);
+ callF114(c11);
+ callF133(c11);
+ callF152(c11);
+ callF171(c11);
+ callF190(c11);
+ callF209(c11);
+ callF0(c11);
+ callF19(c11);
+ callF38(c11);
+ callF57(c11);
+ callF76(c11);
+ callF95(c11);
+ callF114(c11);
+ callF133(c11);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth13() {
+ C12 c12 = new C12();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c12);
+ callF19(c12);
+ callF38(c12);
+ callF57(c12);
+ callF76(c12);
+ callF95(c12);
+ callF114(c12);
+ callF133(c12);
+ callF152(c12);
+ callF171(c12);
+ callF190(c12);
+ callF209(c12);
+ callF228(c12);
+ callF0(c12);
+ callF19(c12);
+ callF38(c12);
+ callF57(c12);
+ callF76(c12);
+ callF95(c12);
+ callF114(c12);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth14() {
+ C13 c13 = new C13();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c13);
+ callF19(c13);
+ callF38(c13);
+ callF57(c13);
+ callF76(c13);
+ callF95(c13);
+ callF114(c13);
+ callF133(c13);
+ callF152(c13);
+ callF171(c13);
+ callF190(c13);
+ callF209(c13);
+ callF228(c13);
+ callF247(c13);
+ callF0(c13);
+ callF19(c13);
+ callF38(c13);
+ callF57(c13);
+ callF76(c13);
+ callF95(c13);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth15() {
+ C14 c14 = new C14();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c14);
+ callF19(c14);
+ callF38(c14);
+ callF57(c14);
+ callF76(c14);
+ callF95(c14);
+ callF114(c14);
+ callF133(c14);
+ callF152(c14);
+ callF171(c14);
+ callF190(c14);
+ callF209(c14);
+ callF228(c14);
+ callF247(c14);
+ callF266(c14);
+ callF0(c14);
+ callF19(c14);
+ callF38(c14);
+ callF57(c14);
+ callF76(c14);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth16() {
+ C15 c15 = new C15();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c15);
+ callF19(c15);
+ callF38(c15);
+ callF57(c15);
+ callF76(c15);
+ callF95(c15);
+ callF114(c15);
+ callF133(c15);
+ callF152(c15);
+ callF171(c15);
+ callF190(c15);
+ callF209(c15);
+ callF228(c15);
+ callF247(c15);
+ callF266(c15);
+ callF285(c15);
+ callF0(c15);
+ callF19(c15);
+ callF38(c15);
+ callF57(c15);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth17() {
+ C16 c16 = new C16();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c16);
+ callF19(c16);
+ callF38(c16);
+ callF57(c16);
+ callF76(c16);
+ callF95(c16);
+ callF114(c16);
+ callF133(c16);
+ callF152(c16);
+ callF171(c16);
+ callF190(c16);
+ callF209(c16);
+ callF228(c16);
+ callF247(c16);
+ callF266(c16);
+ callF285(c16);
+ callF304(c16);
+ callF0(c16);
+ callF19(c16);
+ callF38(c16);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth18() {
+ C17 c17 = new C17();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c17);
+ callF19(c17);
+ callF38(c17);
+ callF57(c17);
+ callF76(c17);
+ callF95(c17);
+ callF114(c17);
+ callF133(c17);
+ callF152(c17);
+ callF171(c17);
+ callF190(c17);
+ callF209(c17);
+ callF228(c17);
+ callF247(c17);
+ callF266(c17);
+ callF285(c17);
+ callF304(c17);
+ callF323(c17);
+ callF0(c17);
+ callF19(c17);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth19() {
+ C18 c18 = new C18();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c18);
+ callF19(c18);
+ callF38(c18);
+ callF57(c18);
+ callF76(c18);
+ callF95(c18);
+ callF114(c18);
+ callF133(c18);
+ callF152(c18);
+ callF171(c18);
+ callF190(c18);
+ callF209(c18);
+ callF228(c18);
+ callF247(c18);
+ callF266(c18);
+ callF285(c18);
+ callF304(c18);
+ callF323(c18);
+ callF342(c18);
+ callF0(c18);
+ }
+ }
+
+ @Test
+ public void timeConflictDepth20() {
+ C19 c19 = new C19();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ callF0(c19);
+ callF19(c19);
+ callF38(c19);
+ callF57(c19);
+ callF76(c19);
+ callF95(c19);
+ callF114(c19);
+ callF133(c19);
+ callF152(c19);
+ callF171(c19);
+ callF190(c19);
+ callF209(c19);
+ callF228(c19);
+ callF247(c19);
+ callF266(c19);
+ callF285(c19);
+ callF304(c19);
+ callF323(c19);
+ callF342(c19);
+ callF361(c19);
+ }
+ }
+
+ public void callF0(I0 i) {
+ i.f0();
+ }
+
+ public void callF19(I1 i) {
+ i.f19();
+ }
+
+ public void callF38(I2 i) {
+ i.f38();
+ }
+
+ public void callF57(I3 i) {
+ i.f57();
+ }
+
+ public void callF76(I4 i) {
+ i.f76();
+ }
+
+ public void callF95(I5 i) {
+ i.f95();
+ }
+
+ public void callF114(I6 i) {
+ i.f114();
+ }
+
+ public void callF133(I7 i) {
+ i.f133();
+ }
+
+ public void callF152(I8 i) {
+ i.f152();
+ }
+
+ public void callF171(I9 i) {
+ i.f171();
+ }
+
+ public void callF190(I10 i) {
+ i.f190();
+ }
+
+ public void callF209(I11 i) {
+ i.f209();
+ }
+
+ public void callF228(I12 i) {
+ i.f228();
+ }
+
+ public void callF247(I13 i) {
+ i.f247();
+ }
+
+ public void callF266(I14 i) {
+ i.f266();
+ }
+
+ public void callF285(I15 i) {
+ i.f285();
+ }
+
+ public void callF304(I16 i) {
+ i.f304();
+ }
+
+ public void callF323(I17 i) {
+ i.f323();
+ }
+
+ public void callF342(I18 i) {
+ i.f342();
+ }
+
+ public void callF361(I19 i) {
+ i.f361();
+ }
+
+ static class C0 implements I0 {}
+
+ static class C1 implements I0, I1 {}
+
+ static class C2 implements I0, I1, I2 {}
+
+ static class C3 implements I0, I1, I2, I3 {}
+
+ static class C4 implements I0, I1, I2, I3, I4 {}
+
+ static class C5 implements I0, I1, I2, I3, I4, I5 {}
+
+ static class C6 implements I0, I1, I2, I3, I4, I5, I6 {}
+
+ static class C7 implements I0, I1, I2, I3, I4, I5, I6, I7 {}
+
+ static class C8 implements I0, I1, I2, I3, I4, I5, I6, I7, I8 {}
+
+ static class C9 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9 {}
+
+ static class C10 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10 {}
+
+ static class C11 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11 {}
+
+ static class C12 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12 {}
+
+ static class C13 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13 {}
+
+ static class C14 implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14 {}
+
+ static class C15
+ implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15 {}
+
+ static class C16
+ implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, I16 {}
+
+ static class C17
+ implements I0,
+ I1,
+ I2,
+ I3,
+ I4,
+ I5,
+ I6,
+ I7,
+ I8,
+ I9,
+ I10,
+ I11,
+ I12,
+ I13,
+ I14,
+ I15,
+ I16,
+ I17 {}
+
+ static class C18
+ implements I0,
+ I1,
+ I2,
+ I3,
+ I4,
+ I5,
+ I6,
+ I7,
+ I8,
+ I9,
+ I10,
+ I11,
+ I12,
+ I13,
+ I14,
+ I15,
+ I16,
+ I17,
+ I18 {}
+
+ static class C19
+ implements I0,
+ I1,
+ I2,
+ I3,
+ I4,
+ I5,
+ I6,
+ I7,
+ I8,
+ I9,
+ I10,
+ I11,
+ I12,
+ I13,
+ I14,
+ I15,
+ I16,
+ I17,
+ I18,
+ I19 {}
+
+ interface I0 {
+ default void f0() {}
+
+ default void f1() {}
+
+ default void f2() {}
+
+ default void f3() {}
+
+ default void f4() {}
+
+ default void f5() {}
+
+ default void f6() {}
+
+ default void f7() {}
+
+ default void f8() {}
+
+ default void f9() {}
+
+ default void f10() {}
+
+ default void f11() {}
+
+ default void f12() {}
+
+ default void f13() {}
+
+ default void f14() {}
+
+ default void f15() {}
+
+ default void f16() {}
+
+ default void f17() {}
+
+ default void f18() {}
+ }
+
+ interface I1 {
+ default void f19() {}
+
+ default void f20() {}
+
+ default void f21() {}
+
+ default void f22() {}
+
+ default void f23() {}
+
+ default void f24() {}
+
+ default void f25() {}
+
+ default void f26() {}
+
+ default void f27() {}
+
+ default void f28() {}
+
+ default void f29() {}
+
+ default void f30() {}
+
+ default void f31() {}
+
+ default void f32() {}
+
+ default void f33() {}
+
+ default void f34() {}
+
+ default void f35() {}
+
+ default void f36() {}
+
+ default void f37() {}
+ }
+
+ interface I2 {
+ default void f38() {}
+
+ default void f39() {}
+
+ default void f40() {}
+
+ default void f41() {}
+
+ default void f42() {}
+
+ default void f43() {}
+
+ default void f44() {}
+
+ default void f45() {}
+
+ default void f46() {}
+
+ default void f47() {}
+
+ default void f48() {}
+
+ default void f49() {}
+
+ default void f50() {}
+
+ default void f51() {}
+
+ default void f52() {}
+
+ default void f53() {}
+
+ default void f54() {}
+
+ default void f55() {}
+
+ default void f56() {}
+ }
+
+ interface I3 {
+ default void f57() {}
+
+ default void f58() {}
+
+ default void f59() {}
+
+ default void f60() {}
+
+ default void f61() {}
+
+ default void f62() {}
+
+ default void f63() {}
+
+ default void f64() {}
+
+ default void f65() {}
+
+ default void f66() {}
+
+ default void f67() {}
+
+ default void f68() {}
+
+ default void f69() {}
+
+ default void f70() {}
+
+ default void f71() {}
+
+ default void f72() {}
+
+ default void f73() {}
+
+ default void f74() {}
+
+ default void f75() {}
+ }
+
+ interface I4 {
+ default void f76() {}
+
+ default void f77() {}
+
+ default void f78() {}
+
+ default void f79() {}
+
+ default void f80() {}
+
+ default void f81() {}
+
+ default void f82() {}
+
+ default void f83() {}
+
+ default void f84() {}
+
+ default void f85() {}
+
+ default void f86() {}
+
+ default void f87() {}
+
+ default void f88() {}
+
+ default void f89() {}
+
+ default void f90() {}
+
+ default void f91() {}
+
+ default void f92() {}
+
+ default void f93() {}
+
+ default void f94() {}
+ }
+
+ interface I5 {
+ default void f95() {}
+
+ default void f96() {}
+
+ default void f97() {}
+
+ default void f98() {}
+
+ default void f99() {}
+
+ default void f100() {}
+
+ default void f101() {}
+
+ default void f102() {}
+
+ default void f103() {}
+
+ default void f104() {}
+
+ default void f105() {}
+
+ default void f106() {}
+
+ default void f107() {}
+
+ default void f108() {}
+
+ default void f109() {}
+
+ default void f110() {}
+
+ default void f111() {}
+
+ default void f112() {}
+
+ default void f113() {}
+ }
+
+ interface I6 {
+ default void f114() {}
+
+ default void f115() {}
+
+ default void f116() {}
+
+ default void f117() {}
+
+ default void f118() {}
+
+ default void f119() {}
+
+ default void f120() {}
+
+ default void f121() {}
+
+ default void f122() {}
+
+ default void f123() {}
+
+ default void f124() {}
+
+ default void f125() {}
+
+ default void f126() {}
+
+ default void f127() {}
+
+ default void f128() {}
+
+ default void f129() {}
+
+ default void f130() {}
+
+ default void f131() {}
+
+ default void f132() {}
+ }
+
+ interface I7 {
+ default void f133() {}
+
+ default void f134() {}
+
+ default void f135() {}
+
+ default void f136() {}
+
+ default void f137() {}
+
+ default void f138() {}
+
+ default void f139() {}
+
+ default void f140() {}
+
+ default void f141() {}
+
+ default void f142() {}
+
+ default void f143() {}
+
+ default void f144() {}
+
+ default void f145() {}
+
+ default void f146() {}
+
+ default void f147() {}
+
+ default void f148() {}
+
+ default void f149() {}
+
+ default void f150() {}
+
+ default void f151() {}
+ }
+
+ interface I8 {
+ default void f152() {}
+
+ default void f153() {}
+
+ default void f154() {}
+
+ default void f155() {}
+
+ default void f156() {}
+
+ default void f157() {}
+
+ default void f158() {}
+
+ default void f159() {}
+
+ default void f160() {}
+
+ default void f161() {}
+
+ default void f162() {}
+
+ default void f163() {}
+
+ default void f164() {}
+
+ default void f165() {}
+
+ default void f166() {}
+
+ default void f167() {}
+
+ default void f168() {}
+
+ default void f169() {}
+
+ default void f170() {}
+ }
+
+ interface I9 {
+ default void f171() {}
+
+ default void f172() {}
+
+ default void f173() {}
+
+ default void f174() {}
+
+ default void f175() {}
+
+ default void f176() {}
+
+ default void f177() {}
+
+ default void f178() {}
+
+ default void f179() {}
+
+ default void f180() {}
+
+ default void f181() {}
+
+ default void f182() {}
+
+ default void f183() {}
+
+ default void f184() {}
+
+ default void f185() {}
+
+ default void f186() {}
+
+ default void f187() {}
+
+ default void f188() {}
+
+ default void f189() {}
+ }
+
+ interface I10 {
+ default void f190() {}
+
+ default void f191() {}
+
+ default void f192() {}
+
+ default void f193() {}
+
+ default void f194() {}
+
+ default void f195() {}
+
+ default void f196() {}
+
+ default void f197() {}
+
+ default void f198() {}
+
+ default void f199() {}
+
+ default void f200() {}
+
+ default void f201() {}
+
+ default void f202() {}
+
+ default void f203() {}
+
+ default void f204() {}
+
+ default void f205() {}
+
+ default void f206() {}
+
+ default void f207() {}
+
+ default void f208() {}
+ }
+
+ interface I11 {
+ default void f209() {}
+
+ default void f210() {}
+
+ default void f211() {}
+
+ default void f212() {}
+
+ default void f213() {}
+
+ default void f214() {}
+
+ default void f215() {}
+
+ default void f216() {}
+
+ default void f217() {}
+
+ default void f218() {}
+
+ default void f219() {}
+
+ default void f220() {}
+
+ default void f221() {}
+
+ default void f222() {}
+
+ default void f223() {}
+
+ default void f224() {}
+
+ default void f225() {}
+
+ default void f226() {}
+
+ default void f227() {}
+ }
+
+ interface I12 {
+ default void f228() {}
+
+ default void f229() {}
+
+ default void f230() {}
+
+ default void f231() {}
+
+ default void f232() {}
+
+ default void f233() {}
+
+ default void f234() {}
+
+ default void f235() {}
+
+ default void f236() {}
+
+ default void f237() {}
+
+ default void f238() {}
+
+ default void f239() {}
+
+ default void f240() {}
+
+ default void f241() {}
+
+ default void f242() {}
+
+ default void f243() {}
+
+ default void f244() {}
+
+ default void f245() {}
+
+ default void f246() {}
+ }
+
+ interface I13 {
+ default void f247() {}
+
+ default void f248() {}
+
+ default void f249() {}
+
+ default void f250() {}
+
+ default void f251() {}
+
+ default void f252() {}
+
+ default void f253() {}
+
+ default void f254() {}
+
+ default void f255() {}
+
+ default void f256() {}
+
+ default void f257() {}
+
+ default void f258() {}
+
+ default void f259() {}
+
+ default void f260() {}
+
+ default void f261() {}
+
+ default void f262() {}
+
+ default void f263() {}
+
+ default void f264() {}
+
+ default void f265() {}
+ }
+
+ interface I14 {
+ default void f266() {}
+
+ default void f267() {}
+
+ default void f268() {}
+
+ default void f269() {}
+
+ default void f270() {}
+
+ default void f271() {}
+
+ default void f272() {}
+
+ default void f273() {}
+
+ default void f274() {}
+
+ default void f275() {}
+
+ default void f276() {}
+
+ default void f277() {}
+
+ default void f278() {}
+
+ default void f279() {}
+
+ default void f280() {}
+
+ default void f281() {}
+
+ default void f282() {}
+
+ default void f283() {}
+
+ default void f284() {}
+ }
+
+ interface I15 {
+ default void f285() {}
+
+ default void f286() {}
+
+ default void f287() {}
+
+ default void f288() {}
+
+ default void f289() {}
+
+ default void f290() {}
+
+ default void f291() {}
+
+ default void f292() {}
+
+ default void f293() {}
+
+ default void f294() {}
+
+ default void f295() {}
+
+ default void f296() {}
+
+ default void f297() {}
+
+ default void f298() {}
+
+ default void f299() {}
+
+ default void f300() {}
+
+ default void f301() {}
+
+ default void f302() {}
+
+ default void f303() {}
+ }
+
+ interface I16 {
+ default void f304() {}
+
+ default void f305() {}
+
+ default void f306() {}
+
+ default void f307() {}
+
+ default void f308() {}
+
+ default void f309() {}
+
+ default void f310() {}
+
+ default void f311() {}
+
+ default void f312() {}
+
+ default void f313() {}
+
+ default void f314() {}
+
+ default void f315() {}
+
+ default void f316() {}
+
+ default void f317() {}
+
+ default void f318() {}
+
+ default void f319() {}
+
+ default void f320() {}
+
+ default void f321() {}
+
+ default void f322() {}
+ }
+
+ interface I17 {
+ default void f323() {}
+
+ default void f324() {}
+
+ default void f325() {}
+
+ default void f326() {}
+
+ default void f327() {}
+
+ default void f328() {}
+
+ default void f329() {}
+
+ default void f330() {}
+
+ default void f331() {}
+
+ default void f332() {}
+
+ default void f333() {}
+
+ default void f334() {}
+
+ default void f335() {}
+
+ default void f336() {}
+
+ default void f337() {}
+
+ default void f338() {}
+
+ default void f339() {}
+
+ default void f340() {}
+
+ default void f341() {}
+ }
+
+ interface I18 {
+ default void f342() {}
+
+ default void f343() {}
+
+ default void f344() {}
+
+ default void f345() {}
+
+ default void f346() {}
+
+ default void f347() {}
+
+ default void f348() {}
+
+ default void f349() {}
+
+ default void f350() {}
+
+ default void f351() {}
+
+ default void f352() {}
+
+ default void f353() {}
+
+ default void f354() {}
+
+ default void f355() {}
+
+ default void f356() {}
+
+ default void f357() {}
+
+ default void f358() {}
+
+ default void f359() {}
+
+ default void f360() {}
+ }
+
+ interface I19 {
+ default void f361() {}
+
+ default void f362() {}
+
+ default void f363() {}
+
+ default void f364() {}
+
+ default void f365() {}
+
+ default void f366() {}
+
+ default void f367() {}
+
+ default void f368() {}
+
+ default void f369() {}
+
+ default void f370() {}
+
+ default void f371() {}
+
+ default void f372() {}
+
+ default void f373() {}
+
+ default void f374() {}
+
+ default void f375() {}
+
+ default void f376() {}
+
+ default void f377() {}
+
+ default void f378() {}
+
+ default void f379() {}
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTestGen.py b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTestGen.py
new file mode 100755
index 0000000..eea3b84
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ImtConflictPerfTestGen.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python3
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import sys
+
+max_conflict_depth = 20 # In practice does not go above 20 for reasonable IMT sizes
+try:
+ imt_size = int(sys.argv[1])
+except (IndexError, ValueError):
+ print("Usage: python ImtConflictPerfTestGen.py <IMT_SIZE>")
+ sys.exit(1)
+
+license = """\
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+"""
+description = """
+/**
+ * This file is script-generated by ImtConflictPerfTestGen.py.
+ * It measures the performance impact of conflicts in interface method tables.
+ * Run `python ImtConflictPerfTestGen.py > ImtConflictPerfTest.java` to regenerate.
+ *
+ * Each interface has 64 methods, which is the current size of an IMT. C0 implements
+ * one interface, C1 implements two, C2 implements three, and so on. The intent
+ * is that C0 has no conflicts in its IMT, C1 has depth-2 conflicts in
+ * its IMT, C2 has depth-3 conflicts, etc. This is currently guaranteed by
+ * the fact that we hash interface methods by taking their method index modulo 64.
+ * (Note that a "conflict depth" of 1 means no conflict at all.)
+ */\
+"""
+
+print(license)
+print("package android.libcore;")
+imports = """
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+"""
+print(imports)
+print(description)
+
+print("@RunWith(AndroidJUnit4.class)")
+print("@LargeTest")
+print("public class ImtConflictPerfTest {")
+print(" @Rule")
+print(" public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();")
+print("")
+# Warm up interface method tables
+print(" @Before")
+print(" public void setup() {")
+for i in range(max_conflict_depth):
+ print(" C{0} c{0} = new C{0}();".format(i))
+ for j in range(i+1):
+ print(" callF{}(c{});".format(imt_size * j, i))
+print(" }")
+
+# Print test cases--one for each conflict depth
+for i in range(max_conflict_depth):
+ print(" @Test")
+ print(" public void timeConflictDepth{:02d}() {{".format(i+1))
+ print(" C{0} c{0} = new C{0}();".format(i))
+ print(" BenchmarkState state = mPerfStatusReporter.getBenchmarkState();")
+ print(" while (state.keepRunning()) {")
+ # Cycle through each interface method in an IMT entry in order
+ # to test all conflict resolution possibilities
+ for j in range(max_conflict_depth):
+ print(" callF{}(c{});".format(imt_size * (j % (i + 1)), i))
+ print(" }")
+ print(" }")
+
+# Make calls through the IMTs
+for i in range(max_conflict_depth):
+ print(" public void callF{0}(I{1} i) {{ i.f{0}(); }}".format(imt_size*i, i))
+
+# Class definitions, implementing varying amounts of interfaces
+for i in range(max_conflict_depth):
+ interfaces = ", ".join(["I{}".format(j) for j in range(i+1)])
+ print(" static class C{} implements {} {{}}".format(i, interfaces))
+
+# Interface definitions, each with enough methods to fill an entire IMT
+for i in range(max_conflict_depth):
+ print(" interface I{} {{".format(i))
+ for j in range(imt_size):
+ print(" default void f{}() {{}}".format(i*imt_size + j))
+ print(" }")
+
+print("}")
\ No newline at end of file
diff --git a/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java
new file mode 100644
index 0000000..ca99779
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/MethodInvocationPerfTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Compares various kinds of method invocation. */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class MethodInvocationPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ interface I {
+ void emptyInterface();
+ }
+
+ static class C implements I {
+ private int mField;
+
+ private int getField() {
+ return mField;
+ }
+
+ public void timeInternalGetter(BenchmarkState state) {
+ int result = 0;
+ while (state.keepRunning()) {
+ result = getField();
+ }
+ }
+
+ public void timeInternalFieldAccess(BenchmarkState state) {
+ int result = 0;
+ while (state.keepRunning()) {
+ result = mField;
+ }
+ }
+
+ public static void emptyStatic() {}
+
+ public void emptyVirtual() {}
+
+ public void emptyInterface() {}
+ }
+
+ public void timeInternalGetter() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ new C().timeInternalGetter(state);
+ }
+
+ public void timeInternalFieldAccess() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ new C().timeInternalFieldAccess(state);
+ }
+
+ // Test an intrinsic.
+ @Test
+ public void timeStringLength() {
+ int result = 0;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result = "hello, world!".length();
+ }
+ }
+
+ @Test
+ public void timeEmptyStatic() {
+ C c = new C();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ c.emptyStatic();
+ }
+ }
+
+ @Test
+ public void timeEmptyVirtual() {
+ C c = new C();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ c.emptyVirtual();
+ }
+ }
+
+ @Test
+ public void timeEmptyInterface() {
+ I c = new C();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ c.emptyInterface();
+ }
+ }
+
+ public static class Inner {
+ private int mI;
+
+ private void privateMethod() {
+ ++mI;
+ }
+
+ protected void protectedMethod() {
+ ++mI;
+ }
+
+ public void publicMethod() {
+ ++mI;
+ }
+
+ void packageMethod() {
+ ++mI;
+ }
+
+ final void finalPackageMethod() {
+ ++mI;
+ }
+ }
+
+ @Test
+ public void timePrivateInnerPublicMethod() {
+ Inner inner = new Inner();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ inner.publicMethod();
+ }
+ }
+
+ @Test
+ public void timePrivateInnerProtectedMethod() {
+ Inner inner = new Inner();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ inner.protectedMethod();
+ }
+ }
+
+ @Test
+ public void timePrivateInnerPrivateMethod() {
+ Inner inner = new Inner();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ inner.privateMethod();
+ }
+ }
+
+ @Test
+ public void timePrivateInnerPackageMethod() {
+ Inner inner = new Inner();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ inner.packageMethod();
+ }
+ }
+
+ @Test
+ public void timePrivateInnerFinalPackageMethod() {
+ Inner inner = new Inner();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ inner.finalPackageMethod();
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java
new file mode 100644
index 0000000..8496fbe
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/MultiplicationPerfTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** How much do various kinds of multiplication cost? */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class MultiplicationPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void timeMultiplyIntByConstant10() {
+ int result = 1;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result *= 10;
+ }
+ }
+
+ @Test
+ public void timeMultiplyIntByConstant8() {
+ int result = 1;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result *= 8;
+ }
+ }
+
+ @Test
+ public void timeMultiplyIntByVariable10() {
+ int result = 1;
+ int factor = 10;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result *= factor;
+ }
+ }
+
+ @Test
+ public void timeMultiplyIntByVariable8() {
+ int result = 1;
+ int factor = 8;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ result *= factor;
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/OWNERS b/apct-tests/perftests/core/src/android/libcore/OWNERS
new file mode 100644
index 0000000..2d36574
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 24949
+include platform/libcore:/OWNERS
diff --git a/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java
new file mode 100644
index 0000000..bb79424
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ReferenceGetPerfTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+package android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Field;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ReferenceGetPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ boolean mIntrinsicDisabled;
+
+ private Object mObj = "str";
+
+ @Before
+ public void setUp() throws Exception {
+ Field intrinsicDisabledField = Reference.class.getDeclaredField("disableIntrinsic");
+ intrinsicDisabledField.setAccessible(true);
+ intrinsicDisabledField.setBoolean(null, mIntrinsicDisabled);
+ }
+
+ @Test
+ public void timeSoftReferenceGet() throws Exception {
+ Reference soft = new SoftReference(mObj);
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Object o = soft.get();
+ }
+ }
+
+ @Test
+ public void timeWeakReferenceGet() throws Exception {
+ Reference weak = new WeakReference(mObj);
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Object o = weak.get();
+ }
+ }
+
+ @Test
+ public void timeNonPreservedWeakReferenceGet() throws Exception {
+ Reference weak = new WeakReference(mObj);
+ mObj = null;
+ Runtime.getRuntime().gc();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Object o = weak.get();
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java
new file mode 100644
index 0000000..2ef68ca
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ReferencePerfTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.ReferenceQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/** Benchmark to evaluate the performance of References. */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class ReferencePerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private Object mObject;
+
+ // How fast can references can be allocated?
+ @Test
+ public void timeAlloc() {
+ ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ new PhantomReference(mObject, queue);
+ }
+ }
+
+ // How fast can references can be allocated and manually enqueued?
+ @Test
+ public void timeAllocAndEnqueue() {
+ ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ (new PhantomReference<Object>(mObject, queue)).enqueue();
+ }
+ }
+
+ // How fast can references can be allocated, enqueued, and polled?
+ @Test
+ public void timeAllocEnqueueAndPoll() {
+ ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ (new PhantomReference<Object>(mObject, queue)).enqueue();
+ queue.poll();
+ }
+ }
+
+ // How fast can references can be allocated, enqueued, and removed?
+ @Test
+ public void timeAllocEnqueueAndRemove() {
+ ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ (new PhantomReference<Object>(mObject, queue)).enqueue();
+ try {
+ queue.remove();
+ } catch (InterruptedException ie) {
+ }
+ }
+ }
+
+ private static class FinalizableObject {
+ AtomicInteger mCount;
+
+ FinalizableObject(AtomicInteger count) {
+ this.mCount = count;
+ }
+
+ @Override
+ protected void finalize() {
+ mCount.incrementAndGet();
+ }
+ }
+
+ // How fast does finalization run?
+ @Test
+ public void timeFinalization() {
+ // Allocate a bunch of finalizable objects.
+ int n = 0;
+ AtomicInteger count = new AtomicInteger(0);
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ n++;
+ new FinalizableObject(count);
+ }
+
+ // Run GC so the objects will be collected for finalization.
+ Runtime.getRuntime().gc();
+
+ // Wait for finalization.
+ Runtime.getRuntime().runFinalization();
+
+ // Double check all the objects were finalized.
+ int got = count.get();
+ if (n != got) {
+ throw new IllegalStateException(
+ String.format("Only %i of %i objects finalized?", got, n));
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java b/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java
new file mode 100644
index 0000000..65a2fdb
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/SmallBigIntegerPerfTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.math.BigInteger;
+import java.util.Random;
+
+/**
+ * This measures performance of operations on small BigIntegers. We manually determine the number of
+ * iterations so that it should cause total memory allocation on the order of a few hundred
+ * megabytes. Due to BigInteger's reliance on finalization, these may unfortunately all be kept
+ * around at once.
+ *
+ * <p>This is not structured as a proper benchmark; just run main(), e.g. with vogar
+ * libcore/benchmarks/src/benchmarks/SmallBigIntegerBenchmark.java
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class SmallBigIntegerPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ // We allocate about 2 1/3 BigIntegers per iteration.
+ // Assuming 100 bytes/BigInteger, this gives us around 500MB total.
+ static final BigInteger BIG_THREE = BigInteger.valueOf(3);
+ static final BigInteger BIG_FOUR = BigInteger.valueOf(4);
+
+ @Test
+ public void testSmallBigInteger() {
+ final Random r = new Random();
+ BigInteger x = new BigInteger(20, r);
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ // We know this converges, but the compiler doesn't.
+ if (x.and(BigInteger.ONE).equals(BigInteger.ONE)) {
+ x = x.multiply(BIG_THREE).add(BigInteger.ONE);
+ } else {
+ x = x.shiftRight(1);
+ }
+ }
+ if (x.signum() < 0 || x.compareTo(BIG_FOUR) > 0) {
+ throw new AssertionError("Something went horribly wrong.");
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java b/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java
new file mode 100644
index 0000000..4f5c54d
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/StringDexCachePerfTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** How long does it take to access a string in the dex cache? */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class StringDexCachePerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void timeStringDexCacheAccess() {
+ int v = 0;
+ int count = 0;
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ // Deliberately obscured to make optimizations less likely.
+ String s = (count >= 0) ? "hello, world!" : null;
+ v += s.length();
+ ++count;
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java b/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java
new file mode 100644
index 0000000..08ad926
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/StringIterationPerfTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** How do the various schemes for iterating through a string compare? */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class StringIterationPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void timeStringIteration0() {
+ String s = "hello, world!";
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ char ch;
+ for (int i = 0; i < s.length(); ++i) {
+ ch = s.charAt(i);
+ }
+ }
+ }
+
+ @Test
+ public void timeStringIteration1() {
+ String s = "hello, world!";
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ char ch;
+ for (int i = 0, length = s.length(); i < length; ++i) {
+ ch = s.charAt(i);
+ }
+ }
+ }
+
+ @Test
+ public void timeStringIteration2() {
+ String s = "hello, world!";
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ char ch;
+ char[] chars = s.toCharArray();
+ for (int i = 0, length = chars.length; i < length; ++i) {
+ ch = chars[i];
+ }
+ }
+ }
+
+ @Test
+ public void timeStringToCharArray() {
+ String s = "hello, world!";
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ char[] chars = s.toCharArray();
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java b/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java
new file mode 100644
index 0000000..5aacfc2
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/SystemArrayCopyPerfTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2022 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class SystemArrayCopyPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Parameters(name = "arrayLength={0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(
+ new Object[][] {
+ {2}, {4}, {8}, {16}, {32}, {64}, {128}, {256}, {512}, {1024}, {2048}, {4096},
+ {8192}, {16384}, {32768}, {65536}, {131072}, {262144}
+ });
+ }
+
+ @Parameterized.Parameter(0)
+ public int arrayLength;
+
+ // Provides benchmarking for different types of arrays using the arraycopy function.
+ @Test
+ public void timeSystemCharArrayCopy() {
+ final int len = arrayLength;
+ char[] src = new char[len];
+ char[] dst = new char[len];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ System.arraycopy(src, 0, dst, 0, len);
+ }
+ }
+
+ @Test
+ public void timeSystemByteArrayCopy() {
+ final int len = arrayLength;
+ byte[] src = new byte[len];
+ byte[] dst = new byte[len];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ System.arraycopy(src, 0, dst, 0, len);
+ }
+ }
+
+ @Test
+ public void timeSystemShortArrayCopy() {
+ final int len = arrayLength;
+ short[] src = new short[len];
+ short[] dst = new short[len];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ System.arraycopy(src, 0, dst, 0, len);
+ }
+ }
+
+ @Test
+ public void timeSystemIntArrayCopy() {
+ final int len = arrayLength;
+ int[] src = new int[len];
+ int[] dst = new int[len];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ System.arraycopy(src, 0, dst, 0, len);
+ }
+ }
+
+ @Test
+ public void timeSystemLongArrayCopy() {
+ final int len = arrayLength;
+ long[] src = new long[len];
+ long[] dst = new long[len];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ System.arraycopy(src, 0, dst, 0, len);
+ }
+ }
+
+ @Test
+ public void timeSystemFloatArrayCopy() {
+ final int len = arrayLength;
+ float[] src = new float[len];
+ float[] dst = new float[len];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ System.arraycopy(src, 0, dst, 0, len);
+ }
+ }
+
+ @Test
+ public void timeSystemDoubleArrayCopy() {
+ final int len = arrayLength;
+ double[] src = new double[len];
+ double[] dst = new double[len];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ System.arraycopy(src, 0, dst, 0, len);
+ }
+ }
+
+ @Test
+ public void timeSystemBooleanArrayCopy() {
+ final int len = arrayLength;
+ boolean[] src = new boolean[len];
+ boolean[] dst = new boolean[len];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ System.arraycopy(src, 0, dst, 0, len);
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java b/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java
new file mode 100644
index 0000000..7e71976
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/VirtualVersusInterfacePerfTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Is there a performance reason to "Prefer virtual over interface", as the Android documentation
+ * once claimed?
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class VirtualVersusInterfacePerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Test
+ public void timeMapPut() {
+ Map<String, String> map = new HashMap<String, String>();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ map.put("hello", "world");
+ }
+ }
+
+ @Test
+ public void timeHashMapPut() {
+ HashMap<String, String> map = new HashMap<String, String>();
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ map.put("hello", "world");
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java b/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java
new file mode 100644
index 0000000..eec0734
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/XmlSerializePerfTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2022 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.CharArrayWriter;
+import java.lang.reflect.Constructor;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Random;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class XmlSerializePerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Parameters(name = "mDatasetAsString({0}), mSeed({1})")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(
+ new Object[][] {
+ {"0.99 0.7 0.7 0.7 0.7 0.7", 854328},
+ {"0.999 0.3 0.3 0.95 0.9 0.9", 854328},
+ {"0.99 0.7 0.7 0.7 0.7 0.7", 312547},
+ {"0.999 0.3 0.3 0.95 0.9 0.9", 312547}
+ });
+ }
+
+ @Parameterized.Parameter(0)
+ public String mDatasetAsString;
+
+ @Parameterized.Parameter(1)
+ public int mSeed;
+
+ double[] mDataset;
+ private Constructor<? extends XmlSerializer> mKxmlConstructor;
+ private Constructor<? extends XmlSerializer> mFastConstructor;
+
+ private void serializeRandomXml(Constructor<? extends XmlSerializer> ctor, long mSeed)
+ throws Exception {
+ double contChance = mDataset[0];
+ double levelUpChance = mDataset[1];
+ double levelDownChance = mDataset[2];
+ double attributeChance = mDataset[3];
+ double writeChance1 = mDataset[4];
+ double writeChance2 = mDataset[5];
+
+ XmlSerializer serializer = (XmlSerializer) ctor.newInstance();
+
+ CharArrayWriter w = new CharArrayWriter();
+ serializer.setOutput(w);
+ int level = 0;
+ Random r = new Random(mSeed);
+ char[] toWrite = {'a', 'b', 'c', 'd', 's', 'z'};
+ serializer.startDocument("UTF-8", true);
+ while (r.nextDouble() < contChance) {
+ while (level > 0 && r.nextDouble() < levelUpChance) {
+ serializer.endTag("aaaaaa", "bbbbbb");
+ level--;
+ }
+ while (r.nextDouble() < levelDownChance) {
+ serializer.startTag("aaaaaa", "bbbbbb");
+ level++;
+ }
+ serializer.startTag("aaaaaa", "bbbbbb");
+ level++;
+ while (r.nextDouble() < attributeChance) {
+ serializer.attribute("aaaaaa", "cccccc", "dddddd");
+ }
+ serializer.endTag("aaaaaa", "bbbbbb");
+ level--;
+ while (r.nextDouble() < writeChance1) serializer.text(toWrite, 0, 5);
+ while (r.nextDouble() < writeChance2) serializer.text("Textxtsxtxtxt ");
+ }
+ serializer.endDocument();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Before
+ public void setUp() throws Exception {
+ mKxmlConstructor =
+ (Constructor)
+ Class.forName("com.android.org.kxml2.io.KXmlSerializer").getConstructor();
+ mFastConstructor =
+ (Constructor)
+ Class.forName("com.android.internal.util.FastXmlSerializer")
+ .getConstructor();
+ String[] splitStrings = mDatasetAsString.split(" ");
+ mDataset = new double[splitStrings.length];
+ for (int i = 0; i < splitStrings.length; i++) {
+ mDataset[i] = Double.parseDouble(splitStrings[i]);
+ }
+ }
+
+ private void internalTimeSerializer(Constructor<? extends XmlSerializer> ctor)
+ throws Exception {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ serializeRandomXml(ctor, mSeed);
+ }
+ }
+
+ @Test
+ public void timeKxml() throws Exception {
+ internalTimeSerializer(mKxmlConstructor);
+ }
+
+ @Test
+ public void timeFast() throws Exception {
+ internalTimeSerializer(mFastConstructor);
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
new file mode 100644
index 0000000..517e3ce
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ZipFilePerfTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Random;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class ZipFilePerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ private File mFile;
+
+ @Parameters(name = "numEntries={0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] {{128}, {1024}, {8192}});
+ }
+
+ @Parameterized.Parameter(0)
+ public int numEntries;
+
+ @Before
+ public void setUp() throws Exception {
+ mFile = File.createTempFile(getClass().getName(), ".zip");
+ mFile.deleteOnExit();
+ writeEntries(new ZipOutputStream(new FileOutputStream(mFile)), numEntries, 0);
+ ZipFile zipFile = new ZipFile(mFile);
+ for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
+ ZipEntry zipEntry = e.nextElement();
+ }
+ zipFile.close();
+ }
+
+ @Test
+ public void timeZipFileOpen() throws Exception {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ ZipFile zf = new ZipFile(mFile);
+ }
+ }
+
+ /** Compresses the given number of files, each of the given size, into a .zip archive. */
+ protected void writeEntries(ZipOutputStream out, int entryCount, long entrySize)
+ throws IOException {
+ byte[] writeBuffer = new byte[8192];
+ Random random = new Random();
+ try {
+ for (int entry = 0; entry < entryCount; ++entry) {
+ ZipEntry ze = new ZipEntry(Integer.toHexString(entry));
+ ze.setSize(entrySize);
+ out.putNextEntry(ze);
+
+ for (long i = 0; i < entrySize; i += writeBuffer.length) {
+ random.nextBytes(writeBuffer);
+ int byteCount = (int) Math.min(writeBuffer.length, entrySize - i);
+ out.write(writeBuffer, 0, byteCount);
+ }
+
+ out.closeEntry();
+ }
+ } finally {
+ out.close();
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java b/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.java
new file mode 100644
index 0000000..faa9628
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/libcore/ZipFileReadPerfTest.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.libcore;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Random;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class ZipFileReadPerfTest {
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Parameters(name = "readBufferSize={0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] {{1024}, {16384}, {65536}});
+ }
+
+ private File mFile;
+
+ @Parameterized.Parameter(0)
+ public int readBufferSize;
+
+ @Before
+ public void setUp() throws Exception {
+ mFile = File.createTempFile(getClass().getName(), ".zip");
+ writeEntries(new ZipOutputStream(new FileOutputStream(mFile)), 2, 1024 * 1024);
+ ZipFile zipFile = new ZipFile(mFile);
+ for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
+ ZipEntry zipEntry = e.nextElement();
+ }
+ zipFile.close();
+ }
+
+ /** Compresses the given number of files, each of the given size, into a .zip archive. */
+ protected void writeEntries(ZipOutputStream out, int entryCount, long entrySize)
+ throws IOException {
+ byte[] writeBuffer = new byte[8192];
+ Random random = new Random();
+ try {
+ for (int entry = 0; entry < entryCount; ++entry) {
+ ZipEntry ze = new ZipEntry(Integer.toHexString(entry));
+ ze.setSize(entrySize);
+ out.putNextEntry(ze);
+
+ for (long i = 0; i < entrySize; i += writeBuffer.length) {
+ random.nextBytes(writeBuffer);
+ int byteCount = (int) Math.min(writeBuffer.length, entrySize - i);
+ out.write(writeBuffer, 0, byteCount);
+ }
+
+ out.closeEntry();
+ }
+ } finally {
+ out.close();
+ }
+ }
+
+ @Test
+ public void timeZipFileRead() throws Exception {
+ byte[] readBuffer = new byte[readBufferSize];
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ ZipFile zipFile = new ZipFile(mFile);
+ for (Enumeration<? extends ZipEntry> e = zipFile.entries(); e.hasMoreElements(); ) {
+ ZipEntry zipEntry = e.nextElement();
+ InputStream is = zipFile.getInputStream(zipEntry);
+ while (true) {
+ if (is.read(readBuffer, 0, readBuffer.length) < 0) {
+ break;
+ }
+ }
+ }
+ zipFile.close();
+ }
+ }
+}
diff --git a/apex/jobscheduler/OWNERS b/apex/jobscheduler/OWNERS
index c77ea33..58434f1 100644
--- a/apex/jobscheduler/OWNERS
+++ b/apex/jobscheduler/OWNERS
@@ -1,7 +1,9 @@
ctate@android.com
ctate@google.com
dplotnikov@google.com
+jji@google.com
kwekua@google.com
omakoto@google.com
suprabh@google.com
+varunshah@google.com
yamasani@google.com
diff --git a/api/api.go b/api/api.go
index 5e5f60e..9cd0132 100644
--- a/api/api.go
+++ b/api/api.go
@@ -27,6 +27,7 @@
const art = "art.module.public.api"
const conscrypt = "conscrypt.module.public.api"
const i18n = "i18n.module.public.api"
+
var core_libraries_modules = []string{art, conscrypt, i18n}
// The intention behind this soong plugin is to generate a number of "merged"
@@ -92,6 +93,8 @@
type MergedTxtDefinition struct {
// "current.txt" or "removed.txt"
TxtFilename string
+ // Filename in the new dist dir. "android.txt" or "android-removed.txt"
+ DistFilename string
// The module for the non-updatable / non-module part of the api.
BaseTxt string
// The list of modules that are relevant for this merged txt.
@@ -112,7 +115,6 @@
if txt.Scope != "public" {
filename = txt.Scope + "-" + filename
}
-
props := genruleProps{}
props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename)
props.Tools = []string{"metalava"}
@@ -126,9 +128,9 @@
Dest: proptools.StringPtr(filename),
},
{
- Targets: []string{"sdk"},
+ Targets: []string{"api_txt", "sdk"},
Dir: proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"),
- Dest: proptools.StringPtr(txt.TxtFilename),
+ Dest: proptools.StringPtr(txt.DistFilename),
},
}
props.Visibility = []string{"//visibility:public"}
@@ -242,34 +244,39 @@
var textFiles []MergedTxtDefinition
tagSuffix := []string{".api.txt}", ".removed-api.txt}"}
+ distFilename := []string{"android.txt", "android-removed.txt"}
for i, f := range []string{"current.txt", "removed.txt"} {
textFiles = append(textFiles, MergedTxtDefinition{
- TxtFilename: f,
- BaseTxt: ":non-updatable-" + f,
- Modules: bootclasspath,
- ModuleTag: "{.public" + tagSuffix[i],
- Scope: "public",
+ TxtFilename: f,
+ DistFilename: distFilename[i],
+ BaseTxt: ":non-updatable-" + f,
+ Modules: bootclasspath,
+ ModuleTag: "{.public" + tagSuffix[i],
+ Scope: "public",
})
textFiles = append(textFiles, MergedTxtDefinition{
- TxtFilename: f,
- BaseTxt: ":non-updatable-system-" + f,
- Modules: bootclasspath,
- ModuleTag: "{.system" + tagSuffix[i],
- Scope: "system",
+ TxtFilename: f,
+ DistFilename: distFilename[i],
+ BaseTxt: ":non-updatable-system-" + f,
+ Modules: bootclasspath,
+ ModuleTag: "{.system" + tagSuffix[i],
+ Scope: "system",
})
textFiles = append(textFiles, MergedTxtDefinition{
- TxtFilename: f,
- BaseTxt: ":non-updatable-module-lib-" + f,
- Modules: bootclasspath,
- ModuleTag: "{.module-lib" + tagSuffix[i],
- Scope: "module-lib",
+ TxtFilename: f,
+ DistFilename: distFilename[i],
+ BaseTxt: ":non-updatable-module-lib-" + f,
+ Modules: bootclasspath,
+ ModuleTag: "{.module-lib" + tagSuffix[i],
+ Scope: "module-lib",
})
textFiles = append(textFiles, MergedTxtDefinition{
- TxtFilename: f,
- BaseTxt: ":non-updatable-system-server-" + f,
- Modules: system_server_classpath,
- ModuleTag: "{.system-server" + tagSuffix[i],
- Scope: "system-server",
+ TxtFilename: f,
+ DistFilename: distFilename[i],
+ BaseTxt: ":non-updatable-system-server-" + f,
+ Modules: system_server_classpath,
+ ModuleTag: "{.system-server" + tagSuffix[i],
+ Scope: "system-server",
})
}
for _, txt := range textFiles {
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 9c044b5..8b9ab4c 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -37,6 +37,8 @@
import com.android.internal.telecom.ITelecomService;
import java.io.PrintStream;
+import java.util.Arrays;
+import java.util.stream.Collectors;
public final class Telecom extends BaseCommand {
@@ -88,6 +90,10 @@
private static final String COMMAND_GET_MAX_PHONES = "get-max-phones";
private static final String COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_FILTER =
"set-test-emergency-phone-account-package-filter";
+ /**
+ * Command used to emit a distinct "mark" in the logs.
+ */
+ private static final String COMMAND_LOG_MARK = "log-mark";
private ComponentName mComponent;
private String mAccountId;
@@ -156,6 +162,8 @@
+ " package name that will be used for test emergency calls. To clear,"
+ " send an empty package name. Real emergency calls will still be placed"
+ " over Telephony.\n"
+ + "telecom log-mark <MESSAGE>: emits a message into the telecom logs. Useful for "
+ + "testers to indicate where in the logs various test steps take place.\n"
);
}
@@ -257,6 +265,9 @@
case COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_FILTER:
runSetEmergencyPhoneAccountPackageFilter();
break;
+ case COMMAND_LOG_MARK:
+ runLogMark();
+ break;
default:
Log.w(this, "onRun: unknown command: %s", command);
throw new IllegalArgumentException ("unknown command '" + command + "'");
@@ -429,6 +440,11 @@
}
+ private void runLogMark() throws RemoteException {
+ String message = Arrays.stream(mArgs.peekRemainingArgs()).collect(Collectors.joining(" "));
+ mTelecomService.requestLogMark(message);
+ }
+
private PhoneAccountHandle getPhoneAccountHandleFromArgs() throws RemoteException {
if (TextUtils.isEmpty(mArgs.peekNextArg())) {
return null;
diff --git a/core/api/current.txt b/core/api/current.txt
index 8f74ae1..e4fc9f3 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -29612,6 +29612,7 @@
field public static final int S = 31; // 0x1f
field public static final int S_V2 = 32; // 0x20
field public static final int TIRAMISU = 10000; // 0x2710
+ field public static final int UPSIDE_DOWN_CAKE = 10000; // 0x2710
}
public final class Bundle extends android.os.BaseBundle implements java.lang.Cloneable android.os.Parcelable {
@@ -36138,10 +36139,12 @@
public class KeyStoreException extends java.lang.Exception {
method public int getNumericErrorCode();
+ method public int getRetryPolicy();
method public boolean isSystemError();
method public boolean isTransientFailure();
method public boolean requiresUserAuthentication();
field public static final int ERROR_ATTESTATION_CHALLENGE_TOO_LARGE = 9; // 0x9
+ field public static final int ERROR_ATTESTATION_KEYS_UNAVAILABLE = 16; // 0x10
field public static final int ERROR_ID_ATTESTATION_FAILURE = 8; // 0x8
field public static final int ERROR_INCORRECT_USAGE = 13; // 0xd
field public static final int ERROR_INTERNAL_SYSTEM_ERROR = 4; // 0x4
@@ -36156,6 +36159,9 @@
field public static final int ERROR_PERMISSION_DENIED = 5; // 0x5
field public static final int ERROR_UNIMPLEMENTED = 12; // 0xc
field public static final int ERROR_USER_AUTHENTICATION_REQUIRED = 2; // 0x2
+ field public static final int RETRY_NEVER = 1; // 0x1
+ field public static final int RETRY_WHEN_CONNECTIVITY_AVAILABLE = 3; // 0x3
+ field public static final int RETRY_WITH_EXPONENTIAL_BACKOFF = 2; // 0x2
}
@Deprecated public final class KeyStoreParameter implements java.security.KeyStore.ProtectionParameter {
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 9db3cdc..fd87a61 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -100,10 +100,12 @@
field public static final int USB_DATA_TRANSFER_RATE_LOW_SPEED = 2; // 0x2
field public static final int USB_DATA_TRANSFER_RATE_UNKNOWN = -1; // 0xffffffff
field public static final int USB_HAL_NOT_SUPPORTED = -1; // 0xffffffff
+ field public static final int USB_HAL_RETRY = -2; // 0xfffffffe
field public static final int USB_HAL_V1_0 = 10; // 0xa
field public static final int USB_HAL_V1_1 = 11; // 0xb
field public static final int USB_HAL_V1_2 = 12; // 0xc
field public static final int USB_HAL_V1_3 = 13; // 0xd
+ field public static final int USB_HAL_V2_0 = 20; // 0x14
}
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 0a5b91f..abc2b74 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -4169,8 +4169,14 @@
}
public final class UsbPort {
+ method @CheckResult @RequiresPermission(android.Manifest.permission.MANAGE_USB) public int enableUsbData(boolean);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USB) public android.hardware.usb.UsbPortStatus getStatus();
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setRoles(int, int);
+ field public static final int ENABLE_USB_DATA_ERROR_INTERNAL = 1; // 0x1
+ field public static final int ENABLE_USB_DATA_ERROR_NOT_SUPPORTED = 2; // 0x2
+ field public static final int ENABLE_USB_DATA_ERROR_OTHER = 4; // 0x4
+ field public static final int ENABLE_USB_DATA_ERROR_PORT_MISMATCH = 3; // 0x3
+ field public static final int ENABLE_USB_DATA_SUCCESS = 0; // 0x0
}
public final class UsbPortStatus implements android.os.Parcelable {
@@ -8705,6 +8711,7 @@
public final class DeviceConfig {
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static void addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener);
+ method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean deleteProperty(@NonNull String, @NonNull String);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static boolean getBoolean(@NonNull String, @NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static float getFloat(@NonNull String, @NonNull String, float);
method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static int getInt(@NonNull String, @NonNull String, int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 6b588f9..d7c7bb1 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -34,6 +34,7 @@
field public static final String READ_PRIVILEGED_PHONE_STATE = "android.permission.READ_PRIVILEGED_PHONE_STATE";
field public static final String RECORD_BACKGROUND_AUDIO = "android.permission.RECORD_BACKGROUND_AUDIO";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
+ field public static final String REQUEST_UNIQUE_ID_ATTESTATION = "android.permission.REQUEST_UNIQUE_ID_ATTESTATION";
field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS";
field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS";
@@ -682,6 +683,7 @@
ctor public AttributionSource(int, @Nullable String, @Nullable String);
ctor public AttributionSource(int, @Nullable String, @Nullable String, @NonNull android.os.IBinder);
ctor public AttributionSource(int, @Nullable String, @Nullable String, @Nullable java.util.Set<java.lang.String>, @Nullable android.content.AttributionSource);
+ method public void enforceCallingPid();
}
public final class AutofillOptions implements android.os.Parcelable {
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index db45466..6a877f9 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4658,6 +4658,11 @@
}
/** @hide */
+ public static boolean isProcStateConsideredInteraction(@ProcessState int procState) {
+ return (procState <= PROCESS_STATE_TOP || procState == PROCESS_STATE_BOUND_TOP);
+ }
+
+ /** @hide */
public static String procStateToString(int procState) {
final String procStateStr;
switch (procState) {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 4c30f56..7ec3619 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -209,6 +209,12 @@
public abstract void notifyNetworkPolicyRulesUpdated(int uid, long procStateSeq);
/**
+ * Inform ActivityManagerService about the latest {@code blockedReasons} for an uid, which
+ * can be used to understand whether the {@code uid} is allowed to access network or not.
+ */
+ public abstract void onUidBlockedReasonsChanged(int uid, int blockedReasons);
+
+ /**
* @return true if runtime was restarted, false if it's normal boot
*/
public abstract boolean isRuntimeRestarted();
@@ -581,7 +587,7 @@
* @param uid uid
* @param pid pid of the ProcessRecord that is pending top.
*/
- public abstract void addPendingTopUid(int uid, int pid);
+ public abstract void addPendingTopUid(int uid, int pid, @Nullable IApplicationThread thread);
/**
* Delete uid from the ActivityManagerService PendingStartActivityUids list.
@@ -680,4 +686,15 @@
*/
void notifyActivityEventChanged();
}
+
+ /**
+ * Register the UidObserver for NetworkPolicyManager service.
+ *
+ * This is equivalent to calling
+ * {@link IActivityManager#registerUidObserver(IUidObserver, int, int, String)} but having a
+ * separate method for NetworkPolicyManager service so that it's UidObserver can be called
+ * separately outside the usual UidObserver flow.
+ */
+ public abstract void registerNetworkPolicyUidObserver(@NonNull IUidObserver observer,
+ int which, int cutpoint, @NonNull String callingPackage);
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 48edb2e..8bbfd8d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -7026,7 +7026,13 @@
// local, we'll need to wait for the publishing of the provider.
if (holder != null && holder.provider == null && !holder.mLocal) {
synchronized (key.mLock) {
- key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
+ if (key.mHolder != null) {
+ if (DEBUG_PROVIDER) {
+ Slog.i(TAG, "already received provider: " + auth);
+ }
+ } else {
+ key.mLock.wait(ContentResolver.CONTENT_PROVIDER_READY_TIMEOUT_MILLIS);
+ }
holder = key.mHolder;
}
if (holder != null && holder.provider == null) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2c02be7..c3be18e 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -622,6 +622,9 @@
* Bit to be bitwise-ored into the {@link #flags} field that should be
* set if you would only like the sound, vibrate and ticker to be played
* if the notification was not already showing.
+ *
+ * Note that using this flag will stop any ongoing alerting behaviour such
+ * as sound, vibration or blinking notification LED.
*/
public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008;
@@ -4601,6 +4604,9 @@
* Set this flag if you would only like the sound, vibrate
* and ticker to be played if the notification is not already showing.
*
+ * Note that using this flag will stop any ongoing alerting behaviour such
+ * as sound, vibration or blinking notification LED.
+ *
* @see Notification#FLAG_ONLY_ALERT_ONCE
*/
@NonNull
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 157e709..3f2fa21 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -155,8 +155,8 @@
this(AttributionSourceState.CREATOR.createFromParcel(in));
// Since we just unpacked this object as part of it transiting a Binder
- // call, this is the perfect time to enforce that its UID can be trusted
- enforceCallingUid();
+ // call, this is the perfect time to enforce that its UID and PID can be trusted
+ enforceCallingUidAndPid();
}
/** @hide */
@@ -259,13 +259,24 @@
}
/**
+ * If you are handling an IPC and you don't trust the caller you need to validate whether the
+ * attribution source is one for the calling app to prevent the caller to pass you a source from
+ * another app without including themselves in the attribution chain.
+ *
+ * @throws SecurityException if the attribution source cannot be trusted to be from the caller.
+ */
+ private void enforceCallingUidAndPid() {
+ enforceCallingUid();
+ enforceCallingPid();
+ }
+
+ /**
* If you are handling an IPC and you don't trust the caller you need to validate
* whether the attribution source is one for the calling app to prevent the caller
* to pass you a source from another app without including themselves in the
* attribution chain.
*
- * @throws SecurityException if the attribution source cannot be trusted to be
- * from the caller.
+ * @throws SecurityException if the attribution source cannot be trusted to be from the caller.
*/
public void enforceCallingUid() {
if (!checkCallingUid()) {
@@ -294,6 +305,33 @@
return true;
}
+ /**
+ * Validate that the pid being claimed for the calling app is not spoofed
+ *
+ * @throws SecurityException if the attribution source cannot be trusted to be from the caller.
+ * @hide
+ */
+ @TestApi
+ public void enforceCallingPid() {
+ if (!checkCallingPid()) {
+ throw new SecurityException("Calling pid: " + Binder.getCallingPid()
+ + " doesn't match source pid: " + mAttributionSourceState.pid);
+ }
+ }
+
+ /**
+ * Validate that the pid being claimed for the calling app is not spoofed
+ *
+ * @return if the attribution source cannot be trusted to be from the caller.
+ */
+ private boolean checkCallingPid() {
+ final int callingPid = Binder.getCallingPid();
+ if (mAttributionSourceState.pid != -1 && callingPid != mAttributionSourceState.pid) {
+ return false;
+ }
+ return true;
+ }
+
@Override
public String toString() {
if (Build.IS_DEBUGGABLE) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index bde612e..402007d 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -463,7 +463,7 @@
/**
* @hide Flag for {@link #bindService}: For only the case where the binding
- * is coming from the system, set the process state to FOREGROUND_SERVICE
+ * is coming from the system, set the process state to BOUND_FOREGROUND_SERVICE
* instead of the normal maximum of IMPORTANT_FOREGROUND. That is, this is
* saying that the process shouldn't participate in the normal power reduction
* modes (removing network access etc).
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
index 660368a..1c9713d 100644
--- a/core/java/android/content/OWNERS
+++ b/core/java/android/content/OWNERS
@@ -1,7 +1,8 @@
# Remain no owner because multiple modules may touch this file.
per-file Context.java = *
per-file ContextWrapper.java = *
-per-file Content* = file:/services/core/java/com/android/server/am/OWNERS
+per-file *Content* = file:/services/core/java/com/android/server/am/OWNERS
+per-file *Sync* = file:/services/core/java/com/android/server/am/OWNERS
per-file IntentFilter.java = file:/PACKAGE_MANAGER_OWNERS
per-file IntentFilter.java = file:/services/core/java/com/android/server/am/OWNERS
per-file Intent.java = file:/PACKAGE_MANAGER_OWNERS
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index a8a5837..0f9acad 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -739,7 +739,7 @@
* {@link #startMainActivity(ComponentName, UserHandle, Rect, Bundle)}.
*
* @param component The ComponentName of the activity to launch
- * @param startActivityOptions Options to pass to startActivity
+ * @param startActivityOptions This parameter is no longer supported
* @param user The UserHandle of the profile
* @hide
*/
@@ -751,7 +751,8 @@
Log.i(TAG, "GetMainActivityLaunchIntent " + component + " " + user);
}
try {
- return mService.getActivityLaunchIntent(component, startActivityOptions, user);
+ // due to b/209607104, startActivityOptions will be ignored
+ return mService.getActivityLaunchIntent(component, null /* opts */, user);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -846,7 +847,7 @@
*
* @param packageName The packageName of the shortcut
* @param shortcutId The id of the shortcut
- * @param opts Options to pass to the PendingIntent
+ * @param opts This parameter is no longer supported
* @param user The UserHandle of the profile
*/
@Nullable
@@ -858,8 +859,9 @@
Log.i(TAG, "GetShortcutIntent " + packageName + "/" + shortcutId + " " + user);
}
try {
+ // due to b/209607104, opts will be ignored
return mService.getShortcutIntent(
- mContext.getPackageName(), packageName, shortcutId, opts, user);
+ mContext.getPackageName(), packageName, shortcutId, null /* opts */, user);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/content/pm/OWNERS b/core/java/android/content/pm/OWNERS
index 4e674f6..087e61d 100644
--- a/core/java/android/content/pm/OWNERS
+++ b/core/java/android/content/pm/OWNERS
@@ -6,6 +6,7 @@
per-file PackageParser.java = set noparent
per-file PackageParser.java = chiuwinson@google.com,patb@google.com,toddke@google.com
+per-file *Capability* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
per-file AppSearchPerson.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS
per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 755114e..bd6fcd8 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -751,11 +751,25 @@
public static final int SCREEN_WIDTH_DP_UNDEFINED = 0;
/**
- * The current width of the available screen space, in dp units,
- * corresponding to
- * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenWidthQualifier">screen
- * width</a> resource qualifier. Set to
+ * The current width of the available screen space in dp units, excluding
+ * the area occupied by screen decorations at the edges of the display.
+ * Corresponds to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#AvailableWidthHeightQualifier">
+ * available width</a> resource qualifier. Defaults to
* {@link #SCREEN_WIDTH_DP_UNDEFINED} if no width is specified.
+ *
+ * <p>In multi-window mode, equals the width of the available display area
+ * of the app window, not the available display area of the device screen
+ * (for example, when apps are displayed side by side in split-screen mode
+ * in landscape orientation).
+ *
+ * <p>Differs from {@link android.view.WindowMetrics} by not including
+ * screen decorations in the width measurement and by expressing the
+ * measurement in dp rather than px. Use {@code screenWidthDp} to obtain the
+ * horizontal display area available to the app, excluding the area occupied
+ * by screen decorations. Use {@link android.view.WindowMetrics#getBounds()}
+ * to obtain the width of the display area available to the app, including
+ * the area occupied by screen decorations.
*/
public int screenWidthDp;
@@ -766,11 +780,26 @@
public static final int SCREEN_HEIGHT_DP_UNDEFINED = 0;
/**
- * The current height of the available screen space, in dp units,
- * corresponding to
- * <a href="{@docRoot}guide/topics/resources/providing-resources.html#ScreenHeightQualifier">screen
- * height</a> resource qualifier. Set to
+ * The current height of the available screen space in dp units, excluding
+ * the area occupied by screen decorations at the edges of the display (such
+ * as the status bar, navigation bar, and cutouts). Corresponds to the
+ * <a href="{@docRoot}guide/topics/resources/providing-resources.html#AvailableWidthHeightQualifier">
+ * available height</a> resource qualifier. Defaults to
* {@link #SCREEN_HEIGHT_DP_UNDEFINED} if no height is specified.
+ *
+ * <p>In multi-window mode, equals the height of the available display area
+ * of the app window, not the available display area of the device screen
+ * (for example, when apps are displayed one above another in split-screen
+ * mode in portrait orientation).
+ *
+ * <p>Differs from {@link android.view.WindowMetrics} by not including
+ * screen decorations in the height measurement and by expressing the
+ * measurement in dp rather than px. Use {@code screenHeightDp} to obtain
+ * the vertical display area available to the app, excluding the area
+ * occupied by screen decorations. Use
+ * {@link android.view.WindowMetrics#getBounds()} to obtain the height of
+ * the display area available to the app, including the area occupied by
+ * screen decorations.
*/
public int screenHeightDp;
diff --git a/core/java/android/debug/AdbManager.java b/core/java/android/debug/AdbManager.java
index 7714dd8..243f801 100644
--- a/core/java/android/debug/AdbManager.java
+++ b/core/java/android/debug/AdbManager.java
@@ -38,6 +38,7 @@
*
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING)
public static final String WIRELESS_DEBUG_STATE_CHANGED_ACTION =
"com.android.server.adb.WIRELESS_DEBUG_STATUS";
@@ -46,6 +47,7 @@
*
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING)
public static final String WIRELESS_DEBUG_PAIRED_DEVICES_ACTION =
"com.android.server.adb.WIRELESS_DEBUG_PAIRED_DEVICES";
@@ -59,6 +61,7 @@
*
* @hide
*/
+ @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING)
public static final String WIRELESS_DEBUG_PAIRING_RESULT_ACTION =
"com.android.server.adb.WIRELESS_DEBUG_PAIRING_RESULT";
diff --git a/core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl b/core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl
index 69f2911..b2ddef9 100644
--- a/core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl
@@ -22,6 +22,8 @@
* Callback interface definition for HDMI client to get informed of
* the CEC logical device status change event.
*
+ * Only to be used on TV panel and Audio System devices (b/226317598).
+ *
* @hide
*/
oneway interface IHdmiDeviceEventListener {
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 7f07af7..3e79f18 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -18,6 +18,7 @@
import android.app.PendingIntent;
import android.content.ComponentName;
+import android.hardware.usb.IUsbOperationInternal;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.ParcelableUsbPort;
@@ -136,7 +137,7 @@
void resetUsbGadget();
/* Set USB data on or off */
- boolean enableUsbDataSignal(boolean enable);
+ boolean enableUsbData(in String portId, boolean enable, int operationId, in IUsbOperationInternal callback);
/* Gets the USB Hal Version. */
int getUsbHalVersion();
@@ -159,6 +160,6 @@
/* Enable/disable contaminant detection */
void enableContaminantDetection(in String portId, boolean enable);
- /* Sets USB device connection handler. */
- void setUsbDeviceConnectionHandler(in ComponentName usbDeviceConnectionHandler);
+ /* Sets USB device connection handler. */
+ void setUsbDeviceConnectionHandler(in ComponentName usbDeviceConnectionHandler);
}
diff --git a/core/java/android/hardware/usb/IUsbOperationInternal.aidl b/core/java/android/hardware/usb/IUsbOperationInternal.aidl
new file mode 100644
index 0000000..3f3bbf6
--- /dev/null
+++ b/core/java/android/hardware/usb/IUsbOperationInternal.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 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.hardware.usb;
+
+/**
+ * @hide
+ */
+oneway interface IUsbOperationInternal {
+void onOperationComplete(in int status);
+}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 964d7c1..df70bfd 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -36,6 +36,8 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.hardware.usb.gadget.V1_0.GadgetFunction;
import android.hardware.usb.gadget.V1_2.UsbSpeed;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbPort;
import android.os.Build;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
@@ -48,6 +50,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.StringJoiner;
/**
@@ -516,6 +519,14 @@
public static final int USB_DATA_TRANSFER_RATE_40G = 40 * 1024;
/**
+ * Returned when the client has to retry querying the version.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int USB_HAL_RETRY = -2;
+
+ /**
* The Value for USB hal is not presented.
*
* {@hide}
@@ -556,6 +567,14 @@
public static final int USB_HAL_V1_3 = 13;
/**
+ * Value for USB Hal Version v2.0.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static final int USB_HAL_V2_0 = 20;
+
+ /**
* Code for the charging usb function. Passed into {@link #setCurrentFunctions(long)}
* {@hide}
*/
@@ -664,6 +683,7 @@
USB_HAL_V1_1,
USB_HAL_V1_2,
USB_HAL_V1_3,
+ USB_HAL_V2_0,
})
public @interface UsbHalVersion {}
@@ -1168,8 +1188,9 @@
/**
* Enable/Disable the USB data signaling.
* <p>
- * Enables/Disables USB data path in all the USB ports.
+ * Enables/Disables USB data path of the first port..
* It will force to stop or restore USB data signaling.
+ * Call UsbPort API if the device has more than one UsbPort.
* </p>
*
* @param enable enable or disable USB data signaling
@@ -1180,11 +1201,11 @@
*/
@RequiresPermission(Manifest.permission.MANAGE_USB)
public boolean enableUsbDataSignal(boolean enable) {
- try {
- return mService.enableUsbDataSignal(enable);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ List<UsbPort> usbPorts = getPorts();
+ if (usbPorts.size() == 1) {
+ return usbPorts.get(0).enableUsbData(enable) == UsbPort.ENABLE_USB_DATA_SUCCESS;
}
+ return false;
}
/**
@@ -1270,6 +1291,41 @@
}
/**
+ * Should only be called by {@link UsbPort#enableUsbData}.
+ * <p>
+ * Enables or disables USB data on the specific port.
+ *
+ * @param port USB port for which USB data needs to be enabled or disabled.
+ * @param enable Enable USB data when true.
+ * Disable USB data when false.
+ * @param operationId operationId for the request.
+ * @param callback callback object to be invoked when the operation is complete.
+ * @return True when the operation is asynchronous. The caller must therefore call
+ * {@link UsbOperationInternal#waitForOperationComplete} for processing
+ * the result.
+ * False when the operation is synchronous. Caller can proceed reading the result
+ * through {@link UsbOperationInternal#getStatus}
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ boolean enableUsbData(@NonNull UsbPort port, boolean enable, int operationId,
+ IUsbOperationInternal callback) {
+ Objects.requireNonNull(port, "enableUsbData: port must not be null. opId:" + operationId);
+ try {
+ return mService.enableUsbData(port.getId(), enable, operationId, callback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "enableUsbData: failed. opId:" + operationId, e);
+ try {
+ callback.onOperationComplete(UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException r) {
+ Log.e(TAG, "enableUsbData: failed to call onOperationComplete. opId:"
+ + operationId, r);
+ }
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Sets the component that will handle USB device connection.
* <p>
* Setting component allows to specify external USB host manager to handle use cases, where
diff --git a/core/java/android/hardware/usb/UsbOperationInternal.java b/core/java/android/hardware/usb/UsbOperationInternal.java
new file mode 100644
index 0000000..9bc2b38
--- /dev/null
+++ b/core/java/android/hardware/usb/UsbOperationInternal.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2022 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.hardware.usb;
+
+import android.annotation.IntDef;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbPort;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.TimeUnit;
+/**
+ * UsbOperationInternal allows UsbPort to support both synchronous and
+ * asynchronous function irrespective of whether the underlying hal
+ * method is synchronous or asynchronous.
+ *
+ * @hide
+ */
+public final class UsbOperationInternal extends IUsbOperationInternal.Stub {
+ private static final String TAG = "UsbPortStatus";
+ private final int mOperationID;
+ // Cached portId.
+ private final String mId;
+ // True implies operation did not timeout.
+ private boolean mOperationComplete;
+ private @UsbOperationStatus int mStatus;
+ final ReentrantLock mLock = new ReentrantLock();
+ final Condition mOperationWait = mLock.newCondition();
+ // Maximum time the caller has to wait for onOperationComplete to be called.
+ private static final int USB_OPERATION_TIMEOUT_MSECS = 5000;
+
+ /**
+ * The requested operation was successfully completed.
+ * Returned in {@link onOperationComplete} and {@link getStatus}.
+ */
+ public static final int USB_OPERATION_SUCCESS = 0;
+
+ /**
+ * The requested operation failed due to internal error.
+ * Returned in {@link onOperationComplete} and {@link getStatus}.
+ */
+ public static final int USB_OPERATION_ERROR_INTERNAL = 1;
+
+ /**
+ * The requested operation failed as it's not supported.
+ * Returned in {@link onOperationComplete} and {@link getStatus}.
+ */
+ public static final int USB_OPERATION_ERROR_NOT_SUPPORTED = 2;
+
+ /**
+ * The requested operation failed as it's not supported.
+ * Returned in {@link onOperationComplete} and {@link getStatus}.
+ */
+ public static final int USB_OPERATION_ERROR_PORT_MISMATCH = 3;
+
+ @IntDef(prefix = { "USB_OPERATION_" }, value = {
+ USB_OPERATION_SUCCESS,
+ USB_OPERATION_ERROR_INTERNAL,
+ USB_OPERATION_ERROR_NOT_SUPPORTED,
+ USB_OPERATION_ERROR_PORT_MISMATCH
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface UsbOperationStatus{}
+
+ UsbOperationInternal(int operationID, String id) {
+ this.mOperationID = operationID;
+ this.mId = id;
+ }
+
+ /**
+ * Hal glue layer would directly call this function when the requested
+ * operation is complete.
+ */
+ @Override
+ public void onOperationComplete(@UsbOperationStatus int status) {
+ mLock.lock();
+ try {
+ mOperationComplete = true;
+ mStatus = status;
+ Log.i(TAG, "Port:" + mId + " opID:" + mOperationID + " status:" + mStatus);
+ mOperationWait.signal();
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ /**
+ * Caller invokes this function to wait for the operation to be complete.
+ */
+ public void waitForOperationComplete() {
+ mLock.lock();
+ try {
+ long now = System.currentTimeMillis();
+ long deadline = now + USB_OPERATION_TIMEOUT_MSECS;
+ // Wait in loop to overcome spurious wakeups.
+ do {
+ mOperationWait.await(deadline - System.currentTimeMillis(),
+ TimeUnit.MILLISECONDS);
+ } while (!mOperationComplete && System.currentTimeMillis() < deadline);
+ if (!mOperationComplete) {
+ Log.e(TAG, "Port:" + mId + " opID:" + mOperationID
+ + " operationComplete not received in " + USB_OPERATION_TIMEOUT_MSECS
+ + "msecs");
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Port:" + mId + " opID:" + mOperationID + " operationComplete interrupted");
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ public @UsbOperationStatus int getStatus() {
+ return mOperationComplete ? mStatus : USB_OPERATION_ERROR_INTERNAL;
+ }
+}
diff --git a/core/java/android/hardware/usb/UsbPort.java b/core/java/android/hardware/usb/UsbPort.java
index 274e23f..f469a3e 100644
--- a/core/java/android/hardware/usb/UsbPort.java
+++ b/core/java/android/hardware/usb/UsbPort.java
@@ -16,6 +16,10 @@
package android.hardware.usb;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_NOT_SUPPORTED;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_PORT_MISMATCH;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_SUCCESS;
import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_DETECTED;
import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_DISABLED;
import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED;
@@ -34,15 +38,23 @@
import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
import android.Manifest;
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.hardware.usb.UsbOperationInternal;
import android.hardware.usb.V1_0.Constants;
+import android.os.Binder;
+import android.util.Log;
import com.android.internal.util.Preconditions;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Represents a physical USB port and describes its characteristics.
@@ -51,6 +63,7 @@
*/
@SystemApi
public final class UsbPort {
+ private static final String TAG = "UsbPort";
private final String mId;
private final int mSupportedModes;
private final UsbManager mUsbManager;
@@ -64,6 +77,47 @@
*/
private static final int POWER_ROLE_OFFSET = Constants.PortPowerRole.NONE;
+ /**
+ * Counter for tracking UsbOperation operations.
+ */
+ private static final AtomicInteger sUsbOperationCount = new AtomicInteger();
+
+ /**
+ * The {@link #enableUsbData} request was successfully completed.
+ */
+ public static final int ENABLE_USB_DATA_SUCCESS = 0;
+
+ /**
+ * The {@link #enableUsbData} request failed due to internal error.
+ */
+ public static final int ENABLE_USB_DATA_ERROR_INTERNAL = 1;
+
+ /**
+ * The {@link #enableUsbData} request failed as it's not supported.
+ */
+ public static final int ENABLE_USB_DATA_ERROR_NOT_SUPPORTED = 2;
+
+ /**
+ * The {@link #enableUsbData} request failed as port id mismatched.
+ */
+ public static final int ENABLE_USB_DATA_ERROR_PORT_MISMATCH = 3;
+
+ /**
+ * The {@link #enableUsbData} request failed due to other reasons.
+ */
+ public static final int ENABLE_USB_DATA_ERROR_OTHER = 4;
+
+ /** @hide */
+ @IntDef(prefix = { "ENABLE_USB_DATA_" }, value = {
+ ENABLE_USB_DATA_SUCCESS,
+ ENABLE_USB_DATA_ERROR_INTERNAL,
+ ENABLE_USB_DATA_ERROR_NOT_SUPPORTED,
+ ENABLE_USB_DATA_ERROR_PORT_MISMATCH,
+ ENABLE_USB_DATA_ERROR_OTHER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface EnableUsbDataStatus{}
+
/** @hide */
public UsbPort(@NonNull UsbManager usbManager, @NonNull String id, int supportedModes,
int supportedContaminantProtectionModes,
@@ -157,7 +211,7 @@
* {@link UsbPortStatus#isRoleCombinationSupported UsbPortStatus.isRoleCombinationSupported}.
* </p><p>
* Note: This function is asynchronous and may fail silently without applying
- * the requested changes. If this function does cause a status change to occur then
+ * the operationed changes. If this function does cause a status change to occur then
* a {@link UsbManager#ACTION_USB_PORT_CHANGED} broadcast will be sent.
* </p>
*
@@ -177,6 +231,47 @@
}
/**
+ * Enables/Disables Usb data on the port.
+ *
+ * @param enable When true enables USB data if disabled.
+ * When false disables USB data if enabled.
+ * @return {@link #ENABLE_USB_DATA_SUCCESS} when request completes successfully or
+ * {@link #ENABLE_USB_DATA_ERROR_INTERNAL} when request fails due to internal
+ * error or
+ * {@link ENABLE_USB_DATA_ERROR_NOT_SUPPORTED} when not supported or
+ * {@link ENABLE_USB_DATA_ERROR_PORT_MISMATCH} when request fails due to port id
+ * mismatch or
+ * {@link ENABLE_USB_DATA_ERROR_OTHER} when fails due to other reasons.
+ */
+ @CheckResult
+ @RequiresPermission(Manifest.permission.MANAGE_USB)
+ public @EnableUsbDataStatus int enableUsbData(boolean enable) {
+ // UID is added To minimize operationID overlap between two different packages.
+ int operationId = sUsbOperationCount.incrementAndGet() + Binder.getCallingUid();
+ Log.i(TAG, "enableUsbData opId:" + operationId
+ + " callingUid:" + Binder.getCallingUid());
+ UsbOperationInternal opCallback =
+ new UsbOperationInternal(operationId, mId);
+ if (mUsbManager.enableUsbData(this, enable, operationId, opCallback) == true) {
+ opCallback.waitForOperationComplete();
+ }
+
+ int result = opCallback.getStatus();
+ switch (result) {
+ case USB_OPERATION_SUCCESS:
+ return ENABLE_USB_DATA_SUCCESS;
+ case USB_OPERATION_ERROR_INTERNAL:
+ return ENABLE_USB_DATA_ERROR_INTERNAL;
+ case USB_OPERATION_ERROR_NOT_SUPPORTED:
+ return ENABLE_USB_DATA_ERROR_NOT_SUPPORTED;
+ case USB_OPERATION_ERROR_PORT_MISMATCH:
+ return ENABLE_USB_DATA_ERROR_PORT_MISMATCH;
+ default:
+ return ENABLE_USB_DATA_ERROR_OTHER;
+ }
+ }
+
+ /**
* @hide
**/
public void enableContaminantDetection(boolean enable) {
diff --git a/core/java/android/hardware/usb/UsbPortStatus.java b/core/java/android/hardware/usb/UsbPortStatus.java
index bb7aff6..bd2f9aa 100644
--- a/core/java/android/hardware/usb/UsbPortStatus.java
+++ b/core/java/android/hardware/usb/UsbPortStatus.java
@@ -19,7 +19,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
-import android.hardware.usb.V1_0.Constants;
import android.os.Parcel;
import android.os.Parcelable;
@@ -36,27 +35,29 @@
@Immutable
@SystemApi
public final class UsbPortStatus implements Parcelable {
+ private static final String TAG = "UsbPortStatus";
private final int mCurrentMode;
private final @UsbPowerRole int mCurrentPowerRole;
private final @UsbDataRole int mCurrentDataRole;
private final int mSupportedRoleCombinations;
private final @ContaminantProtectionStatus int mContaminantProtectionStatus;
private final @ContaminantDetectionStatus int mContaminantDetectionStatus;
+ private final boolean mUsbDataEnabled;
/**
* Power role: This USB port does not have a power role.
*/
- public static final int POWER_ROLE_NONE = Constants.PortPowerRole.NONE;
+ public static final int POWER_ROLE_NONE = 0;
/**
* Power role: This USB port can act as a source (provide power).
*/
- public static final int POWER_ROLE_SOURCE = Constants.PortPowerRole.SOURCE;
+ public static final int POWER_ROLE_SOURCE = 1;
/**
* Power role: This USB port can act as a sink (receive power).
*/
- public static final int POWER_ROLE_SINK = Constants.PortPowerRole.SINK;
+ public static final int POWER_ROLE_SINK = 2;
@IntDef(prefix = { "POWER_ROLE_" }, value = {
POWER_ROLE_NONE,
@@ -69,17 +70,17 @@
/**
* Power role: This USB port does not have a data role.
*/
- public static final int DATA_ROLE_NONE = Constants.PortDataRole.NONE;
+ public static final int DATA_ROLE_NONE = 0;
/**
* Data role: This USB port can act as a host (access data services).
*/
- public static final int DATA_ROLE_HOST = Constants.PortDataRole.HOST;
+ public static final int DATA_ROLE_HOST = 1;
/**
* Data role: This USB port can act as a device (offer data services).
*/
- public static final int DATA_ROLE_DEVICE = Constants.PortDataRole.DEVICE;
+ public static final int DATA_ROLE_DEVICE = 2;
@IntDef(prefix = { "DATA_ROLE_" }, value = {
DATA_ROLE_NONE,
@@ -92,15 +93,7 @@
/**
* There is currently nothing connected to this USB port.
*/
- public static final int MODE_NONE = Constants.PortMode.NONE;
-
- /**
- * This USB port can act as a downstream facing port (host).
- *
- * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and
- * {@link #DATA_ROLE_HOST} combination of roles (and possibly others as well).
- */
- public static final int MODE_DFP = Constants.PortMode.DFP;
+ public static final int MODE_NONE = 0;
/**
* This USB port can act as an upstream facing port (device).
@@ -108,7 +101,15 @@
* <p> Implies that the port supports the {@link #POWER_ROLE_SINK} and
* {@link #DATA_ROLE_DEVICE} combination of roles (and possibly others as well).
*/
- public static final int MODE_UFP = Constants.PortMode.UFP;
+ public static final int MODE_UFP = 1 << 0;
+
+ /**
+ * This USB port can act as a downstream facing port (host).
+ *
+ * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and
+ * {@link #DATA_ROLE_HOST} combination of roles (and possibly others as well).
+ */
+ public static final int MODE_DFP = 1 << 1;
/**
* This USB port can act either as an downstream facing port (host) or as
@@ -120,87 +121,76 @@
*
* @hide
*/
- public static final int MODE_DUAL = Constants.PortMode.DRP;
+ public static final int MODE_DUAL = MODE_UFP | MODE_DFP;
/**
* This USB port can support USB Type-C Audio accessory.
*/
- public static final int MODE_AUDIO_ACCESSORY =
- android.hardware.usb.V1_1.Constants.PortMode_1_1.AUDIO_ACCESSORY;
+ public static final int MODE_AUDIO_ACCESSORY = 1 << 2;
/**
* This USB port can support USB Type-C debug accessory.
*/
- public static final int MODE_DEBUG_ACCESSORY =
- android.hardware.usb.V1_1.Constants.PortMode_1_1.DEBUG_ACCESSORY;
+ public static final int MODE_DEBUG_ACCESSORY = 1 << 3;
/**
* Contaminant presence detection not supported by the device.
* @hide
*/
- public static final int CONTAMINANT_DETECTION_NOT_SUPPORTED =
- android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.NOT_SUPPORTED;
+ public static final int CONTAMINANT_DETECTION_NOT_SUPPORTED = 0;
/**
* Contaminant presence detection supported but disabled.
* @hide
*/
- public static final int CONTAMINANT_DETECTION_DISABLED =
- android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.DISABLED;
+ public static final int CONTAMINANT_DETECTION_DISABLED = 1;
/**
* Contaminant presence enabled but not detected.
* @hide
*/
- public static final int CONTAMINANT_DETECTION_NOT_DETECTED =
- android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.NOT_DETECTED;
+ public static final int CONTAMINANT_DETECTION_NOT_DETECTED = 2;
/**
* Contaminant presence enabled and detected.
* @hide
*/
- public static final int CONTAMINANT_DETECTION_DETECTED =
- android.hardware.usb.V1_2.Constants.ContaminantDetectionStatus.DETECTED;
+ public static final int CONTAMINANT_DETECTION_DETECTED = 3;
/**
* Contaminant protection - No action performed upon detection of
* contaminant presence.
* @hide
*/
- public static final int CONTAMINANT_PROTECTION_NONE =
- android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.NONE;
+ public static final int CONTAMINANT_PROTECTION_NONE = 0;
/**
* Contaminant protection - Port is forced to sink upon detection of
* contaminant presence.
* @hide
*/
- public static final int CONTAMINANT_PROTECTION_SINK =
- android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.FORCE_SINK;
+ public static final int CONTAMINANT_PROTECTION_SINK = 1 << 0;
/**
* Contaminant protection - Port is forced to source upon detection of
* contaminant presence.
* @hide
*/
- public static final int CONTAMINANT_PROTECTION_SOURCE =
- android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.FORCE_SOURCE;
+ public static final int CONTAMINANT_PROTECTION_SOURCE = 1 << 1;
/**
* Contaminant protection - Port is disabled upon detection of
* contaminant presence.
* @hide
*/
- public static final int CONTAMINANT_PROTECTION_FORCE_DISABLE =
- android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.FORCE_DISABLE;
+ public static final int CONTAMINANT_PROTECTION_FORCE_DISABLE = 1 << 2;
/**
* Contaminant protection - Port is disabled upon detection of
* contaminant presence.
* @hide
*/
- public static final int CONTAMINANT_PROTECTION_DISABLED =
- android.hardware.usb.V1_2.Constants.ContaminantProtectionStatus.DISABLED;
+ public static final int CONTAMINANT_PROTECTION_DISABLED = 1 << 3;
@IntDef(prefix = { "CONTAMINANT_DETECTION_" }, value = {
CONTAMINANT_DETECTION_NOT_SUPPORTED,
@@ -234,6 +224,19 @@
/** @hide */
public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
int supportedRoleCombinations, int contaminantProtectionStatus,
+ int contaminantDetectionStatus, boolean usbDataEnabled) {
+ mCurrentMode = currentMode;
+ mCurrentPowerRole = currentPowerRole;
+ mCurrentDataRole = currentDataRole;
+ mSupportedRoleCombinations = supportedRoleCombinations;
+ mContaminantProtectionStatus = contaminantProtectionStatus;
+ mContaminantDetectionStatus = contaminantDetectionStatus;
+ mUsbDataEnabled = usbDataEnabled;
+ }
+
+ /** @hide */
+ public UsbPortStatus(int currentMode, int currentPowerRole, int currentDataRole,
+ int supportedRoleCombinations, int contaminantProtectionStatus,
int contaminantDetectionStatus) {
mCurrentMode = currentMode;
mCurrentPowerRole = currentPowerRole;
@@ -241,6 +244,7 @@
mSupportedRoleCombinations = supportedRoleCombinations;
mContaminantProtectionStatus = contaminantProtectionStatus;
mContaminantDetectionStatus = contaminantDetectionStatus;
+ mUsbDataEnabled = true;
}
/**
@@ -323,6 +327,15 @@
return mContaminantProtectionStatus;
}
+ /**
+ * Returns UsbData status.
+ *
+ * @hide
+ */
+ public boolean getUsbDataStatus() {
+ return mUsbDataEnabled;
+ }
+
@NonNull
@Override
public String toString() {
@@ -336,6 +349,8 @@
+ getContaminantDetectionStatus()
+ ", contaminantProtectionStatus="
+ getContaminantProtectionStatus()
+ + ", usbDataEnabled="
+ + getUsbDataStatus()
+ "}";
}
@@ -352,6 +367,7 @@
dest.writeInt(mSupportedRoleCombinations);
dest.writeInt(mContaminantProtectionStatus);
dest.writeInt(mContaminantDetectionStatus);
+ dest.writeBoolean(mUsbDataEnabled);
}
public static final @NonNull Parcelable.Creator<UsbPortStatus> CREATOR =
@@ -364,9 +380,10 @@
int supportedRoleCombinations = in.readInt();
int contaminantProtectionStatus = in.readInt();
int contaminantDetectionStatus = in.readInt();
+ boolean usbDataEnabled = in.readBoolean();
return new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
supportedRoleCombinations, contaminantProtectionStatus,
- contaminantDetectionStatus);
+ contaminantDetectionStatus, usbDataEnabled);
}
@Override
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index 3abe83b..ba0546a 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -25,12 +25,6 @@
import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA512;
import static android.net.IpSecAlgorithm.CRYPT_AES_CBC;
import static android.net.IpSecAlgorithm.CRYPT_AES_CTR;
-import static android.net.eap.EapSessionConfig.EapMsChapV2Config;
-import static android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig;
-import static android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignLocalConfig;
-import static android.net.ipsec.ike.IkeSessionParams.IkeAuthDigitalSignRemoteConfig;
-import static android.net.ipsec.ike.IkeSessionParams.IkeAuthEapConfig;
-import static android.net.ipsec.ike.IkeSessionParams.IkeAuthPskConfig;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
import static com.android.internal.util.Preconditions.checkStringNotEmpty;
@@ -40,6 +34,7 @@
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.content.pm.PackageManager;
+import android.net.ipsec.ike.IkeDerAsn1DnIdentification;
import android.net.ipsec.ike.IkeFqdnIdentification;
import android.net.ipsec.ike.IkeIdentification;
import android.net.ipsec.ike.IkeIpv4AddrIdentification;
@@ -119,8 +114,8 @@
DEFAULT_ALGORITHMS = Collections.unmodifiableList(algorithms);
}
- @NonNull private final String mServerAddr;
- @NonNull private final String mUserIdentity;
+ @Nullable private final String mServerAddr;
+ @Nullable private final String mUserIdentity;
// PSK authentication
@Nullable private final byte[] mPresharedKey;
@@ -146,8 +141,8 @@
private Ikev2VpnProfile(
int type,
- @NonNull String serverAddr,
- @NonNull String userIdentity,
+ @Nullable String serverAddr,
+ @Nullable String userIdentity,
@Nullable byte[] presharedKey,
@Nullable X509Certificate serverRootCaCert,
@Nullable String username,
@@ -165,8 +160,6 @@
@Nullable IkeTunnelConnectionParams ikeTunConnParams) {
super(type, excludeLocalRoutes, requiresInternetValidation);
- checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "Server address");
- checkNotNull(userIdentity, MISSING_PARAM_MSG_TMPL, "User Identity");
checkNotNull(allowedAlgorithms, MISSING_PARAM_MSG_TMPL, "Allowed Algorithms");
mServerAddr = serverAddr;
@@ -191,18 +184,12 @@
mIsMetered = isMetered;
mMaxMtu = maxMtu;
mIsRestrictedToTestNetworks = restrictToTestNetworks;
-
mIkeTunConnParams = ikeTunConnParams;
validate();
}
private void validate() {
- // Server Address not validated except to check an address was provided. This allows for
- // dual-stack servers and hostname based addresses.
- checkStringNotEmpty(mServerAddr, MISSING_PARAM_MSG_TMPL, "Server Address");
- checkStringNotEmpty(mUserIdentity, MISSING_PARAM_MSG_TMPL, "User Identity");
-
// IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6
// networks, the VPN must provide a link fulfilling the stricter of the two conditions
// (at least that of the IPv6 MTU).
@@ -210,6 +197,15 @@
throw new IllegalArgumentException("Max MTU must be at least" + IPV6_MIN_MTU);
}
+ // Skip validating the other fields if mIkeTunConnParams is set because the required
+ // information should all come from the mIkeTunConnParams.
+ if (mIkeTunConnParams != null) return;
+
+ // Server Address not validated except to check an address was provided. This allows for
+ // dual-stack servers and hostname based addresses.
+ checkStringNotEmpty(mServerAddr, MISSING_PARAM_MSG_TMPL, "Server Address");
+ checkStringNotEmpty(mUserIdentity, MISSING_PARAM_MSG_TMPL, "User Identity");
+
switch (mType) {
case TYPE_IKEV2_IPSEC_USER_PASS:
checkNotNull(mUsername, MISSING_PARAM_MSG_TMPL, "Username");
@@ -286,22 +282,31 @@
/** Retrieves the server address string. */
@NonNull
public String getServerAddr() {
- return mServerAddr;
+ if (mIkeTunConnParams == null) return mServerAddr;
+
+ final IkeSessionParams ikeSessionParams = mIkeTunConnParams.getIkeSessionParams();
+ return ikeSessionParams.getServerHostname();
}
/** Retrieves the user identity. */
@NonNull
public String getUserIdentity() {
- return mUserIdentity;
+ if (mIkeTunConnParams == null) return mUserIdentity;
+
+ final IkeSessionParams ikeSessionParams = mIkeTunConnParams.getIkeSessionParams();
+ return getUserIdentityFromIkeSession(ikeSessionParams);
}
/**
* Retrieves the pre-shared key.
*
- * <p>May be null if the profile is not using Pre-shared key authentication.
+ * <p>May be null if the profile is not using Pre-shared key authentication, or the profile is
+ * built from an {@link IkeTunnelConnectionParams}.
*/
@Nullable
public byte[] getPresharedKey() {
+ if (mIkeTunConnParams != null) return null;
+
return mPresharedKey == null ? null : Arrays.copyOf(mPresharedKey, mPresharedKey.length);
}
@@ -309,46 +314,62 @@
* Retrieves the certificate for the server's root CA.
*
* <p>May be null if the profile is not using RSA Digital Signature Authentication or
- * Username/Password authentication
+ * Username/Password authentication, or the profile is built from an
+ * {@link IkeTunnelConnectionParams}.
*/
@Nullable
public X509Certificate getServerRootCaCert() {
+ if (mIkeTunConnParams != null) return null;
+
return mServerRootCaCert;
}
-
/**
* Retrieves the username.
*
- * <p>May be null if the profile is not using Username/Password authentication
+ * <p>May be null if the profile is not using Username/Password authentication, or the profile
+ * is built from an {@link IkeTunnelConnectionParams}.
*/
@Nullable
public String getUsername() {
+ if (mIkeTunConnParams != null) return null;
+
return mUsername;
}
/**
* Retrieves the password.
*
- * <p>May be null if the profile is not using Username/Password authentication
+ * <p>May be null if the profile is not using Username/Password authentication, or the profile
+ * is built from an {@link IkeTunnelConnectionParams}.
*/
@Nullable
public String getPassword() {
+ if (mIkeTunConnParams != null) return null;
+
return mPassword;
}
/**
* Retrieves the RSA private key.
*
- * <p>May be null if the profile is not using RSA Digital Signature authentication
+ * <p>May be null if the profile is not using RSA Digital Signature authentication, or the
+ * profile is built from an {@link IkeTunnelConnectionParams}.
*/
@Nullable
public PrivateKey getRsaPrivateKey() {
+ if (mIkeTunConnParams != null) return null;
+
return mRsaPrivateKey;
}
- /** Retrieves the user certificate, if any was set. */
+ /** Retrieves the user certificate, if any was set.
+ *
+ * <p>May be null if the profile is built from an {@link IkeTunnelConnectionParams}.
+ */
@Nullable
public X509Certificate getUserCert() {
+ if (mIkeTunConnParams != null) return null;
+
return mUserCert;
}
@@ -358,9 +379,14 @@
return mProxyInfo;
}
- /** Returns all the algorithms allowed by this VPN profile. */
+ /** Returns all the algorithms allowed by this VPN profile.
+ *
+ * <p>May be an empty list if the profile is built from an {@link IkeTunnelConnectionParams}.
+ */
@NonNull
public List<String> getAllowedAlgorithms() {
+ if (mIkeTunConnParams != null) return new ArrayList<>();
+
return mAllowedAlgorithms;
}
@@ -455,18 +481,25 @@
@NonNull
public VpnProfile toVpnProfile() throws IOException, GeneralSecurityException {
final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */,
- mIsRestrictedToTestNetworks, mExcludeLocalRoutes, mRequiresInternetValidation);
- profile.type = mType;
- profile.server = mServerAddr;
- profile.ipsecIdentifier = mUserIdentity;
+ mIsRestrictedToTestNetworks, mExcludeLocalRoutes, mRequiresInternetValidation,
+ mIkeTunConnParams);
+
+ profile.server = getServerAddr();
+ profile.ipsecIdentifier = getUserIdentity();
profile.proxy = mProxyInfo;
- profile.setAllowedAlgorithms(mAllowedAlgorithms);
profile.isBypassable = mIsBypassable;
profile.isMetered = mIsMetered;
profile.maxMtu = mMaxMtu;
profile.areAuthParamsInline = true;
profile.saveLogin = true;
+ // The other fields should come from mIkeTunConnParams if it's available.
+ if (mIkeTunConnParams != null) {
+ profile.type = VpnProfile.TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS;
+ return profile;
+ }
+ profile.type = mType;
+ profile.setAllowedAlgorithms(mAllowedAlgorithms);
switch (mType) {
case TYPE_IKEV2_IPSEC_USER_PASS:
profile.username = mUsername;
@@ -516,10 +549,47 @@
@NonNull
public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile)
throws GeneralSecurityException {
- // TODO: Build the VpnProfile from mIkeTunConnParams if it exists.
- final Builder builder = new Builder(profile.server, profile.ipsecIdentifier);
+ final Builder builder;
+ if (profile.ikeTunConnParams == null) {
+ builder = new Builder(profile.server, profile.ipsecIdentifier);
+ builder.setAllowedAlgorithms(profile.getAllowedAlgorithms());
+
+ switch (profile.type) {
+ case TYPE_IKEV2_IPSEC_USER_PASS:
+ builder.setAuthUsernamePassword(
+ profile.username,
+ profile.password,
+ certificateFromPemString(profile.ipsecCaCert));
+ break;
+ case TYPE_IKEV2_IPSEC_PSK:
+ builder.setAuthPsk(decodeFromIpsecSecret(profile.ipsecSecret));
+ break;
+ case TYPE_IKEV2_IPSEC_RSA:
+ final PrivateKey key;
+ if (profile.ipsecSecret.startsWith(PREFIX_KEYSTORE_ALIAS)) {
+ final String alias =
+ profile.ipsecSecret.substring(PREFIX_KEYSTORE_ALIAS.length());
+ key = getPrivateKeyFromAndroidKeystore(alias);
+ } else if (profile.ipsecSecret.startsWith(PREFIX_INLINE)) {
+ key = getPrivateKey(profile.ipsecSecret.substring(PREFIX_INLINE.length()));
+ } else {
+ throw new IllegalArgumentException("Invalid RSA private key prefix");
+ }
+
+ final X509Certificate userCert =
+ certificateFromPemString(profile.ipsecUserCert);
+ final X509Certificate serverRootCa =
+ certificateFromPemString(profile.ipsecCaCert);
+ builder.setAuthDigitalSignature(userCert, key, serverRootCa);
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid auth method set");
+ }
+ } else {
+ builder = new Builder(profile.ikeTunConnParams);
+ }
+
builder.setProxy(profile.proxy);
- builder.setAllowedAlgorithms(profile.getAllowedAlgorithms());
builder.setBypassable(profile.isBypassable);
builder.setMetered(profile.isMetered);
builder.setMaxMtu(profile.maxMtu);
@@ -527,36 +597,6 @@
builder.restrictToTestNetworks();
}
- switch (profile.type) {
- case TYPE_IKEV2_IPSEC_USER_PASS:
- builder.setAuthUsernamePassword(
- profile.username,
- profile.password,
- certificateFromPemString(profile.ipsecCaCert));
- break;
- case TYPE_IKEV2_IPSEC_PSK:
- builder.setAuthPsk(decodeFromIpsecSecret(profile.ipsecSecret));
- break;
- case TYPE_IKEV2_IPSEC_RSA:
- final PrivateKey key;
- if (profile.ipsecSecret.startsWith(PREFIX_KEYSTORE_ALIAS)) {
- final String alias =
- profile.ipsecSecret.substring(PREFIX_KEYSTORE_ALIAS.length());
- key = getPrivateKeyFromAndroidKeystore(alias);
- } else if (profile.ipsecSecret.startsWith(PREFIX_INLINE)) {
- key = getPrivateKey(profile.ipsecSecret.substring(PREFIX_INLINE.length()));
- } else {
- throw new IllegalArgumentException("Invalid RSA private key prefix");
- }
-
- final X509Certificate userCert = certificateFromPemString(profile.ipsecUserCert);
- final X509Certificate serverRootCa = certificateFromPemString(profile.ipsecCaCert);
- builder.setAuthDigitalSignature(userCert, key, serverRootCa);
- break;
- default:
- throw new IllegalArgumentException("Invalid auth method set");
- }
-
if (profile.excludeLocalRoutes && !profile.isBypassable) {
Log.w(TAG, "ExcludeLocalRoutes should only be set in the bypassable VPN");
}
@@ -678,82 +718,13 @@
}
private static void checkBuilderSetter(boolean constructedFromIkeTunConParams,
- @NonNull String message) {
+ @NonNull String field) {
if (constructedFromIkeTunConParams) {
- throw new IllegalArgumentException("Constructed using IkeTunnelConnectionParams "
- + "should not set " + message);
+ throw new IllegalArgumentException(
+ field + " can't be set with IkeTunnelConnectionParams builder");
}
}
- private static int getTypeFromIkeSession(@NonNull IkeSessionParams params) {
- final IkeAuthConfig config = params.getLocalAuthConfig();
- if (config instanceof IkeAuthDigitalSignLocalConfig) {
- return TYPE_IKEV2_IPSEC_RSA;
- } else if (config instanceof IkeAuthEapConfig) {
- return TYPE_IKEV2_IPSEC_USER_PASS;
- } else if (config instanceof IkeAuthPskConfig) {
- return TYPE_IKEV2_IPSEC_PSK;
- } else {
- throw new IllegalStateException("Invalid local IkeAuthConfig");
- }
- }
-
- @Nullable
- private static String getPasswordFromIkeSession(@NonNull IkeSessionParams params) {
- if (!(params.getLocalAuthConfig() instanceof IkeAuthEapConfig)) return null;
-
- final IkeAuthEapConfig ikeAuthEapConfig = (IkeAuthEapConfig) params.getLocalAuthConfig();
- final EapMsChapV2Config eapMsChapV2Config =
- ikeAuthEapConfig.getEapConfig().getEapMsChapV2Config();
- return (eapMsChapV2Config != null) ? eapMsChapV2Config.getPassword() : null;
- }
-
- @Nullable
- private static String getUsernameFromIkeSession(@NonNull IkeSessionParams params) {
- if (!(params.getLocalAuthConfig() instanceof IkeAuthEapConfig)) return null;
-
- final IkeAuthEapConfig ikeAuthEapConfig = (IkeAuthEapConfig) params.getLocalAuthConfig();
- final EapMsChapV2Config eapMsChapV2Config =
- ikeAuthEapConfig.getEapConfig().getEapMsChapV2Config();
- return (eapMsChapV2Config != null) ? eapMsChapV2Config.getUsername() : null;
- }
-
- @Nullable
- private static X509Certificate getUserCertFromIkeSession(@NonNull IkeSessionParams params) {
- if (!(params.getLocalAuthConfig() instanceof IkeAuthDigitalSignLocalConfig)) return null;
-
- final IkeAuthDigitalSignLocalConfig config =
- (IkeAuthDigitalSignLocalConfig) params.getLocalAuthConfig();
- return config.getClientEndCertificate();
- }
-
- @Nullable
- private static X509Certificate getServerRootCaCertFromIkeSession(
- @NonNull IkeSessionParams params) {
- if (!(params.getRemoteAuthConfig() instanceof IkeAuthDigitalSignRemoteConfig)) return null;
-
- final IkeAuthDigitalSignRemoteConfig config =
- (IkeAuthDigitalSignRemoteConfig) params.getRemoteAuthConfig();
- return config.getRemoteCaCert();
- }
-
- @Nullable
- private static PrivateKey getRsaPrivateKeyFromIkeSession(@NonNull IkeSessionParams params) {
- if (!(params.getLocalAuthConfig() instanceof IkeAuthDigitalSignLocalConfig)) return null;
-
- final IkeAuthDigitalSignLocalConfig config =
- (IkeAuthDigitalSignLocalConfig) params.getLocalAuthConfig();
- return config.getPrivateKey();
- }
-
- @Nullable
- private static byte[] getPresharedKeyFromIkeSession(@NonNull IkeSessionParams params) {
- if (!(params.getLocalAuthConfig() instanceof IkeAuthPskConfig)) return null;
-
- final IkeAuthPskConfig config = (IkeAuthPskConfig) params.getLocalAuthConfig();
- return config.getPsk();
- }
-
@NonNull
private static String getUserIdentityFromIkeSession(@NonNull IkeSessionParams params) {
final IkeIdentification ident = params.getLocalIdentification();
@@ -768,6 +739,8 @@
return ((IkeIpv4AddrIdentification) ident).ipv4Address.getHostAddress();
} else if (ident instanceof IkeIpv6AddrIdentification) {
return ((IkeIpv6AddrIdentification) ident).ipv6Address.getHostAddress();
+ } else if (ident instanceof IkeDerAsn1DnIdentification) {
+ throw new IllegalArgumentException("Unspported ASN.1 encoded identities");
} else {
throw new IllegalArgumentException("Unknown IkeIdentification to get user identity");
}
@@ -776,8 +749,8 @@
/** A incremental builder for IKEv2 VPN profiles */
public static final class Builder {
private int mType = -1;
- @NonNull private final String mServerAddr;
- @NonNull private final String mUserIdentity;
+ @Nullable private final String mServerAddr;
+ @Nullable private final String mUserIdentity;
// PSK authentication
@Nullable private byte[] mPresharedKey;
@@ -831,19 +804,8 @@
checkNotNull(ikeTunConnParams, MISSING_PARAM_MSG_TMPL, "ikeTunConnParams");
mIkeTunConnParams = ikeTunConnParams;
-
- final IkeSessionParams ikeSessionParams = mIkeTunConnParams.getIkeSessionParams();
- mServerAddr = ikeSessionParams.getServerHostname();
-
- mType = getTypeFromIkeSession(ikeSessionParams);
- mUserCert = getUserCertFromIkeSession(ikeSessionParams);
- mServerRootCaCert = getServerRootCaCertFromIkeSession(ikeSessionParams);
- mRsaPrivateKey = getRsaPrivateKeyFromIkeSession(ikeSessionParams);
- mServerRootCaCert = getServerRootCaCertFromIkeSession(ikeSessionParams);
- mUsername = getUsernameFromIkeSession(ikeSessionParams);
- mPassword = getPasswordFromIkeSession(ikeSessionParams);
- mPresharedKey = getPresharedKeyFromIkeSession(ikeSessionParams);
- mUserIdentity = getUserIdentityFromIkeSession(ikeSessionParams);
+ mServerAddr = null;
+ mUserIdentity = null;
}
private void resetAuthParams() {
@@ -862,6 +824,10 @@
* authentication method may be set. This method will overwrite any previously set
* authentication method.
*
+ * <p>If this {@link Builder} is constructed with an {@link IkeTunnelConnectionParams},
+ * authentication details should be configured there, and calling this method will result
+ * in an exception being thrown.
+ *
* @param user the username to be used for EAP-MSCHAPv2 authentication
* @param pass the password to be used for EAP-MSCHAPv2 authentication
* @param serverRootCa the root certificate to be used for verifying the identity of the
@@ -898,6 +864,10 @@
* Only one authentication method may be set. This method will overwrite any previously set
* authentication method.
*
+ * <p>If this {@link Builder} is constructed with an {@link IkeTunnelConnectionParams},
+ * authentication details should be configured there, and calling this method will result in
+ * an exception being thrown.
+ *
* @param userCert the username to be used for RSA Digital signiture authentication
* @param key the PrivateKey instance associated with the user ceritificate, used for
* constructing the signature
@@ -936,6 +906,10 @@
* authentication method may be set. This method will overwrite any previously set
* authentication method.
*
+ * <p>If this {@link Builder} is constructed with an {@link IkeTunnelConnectionParams},
+ * authentication details should be configured there, and calling this method will result in
+ * an exception being thrown.
+ *
* @param psk the key to be used for Pre-Shared Key authentication
* @return this {@link Builder} object to facilitate chaining of method calls
*/
@@ -1068,6 +1042,10 @@
* Authentication, and one that provides Encryption. Authenticated Encryption with
* Associated Data (AEAD) algorithms provide both Authentication and Encryption.
*
+ * <p>If this {@link Builder} is constructed with an {@link IkeTunnelConnectionParams},
+ * authentication details should be configured there, and calling this method will result in
+ * an exception being thrown.
+ *
* <p>By default, this profile will use any algorithm defined in {@link IpSecAlgorithm},
* with the exception of those considered insecure (as described above).
*
@@ -1079,6 +1057,7 @@
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
public Builder setAllowedAlgorithms(@NonNull List<String> algorithmNames) {
checkNotNull(algorithmNames, MISSING_PARAM_MSG_TMPL, "algorithmNames");
+ checkBuilderSetter(mIkeTunConnParams != null, "algorithmNames");
validateAllowedAlgorithms(algorithmNames);
mAllowedAlgorithms = algorithmNames;
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index 4b35b0d..16ad0b8 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -396,7 +396,8 @@
return true;
case MATCH_CARRIER:
case MATCH_MOBILE:
- return !template.getSubscriberIds().isEmpty();
+ return !template.getSubscriberIds().isEmpty()
+ && template.getMeteredness() == METERED_YES;
case MATCH_WIFI:
if (template.getWifiNetworkKeys().isEmpty()
&& template.getSubscriberIds().isEmpty()) {
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 43ae4fc..9341105 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -176,6 +176,9 @@
public static final int FOREGROUND_THRESHOLD_STATE =
ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+ /** @hide */
+ public static final int TOP_THRESHOLD_STATE = ActivityManager.PROCESS_STATE_BOUND_TOP;
+
/**
* {@link Intent} extra that indicates which {@link NetworkTemplate} rule it
* applies to.
@@ -247,6 +250,20 @@
*/
public static final int ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS = 1 << 4;
/**
+ * Flag to indicate that app is exempt from certain network restrictions because of it being
+ * in the bound top or top procstate.
+ *
+ * @hide
+ */
+ public static final int ALLOWED_REASON_TOP = 1 << 5;
+ /**
+ * Flag to indicate that app is exempt from low power standby restrictions because of it being
+ * allowlisted.
+ *
+ * @hide
+ */
+ public static final int ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST = 1 << 6;
+ /**
* Flag to indicate that app is exempt from certain metered network restrictions because user
* explicitly exempted it.
*
@@ -770,6 +787,14 @@
|| (capability & ActivityManager.PROCESS_CAPABILITY_NETWORK) != 0;
}
+ /** @hide */
+ public static boolean isProcStateAllowedWhileInLowPowerStandby(@Nullable UidState uidState) {
+ if (uidState == null) {
+ return false;
+ }
+ return uidState.procState <= TOP_THRESHOLD_STATE;
+ }
+
/**
* Returns true if {@param procState} is considered foreground and as such will be allowed
* to access network when the device is in data saver mode. Otherwise, false.
@@ -792,11 +817,13 @@
public static final class UidState {
public int uid;
public int procState;
+ public long procStateSeq;
public int capability;
- public UidState(int uid, int procState, int capability) {
+ public UidState(int uid, int procState, long procStateSeq, int capability) {
this.uid = uid;
this.procState = procState;
+ this.procStateSeq = procStateSeq;
this.capability = capability;
}
@@ -805,6 +832,8 @@
final StringBuilder sb = new StringBuilder();
sb.append("{procState=");
sb.append(procStateToString(procState));
+ sb.append(",seq=");
+ sb.append(procStateSeq);
sb.append(",cap=");
ActivityManager.printCapabilitiesSummary(sb, capability);
sb.append("}");
diff --git a/core/java/android/net/TEST_MAPPING b/core/java/android/net/TEST_MAPPING
index a379c33..3df5616 100644
--- a/core/java/android/net/TEST_MAPPING
+++ b/core/java/android/net/TEST_MAPPING
@@ -17,7 +17,7 @@
"path": "frameworks/opt/net/wifi"
}
],
- "postsubmit": [
+ "presubmit": [
{
"name": "FrameworksCoreTests",
"options": [
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index ae7d91f..37eb74a 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -187,14 +187,24 @@
/**
* The network that was underlying the VPN when the event occurred, as a {@link Network}.
*
- * This extra will be null if there was no underlying network at the time of the event.
+ * <p>This extra will be null if there was no underlying network at the time of the event, or
+ * the underlying network has no bearing on the event, as in the case of:
+ * <ul>
+ * <li>CATEGORY_EVENT_DEACTIVATED_BY_USER
+ * <li>CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED
+ * </ul>
*/
public static final String EXTRA_UNDERLYING_NETWORK = "android.net.extra.UNDERLYING_NETWORK";
/**
* The {@link NetworkCapabilities} of the underlying network when the event occurred.
*
- * This extra will be null if there was no underlying network at the time of the event.
+ * <p>This extra will be null if there was no underlying network at the time of the event, or
+ * the underlying network has no bearing on the event, as in the case of:
+ * <ul>
+ * <li>CATEGORY_EVENT_DEACTIVATED_BY_USER
+ * <li>CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED
+ * </ul>
*/
public static final String EXTRA_UNDERLYING_NETWORK_CAPABILITIES =
"android.net.extra.UNDERLYING_NETWORK_CAPABILITIES";
@@ -202,7 +212,12 @@
/**
* The {@link LinkProperties} of the underlying network when the event occurred.
*
- * This extra will be null if there was no underlying network at the time of the event.
+ * <p>This extra will be null if there was no underlying network at the time of the event, or
+ * the underlying network has no bearing on the event, as in the case of:
+ * <ul>
+ * <li>CATEGORY_EVENT_DEACTIVATED_BY_USER
+ * <li>CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED
+ * </ul>
*/
public static final String EXTRA_UNDERLYING_LINK_PROPERTIES =
"android.net.extra.UNDERLYING_LINK_PROPERTIES";
diff --git a/core/java/android/net/VpnProfileState.java b/core/java/android/net/VpnProfileState.java
index c69ea1a..552a2c1 100644
--- a/core/java/android/net/VpnProfileState.java
+++ b/core/java/android/net/VpnProfileState.java
@@ -24,6 +24,8 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.StringJoiner;
/**
* Describe the state of VPN.
@@ -150,4 +152,44 @@
mAlwaysOn = in.readBoolean();
mLockdown = in.readBoolean();
}
+
+ private String convertStateToString(@State int state) {
+ switch (state) {
+ case STATE_CONNECTED:
+ return "CONNECTED";
+ case STATE_CONNECTING:
+ return "CONNECTING";
+ case STATE_DISCONNECTED:
+ return "DISCONNECTED";
+ case STATE_FAILED:
+ return "FAILED";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ @Override
+ public String toString() {
+ final StringJoiner resultJoiner = new StringJoiner(", ", "{", "}");
+ resultJoiner.add("State: " + convertStateToString(getState()));
+ resultJoiner.add("SessionId: " + getSessionId());
+ resultJoiner.add("Always-on: " + isAlwaysOn());
+ resultJoiner.add("Lockdown: " + isLockdownEnabled());
+ return resultJoiner.toString();
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (!(obj instanceof VpnProfileState)) return false;
+ final VpnProfileState that = (VpnProfileState) obj;
+ return (getState() == that.getState()
+ && Objects.equals(getSessionId(), that.getSessionId())
+ && isAlwaysOn() == that.isAlwaysOn()
+ && isLockdownEnabled() == that.isLockdownEnabled());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getState(), getSessionId(), isAlwaysOn(), isLockdownEnabled());
+ }
}
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
index 69e6313..2d1a3fe 100644
--- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java
@@ -57,9 +57,11 @@
@NonNull private final Set<Integer> mAllowedSpecificCarrierIds;
private static final String ROAMING_MATCH_KEY = "mRoamingMatchCriteria";
+ private static final int DEFAULT_ROAMING_MATCH_CRITERIA = MATCH_ANY;
private final int mRoamingMatchCriteria;
private static final String OPPORTUNISTIC_MATCH_KEY = "mOpportunisticMatchCriteria";
+ private static final int DEFAULT_OPPORTUNISTIC_MATCH_CRITERIA = MATCH_ANY;
private final int mOpportunisticMatchCriteria;
private VcnCellUnderlyingNetworkTemplate(
@@ -253,23 +255,31 @@
/** @hide */
@Override
void dumpTransportSpecificFields(IndentingPrintWriter pw) {
- pw.println("mAllowedNetworkPlmnIds: " + mAllowedNetworkPlmnIds.toString());
- pw.println("mAllowedSpecificCarrierIds: " + mAllowedSpecificCarrierIds.toString());
- pw.println("mRoamingMatchCriteria: " + getMatchCriteriaString(mRoamingMatchCriteria));
- pw.println(
- "mOpportunisticMatchCriteria: "
- + getMatchCriteriaString(mOpportunisticMatchCriteria));
+ if (!mAllowedNetworkPlmnIds.isEmpty()) {
+ pw.println("mAllowedNetworkPlmnIds: " + mAllowedNetworkPlmnIds);
+ }
+ if (!mAllowedNetworkPlmnIds.isEmpty()) {
+ pw.println("mAllowedSpecificCarrierIds: " + mAllowedSpecificCarrierIds);
+ }
+ if (mRoamingMatchCriteria != DEFAULT_ROAMING_MATCH_CRITERIA) {
+ pw.println("mRoamingMatchCriteria: " + getMatchCriteriaString(mRoamingMatchCriteria));
+ }
+ if (mOpportunisticMatchCriteria != DEFAULT_OPPORTUNISTIC_MATCH_CRITERIA) {
+ pw.println(
+ "mOpportunisticMatchCriteria: "
+ + getMatchCriteriaString(mOpportunisticMatchCriteria));
+ }
}
/** This class is used to incrementally build VcnCellUnderlyingNetworkTemplate objects. */
public static final class Builder {
- private int mMeteredMatchCriteria = MATCH_ANY;
+ private int mMeteredMatchCriteria = DEFAULT_METERED_MATCH_CRITERIA;
@NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>();
@NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>();
- private int mRoamingMatchCriteria = MATCH_ANY;
- private int mOpportunisticMatchCriteria = MATCH_ANY;
+ private int mRoamingMatchCriteria = DEFAULT_ROAMING_MATCH_CRITERIA;
+ private int mOpportunisticMatchCriteria = DEFAULT_OPPORTUNISTIC_MATCH_CRITERIA;
private int mMinEntryUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
private int mMinExitUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS;
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 390c3b9..40e4083 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -104,6 +104,14 @@
// TODO: Add separate signal strength thresholds for 2.4 GHz and 5GHz
+ /** List of Carrier Config options to extract from Carrier Config bundles. @hide */
+ @NonNull
+ public static final String[] VCN_RELATED_CARRIER_CONFIG_KEYS =
+ new String[] {
+ VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
+ VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY
+ };
+
private static final Map<
VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder>
REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>();
@@ -172,11 +180,11 @@
*
* <p>An app that has carrier privileges for any of the subscriptions in the given group may
* clear a VCN configuration. This API is ONLY permitted for callers running as the primary
- * user. Any active VCN will be torn down.
+ * user. Any active VCN associated with this configuration will be torn down.
*
* @param subscriptionGroup the subscription group that the configuration should be applied to
- * @throws SecurityException if the caller does not have carrier privileges, or is not running
- * as the primary user
+ * @throws SecurityException if the caller does not have carrier privileges, is not the owner of
+ * the associated configuration, or is not running as the primary user
* @throws IOException if the configuration failed to be cleared from disk. This may occur due
* to temporary disk errors, or more permanent conditions such as a full disk.
*/
@@ -196,8 +204,13 @@
/**
* Retrieves the list of Subscription Groups for which a VCN Configuration has been set.
*
- * <p>The returned list will include only subscription groups for which the carrier app is
- * privileged, and which have an associated {@link VcnConfig}.
+ * <p>The returned list will include only subscription groups for which an associated {@link
+ * VcnConfig} exists, and the app is either:
+ *
+ * <ul>
+ * <li>Carrier privileged for that subscription group, or
+ * <li>Is the provisioning package of the config
+ * </ul>
*
* @throws SecurityException if the caller is not running as the primary user
*/
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
index 3a9ca3e..9235d09 100644
--- a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java
@@ -15,8 +15,6 @@
*/
package android.net.vcn;
-import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY;
-
import static com.android.internal.annotations.VisibleForTesting.Visibility;
import android.annotation.IntDef;
@@ -88,6 +86,9 @@
/** @hide */
static final String METERED_MATCH_KEY = "mMeteredMatchCriteria";
+ /** @hide */
+ static final int DEFAULT_METERED_MATCH_CRITERIA = MATCH_ANY;
+
private final int mMeteredMatchCriteria;
/** @hide */
@@ -237,11 +238,21 @@
pw.println(this.getClass().getSimpleName() + ":");
pw.increaseIndent();
- pw.println("mMeteredMatchCriteria: " + getMatchCriteriaString(mMeteredMatchCriteria));
- pw.println("mMinEntryUpstreamBandwidthKbps: " + mMinEntryUpstreamBandwidthKbps);
- pw.println("mMinExitUpstreamBandwidthKbps: " + mMinExitUpstreamBandwidthKbps);
- pw.println("mMinEntryDownstreamBandwidthKbps: " + mMinEntryDownstreamBandwidthKbps);
- pw.println("mMinExitDownstreamBandwidthKbps: " + mMinExitDownstreamBandwidthKbps);
+ if (mMeteredMatchCriteria != DEFAULT_METERED_MATCH_CRITERIA) {
+ pw.println("mMeteredMatchCriteria: " + getMatchCriteriaString(mMeteredMatchCriteria));
+ }
+ if (mMinEntryUpstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) {
+ pw.println("mMinEntryUpstreamBandwidthKbps: " + mMinEntryUpstreamBandwidthKbps);
+ }
+ if (mMinExitUpstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) {
+ pw.println("mMinExitUpstreamBandwidthKbps: " + mMinExitUpstreamBandwidthKbps);
+ }
+ if (mMinEntryDownstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) {
+ pw.println("mMinEntryDownstreamBandwidthKbps: " + mMinEntryDownstreamBandwidthKbps);
+ }
+ if (mMinExitDownstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) {
+ pw.println("mMinExitDownstreamBandwidthKbps: " + mMinExitDownstreamBandwidthKbps);
+ }
dumpTransportSpecificFields(pw);
pw.decreaseIndent();
diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
index 23a07ab..2544a6d 100644
--- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
+++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java
@@ -147,7 +147,9 @@
/** @hide */
@Override
void dumpTransportSpecificFields(IndentingPrintWriter pw) {
- pw.println("mSsids: " + mSsids);
+ if (!mSsids.isEmpty()) {
+ pw.println("mSsids: " + mSsids);
+ }
}
/**
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 45cc324d..141b141 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -406,7 +406,7 @@
public static final String CODENAME = getString("ro.build.version.codename");
/**
- * All known codenames starting from {@link VERSION_CODES.Q}.
+ * All known codenames that are present in {@link VERSION_CODES}.
*
* <p>This includes in development codenames as well, i.e. if {@link #CODENAME} is not "REL"
* then the value of that is present in this set.
@@ -1167,6 +1167,11 @@
* Tiramisu.
*/
public static final int TIRAMISU = CUR_DEVELOPMENT;
+
+ /**
+ * Upside Down Cake.
+ */
+ public static final int UPSIDE_DOWN_CAKE = CUR_DEVELOPMENT;
}
/** The type of build, like "user" or "eng". */
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 8e5ed8f..1f3108a 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -66,3 +66,6 @@
# VINTF
per-file Vintf* = file:/platform/system/libvintf:/OWNERS
+
+# Tracing
+per-file Trace.java = file:/TRACE_OWNERS
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 9b03172..08ab73b 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -2163,7 +2163,7 @@
* sized with the exact size of dimensions.
*
* @see #readFixedArray
- * @see #createFixedArray
+ * @see #createFixedArray createFixedArray(Class<T>, Parcelable.Creator<S>, int...)
* @see #writeBooleanArray
* @see #writeByteArray
* @see #writeCharArray
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index 82d4443..aa283a2 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -226,9 +226,10 @@
*/
@UnsupportedAppUsage
public static void set(@NonNull String key, @Nullable String val) {
- if (val != null && !key.startsWith("ro.") && val.length() > PROP_VALUE_MAX) {
+ if (val != null && !key.startsWith("ro.") && val.getBytes(StandardCharsets.UTF_8).length
+ > PROP_VALUE_MAX) {
throw new IllegalArgumentException("value of system property '" + key
- + "' is longer than " + PROP_VALUE_MAX + " characters: " + val);
+ + "' is longer than " + PROP_VALUE_MAX + " bytes: " + val);
}
if (TRACK_KEY_ACCESS) onKeyAccess(key);
native_set(key, val);
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 6b869f1..231c22b 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -136,6 +136,12 @@
@FastNative
private static native void nativeAsyncTraceEnd(long tag, String name, int cookie);
@FastNative
+ private static native void nativeAsyncTraceForTrackBegin(long tag,
+ String trackName, String name, int cookie);
+ @FastNative
+ private static native void nativeAsyncTraceForTrackEnd(long tag,
+ String trackName, String name, int cookie);
+ @FastNative
private static native void nativeInstant(long tag, String name);
@FastNative
private static native void nativeInstantForTrack(long tag, String trackName, String name);
@@ -271,6 +277,47 @@
}
}
+
+ /**
+ * Writes a trace message to indicate that a given section of code has
+ * begun. Must be followed by a call to {@link #asyncTraceForTrackEnd} using the same
+ * tag. This function operates exactly like {@link #asyncTraceBegin(long, String, int)},
+ * except with the inclusion of a track name argument for where this method should appear.
+ *
+ * @param traceTag The trace tag.
+ * @param trackName The track where the event should appear in the trace.
+ * @param methodName The method name to appear in the trace.
+ * @param cookie Unique identifier for distinguishing simultaneous events
+ *
+ * @hide
+ */
+ public static void asyncTraceForTrackBegin(long traceTag,
+ @NonNull String trackName, @NonNull String methodName, int cookie) {
+ if (isTagEnabled(traceTag)) {
+ nativeAsyncTraceForTrackBegin(traceTag, trackName, methodName, cookie);
+ }
+ }
+
+ /**
+ * Writes a trace message to indicate that the current method has ended.
+ * Must be called exactly once for each call to
+ * {@link #asyncTraceForTrackBegin(long, String, String, int)}
+ * using the same tag, track name, name and cookie.
+ *
+ * @param traceTag The trace tag.
+ * @param trackName The track where the event should appear in the trace.
+ * @param methodName The method name to appear in the trace.
+ * @param cookie Unique identifier for distinguishing simultaneous events
+ *
+ * @hide
+ */
+ public static void asyncTraceForTrackEnd(long traceTag,
+ @NonNull String trackName, @NonNull String methodName, int cookie) {
+ if (isTagEnabled(traceTag)) {
+ nativeAsyncTraceForTrackEnd(traceTag, trackName, methodName, cookie);
+ }
+ }
+
/**
* Writes a trace message to indicate that a given section of code was invoked.
*
diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java
index e8e4785..9610b16 100644
--- a/core/java/android/os/image/DynamicSystemManager.java
+++ b/core/java/android/os/image/DynamicSystemManager.java
@@ -16,13 +16,16 @@
package android.os.image;
+import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
import android.gsi.AvbPublicKey;
import android.gsi.GsiProgress;
+import android.gsi.IGsiService;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.util.Pair;
/**
* The DynamicSystemManager offers a mechanism to use a new system image temporarily. After the
@@ -138,17 +141,18 @@
* @param name The DSU partition name
* @param size Size of the DSU image in bytes
* @param readOnly True if the partition is read only, e.g. system.
- * @return {@code true} if the call succeeds. {@code false} either the device does not contain
- * enough space or a DynamicSystem is currently in use where the {@link #isInUse} would be
- * true.
+ * @return {@code Integer} an IGsiService.INSTALL_* status code. {@link Session} an installation
+ * session object if successful, otherwise {@code null}.
*/
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
- public Session createPartition(String name, long size, boolean readOnly) {
+ public @NonNull Pair<Integer, Session> createPartition(
+ String name, long size, boolean readOnly) {
try {
- if (mService.createPartition(name, size, readOnly)) {
- return new Session();
+ int status = mService.createPartition(name, size, readOnly);
+ if (status == IGsiService.INSTALL_OK) {
+ return new Pair<>(status, new Session());
} else {
- return null;
+ return new Pair<>(status, null);
}
} catch (RemoteException e) {
throw new RuntimeException(e.toString());
diff --git a/core/java/android/os/image/IDynamicSystemService.aidl b/core/java/android/os/image/IDynamicSystemService.aidl
index 4e69952..755368a 100644
--- a/core/java/android/os/image/IDynamicSystemService.aidl
+++ b/core/java/android/os/image/IDynamicSystemService.aidl
@@ -35,10 +35,10 @@
* @param name The DSU partition name
* @param size Size of the DSU image in bytes
* @param readOnly True if this partition is readOnly
- * @return true if the call succeeds
+ * @return IGsiService.INSTALL_* status code
*/
@EnforcePermission("MANAGE_DYNAMIC_SYSTEM")
- boolean createPartition(@utf8InCpp String name, long size, boolean readOnly);
+ int createPartition(@utf8InCpp String name, long size, boolean readOnly);
/**
* Complete the current partition installation.
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 134cfa1..e1542a8 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1578,18 +1578,13 @@
}
/** {@hide}
- * Is this device encryptable or already encrypted?
- * @return true for encryptable or encrypted
- * false not encrypted and not encryptable
- */
- public static boolean isEncryptable() {
- return RoSystemProperties.CRYPTO_ENCRYPTABLE;
- }
-
- /** {@hide}
- * Is this device already encrypted?
- * @return true for encrypted. (Implies isEncryptable() == true)
- * false not encrypted
+ * Is this device encrypted?
+ * <p>
+ * Note: all devices launching with Android 10 (API level 29) or later are
+ * required to be encrypted. This should only ever return false for
+ * in-development devices on which encryption has not yet been configured.
+ *
+ * @return true if encrypted, false if not encrypted
*/
public static boolean isEncrypted() {
return RoSystemProperties.CRYPTO_ENCRYPTED;
@@ -1598,7 +1593,7 @@
/** {@hide}
* Is this device file encrypted?
* @return true for file encrypted. (Implies isEncrypted() == true)
- * false not encrypted or block encrypted
+ * false not encrypted or using "managed" encryption
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public static boolean isFileEncryptedNativeOnly() {
@@ -1608,54 +1603,6 @@
return RoSystemProperties.CRYPTO_FILE_ENCRYPTED;
}
- /** {@hide}
- * Is this device block encrypted?
- * @return true for block encrypted. (Implies isEncrypted() == true)
- * false not encrypted or file encrypted
- */
- public static boolean isBlockEncrypted() {
- return false;
- }
-
- /** {@hide}
- * Is this device block encrypted with credentials?
- * @return true for crediential block encrypted.
- * (Implies isBlockEncrypted() == true)
- * false not encrypted, file encrypted or default block encrypted
- */
- public static boolean isNonDefaultBlockEncrypted() {
- return false;
- }
-
- /** {@hide}
- * Is this device in the process of being block encrypted?
- * @return true for encrypting.
- * false otherwise
- * Whether device isEncrypted at this point is undefined
- * Note that only system services and CryptKeeper will ever see this return
- * true - no app will ever be launched in this state.
- * Also note that this state will not change without a teardown of the
- * framework, so no service needs to check for changes during their lifespan
- */
- public static boolean isBlockEncrypting() {
- return false;
- }
-
- /** {@hide}
- * Is this device non default block encrypted and in the process of
- * prompting for credentials?
- * @return true for prompting for credentials.
- * (Implies isNonDefaultBlockEncrypted() == true)
- * false otherwise
- * Note that only system services and CryptKeeper will ever see this return
- * true - no app will ever be launched in this state.
- * Also note that this state will not change without a teardown of the
- * framework, so no service needs to check for changes during their lifespan
- */
- public static boolean inCryptKeeperBounce() {
- return false;
- }
-
/** {@hide} */
public static boolean isFileEncryptedEmulatedOnly() {
return SystemProperties.getBoolean(StorageManager.PROP_EMULATE_FBE, false);
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index d61f408..71b5354 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -822,7 +822,7 @@
}
/**
- * Create a new property with the the provided name and value in the provided namespace, or
+ * Create a new property with the provided name and value in the provided namespace, or
* update the value of such a property if it already exists. The same name can exist in multiple
* namespaces and might have different values in any or all namespaces.
* <p>
@@ -836,7 +836,8 @@
* @param name The name of the property to create or update.
* @param value The value to store for the property.
* @param makeDefault Whether to make the new value the default one.
- * @return True if the value was set, false if the storage implementation throws errors.
+ * @return {@code true} if the value was set, {@code false} if the storage implementation throws
+ * errors.
* @hide
* @see #resetToDefaults(int, String).
*/
@@ -860,7 +861,7 @@
*
* @param properties the complete set of properties to set for a specific namespace.
* @throws BadConfigException if the provided properties are banned by RescueParty.
- * @return True if the values were set, false otherwise.
+ * @return {@code true} if the values were set, {@code false} otherwise.
* @hide
*/
@SystemApi
@@ -872,6 +873,22 @@
}
/**
+ * Delete a property with the provided name and value in the provided namespace
+ *
+ * @param namespace The namespace containing the property to delete.
+ * @param name The name of the property to delete.
+ * @return {@code true} if the property was deleted or it did not exist in the first place.
+ * Return {@code false} if the storage implementation throws errors.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(WRITE_DEVICE_CONFIG)
+ public static boolean deleteProperty(@NonNull String namespace, @NonNull String name) {
+ ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver();
+ return Settings.Config.deleteString(contentResolver, namespace, name);
+ }
+
+ /**
* Reset properties to their default values by removing the underlying values.
* <p>
* The method accepts an optional namespace parameter. If provided, only properties set within
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4372f19..b7fcc03 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2829,6 +2829,7 @@
// for the fast path of retrieving settings.
private final String mCallGetCommand;
private final String mCallSetCommand;
+ private final String mCallDeleteCommand;
private final String mCallListCommand;
private final String mCallSetAllCommand;
@@ -2840,17 +2841,19 @@
private GenerationTracker mGenerationTracker;
<T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
- String setCommand, ContentProviderHolder providerHolder, Class<T> callerClass) {
- this(uri, getCommand, setCommand, null, null, providerHolder,
+ String setCommand, String deleteCommand, ContentProviderHolder providerHolder,
+ Class<T> callerClass) {
+ this(uri, getCommand, setCommand, deleteCommand, null, null, providerHolder,
callerClass);
}
private <T extends NameValueTable> NameValueCache(Uri uri, String getCommand,
- String setCommand, String listCommand, String setAllCommand,
+ String setCommand, String deleteCommand, String listCommand, String setAllCommand,
ContentProviderHolder providerHolder, Class<T> callerClass) {
mUri = uri;
mCallGetCommand = getCommand;
mCallSetCommand = setCommand;
+ mCallDeleteCommand = deleteCommand;
mCallListCommand = listCommand;
mCallSetAllCommand = setAllCommand;
mProviderHolder = providerHolder;
@@ -2908,6 +2911,20 @@
}
}
+ public boolean deleteStringForUser(ContentResolver cr, String name, final int userHandle) {
+ try {
+ Bundle arg = new Bundle();
+ arg.putInt(CALL_METHOD_USER_KEY, userHandle);
+ IContentProvider cp = mProviderHolder.getProvider(cr);
+ cp.call(cr.getAttributionSource(),
+ mProviderHolder.mUri.getAuthority(), mCallDeleteCommand, name, arg);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Can't delete key " + name + " in " + mUri, e);
+ return false;
+ }
+ return true;
+ }
+
@UnsupportedAppUsage
public String getStringForUser(ContentResolver cr, String name, final int userHandle) {
// Check if the target settings key is readable. Reject if the caller is not system and
@@ -3370,6 +3387,7 @@
CONTENT_URI,
CALL_METHOD_GET_SYSTEM,
CALL_METHOD_PUT_SYSTEM,
+ CALL_METHOD_DELETE_SYSTEM,
sProviderHolder,
System.class);
@@ -5690,6 +5708,7 @@
CONTENT_URI,
CALL_METHOD_GET_SECURE,
CALL_METHOD_PUT_SECURE,
+ CALL_METHOD_DELETE_SECURE,
sProviderHolder,
Secure.class);
@@ -15159,6 +15178,7 @@
CONTENT_URI,
CALL_METHOD_GET_GLOBAL,
CALL_METHOD_PUT_GLOBAL,
+ CALL_METHOD_DELETE_GLOBAL,
sProviderHolder,
Global.class);
@@ -16391,6 +16411,7 @@
DeviceConfig.CONTENT_URI,
CALL_METHOD_GET_CONFIG,
CALL_METHOD_PUT_CONFIG,
+ CALL_METHOD_DELETE_CONFIG,
CALL_METHOD_LIST_CONFIG,
CALL_METHOD_SET_ALL_CONFIG,
sProviderHolder,
@@ -16500,6 +16521,26 @@
}
/**
+ * Delete a name/value pair from the database for the specified namespace.
+ *
+ * @param resolver to access the database with.
+ * @param namespace to delete the name/value pair from.
+ * @param name to delete.
+ * @return true if the value was deleted, false on database errors. If the name/value pair
+ * did not exist, return True.
+ *
+ * @see #resetToDefaults(ContentResolver, int, String)
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.WRITE_DEVICE_CONFIG)
+ static boolean deleteString(@NonNull ContentResolver resolver, @NonNull String namespace,
+ @NonNull String name) {
+ return sNameValueCache.deleteStringForUser(resolver,
+ createCompositeName(namespace, name), resolver.getUserId());
+ }
+
+ /**
* Reset the values to their defaults.
* <p>
* The method accepts an optional prefix parameter. If provided, only pairs with a name that
diff --git a/core/java/android/service/carrier/OWNERS b/core/java/android/service/carrier/OWNERS
index d768ef4..447cd51 100644
--- a/core/java/android/service/carrier/OWNERS
+++ b/core/java/android/service/carrier/OWNERS
@@ -1,5 +1,3 @@
-# Bug component: 20868
+set noparent
-rgreenwalt@google.com
-tgunn@google.com
-fionaxu@google.com
+file:platform/frameworks/base:/telephony/OWNERS
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index d0fd2b3..509f4d9 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -350,6 +350,53 @@
return ret;
}
+
+ /**
+ * Returns the longest prefix of a string for which the UTF-8 encoding fits into the given
+ * number of bytes, with the additional guarantee that the string is not truncated in the middle
+ * of a valid surrogate pair.
+ *
+ * <p>Unpaired surrogates are counted as taking 3 bytes of storage. However, a subsequent
+ * attempt to actually encode a string containing unpaired surrogates is likely to be rejected
+ * by the UTF-8 implementation.
+ *
+ * (copied from google/thirdparty)
+ *
+ * @param str a string
+ * @param maxbytes the maximum number of UTF-8 encoded bytes
+ * @return the beginning of the string, so that it uses at most maxbytes bytes in UTF-8
+ * @throws IndexOutOfBoundsException if maxbytes is negative
+ *
+ * @hide
+ */
+ public static String truncateStringForUtf8Storage(String str, int maxbytes) {
+ if (maxbytes < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ int bytes = 0;
+ for (int i = 0, len = str.length(); i < len; i++) {
+ char c = str.charAt(i);
+ if (c < 0x80) {
+ bytes += 1;
+ } else if (c < 0x800) {
+ bytes += 2;
+ } else if (c < Character.MIN_SURROGATE
+ || c > Character.MAX_SURROGATE
+ || str.codePointAt(i) < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
+ bytes += 3;
+ } else {
+ bytes += 4;
+ i += (bytes > maxbytes) ? 0 : 1;
+ }
+ if (bytes > maxbytes) {
+ return str.substring(0, i);
+ }
+ }
+ return str;
+ }
+
+
/**
* Returns a string containing the tokens joined by delimiters.
*
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index 01a037a..4e7b3a5 100644
--- a/core/java/android/util/NtpTrustedTime.java
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -193,6 +193,16 @@
}
final Network network = connectivityManager.getActiveNetwork();
final NetworkInfo ni = connectivityManager.getNetworkInfo(network);
+
+ // This connectivity check is to avoid performing a DNS lookup for the time server on a
+ // unconnected network. There are races to obtain time in Android when connectivity
+ // changes, which means that forceRefresh() can be called by various components before
+ // the network is actually available. This led in the past to DNS lookup failures being
+ // cached (~2 seconds) thereby preventing the device successfully making an NTP request
+ // when connectivity had actually been established.
+ // A side effect of check is that tests that run a fake NTP server on the device itself
+ // will only be able to use it if the active network is connected, even though loopback
+ // addresses are actually reachable.
if (ni == null || !ni.isConnected()) {
if (LOGD) Log.d(TAG, "forceRefresh: no connectivity");
return false;
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 75b69cb..d15d7c7 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -391,6 +391,17 @@
processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap,
insets, Type.SYSTEM_GESTURES);
}
+ if (type == Type.CAPTION_BAR) {
+ // Caption should also be gesture and tappable elements. This should not be needed when
+ // the caption is added from the shell, as the shell can add other types at the same
+ // time.
+ processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap,
+ insets, Type.SYSTEM_GESTURES);
+ processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap,
+ insets, Type.MANDATORY_SYSTEM_GESTURES);
+ processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap,
+ insets, Type.TAPPABLE_ELEMENT);
+ }
}
private void processSourceAsPublicType(InsetsSource source, Insets[] typeInsetsMap,
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 80ffd40..c33c2ab 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -2263,8 +2263,11 @@
}
/**
- * {@link #getX(int)} for the first pointer index (may be an
- * arbitrary pointer identifier).
+ * Equivalent to {@link #getX(int)} for pointer index 0 (regardless of the
+ * pointer identifier).
+ *
+ * @return The X coordinate of the first pointer index in the coordinate
+ * space of the view that received this motion event.
*
* @see #AXIS_X
*/
@@ -2273,8 +2276,11 @@
}
/**
- * {@link #getY(int)} for the first pointer index (may be an
- * arbitrary pointer identifier).
+ * Equivalent to {@link #getY(int)} for pointer index 0 (regardless of the
+ * pointer identifier).
+ *
+ * @return The Y coordinate of the first pointer index in the coordinate
+ * space of the view that received this motion event.
*
* @see #AXIS_Y
*/
@@ -2416,13 +2422,20 @@
}
/**
- * Returns the X coordinate of this event for the given pointer
- * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
- * identifier for this index).
- * Whole numbers are pixels; the
- * value may have a fraction for input devices that are sub-pixel precise.
- * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
- * (the first pointer that is down) to {@link #getPointerCount()}-1.
+ * Returns the X coordinate of the pointer referenced by
+ * {@code pointerIndex} for this motion event. The coordinate is in the
+ * coordinate space of the view that received this motion event.
+ *
+ * <p>Use {@link #getPointerId(int)} to get the pointer identifier for the
+ * pointer referenced by {@code pointerIndex}.
+ *
+ * @param pointerIndex Index of the pointer for which the X coordinate is
+ * returned. May be a value in the range of 0 (the first pointer that
+ * is down) to {@link #getPointerCount()} - 1.
+ * @return The X coordinate of the pointer referenced by
+ * {@code pointerIndex} for this motion event. The unit is pixels. The
+ * value may contain a fractional portion for devices that are subpixel
+ * precise.
*
* @see #AXIS_X
*/
@@ -2431,13 +2444,20 @@
}
/**
- * Returns the Y coordinate of this event for the given pointer
- * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
- * identifier for this index).
- * Whole numbers are pixels; the
- * value may have a fraction for input devices that are sub-pixel precise.
- * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
- * (the first pointer that is down) to {@link #getPointerCount()}-1.
+ * Returns the Y coordinate of the pointer referenced by
+ * {@code pointerIndex} for this motion event. The coordinate is in the
+ * coordinate space of the view that received this motion event.
+ *
+ * <p>Use {@link #getPointerId(int)} to get the pointer identifier for the
+ * pointer referenced by {@code pointerIndex}.
+ *
+ * @param pointerIndex Index of the pointer for which the Y coordinate is
+ * returned. May be a value in the range of 0 (the first pointer that
+ * is down) to {@link #getPointerCount()} - 1.
+ * @return The Y coordinate of the pointer referenced by
+ * {@code pointerIndex} for this motion event. The unit is pixels. The
+ * value may contain a fractional portion for devices that are subpixel
+ * precise.
*
* @see #AXIS_Y
*/
@@ -2683,12 +2703,13 @@
}
/**
- * Returns the original raw X coordinate of this event. For touch
- * events on the screen, this is the original location of the event
- * on the screen, before it had been adjusted for the containing window
- * and views.
+ * Equivalent to {@link #getRawX(int)} for pointer index 0 (regardless of
+ * the pointer identifier).
*
- * @see #getX(int)
+ * @return The X coordinate of the first pointer index in the coordinate
+ * space of the device display.
+ *
+ * @see #getX()
* @see #AXIS_X
*/
public final float getRawX() {
@@ -2696,12 +2717,13 @@
}
/**
- * Returns the original raw Y coordinate of this event. For touch
- * events on the screen, this is the original location of the event
- * on the screen, before it had been adjusted for the containing window
- * and views.
+ * Equivalent to {@link #getRawY(int)} for pointer index 0 (regardless of
+ * the pointer identifier).
*
- * @see #getY(int)
+ * @return The Y coordinate of the first pointer index in the coordinate
+ * space of the device display.
+ *
+ * @see #getY()
* @see #AXIS_Y
*/
public final float getRawY() {
@@ -2709,13 +2731,38 @@
}
/**
- * Returns the original raw X coordinate of this event. For touch
- * events on the screen, this is the original location of the event
- * on the screen, before it had been adjusted for the containing window
- * and views.
+ * Returns the X coordinate of the pointer referenced by
+ * {@code pointerIndex} for this motion event. The coordinate is in the
+ * coordinate space of the device display, irrespective of system
+ * decorations and whether or not the system is in multi-window mode. If the
+ * app spans multiple screens in a multiple-screen environment, the
+ * coordinate space includes all of the spanned screens.
*
- * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
- * (the first pointer that is down) to {@link #getPointerCount()}-1.
+ * <p>In multi-window mode, the coordinate space extends beyond the bounds
+ * of the app window to encompass the entire display area. For example, if
+ * the motion event occurs in the right-hand window of split-screen mode in
+ * landscape orientation, the left edge of the screen—not the left
+ * edge of the window—is the origin from which the X coordinate is
+ * calculated.
+ *
+ * <p>In multiple-screen scenarios, the coordinate space can span screens.
+ * For example, if the app is spanning both screens of a dual-screen device,
+ * and the motion event occurs on the right-hand screen, the X coordinate is
+ * calculated from the left edge of the left-hand screen to the point of the
+ * motion event on the right-hand screen. When the app is restricted to a
+ * single screen in a multiple-screen environment, the coordinate space
+ * includes only the screen on which the app is running.
+ *
+ * <p>Use {@link #getPointerId(int)} to get the pointer identifier for the
+ * pointer referenced by {@code pointerIndex}.
+ *
+ * @param pointerIndex Index of the pointer for which the X coordinate is
+ * returned. May be a value in the range of 0 (the first pointer that
+ * is down) to {@link #getPointerCount()} - 1.
+ * @return The X coordinate of the pointer referenced by
+ * {@code pointerIndex} for this motion event. The unit is pixels. The
+ * value may contain a fractional portion for devices that are subpixel
+ * precise.
*
* @see #getX(int)
* @see #AXIS_X
@@ -2725,13 +2772,38 @@
}
/**
- * Returns the original raw Y coordinate of this event. For touch
- * events on the screen, this is the original location of the event
- * on the screen, before it had been adjusted for the containing window
- * and views.
+ * Returns the Y coordinate of the pointer referenced by
+ * {@code pointerIndex} for this motion event. The coordinate is in the
+ * coordinate space of the device display, irrespective of system
+ * decorations and whether or not the system is in multi-window mode. If the
+ * app spans multiple screens in a multiple-screen environment, the
+ * coordinate space includes all of the spanned screens.
*
- * @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
- * (the first pointer that is down) to {@link #getPointerCount()}-1.
+ * <p>In multi-window mode, the coordinate space extends beyond the bounds
+ * of the app window to encompass the entire device screen. For example, if
+ * the motion event occurs in the lower window of split-screen mode in
+ * portrait orientation, the top edge of the screen—not the top edge
+ * of the window—is the origin from which the Y coordinate is
+ * determined.
+ *
+ * <p>In multiple-screen scenarios, the coordinate space can span screens.
+ * For example, if the app is spanning both screens of a dual-screen device
+ * that's rotated 90 degrees, and the motion event occurs on the lower
+ * screen, the Y coordinate is calculated from the top edge of the upper
+ * screen to the point of the motion event on the lower screen. When the app
+ * is restricted to a single screen in a multiple-screen environment, the
+ * coordinate space includes only the screen on which the app is running.
+ *
+ * <p>Use {@link #getPointerId(int)} to get the pointer identifier for the
+ * pointer referenced by {@code pointerIndex}.
+ *
+ * @param pointerIndex Index of the pointer for which the Y coordinate is
+ * returned. May be a value in the range of 0 (the first pointer that
+ * is down) to {@link #getPointerCount()} - 1.
+ * @return The Y coordinate of the pointer referenced by
+ * {@code pointerIndex} for this motion event. The unit is pixels. The
+ * value may contain a fractional portion for devices that are subpixel
+ * precise.
*
* @see #getY(int)
* @see #AXIS_Y
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index b166fa6..407648d 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -10,6 +10,7 @@
jjaggi@google.com
roosa@google.com
jreck@google.com
+siyamed@google.com
# Autofill
per-file ViewStructure.java = file:/core/java/android/service/autofill/OWNERS
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f2ddf52..ffa577a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -28283,7 +28283,7 @@
* {@link InputDevice#SOURCE_MOUSE_RELATIVE}, and relative position changes will be
* available through {@link MotionEvent#getX} and {@link MotionEvent#getY}.</li>
*
- * <li>Events from a touchpad will be delivered with the source
+ * <li>Events from a touchpad or trackpad will be delivered with the source
* {@link InputDevice#SOURCE_TOUCHPAD}, where the absolute position of each of the pointers
* on the touchpad will be available through {@link MotionEvent#getX(int)} and
* {@link MotionEvent#getY(int)}, and their relative movements are stored in
@@ -28292,6 +28292,12 @@
* <li>Events from other types of devices, such as touchscreens, will not be affected.</li>
* </ul>
* <p>
+ * When pointer capture changes, connected mouse and trackpad devices may be reconfigured,
+ * and their properties (such as their sources or motion ranges) may change. Use an
+ * {@link android.hardware.input.InputManager.InputDeviceListener} to be notified when a device
+ * changes (which may happen after enabling or disabling pointer capture), and use
+ * {@link InputDevice#getDevice(int)} to get the updated {@link InputDevice}.
+ * <p>
* Events captured through pointer capture will be dispatched to
* {@link OnCapturedPointerListener#onCapturedPointer(View, MotionEvent)} if an
* {@link OnCapturedPointerListener} is set, and otherwise to
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
index 4a20ad0..fc2c8cc 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java
@@ -33,7 +33,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.os.Build;
-import android.os.storage.StorageManager;
import android.provider.Settings;
import android.text.BidiFormatter;
import android.view.LayoutInflater;
@@ -282,19 +281,7 @@
Context.LAYOUT_INFLATER_SERVICE);
final View content = inflater.inflate(
- R.layout.accessibility_enable_service_encryption_warning, /* root= */ null);
-
- final TextView encryptionWarningView = (TextView) content.findViewById(
- R.id.accessibility_encryption_warning);
- if (StorageManager.isNonDefaultBlockEncrypted()) {
- final String text = context.getString(
- R.string.accessibility_enable_service_encryption_warning,
- getServiceName(context, target.getLabel()));
- encryptionWarningView.setText(text);
- encryptionWarningView.setVisibility(View.VISIBLE);
- } else {
- encryptionWarningView.setVisibility(View.GONE);
- }
+ R.layout.accessibility_enable_service_warning, /* root= */ null);
final ImageView dialogIcon = content.findViewById(
R.id.accessibility_permissionDialog_icon);
diff --git a/core/java/com/android/internal/app/HarmfulAppWarningActivity.java b/core/java/com/android/internal/app/HarmfulAppWarningActivity.java
index ce2d229..33209e1 100644
--- a/core/java/com/android/internal/app/HarmfulAppWarningActivity.java
+++ b/core/java/com/android/internal/app/HarmfulAppWarningActivity.java
@@ -16,6 +16,8 @@
package com.android.internal.app;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -27,6 +29,7 @@
import android.util.Log;
import android.view.View;
import android.widget.TextView;
+
import com.android.internal.R;
/**
@@ -48,6 +51,7 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
final Intent intent = getIntent();
mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
mTarget = intent.getParcelableExtra(Intent.EXTRA_INTENT);
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index bd3e898..8797381 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -22,12 +22,17 @@
import android.net.PlatformVpnProfile;
import android.net.ProxyInfo;
import android.net.Uri;
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
+import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtils;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.text.TextUtils;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.HexDump;
import com.android.net.module.util.ProxyUtils;
import java.io.UnsupportedEncodingException;
@@ -69,7 +74,8 @@
public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6;
public static final int TYPE_IKEV2_IPSEC_PSK = 7;
public static final int TYPE_IKEV2_IPSEC_RSA = 8;
- public static final int TYPE_MAX = 8;
+ public static final int TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS = 9;
+ public static final int TYPE_MAX = 9;
// Match these constants with R.array.vpn_proxy_settings.
public static final int PROXY_NONE = 0;
@@ -145,25 +151,27 @@
public final boolean excludeLocalRoutes; // 25
public final boolean requiresInternetValidation; // 26
+ public final IkeTunnelConnectionParams ikeTunConnParams; // 27
// Helper fields.
@UnsupportedAppUsage
public transient boolean saveLogin = false;
public VpnProfile(String key) {
- this(key, false, false, false);
+ this(key, false, false, false, null);
}
public VpnProfile(String key, boolean isRestrictedToTestNetworks) {
- this(key, isRestrictedToTestNetworks, false, false);
+ this(key, isRestrictedToTestNetworks, false, false, null);
}
public VpnProfile(String key, boolean isRestrictedToTestNetworks, boolean excludeLocalRoutes,
- boolean requiresInternetValidation) {
+ boolean requiresInternetValidation, IkeTunnelConnectionParams ikeTunConnParams) {
this.key = key;
this.isRestrictedToTestNetworks = isRestrictedToTestNetworks;
this.excludeLocalRoutes = excludeLocalRoutes;
this.requiresInternetValidation = requiresInternetValidation;
+ this.ikeTunConnParams = ikeTunConnParams;
}
@UnsupportedAppUsage
@@ -195,6 +203,10 @@
isRestrictedToTestNetworks = in.readBoolean();
excludeLocalRoutes = in.readBoolean();
requiresInternetValidation = in.readBoolean();
+ final PersistableBundle bundle =
+ in.readParcelable(PersistableBundle.class.getClassLoader());
+ ikeTunConnParams = (bundle == null) ? null
+ : TunnelConnectionParamsUtils.fromPersistableBundle(bundle);
}
/**
@@ -244,6 +256,8 @@
out.writeBoolean(isRestrictedToTestNetworks);
out.writeBoolean(excludeLocalRoutes);
out.writeBoolean(requiresInternetValidation);
+ out.writeParcelable(ikeTunConnParams == null ? null
+ : TunnelConnectionParamsUtils.toPersistableBundle(ikeTunConnParams), flags);
}
/**
@@ -259,15 +273,17 @@
}
String[] values = new String(value, StandardCharsets.UTF_8).split(VALUE_DELIMITER, -1);
+
// Acceptable numbers of values are:
// 14-19: Standard profile, with option for serverCert, proxy
// 24: Standard profile with serverCert, proxy and platform-VPN parameters
// 25: Standard profile with platform-VPN parameters and isRestrictedToTestNetworks
// 26: ...and excludeLocalRoutes
- // (26 can only be found on dogfood devices)
// 27: ...and requiresInternetValidation
+ // (26,27 can only be found on dogfood devices)
+ // 28: ...and ikeTunConnParams
if ((values.length < 14 || (values.length > 19 && values.length < 24)
- || values.length > 27)) {
+ || values.length > 28)) {
return null;
}
@@ -292,8 +308,22 @@
requiresInternetValidation = false;
}
+ final IkeTunnelConnectionParams tempIkeTunConnParams;
+ // Assign null directly if the ikeTunConParams field is empty.
+ if (values.length >= 28 && values[27].length() != 0) {
+ final Parcel parcel = Parcel.obtain();
+ final byte[] bytes = HexDump.hexStringToByteArray(values[27]);
+ parcel.unmarshall(bytes, 0, bytes.length);
+ parcel.setDataPosition(0);
+ final PersistableBundle bundle = (PersistableBundle) parcel.readValue(
+ PersistableBundle.class.getClassLoader());
+ tempIkeTunConnParams = TunnelConnectionParamsUtils.fromPersistableBundle(bundle);
+ } else {
+ tempIkeTunConnParams = null;
+ }
+
VpnProfile profile = new VpnProfile(key, isRestrictedToTestNetworks,
- excludeLocalRoutes, requiresInternetValidation);
+ excludeLocalRoutes, requiresInternetValidation, tempIkeTunConnParams);
profile.name = values[0];
profile.type = Integer.parseInt(values[1]);
if (profile.type < 0 || profile.type > TYPE_MAX) {
@@ -345,6 +375,7 @@
profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
return profile;
} catch (Exception e) {
+ Log.d(TAG, "Got exception in decode.", e);
// ignore
}
return null;
@@ -406,6 +437,17 @@
builder.append(VALUE_DELIMITER).append(excludeLocalRoutes);
builder.append(VALUE_DELIMITER).append(requiresInternetValidation);
+ if (ikeTunConnParams != null) {
+ final PersistableBundle bundle =
+ TunnelConnectionParamsUtils.toPersistableBundle(ikeTunConnParams);
+ final Parcel parcel = Parcel.obtain();
+ parcel.writeValue(bundle);
+ final byte[] bytes = parcel.marshall();
+ builder.append(VALUE_DELIMITER).append(HexDump.toHexString(bytes));
+ } else {
+ builder.append(VALUE_DELIMITER).append("");
+ }
+
return builder.toString().getBytes(StandardCharsets.UTF_8);
}
@@ -486,7 +528,8 @@
key, type, server, username, password, dnsServers, searchDomains, routes, mppe,
l2tpSecret, ipsecIdentifier, ipsecSecret, ipsecUserCert, ipsecCaCert, ipsecServerCert,
proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline,
- isRestrictedToTestNetworks, excludeLocalRoutes, requiresInternetValidation);
+ isRestrictedToTestNetworks, excludeLocalRoutes, requiresInternetValidation,
+ ikeTunConnParams);
}
/** Checks VPN profiles for interior equality. */
@@ -521,7 +564,8 @@
&& areAuthParamsInline == other.areAuthParamsInline
&& isRestrictedToTestNetworks == other.isRestrictedToTestNetworks
&& excludeLocalRoutes == other.excludeLocalRoutes
- && requiresInternetValidation == other.requiresInternetValidation;
+ && requiresInternetValidation == other.requiresInternetValidation
+ && Objects.equals(ikeTunConnParams, other.ikeTunConnParams);
}
@NonNull
diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java
index 8b659f9..98d81c9 100644
--- a/core/java/com/android/internal/os/RoSystemProperties.java
+++ b/core/java/com/android/internal/os/RoSystemProperties.java
@@ -60,14 +60,10 @@
public static final CryptoProperties.type_values CRYPTO_TYPE =
CryptoProperties.type().orElse(CryptoProperties.type_values.NONE);
// These are pseudo-properties
- public static final boolean CRYPTO_ENCRYPTABLE =
- CRYPTO_STATE != CryptoProperties.state_values.UNSUPPORTED;
public static final boolean CRYPTO_ENCRYPTED =
CRYPTO_STATE == CryptoProperties.state_values.ENCRYPTED;
public static final boolean CRYPTO_FILE_ENCRYPTED =
CRYPTO_TYPE == CryptoProperties.type_values.FILE;
- public static final boolean CRYPTO_BLOCK_ENCRYPTED =
- CRYPTO_TYPE == CryptoProperties.type_values.BLOCK;
public static final boolean CONTROL_PRIVAPP_PERMISSIONS_LOG =
"log".equalsIgnoreCase(CONTROL_PRIVAPP_PERMISSIONS);
diff --git a/core/java/com/android/internal/security/OWNERS b/core/java/com/android/internal/security/OWNERS
index 41d1d66..b702df8 100644
--- a/core/java/com/android/internal/security/OWNERS
+++ b/core/java/com/android/internal/security/OWNERS
@@ -1,3 +1,3 @@
# Bug component: 36824
-per-file VerityUtils.java = victorhsieh@google.com
+per-file VerityUtils.java = file:platform/system/security:/fsverity/OWNERS
diff --git a/core/java/com/android/internal/usb/DumpUtils.java b/core/java/com/android/internal/usb/DumpUtils.java
index 3260136..744fe59 100644
--- a/core/java/com/android/internal/usb/DumpUtils.java
+++ b/core/java/com/android/internal/usb/DumpUtils.java
@@ -244,7 +244,8 @@
writeContaminantPresenceStatus(dump, "contaminant_presence_status",
UsbPortStatusProto.CONTAMINANT_PRESENCE_STATUS,
status.getContaminantDetectionStatus());
-
+ dump.write("usb_data_enabled", UsbPortStatusProto.USB_DATA_ENABLED,
+ status.getUsbDataStatus());
dump.end(token);
}
}
diff --git a/core/java/com/android/internal/usb/OWNERS b/core/java/com/android/internal/usb/OWNERS
new file mode 100644
index 0000000..f7b2a37
--- /dev/null
+++ b/core/java/com/android/internal/usb/OWNERS
@@ -0,0 +1 @@
+include /services/usb/OWNERS
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 8cac2e8c..4072687 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -56,6 +56,7 @@
per-file android_media_midi_* = file:/media/java/android/media/midi/OWNERS
per-file android_opengl_* = file:/opengl/java/android/opengl/OWNERS
per-file android_os_storage_* = file:/core/java/android/os/storage/OWNERS
+per-file android_os_Trace* = file:/TRACE_OWNERS
per-file android_se_* = file:/omapi/java/android/se/OWNERS
per-file android_security_* = file:/core/java/android/security/OWNERS
per-file android_view_* = file:/core/java/android/view/OWNERS
@@ -80,7 +81,7 @@
per-file LayoutlibLoader.cpp = diegoperez@google.com, jgaillard@google.com
# Verity
-per-file com_android_internal_security_Verity* = ebiggers@google.com, victorhsieh@google.com
+per-file com_android_internal_security_Verity* = file:platform/system/security:/fsverity/OWNERS
# VINTF
per-file android_os_VintfObject* = file:platform/system/libvintf:/OWNERS
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index 85fd5d9..734b6ca 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -82,6 +82,26 @@
});
}
+static void android_os_Trace_nativeAsyncTraceForTrackBegin(JNIEnv* env, jclass, jlong tag,
+ jstring trackStr, jstring nameStr,
+ jint cookie) {
+ withString(env, trackStr, [env, tag, nameStr, cookie](char* track) {
+ withString(env, nameStr, [tag, track, cookie](char* name) {
+ atrace_async_for_track_begin(tag, track, name, cookie);
+ });
+ });
+}
+
+static void android_os_Trace_nativeAsyncTraceForTrackEnd(JNIEnv* env, jclass, jlong tag,
+ jstring trackStr, jstring nameStr,
+ jint cookie) {
+ withString(env, trackStr, [env, tag, nameStr, cookie](char* track) {
+ withString(env, nameStr, [tag, track, cookie](char* name) {
+ atrace_async_for_track_end(tag, track, name, cookie);
+ });
+ });
+}
+
static void android_os_Trace_nativeSetAppTracingAllowed(JNIEnv*, jclass, jboolean allowed) {
atrace_update_tags();
}
@@ -132,6 +152,12 @@
{ "nativeAsyncTraceEnd",
"(JLjava/lang/String;I)V",
(void*)android_os_Trace_nativeAsyncTraceEnd },
+ { "nativeAsyncTraceForTrackBegin",
+ "(JLjava/lang/String;Ljava/lang/String;I)V",
+ (void*)android_os_Trace_nativeAsyncTraceForTrackBegin },
+ { "nativeAsyncTraceForTrackEnd",
+ "(JLjava/lang/String;Ljava/lang/String;I)V",
+ (void*)android_os_Trace_nativeAsyncTraceForTrackEnd },
{ "nativeInstant",
"(JLjava/lang/String;)V",
(void*)android_os_Trace_nativeInstant },
diff --git a/core/jni/android_util_Binder.h b/core/jni/android_util_Binder.h
index 9098d46..d73db62 100644
--- a/core/jni/android_util_Binder.h
+++ b/core/jni/android_util_Binder.h
@@ -24,8 +24,18 @@
namespace android {
-// Converstion to/from Java IBinder Object and C++ IBinder instance.
+/**
+ * Conversion to Java IBinder Object from C++ IBinder instance.
+ *
+ * WARNING: this function returns global and local references. This can be
+ * figured out using GetObjectRefType. Though, when this function is called
+ * from within a Java context, the local ref will automatically be cleaned
+ * up. If this is called outside of a Java frame,
+ * PushObjectFrame/PopObjectFrame can simulate this automatic cleanup. The
+ * platform provides ScopedLocalFrame as an RAII object for this.
+ */
extern jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val);
+/** Conversion from Java IBinder Object to C++ IBinder instance. */
extern sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj);
extern jobject newParcelFileDescriptor(JNIEnv* env, jobject fileDesc);
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 4d8d4db..71b51ff 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -991,7 +991,7 @@
end = i;
i++;
} else if ((mode&PROC_QUOTES) != 0) {
- while (buffer[i] != '"' && i < endIndex) {
+ while (i < endIndex && buffer[i] != '"') {
i++;
}
end = i;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 5023927..86e97d5 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -110,6 +110,8 @@
using android::zygote::ZygoteFailure;
+using Action = android_mallopt_gwp_asan_options_t::Action;
+
// This type is duplicated in fd_utils.h
typedef const std::function<void(std::string)>& fail_fn_t;
@@ -1597,10 +1599,11 @@
// since the directory is owned by root.
if (!is_system_server && getuid() == 0) {
const int rc = createProcessGroup(uid, getpid());
- if (rc == -EROFS) {
- ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
- } else if (rc != 0) {
- ALOGE("createProcessGroup(%d, %d) failed: %s", uid, /* pid= */ 0, strerror(-rc));
+ if (rc != 0) {
+ fail_fn(rc == -EROFS ? CREATE_ERROR("createProcessGroup failed, kernel missing "
+ "CONFIG_CGROUP_CPUACCT?")
+ : CREATE_ERROR("createProcessGroup(%d, %d) failed: %s", uid,
+ /* pid= */ 0, strerror(-rc)));
}
}
@@ -1717,16 +1720,24 @@
// runtime.
runtime_flags &= ~RuntimeFlags::NATIVE_HEAP_ZERO_INIT;
- bool forceEnableGwpAsan = false;
+ const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;
+ android_mallopt_gwp_asan_options_t gwp_asan_options;
+ // The system server doesn't have its nice name set by the time SpecializeCommon is called.
+ gwp_asan_options.program_name = nice_name_ptr ?: process_name;
switch (runtime_flags & RuntimeFlags::GWP_ASAN_LEVEL_MASK) {
default:
case RuntimeFlags::GWP_ASAN_LEVEL_NEVER:
+ gwp_asan_options.desire = Action::DONT_TURN_ON_UNLESS_OVERRIDDEN;
+ android_mallopt(M_INITIALIZE_GWP_ASAN, &gwp_asan_options, sizeof(gwp_asan_options));
break;
case RuntimeFlags::GWP_ASAN_LEVEL_ALWAYS:
- forceEnableGwpAsan = true;
- [[fallthrough]];
+ gwp_asan_options.desire = Action::TURN_ON_FOR_APP;
+ android_mallopt(M_INITIALIZE_GWP_ASAN, &gwp_asan_options, sizeof(gwp_asan_options));
+ break;
case RuntimeFlags::GWP_ASAN_LEVEL_LOTTERY:
- android_mallopt(M_INITIALIZE_GWP_ASAN, &forceEnableGwpAsan, sizeof(forceEnableGwpAsan));
+ gwp_asan_options.desire = Action::TURN_ON_WITH_SAMPLING;
+ android_mallopt(M_INITIALIZE_GWP_ASAN, &gwp_asan_options, sizeof(gwp_asan_options));
+ break;
}
// Now that we've used the flag, clear it so that we don't pass unknown flags to the ART
// runtime.
@@ -1739,7 +1750,6 @@
AStatsSocket_close();
const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr;
- const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;
if (selinux_android_setcontext(uid, is_system_server, se_info_ptr, nice_name_ptr) == -1) {
fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
diff --git a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
index 679a4f0..add645de 100644
--- a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
+++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
@@ -296,7 +296,7 @@
++buffersAllocd;
// MMap explicitly to get it page aligned.
void *bufferMem = mmap(NULL, sizeof(NativeCommandBuffer), PROT_READ | PROT_WRITE,
- MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0);
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
// Currently we mmap and unmap one for every request handled by the Java code.
// That could be improved, but unclear it matters.
if (bufferMem == MAP_FAILED) {
diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto
index 45f8c132..cd002da 100644
--- a/core/proto/android/service/usb.proto
+++ b/core/proto/android/service/usb.proto
@@ -195,9 +195,19 @@
message UsbPortManagerProto {
option (android.msg_privacy).dest = DEST_AUTOMATIC;
+ enum HalVersion {
+ V_UNKNOWN = 0;
+ V1_0 = 10;
+ V1_1 = 11;
+ V1_2 = 12;
+ V1_3 = 13;
+ V2 = 20;
+ }
+
optional bool is_simulation_active = 1;
repeated UsbPortInfoProto usb_ports = 2;
optional bool enable_usb_data_signaling = 3;
+ optional HalVersion hal_version = 4;
}
message UsbPortInfoProto {
@@ -253,6 +263,7 @@
optional DataRole data_role = 4;
repeated UsbPortStatusRoleCombinationProto role_combinations = 5;
optional android.service.ContaminantPresenceStatus contaminant_presence_status = 6;
+ optional bool usb_data_enabled = 7;
}
message UsbPortStatusRoleCombinationProto {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b87befb..f81ac3d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3409,6 +3409,12 @@
<permission android:name="android.permission.UPDATE_FONTS"
android:protectionLevel="signature|privileged" />
+ <!-- Allows the caller to generate keymint keys with the INCLUDE_UNIQUE_ID tag, which
+ uniquely identifies the device via the attestation certificate.
+ @hide @TestApi -->
+ <permission android:name="android.permission.REQUEST_UNIQUE_ID_ATTESTATION"
+ android:protectionLevel="signature" />
+
<!-- ========================================= -->
<!-- Permissions for special development tools -->
<!-- ========================================= -->
diff --git a/core/res/res/layout/accessibility_enable_service_encryption_warning.xml b/core/res/res/layout/accessibility_enable_service_warning.xml
similarity index 94%
rename from core/res/res/layout/accessibility_enable_service_encryption_warning.xml
rename to core/res/res/layout/accessibility_enable_service_warning.xml
index 4000516..01ef101 100644
--- a/core/res/res/layout/accessibility_enable_service_encryption_warning.xml
+++ b/core/res/res/layout/accessibility_enable_service_warning.xml
@@ -54,14 +54,6 @@
android:fontFamily="google-sans-medium"/>
<TextView
- android:id="@+id/accessibility_encryption_warning"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:padding="10dip"
- android:textAlignment="viewStart"
- android:textAppearance="?android:attr/textAppearanceMedium"/>
-
- <TextView
android:id="@+id/accessibility_permissionDialog_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index cb3e465..c5d50fb 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2566,23 +2566,27 @@
rat-name:rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max
If no value is found for the rat-name in use, the system default will be applied.
- This is deprecated. Please use config_tcp_buffers.
+ This is deprecated. Please use config_mobile_tcp_buffers for rat-based TCP buffers sizes or
+ config_tcp_buffers for rat-independent TCP buffer sizes.
-->
<string-array name="config_mobile_tcp_buffers">
</string-array>
+ <!-- Configure tcp buffer sizes per network type in the form:
+ network-type:rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max
+
+ The network-type must be a valid DataConfigNetworkType value. If no value is found for the
+ network-type in use, config_tcp_buffers will be used instead.
+ -->
+ <string-array name="config_network_type_tcp_buffers">
+ </string-array>
+
<!-- Configure tcp buffer sizes in the form:
rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max
If this is configured as an empty string, the system default will be applied.
For now this config is used by mobile data only. In the future it should be
used by Wi-Fi as well.
-
- Note that starting from Android 13, the TCP buffer size is fixed after boot up, and should
- never be changed based on carriers or the network types. The value should be configured
- appropriately based on the device's memory and performance. It is recommended to use lower
- values if the device has low memory or doesn't support high-speed network such like LTE,
- NR, or Wifi.
-->
<string name="config_tcp_buffers" translatable="false"></string>
@@ -2595,6 +2599,9 @@
Values are bandwidth_estimator, carrier_config and modem. -->
<string name="config_bandwidthEstimateSource">bandwidth_estimator</string>
+ <!-- Whether force to enable telephony new data stack or not -->
+ <bool name="config_force_enable_telephony_new_data_stack">false</bool>
+
<!-- Whether WiFi display is supported by this device.
There are many prerequisites for this feature to work correctly.
Here are a few of them:
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7663192..db0cc27 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4687,13 +4687,6 @@
<xliff:g id="service" example="TalkBack">%1$s</xliff:g> to have full control of your
device?</string>
- <!-- Warning that the device data will not be encrypted with password or PIN if
- enabling an accessibility service and there is a secure lock setup. [CHAR LIMIT=NONE] -->
- <string name="accessibility_enable_service_encryption_warning">If you turn on
- <xliff:g id="service" example="TalkBack">%1$s</xliff:g>, your device won’t use your screen
- lock to enhance data encryption.
- </string>
-
<!-- Warning description that explains that it's appropriate for accessibility
services to have full control to help users with accessibility needs. [CHAR LIMIT=NONE] -->
<string name="accessibility_service_warning_description">Full control is appropriate for apps
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 55e2ca6..6e0fd91 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -466,7 +466,9 @@
<java-symbol type="integer" name="config_safe_media_volume_usb_mB" />
<java-symbol type="integer" name="config_mobile_mtu" />
<java-symbol type="array" name="config_mobile_tcp_buffers" />
+ <java-symbol type="array" name="config_network_type_tcp_buffers" />
<java-symbol type="string" name="config_tcp_buffers" />
+ <java-symbol type="bool" name="config_force_enable_telephony_new_data_stack" />
<java-symbol type="integer" name="config_volte_replacement_rat"/>
<java-symbol type="integer" name="config_valid_wappush_index" />
<java-symbol type="integer" name="config_overrideHasPermanentMenuKey" />
@@ -3413,14 +3415,12 @@
<java-symbol type="string" name="accessibility_edit_shortcut_menu_volume_title" />
<java-symbol type="string" name="accessibility_uncheck_legacy_item_warning" />
- <java-symbol type="layout" name="accessibility_enable_service_encryption_warning" />
+ <java-symbol type="layout" name="accessibility_enable_service_warning" />
<java-symbol type="id" name="accessibility_permissionDialog_icon" />
<java-symbol type="id" name="accessibility_permissionDialog_title" />
- <java-symbol type="id" name="accessibility_encryption_warning" />
<java-symbol type="id" name="accessibility_permission_enable_allow_button" />
<java-symbol type="id" name="accessibility_permission_enable_deny_button" />
<java-symbol type="string" name="accessibility_enable_service_title" />
- <java-symbol type="string" name="accessibility_enable_service_encryption_warning" />
<java-symbol type="layout" name="accessibility_shortcut_chooser_item" />
<java-symbol type="id" name="accessibility_shortcut_target_checkbox" />
diff --git a/core/tests/coretests/src/android/net/OWNERS b/core/tests/coretests/src/android/net/OWNERS
index 4e5136f..a779c00 100644
--- a/core/tests/coretests/src/android/net/OWNERS
+++ b/core/tests/coretests/src/android/net/OWNERS
@@ -1,3 +1,4 @@
include /services/core/java/com/android/server/net/OWNERS
+per-file SSL*,Uri*,Url* = prb@google.com,oth@google.com,narayan@google.com,ngeoffray@google.com
per-file SntpClient* = file:/services/core/java/com/android/server/timedetector/OWNERS
diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
index fd39cde..8d4f29a 100644
--- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java
+++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java
@@ -827,4 +827,80 @@
return compositeName.equals(result.getString(Settings.NameValueTable.VALUE));
}
+ @Test
+ public void deleteProperty_nullNamespace() {
+ try {
+ DeviceConfig.deleteProperty(null, KEY);
+ Assert.fail("Null namespace should have resulted in an NPE.");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void deleteProperty_nullName() {
+ try {
+ DeviceConfig.deleteProperty(NAMESPACE, null);
+ Assert.fail("Null name should have resulted in an NPE.");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void deletePropertyString() {
+ final String value = "new_value";
+ final String default_value = "default";
+ DeviceConfig.setProperty(NAMESPACE, KEY, value, false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final String result = DeviceConfig.getString(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deletePropertyBoolean() {
+ final boolean value = true;
+ final boolean default_value = false;
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final boolean result = DeviceConfig.getBoolean(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deletePropertyInt() {
+ final int value = 123;
+ final int default_value = 999;
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final int result = DeviceConfig.getInt(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deletePropertyLong() {
+ final long value = 456789;
+ final long default_value = 123456;
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final long result = DeviceConfig.getLong(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deletePropertyFloat() {
+ final float value = 456.789f;
+ final float default_value = 123.456f;
+ DeviceConfig.setProperty(NAMESPACE, KEY, String.valueOf(value), false);
+ DeviceConfig.deleteProperty(NAMESPACE, KEY);
+ final float result = DeviceConfig.getFloat(NAMESPACE, KEY, default_value);
+ assertThat(result).isEqualTo(default_value);
+ }
+
+ @Test
+ public void deleteProperty_empty() {
+ assertThat(DeviceConfig.deleteProperty(NAMESPACE, KEY)).isTrue();
+ final String result = DeviceConfig.getString(NAMESPACE, KEY, null);
+ assertThat(result).isNull();
+ }
}
diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java
index a0fc349..c4bcfd4 100644
--- a/core/tests/coretests/src/android/text/TextUtilsTest.java
+++ b/core/tests/coretests/src/android/text/TextUtilsTest.java
@@ -43,6 +43,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -782,6 +783,81 @@
}
@Test
+ public void truncateStringForUtf8Storage() {
+ assertEquals("", TextUtils.truncateStringForUtf8Storage("abc", 0));
+
+ //================ long normal case ================
+ StringBuilder builder = new StringBuilder();
+
+ int n = 50;
+ for (int i = 0; i < 2 * n; i++) {
+ builder.append("哈");
+ }
+ String initial = builder.toString();
+ String result = TextUtils.truncateStringForUtf8Storage(initial, n);
+
+ // Result should be the beginning of initial
+ assertTrue(initial.startsWith(result));
+
+ // Result should take less than n bytes in UTF-8
+ assertTrue(result.getBytes(StandardCharsets.UTF_8).length <= n);
+
+ // result + the next codePoint should take strictly more than
+ // n bytes in UTF-8
+ assertTrue(initial.substring(0, initial.offsetByCodePoints(result.length(), 1))
+ .getBytes(StandardCharsets.UTF_8).length > n);
+
+ // =================== short normal case =====================
+ String s = "sf\u20ACgk\u00E9ls\u00E9fg";
+ result = TextUtils.truncateStringForUtf8Storage(s, 100);
+ assertEquals(s, result);
+ }
+
+ @Test
+ public void testTruncateInMiddleOfSurrogate() {
+ StringBuilder builder = new StringBuilder();
+ String beginning = "a";
+ builder.append(beginning);
+ builder.append(Character.toChars(0x1D11E));
+
+ String result = TextUtils.truncateStringForUtf8Storage(builder.toString(), 3);
+
+ // \u1D11E is a surrogate and needs 4 bytes in UTF-8. beginning == "a" uses
+ // only 1 bytes in UTF8
+ // As we allow only 3 bytes for the whole string, so just 2 for this
+ // codePoint, there is not enough place and the string will be truncated
+ // just before it
+ assertEquals(beginning, result);
+ }
+
+ @Test
+ public void testTruncateInMiddleOfChar() {
+ StringBuilder builder = new StringBuilder();
+ String beginning = "a";
+ builder.append(beginning);
+ builder.append(Character.toChars(0x20AC));
+
+ String result = TextUtils.truncateStringForUtf8Storage(builder.toString(), 3);
+
+ // Like above, \u20AC uses 3 bytes in UTF-8, with "beginning", that makes
+ // 4 bytes so it is too big and should be truncated
+ assertEquals(beginning, result);
+ }
+
+ @Test
+ public void testTruncateSubString() {
+ String test = "sdgkl;hjsl;gjhdgkljdfhglkdj";
+ String sub = test.substring(10, 20);
+ String res = TextUtils.truncateStringForUtf8Storage(sub, 255);
+ assertEquals(sub, res);
+ }
+
+ @Test(expected = IndexOutOfBoundsException.class)
+ public void truncateStringForUtf8StorageThrowsExceptionForNegativeSize() {
+ TextUtils.truncateStringForUtf8Storage("abc", -1);
+ }
+
+ @Test
public void length() {
assertEquals(0, TextUtils.length(null));
assertEquals(0, TextUtils.length(""));
diff --git a/core/tests/coretests/src/com/android/internal/widget/OWNERS b/core/tests/coretests/src/com/android/internal/widget/OWNERS
index b40fe24..659a3d1 100644
--- a/core/tests/coretests/src/com/android/internal/widget/OWNERS
+++ b/core/tests/coretests/src/com/android/internal/widget/OWNERS
@@ -1,3 +1,7 @@
+include /core/java/com/android/internal/widget/OWNERS
+
+# Notifications related
+per-file CachingIconViewTest.java = file:/services/core/java/com/android/server/notification/OWNERS
# LockSettings related
per-file *LockPattern* = file:/services/core/java/com/android/server/locksettings/OWNERS
per-file *Lockscreen* = file:/services/core/java/com/android/server/locksettings/OWNERS
diff --git a/data/keyboards/Vendor_0e6f_Product_f501.kl b/data/keyboards/Vendor_0e6f_Product_f501.kl
new file mode 100644
index 0000000..b46c005
--- /dev/null
+++ b/data/keyboards/Vendor_0e6f_Product_f501.kl
@@ -0,0 +1,55 @@
+# Copyright (C) 2022 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.
+
+#
+# XBox-compatible USB Controller
+#
+
+key 304 BUTTON_A
+key 305 BUTTON_B
+key 307 BUTTON_X
+key 308 BUTTON_Y
+key 310 BUTTON_L1
+key 311 BUTTON_R1
+
+key 317 BUTTON_THUMBL
+key 318 BUTTON_THUMBR
+
+# Left and right stick.
+# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd.
+# This confuses applications that rely on the flat value because the joystick actually
+# settles in a flat range of +/- 4096 or so.
+axis 0x00 X flat 4096
+axis 0x01 Y flat 4096
+axis 0x03 Z flat 4096
+axis 0x04 RZ flat 4096
+
+# Triggers.
+axis 0x02 LTRIGGER
+axis 0x05 RTRIGGER
+
+# Hat.
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt
+
+# Button labeled as "BACK" (left-pointing triangle)
+key 314 BUTTON_SELECT
+
+# The branded "X" button in the center of the controller
+key 316 BUTTON_MODE
+
+# Button labeled as "START" (right-pointing triangle)
+key 315 BUTTON_START
diff --git a/graphics/TEST_MAPPING b/graphics/TEST_MAPPING
index 10bd0ee..abeaf19 100644
--- a/graphics/TEST_MAPPING
+++ b/graphics/TEST_MAPPING
@@ -1,7 +1,12 @@
{
"presubmit": [
{
- "name": "CtsGraphicsTestCases"
+ "name": "CtsGraphicsTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ }
+ ]
}
]
}
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
index fb0880c..bbaf086 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
@@ -19,7 +19,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.os.RemoteException;
import android.os.ServiceManager;
+import android.security.GenerateRkpKey;
+import android.security.keymaster.KeymasterDefs;
class CredstoreIdentityCredentialStore extends IdentityCredentialStore {
@@ -104,6 +107,16 @@
try {
IWritableCredential wc;
wc = mStore.createCredential(credentialName, docType);
+ try {
+ GenerateRkpKey keyGen = new GenerateRkpKey(mContext);
+ // We don't know what the security level is for the backing keymint, so go ahead and
+ // poke the provisioner for both TEE and SB.
+ keyGen.notifyKeyGenerated(KeymasterDefs.KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT);
+ keyGen.notifyKeyGenerated(KeymasterDefs.KM_SECURITY_LEVEL_STRONGBOX);
+ } catch (RemoteException e) {
+ // Not really an error state. Does not apply at all if RKP is unsupported or
+ // disabled on a given device.
+ }
return new CredstoreWritableIdentityCredential(mContext, credentialName, docType, wc);
} catch (android.os.RemoteException e) {
throw new RuntimeException("Unexpected RemoteException ", e);
diff --git a/keystore/java/android/security/GenerateRkpKey.java b/keystore/java/android/security/GenerateRkpKey.java
index 2e54e63..6981332 100644
--- a/keystore/java/android/security/GenerateRkpKey.java
+++ b/keystore/java/android/security/GenerateRkpKey.java
@@ -16,6 +16,8 @@
package android.security;
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -24,6 +26,8 @@
import android.os.RemoteException;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -57,6 +61,21 @@
private Context mContext;
private CountDownLatch mCountDownLatch;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, value = {
+ IGenerateRkpKeyService.Status.OK,
+ IGenerateRkpKeyService.Status.NO_NETWORK_CONNECTIVITY,
+ IGenerateRkpKeyService.Status.NETWORK_COMMUNICATION_ERROR,
+ IGenerateRkpKeyService.Status.DEVICE_NOT_REGISTERED,
+ IGenerateRkpKeyService.Status.HTTP_CLIENT_ERROR,
+ IGenerateRkpKeyService.Status.HTTP_SERVER_ERROR,
+ IGenerateRkpKeyService.Status.HTTP_UNKNOWN_ERROR,
+ IGenerateRkpKeyService.Status.INTERNAL_ERROR,
+ })
+ public @interface Status {
+ }
+
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
@@ -81,12 +100,14 @@
mContext = context;
}
- private void bindAndSendCommand(int command, int securityLevel) throws RemoteException {
+ @Status
+ private int bindAndSendCommand(int command, int securityLevel) throws RemoteException {
Intent intent = new Intent(IGenerateRkpKeyService.class.getName());
ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+ int returnCode = IGenerateRkpKeyService.Status.OK;
if (comp == null) {
// On a system that does not use RKP, the RemoteProvisioner app won't be installed.
- return;
+ return returnCode;
}
intent.setComponent(comp);
mCountDownLatch = new CountDownLatch(1);
@@ -102,7 +123,7 @@
if (mBinder != null) {
switch (command) {
case NOTIFY_EMPTY:
- mBinder.generateKey(securityLevel);
+ returnCode = mBinder.generateKey(securityLevel);
break;
case NOTIFY_KEY_GENERATED:
mBinder.notifyKeyGenerated(securityLevel);
@@ -112,16 +133,21 @@
}
} else {
Log.e(TAG, "Binder object is null; failed to bind to GenerateRkpKeyService.");
+ returnCode = IGenerateRkpKeyService.Status.INTERNAL_ERROR;
}
mContext.unbindService(mConnection);
+ return returnCode;
}
/**
* Fulfills the use case of (2) described in the class documentation. Blocks until the
* RemoteProvisioner application can get new attestation keys signed by the server.
+ * @return the status of the key generation
*/
- public void notifyEmpty(int securityLevel) throws RemoteException {
- bindAndSendCommand(NOTIFY_EMPTY, securityLevel);
+ @CheckResult
+ @Status
+ public int notifyEmpty(int securityLevel) throws RemoteException {
+ return bindAndSendCommand(NOTIFY_EMPTY, securityLevel);
}
/**
diff --git a/keystore/java/android/security/IGenerateRkpKeyService.aidl b/keystore/java/android/security/IGenerateRkpKeyService.aidl
index 5f1d669..eeaeb27 100644
--- a/keystore/java/android/security/IGenerateRkpKeyService.aidl
+++ b/keystore/java/android/security/IGenerateRkpKeyService.aidl
@@ -26,11 +26,35 @@
* @hide
*/
interface IGenerateRkpKeyService {
+ @JavaDerive(toString=true)
+ @Backing(type="int")
+ enum Status {
+ /** No error(s) occurred */
+ OK = 0,
+ /** Unable to provision keys due to a lack of internet connectivity. */
+ NO_NETWORK_CONNECTIVITY = 1,
+ /** An error occurred while communicating with the RKP server. */
+ NETWORK_COMMUNICATION_ERROR = 2,
+ /** The given device was not registered with the RKP backend. */
+ DEVICE_NOT_REGISTERED = 4,
+ /** The RKP server returned an HTTP client error, indicating a misbehaving client. */
+ HTTP_CLIENT_ERROR = 5,
+ /** The RKP server returned an HTTP server error, indicating something went wrong on the server. */
+ HTTP_SERVER_ERROR = 6,
+ /** The RKP server returned an HTTP status that is unknown. This should never happen. */
+ HTTP_UNKNOWN_ERROR = 7,
+ /** An unexpected internal error occurred. This should never happen. */
+ INTERNAL_ERROR = 8,
+ }
+
/**
* Ping the provisioner service to let it know an app generated a key. This may or may not have
* consumed a remotely provisioned attestation key, so the RemoteProvisioner app should check.
*/
oneway void notifyKeyGenerated(in int securityLevel);
- /** Ping the provisioner service to indicate there are no remaining attestation keys left. */
- void generateKey(in int securityLevel);
+
+ /**
+ * Ping the provisioner service to indicate there are no remaining attestation keys left.
+ */
+ Status generateKey(in int securityLevel);
}
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index 3d53cfb..c2cd6ff 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -345,6 +345,12 @@
case ResponseCode.KEY_PERMANENTLY_INVALIDATED:
return new KeyStoreException(errorCode, "Key permanently invalidated",
serviceErrorMessage);
+ case ResponseCode.OUT_OF_KEYS:
+ // Getting a more specific RKP status requires the security level, which we
+ // don't have here. Higher layers of the stack can interpret this exception
+ // and add more flavor.
+ return new KeyStoreException(errorCode, serviceErrorMessage,
+ KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE);
default:
return new KeyStoreException(errorCode, String.valueOf(errorCode),
serviceErrorMessage);
diff --git a/keystore/java/android/security/KeyStoreException.java b/keystore/java/android/security/KeyStoreException.java
index 54184db..1a81dda 100644
--- a/keystore/java/android/security/KeyStoreException.java
+++ b/keystore/java/android/security/KeyStoreException.java
@@ -21,6 +21,7 @@
import android.annotation.TestApi;
import android.security.keymaster.KeymasterDefs;
import android.system.keystore2.ResponseCode;
+import android.util.Log;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -36,6 +37,8 @@
* is likely to succeed.
*/
public class KeyStoreException extends Exception {
+ private static final String TAG = "KeyStoreException";
+
/**
* This error code is for mapping errors that the caller will not know about. If the caller is
* targeting an API level earlier than the one the error was introduced in, then the error will
@@ -114,6 +117,27 @@
* The caller should re-create the crypto object and try again.
*/
public static final int ERROR_KEY_OPERATION_EXPIRED = 15;
+ /**
+ * There are no keys available for attestation.
+ * This error is returned only on devices that rely solely on remotely-provisioned keys (see
+ * <a href=
+ * "https://android-developers.googleblog.com/2022/03/upgrading-android-attestation-remote.html"
+ * >Remote Key Provisioning</a>).
+ *
+ * <p>On such a device, if the caller requests key generation and includes an attestation
+ * challenge (indicating key attestation is required), the error will be returned in one of
+ * the following cases:
+ * <ul>
+ * <li>The pool of remotely-provisioned keys has been exhausted.</li>
+ * <li>The device is not registered with the key provisioning server.</li>
+ * </ul>
+ * </p>
+ *
+ * <p>This error is a transient one if the pool of remotely-provisioned keys has been
+ * exhausted. However, if the device is not registered with the server, or the key
+ * provisioning server refuses key issuance, this is a permanent error.</p>
+ */
+ public static final int ERROR_ATTESTATION_KEYS_UNAVAILABLE = 16;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -132,11 +156,68 @@
ERROR_UNIMPLEMENTED,
ERROR_INCORRECT_USAGE,
ERROR_KEY_NOT_TEMPORALLY_VALID,
- ERROR_KEY_OPERATION_EXPIRED
+ ERROR_KEY_OPERATION_EXPIRED,
+ ERROR_ATTESTATION_KEYS_UNAVAILABLE
})
public @interface PublicErrorCode {
}
+ /**
+ * Never re-try the operation that led to this error, since it's a permanent error.
+ *
+ * This value is always returned when {@link #isTransientFailure()} is {@code false}.
+ */
+ public static final int RETRY_NEVER = 1;
+ /**
+ * Re-try the operation that led to this error with an exponential back-off delay.
+ * The first delay should be between 5 to 30 seconds, and each subsequent re-try should double
+ * the delay time.
+ *
+ * This value is returned when {@link #isTransientFailure()} is {@code true}.
+ */
+ public static final int RETRY_WITH_EXPONENTIAL_BACKOFF = 2;
+ /**
+ * Re-try the operation that led to this error when the device regains connectivity.
+ * Remote provisioning of keys requires reaching the remote server, and the device is
+ * currently unable to due that due to lack of network connectivity.
+ *
+ * This value is returned when {@link #isTransientFailure()} is {@code true}.
+ */
+ public static final int RETRY_WHEN_CONNECTIVITY_AVAILABLE = 3;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = {"RETRY_"}, value = {
+ RETRY_NEVER,
+ RETRY_WITH_EXPONENTIAL_BACKOFF,
+ RETRY_WHEN_CONNECTIVITY_AVAILABLE,
+ })
+ public @interface RetryPolicy {
+ }
+
+ // RKP-specific error information.
+ /**
+ * Remote provisioning of attestation keys has completed successfully.
+ * @hide */
+ public static final int RKP_SUCCESS = 0;
+ /**
+ * Remotely-provisioned keys are temporarily unavailable. This could be because of RPC
+ * error when talking to the remote provisioner or keys are being currently fetched and will
+ * be available soon.
+ * @hide */
+ public static final int RKP_TEMPORARILY_UNAVAILABLE = 1;
+ /**
+ * Permanent failure: The RKP server has declined issuance of keys to this device. Either
+ * because the device is not registered with the server or the server considers the device
+ * not to be trustworthy.
+ * @hide */
+ public static final int RKP_SERVER_REFUSED_ISSUANCE = 2;
+ /**
+ * The RKP server is unavailable due to lack of connectivity. The caller should re-try
+ * when the device has connectivity again.
+ * @hide */
+ public static final int RKP_FETCHING_PENDING_CONNECTIVITY = 3;
+
// Constants for encoding information about the error encountered:
// Whether the error relates to the system state/implementation as a whole, or a specific key.
private static final int IS_SYSTEM_ERROR = 1 << 1;
@@ -148,6 +229,21 @@
// The internal error code. NOT to be returned directly to callers or made part of the
// public API.
private final int mErrorCode;
+ // The Remote Key Provisioning status. Applicable if and only if {@link #mErrorCode} is equal
+ // to {@link ResponseCode.OUT_OF_KEYS}.
+ private final int mRkpStatus;
+
+ private static int initializeRkpStatusForRegularErrors(int errorCode) {
+ // Check if the system code mistakenly called a constructor of KeyStoreException with
+ // the OUT_OF_KEYS error code but without RKP status.
+ if (errorCode == ResponseCode.OUT_OF_KEYS) {
+ Log.e(TAG, "RKP error code without RKP status");
+ // Set RKP status to RKP_SERVER_REFUSED_ISSUANCE so that the caller never retries.
+ return RKP_SERVER_REFUSED_ISSUANCE;
+ } else {
+ return RKP_SUCCESS;
+ }
+ }
/**
* @hide
@@ -155,6 +251,7 @@
public KeyStoreException(int errorCode, @Nullable String message) {
super(message);
mErrorCode = errorCode;
+ mRkpStatus = initializeRkpStatusForRegularErrors(errorCode);
}
/**
@@ -165,6 +262,19 @@
super(message + " (internal Keystore code: " + errorCode + " message: "
+ keystoreErrorMessage + ")");
mErrorCode = errorCode;
+ mRkpStatus = initializeRkpStatusForRegularErrors(errorCode);
+ }
+
+ /**
+ * @hide
+ */
+ public KeyStoreException(int errorCode, @Nullable String message, int rkpStatus) {
+ super(message);
+ mErrorCode = errorCode;
+ mRkpStatus = rkpStatus;
+ if (mErrorCode != ResponseCode.OUT_OF_KEYS) {
+ Log.e(TAG, "Providing RKP status for error code " + errorCode + " has no effect.");
+ }
}
/**
@@ -198,6 +308,17 @@
*/
public boolean isTransientFailure() {
PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
+ // Special-case handling for RKP failures:
+ if (mRkpStatus != RKP_SUCCESS && mErrorCode == ResponseCode.OUT_OF_KEYS) {
+ switch (mRkpStatus) {
+ case RKP_TEMPORARILY_UNAVAILABLE:
+ case RKP_FETCHING_PENDING_CONNECTIVITY:
+ return true;
+ case RKP_SERVER_REFUSED_ISSUANCE:
+ default:
+ return false;
+ }
+ }
return (failureInfo.indicators & IS_TRANSIENT_ERROR) != 0;
}
@@ -225,6 +346,34 @@
return (failureInfo.indicators & IS_SYSTEM_ERROR) != 0;
}
+ /**
+ * Returns the re-try policy for transient failures. Valid only if
+ * {@link #isTransientFailure()} returns {@code True}.
+ */
+ @RetryPolicy
+ public int getRetryPolicy() {
+ PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
+ // Special-case handling for RKP failures:
+ if (mRkpStatus != RKP_SUCCESS) {
+ switch (mRkpStatus) {
+ case RKP_TEMPORARILY_UNAVAILABLE:
+ return RETRY_WITH_EXPONENTIAL_BACKOFF;
+ case RKP_FETCHING_PENDING_CONNECTIVITY:
+ return RETRY_WHEN_CONNECTIVITY_AVAILABLE;
+ case RKP_SERVER_REFUSED_ISSUANCE:
+ return RETRY_NEVER;
+ default:
+ return (failureInfo.indicators & IS_TRANSIENT_ERROR) != 0
+ ? RETRY_WITH_EXPONENTIAL_BACKOFF : RETRY_NEVER;
+ }
+ }
+ if ((failureInfo.indicators & IS_TRANSIENT_ERROR) != 0) {
+ return RETRY_WITH_EXPONENTIAL_BACKOFF;
+ } else {
+ return RETRY_NEVER;
+ }
+ }
+
@Override
public String toString() {
String errorCodes = String.format(" (public error code: %d internal Keystore code: %d)",
@@ -469,5 +618,7 @@
new PublicErrorInformation(0, ERROR_KEY_CORRUPTED));
sErrorCodeToFailureInfo.put(ResponseCode.KEY_PERMANENTLY_INVALIDATED,
new PublicErrorInformation(0, ERROR_KEY_DOES_NOT_EXIST));
+ sErrorCodeToFailureInfo.put(ResponseCode.OUT_OF_KEYS,
+ new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_ATTESTATION_KEYS_UNAVAILABLE));
}
}
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 54bab4a..ffd041f6 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -1637,8 +1637,8 @@
* Sets whether the keystore requires the screen to be unlocked before allowing decryption
* using this key. If this is set to {@code true}, any attempt to decrypt or sign using this
* key while the screen is locked will fail. A locked device requires a PIN, password,
- * biometric, or other trusted factor to access. While the screen is locked, the key can
- * still be used for encryption or signature verification.
+ * biometric, or other trusted factor to access. While the screen is locked, any associated
+ * public key can still be used (e.g for signature verification).
*/
@NonNull
public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index e7961c9..40659f5 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -28,7 +28,7 @@
import android.os.Build;
import android.os.RemoteException;
import android.security.GenerateRkpKey;
-import android.security.GenerateRkpKeyException;
+import android.security.IGenerateRkpKeyService;
import android.security.KeyPairGeneratorSpec;
import android.security.KeyStore2;
import android.security.KeyStoreException;
@@ -618,18 +618,44 @@
@Override
public KeyPair generateKeyPair() {
- try {
- return generateKeyPairHelper();
- } catch (GenerateRkpKeyException e) {
- try {
- return generateKeyPairHelper();
- } catch (GenerateRkpKeyException f) {
- throw new ProviderException("Failed to provision new attestation keys.");
+ GenerateKeyPairHelperResult result = new GenerateKeyPairHelperResult(0, null);
+ for (int i = 0; i < 2; i++) {
+ /**
+ * NOTE: There is no need to delay between re-tries because the call to
+ * GenerateRkpKey.notifyEmpty() will delay for a while before returning.
+ */
+ result = generateKeyPairHelper();
+ if (result.rkpStatus == KeyStoreException.RKP_SUCCESS && result.keyPair != null) {
+ return result.keyPair;
}
}
+
+ // RKP failure
+ if (result.rkpStatus != KeyStoreException.RKP_SUCCESS) {
+ KeyStoreException ksException = new KeyStoreException(ResponseCode.OUT_OF_KEYS,
+ "Could not get RKP keys", result.rkpStatus);
+ throw new ProviderException("Failed to provision new attestation keys.", ksException);
+ }
+
+ return result.keyPair;
+ }
+
+ private static class GenerateKeyPairHelperResult {
+ // Zero indicates success, non-zero indicates failure. Values should be
+ // {@link android.security.KeyStoreException#RKP_TEMPORARILY_UNAVAILABLE},
+ // {@link android.security.KeyStoreException#RKP_SERVER_REFUSED_ISSUANCE},
+ // {@link android.security.KeyStoreException#RKP_FETCHING_PENDING_CONNECTIVITY}
+ public final int rkpStatus;
+ @Nullable
+ public final KeyPair keyPair;
+
+ private GenerateKeyPairHelperResult(int rkpStatus, KeyPair keyPair) {
+ this.rkpStatus = rkpStatus;
+ this.keyPair = keyPair;
+ }
}
- private KeyPair generateKeyPairHelper() throws GenerateRkpKeyException {
+ private GenerateKeyPairHelperResult generateKeyPairHelper() {
if (mKeyStore == null || mSpec == null) {
throw new IllegalStateException("Not initialized");
}
@@ -679,20 +705,14 @@
Log.d(TAG, "Couldn't connect to the RemoteProvisioner backend.", e);
}
success = true;
- return new KeyPair(publicKey, publicKey.getPrivateKey());
- } catch (android.security.KeyStoreException e) {
+ KeyPair kp = new KeyPair(publicKey, publicKey.getPrivateKey());
+ return new GenerateKeyPairHelperResult(0, kp);
+ } catch (KeyStoreException e) {
switch (e.getErrorCode()) {
case KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE:
throw new StrongBoxUnavailableException("Failed to generated key pair.", e);
case ResponseCode.OUT_OF_KEYS:
- GenerateRkpKey keyGen = new GenerateRkpKey(ActivityThread
- .currentApplication());
- try {
- keyGen.notifyEmpty(securityLevel);
- } catch (RemoteException f) {
- throw new ProviderException("Failed to talk to RemoteProvisioner", f);
- }
- throw new GenerateRkpKeyException();
+ throw makeOutOfKeysException(e, securityLevel);
default:
ProviderException p = new ProviderException("Failed to generate key pair.", e);
if ((mSpec.getPurposes() & KeyProperties.PURPOSE_WRAP_KEY) != 0) {
@@ -718,6 +738,52 @@
}
}
+ // In case keystore reports OUT_OF_KEYS, call this handler in an attempt to remotely provision
+ // some keys.
+ private ProviderException makeOutOfKeysException(KeyStoreException e, int securityLevel) {
+ GenerateRkpKey keyGen = new GenerateRkpKey(ActivityThread
+ .currentApplication());
+ KeyStoreException ksException;
+ try {
+ final int keyGenStatus = keyGen.notifyEmpty(securityLevel);
+ // Default stance: temporary error. This is a hint to the caller to try again with
+ // exponential back-off.
+ int rkpStatus;
+ switch (keyGenStatus) {
+ case IGenerateRkpKeyService.Status.NO_NETWORK_CONNECTIVITY:
+ rkpStatus = KeyStoreException.RKP_FETCHING_PENDING_CONNECTIVITY;
+ break;
+ case IGenerateRkpKeyService.Status.DEVICE_NOT_REGISTERED:
+ rkpStatus = KeyStoreException.RKP_SERVER_REFUSED_ISSUANCE;
+ break;
+ case IGenerateRkpKeyService.Status.OK:
+ // This will actually retry once immediately, so on "OK" go ahead and return
+ // "temporarily unavailable". @see generateKeyPair
+ case IGenerateRkpKeyService.Status.NETWORK_COMMUNICATION_ERROR:
+ case IGenerateRkpKeyService.Status.HTTP_CLIENT_ERROR:
+ case IGenerateRkpKeyService.Status.HTTP_SERVER_ERROR:
+ case IGenerateRkpKeyService.Status.HTTP_UNKNOWN_ERROR:
+ case IGenerateRkpKeyService.Status.INTERNAL_ERROR:
+ default:
+ // These errors really should never happen. The best we can do is assume they
+ // are transient and hint to the caller to retry with back-off.
+ rkpStatus = KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE;
+ break;
+ }
+ ksException = new KeyStoreException(
+ ResponseCode.OUT_OF_KEYS,
+ "Out of RKP keys due to IGenerateRkpKeyService status: " + keyGenStatus,
+ rkpStatus);
+ } catch (RemoteException f) {
+ ksException = new KeyStoreException(
+ ResponseCode.OUT_OF_KEYS,
+ "Remote exception: " + f.getMessage(),
+ KeyStoreException.RKP_TEMPORARILY_UNAVAILABLE);
+ }
+ ksException.initCause(e);
+ return new ProviderException("Failed to talk to RemoteProvisioner", ksException);
+ }
+
private void addAttestationParameters(@NonNull List<KeyParameter> params)
throws ProviderException, IllegalArgumentException, DeviceIdAttestationException {
byte[] challenge = mSpec.getAttestationChallenge();
diff --git a/libs/WindowManager/Jetpack/tests/OWNERS b/libs/WindowManager/Jetpack/tests/OWNERS
index f2c3388..ac522b2 100644
--- a/libs/WindowManager/Jetpack/tests/OWNERS
+++ b/libs/WindowManager/Jetpack/tests/OWNERS
@@ -1,4 +1,4 @@
-# Bug component: 909476
+# Bug component: 1157642
# includes OWNERS from parent directories
charlesccchen@google.com
diegovela@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index f0b2716..a201616 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -825,6 +825,10 @@
}
private void fadeExistingPip(boolean show) {
+ if (mLeash == null || !mLeash.isValid()) {
+ Log.w(TAG, "Invalid leash on fadeExistingPip: " + mLeash);
+ return;
+ }
final float alphaStart = show ? 0 : 1;
final float alphaEnd = show ? 1 : 0;
mPipAnimationController
diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS
index f49e80a..c877250 100644
--- a/libs/WindowManager/Shell/tests/OWNERS
+++ b/libs/WindowManager/Shell/tests/OWNERS
@@ -1,4 +1,4 @@
-# Bug component: 909476
+# Bug component: 1157642
# includes OWNERS from parent directories
natanieljr@google.com
pablogamito@google.com
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index 05278f2..4003f0b 100755
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -686,16 +686,14 @@
}
return data->ptr != nullptr;
}));
- inPlaceCallback(std::move(data.ptr), data.size);
- return STATUS_OK;
+ return inPlaceCallback(std::move(data.ptr), data.size);
} else if (type == BlobType::ASHMEM) {
int rawFd = -1;
int32_t size = 0;
ON_ERROR_RETURN(AParcel_readInt32(parcel, &size));
ON_ERROR_RETURN(AParcel_readParcelFileDescriptor(parcel, &rawFd));
android::base::unique_fd fd(rawFd);
- ashmemCallback(std::move(fd), size);
- return STATUS_OK;
+ return ashmemCallback(std::move(fd), size);
} else {
// Although the above if/else was "exhaustive" guard against unknown types
return STATUS_UNKNOWN_ERROR;
@@ -768,7 +766,7 @@
// framework, we may need to update this maximum size.
static constexpr size_t kMaxColorSpaceSerializedBytes = 80;
-static constexpr auto RuntimeException = "java/lang/RuntimeException";
+static constexpr auto BadParcelableException = "android/os/BadParcelableException";
static bool validateImageInfo(const SkImageInfo& info, int32_t rowBytes) {
// TODO: Can we avoid making a SkBitmap for this?
@@ -809,7 +807,7 @@
kRGB_565_SkColorType != colorType &&
kARGB_4444_SkColorType != colorType &&
kAlpha_8_SkColorType != colorType) {
- jniThrowExceptionFmt(env, RuntimeException,
+ jniThrowExceptionFmt(env, BadParcelableException,
"Bitmap_createFromParcel unknown colortype: %d\n", colorType);
return NULL;
}
@@ -821,7 +819,7 @@
return NULL;
}
if (!Bitmap::computeAllocationSize(rowBytes, height, &allocationSize)) {
- jniThrowExceptionFmt(env, RuntimeException,
+ jniThrowExceptionFmt(env, BadParcelableException,
"Received bad bitmap size: width=%d, height=%d, rowBytes=%d", width,
height, rowBytes);
return NULL;
@@ -831,13 +829,23 @@
p.get(),
// In place callback
[&](std::unique_ptr<int8_t[]> buffer, int32_t size) {
+ if (allocationSize > size) {
+ android_errorWriteLog(0x534e4554, "213169612");
+ return STATUS_BAD_VALUE;
+ }
nativeBitmap = Bitmap::allocateHeapBitmap(allocationSize, imageInfo, rowBytes);
if (nativeBitmap) {
- memcpy(nativeBitmap->pixels(), buffer.get(), size);
+ memcpy(nativeBitmap->pixels(), buffer.get(), allocationSize);
+ return STATUS_OK;
}
+ return STATUS_NO_MEMORY;
},
// Ashmem callback
[&](android::base::unique_fd fd, int32_t size) {
+ if (allocationSize > size) {
+ android_errorWriteLog(0x534e4554, "213169612");
+ return STATUS_BAD_VALUE;
+ }
int flags = PROT_READ;
if (isMutable) {
flags |= PROT_WRITE;
@@ -846,18 +854,21 @@
if (addr == MAP_FAILED) {
const int err = errno;
ALOGW("mmap failed, error %d (%s)", err, strerror(err));
- return;
+ return STATUS_NO_MEMORY;
}
nativeBitmap =
Bitmap::createFrom(imageInfo, rowBytes, fd.release(), addr, size, !isMutable);
+ return STATUS_OK;
});
- if (error != STATUS_OK) {
+
+ if (error != STATUS_OK && error != STATUS_NO_MEMORY) {
// TODO: Stringify the error, see signalExceptionForError in android_util_Binder.cpp
- jniThrowExceptionFmt(env, RuntimeException, "Failed to read from Parcel, error=%d", error);
+ jniThrowExceptionFmt(env, BadParcelableException, "Failed to read from Parcel, error=%d",
+ error);
return nullptr;
}
- if (!nativeBitmap) {
- jniThrowRuntimeException(env, "Could not allocate java pixel ref.");
+ if (error == STATUS_NO_MEMORY || !nativeBitmap) {
+ jniThrowRuntimeException(env, "Could not allocate bitmap data.");
return nullptr;
}
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
index a7ed091..4385a80 100644
--- a/media/TEST_MAPPING
+++ b/media/TEST_MAPPING
@@ -1,6 +1,14 @@
{
"presubmit": [
{
+ "name": "CtsCameraTestCases",
+ "options" : [
+ {
+ "include-filter": "android.hardware.camera2.cts.ImageReaderTest#testP010"
+ }
+ ]
+ },
+ {
"name": "GtsMediaTestCases",
"options" : [
{
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 77709d7..5c75e47 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -3634,6 +3634,7 @@
.parseIntRange(info.getString("quality-range"), mQualityRange);
}
if (info.containsKey("feature-bitrate-modes")) {
+ mBitControl = 0;
for (String mode: info.getString("feature-bitrate-modes").split(",")) {
mBitControl |= (1 << parseBitrateMode(mode));
}
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 522b021..050ae72 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -1147,7 +1147,7 @@
* A key describing the per-frame average block QP (Quantization Parameter).
* This is a part of a video 'Encoding Statistics' export feature.
* This value is emitted from video encoder for a video frame.
- * The average value is rounded down (using floor()) to integer value.
+ * The average value is rounded to the nearest integer value.
*
* The associated value is an integer.
*/
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 5174c0c..f8066fe 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -589,6 +589,12 @@
// (HAL_PIXEL_FORMAT_YCbCr_420_888) as HAL_PIXEL_FORMAT_YCbCr_420_888.
ALOGV("%s: Treat buffer format to 0x%x as HAL_PIXEL_FORMAT_YCbCr_420_888",
__FUNCTION__, bufferFormat);
+ } else if (imgReaderFmt == HAL_PIXEL_FORMAT_YCBCR_P010 &&
+ isPossibly10BitYUV(bufferFormat)) {
+ // Treat formats that are compatible with flexible 10-bit YUV
+ // (HAL_PIXEL_FORMAT_YCBCR_P010) as HAL_PIXEL_FORMAT_YCBCR_P010.
+ ALOGV("%s: Treat buffer format to 0x%x as HAL_PIXEL_FORMAT_YCBCR_P010",
+ __FUNCTION__, bufferFormat);
} else if (imgReaderFmt == HAL_PIXEL_FORMAT_BLOB &&
bufferFormat == HAL_PIXEL_FORMAT_RGBA_8888) {
// Using HAL_PIXEL_FORMAT_RGBA_8888 Gralloc buffers containing JPEGs to get around
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index 39b560b..f4a39b3 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -17,7 +17,10 @@
// #define LOG_NDEBUG 0
#define LOG_TAG "AndroidMediaUtils"
+#include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h>
#include <hardware/camera3.h>
+#include <ui/GraphicBufferMapper.h>
+#include <ui/GraphicTypes.h>
#include <utils/Log.h>
#include "android_media_Utils.h"
@@ -81,6 +84,32 @@
}
}
+bool isPossibly10BitYUV(PixelFormat format) {
+ switch (static_cast<int>(format)) {
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ case HAL_PIXEL_FORMAT_RGB_888:
+ case HAL_PIXEL_FORMAT_RGB_565:
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ case HAL_PIXEL_FORMAT_Y8:
+ case HAL_PIXEL_FORMAT_Y16:
+ case HAL_PIXEL_FORMAT_RAW16:
+ case HAL_PIXEL_FORMAT_RAW12:
+ case HAL_PIXEL_FORMAT_RAW10:
+ case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+ case HAL_PIXEL_FORMAT_BLOB:
+ case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+ case HAL_PIXEL_FORMAT_YV12:
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ return false;
+
+ case HAL_PIXEL_FORMAT_YCBCR_P010:
+ default:
+ return true;
+ }
+}
+
uint32_t Image_getBlobSize(LockedImage* buffer, bool usingRGBAOverride) {
ALOGV("%s", __FUNCTION__);
LOG_ALWAYS_FATAL_IF(buffer == NULL, "Input buffer is NULL!!!");
@@ -279,6 +308,27 @@
return BAD_VALUE;
}
+ if (buffer->dataCb && buffer->dataCr) {
+ pData =
+ (idx == 0) ?
+ buffer->data :
+ (idx == 1) ?
+ buffer->dataCb :
+ buffer->dataCr;
+ // only map until last pixel
+ if (idx == 0) {
+ pStride = 2;
+ rStride = buffer->stride;
+ dataSize = buffer->stride * (buffer->height - 1) + buffer->width * 2;
+ } else {
+ pStride = buffer->chromaStep;
+ rStride = buffer->chromaStride;
+ dataSize = buffer->chromaStride * (buffer->height / 2 - 1) +
+ buffer->chromaStep * (buffer->width / 2);
+ }
+ break;
+ }
+
ySize = (buffer->stride * 2) * buffer->height;
cSize = ySize / 2;
pStride = (idx == 0) ? 2 : 4;
@@ -404,6 +454,7 @@
rStride = buffer->stride * 3;
break;
default:
+ ALOGV("%s: unrecognized format 0x%x", __FUNCTION__, fmt);
return BAD_VALUE;
}
@@ -415,6 +466,79 @@
return OK;
}
+static status_t extractP010Gralloc4PlaneLayout(
+ sp<GraphicBuffer> buffer, void *pData, int format, LockedImage *outputImage) {
+ using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
+ using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+
+ GraphicBufferMapper& mapper = GraphicBufferMapper::get();
+ std::vector<ui::PlaneLayout> planeLayouts;
+ status_t res = mapper.getPlaneLayouts(buffer->handle, &planeLayouts);
+ if (res != OK) {
+ return res;
+ }
+ constexpr int64_t Y_PLANE_COMPONENTS = int64_t(PlaneLayoutComponentType::Y);
+ constexpr int64_t CBCR_PLANE_COMPONENTS =
+ int64_t(PlaneLayoutComponentType::CB) | int64_t(PlaneLayoutComponentType::CR);
+ uint8_t *dataY = nullptr;
+ uint8_t *dataCb = nullptr;
+ uint8_t *dataCr = nullptr;
+ uint32_t strideY = 0;
+ uint32_t strideCbCr = 0;
+ for (const ui::PlaneLayout &layout : planeLayouts) {
+ ALOGV("gralloc4 plane: %s", layout.toString().c_str());
+ int64_t components = 0;
+ for (const PlaneLayoutComponent &component : layout.components) {
+ if (component.sizeInBits != 10) {
+ return BAD_VALUE;
+ }
+ components |= component.type.value;
+ }
+ if (components == Y_PLANE_COMPONENTS) {
+ if (layout.sampleIncrementInBits != 16) {
+ return BAD_VALUE;
+ }
+ if (layout.components[0].offsetInBits != 6) {
+ return BAD_VALUE;
+ }
+ dataY = (uint8_t *)pData + layout.offsetInBytes;
+ strideY = layout.strideInBytes;
+ } else if (components == CBCR_PLANE_COMPONENTS) {
+ if (layout.sampleIncrementInBits != 32) {
+ return BAD_VALUE;
+ }
+ for (const PlaneLayoutComponent &component : layout.components) {
+ if (component.type.value == int64_t(PlaneLayoutComponentType::CB)
+ && component.offsetInBits != 6) {
+ return BAD_VALUE;
+ }
+ if (component.type.value == int64_t(PlaneLayoutComponentType::CR)
+ && component.offsetInBits != 22) {
+ return BAD_VALUE;
+ }
+ }
+ dataCb = (uint8_t *)pData + layout.offsetInBytes;
+ dataCr = (uint8_t *)pData + layout.offsetInBytes + 2;
+ strideCbCr = layout.strideInBytes;
+ } else {
+ return BAD_VALUE;
+ }
+ }
+
+ outputImage->data = dataY;
+ outputImage->width = buffer->getWidth();
+ outputImage->height = buffer->getHeight();
+ outputImage->format = format;
+ outputImage->flexFormat = HAL_PIXEL_FORMAT_YCBCR_P010;
+ outputImage->stride = strideY;
+
+ outputImage->dataCb = dataCb;
+ outputImage->dataCr = dataCr;
+ outputImage->chromaStride = strideCbCr;
+ outputImage->chromaStep = 4;
+ return OK;
+}
+
status_t lockImageFromBuffer(sp<GraphicBuffer> buffer, uint32_t inUsage,
const Rect& rect, int fenceFd, LockedImage* outputImage) {
ALOGV("%s: Try to lock the GraphicBuffer", __FUNCTION__);
@@ -433,11 +557,12 @@
status_t res;
int format = buffer->getPixelFormat();
int flexFormat = format;
+
if (isPossiblyYUV(format)) {
res = buffer->lockAsyncYCbCr(inUsage, rect, &ycbcr, fenceFd);
if (res != OK) {
- ALOGW("lockAsyncYCbCr failed with error %d", res);
+ ALOGW("lockAsyncYCbCr failed with error %d (format = 0x%x)", res, format);
}
pData = ycbcr.y;
@@ -451,6 +576,11 @@
ALOGE("Lock buffer failed!");
return res;
}
+ if (isPossibly10BitYUV(format)
+ && OK == extractP010Gralloc4PlaneLayout(buffer, pData, format, outputImage)) {
+ ALOGV("%s: Successfully locked the P010 image", __FUNCTION__);
+ return OK;
+ }
}
outputImage->data = reinterpret_cast<uint8_t*>(pData);
diff --git a/media/jni/android_media_Utils.h b/media/jni/android_media_Utils.h
index 12841c0..4feb4f51 100644
--- a/media/jni/android_media_Utils.h
+++ b/media/jni/android_media_Utils.h
@@ -35,6 +35,8 @@
bool isPossiblyYUV(PixelFormat format);
+bool isPossibly10BitYUV(PixelFormat format);
+
status_t getLockedImageInfo(LockedImage* buffer, int idx, int32_t containerFormat,
uint8_t **base, uint32_t *size, int *pixelStride, int *rowStride);
diff --git a/mms/OWNERS b/mms/OWNERS
index 2e419c1..7daef4e 100644
--- a/mms/OWNERS
+++ b/mms/OWNERS
@@ -1,18 +1,4 @@
set noparent
-tgunn@google.com
-breadley@google.com
-rgreenwalt@google.com
-amitmahajan@google.com
-fionaxu@google.com
-jackyu@google.com
-jminjie@google.com
-satk@google.com
-shuoq@google.com
-sarahchin@google.com
-xiaotonj@google.com
-huiwang@google.com
-jayachandranc@google.com
-chinmayd@google.com
-amruthr@google.com
-sasindran@google.com
+file:platform/frameworks/base:/telephony/OWNERS
+
diff --git a/native/android/libandroid_net.map.txt b/native/android/libandroid_net.map.txt
index 32fd734..e9acdae 100644
--- a/native/android/libandroid_net.map.txt
+++ b/native/android/libandroid_net.map.txt
@@ -5,20 +5,20 @@
# which might be a few years old.
LIBANDROID_NET {
global:
- # These functions have been part of the NDK since API 24.
+ # These functions have been part of the LL-NDK since API 24.
android_getaddrinfofornetwork; # llndk
android_setsocknetwork; # llndk
android_setprocnetwork; # llndk
- # These functions have been part of the NDK since API 29.
+ # These functions have been part of the LL-NDK since API 29.
android_res_cancel; # llndk
android_res_nquery; # llndk
android_res_nresult; # llndk
android_res_nsend; # llndk
- # These functions have been part of the NDK since API 31.
+ # These functions have been part of the LL-NDK since API 31.
android_getprocnetwork; # llndk
android_setprocdns; # llndk
android_getprocdns; # llndk
- # These functions have been part of the NDK since API 33.
+ # These functions have been part of the LL-NDK since API 33.
android_tag_socket_with_uid; # llndk
android_tag_socket; # llndk
android_untag_socket; # llndk
diff --git a/native/android/net.c b/native/android/net.c
index d7c22e1..74db184 100644
--- a/native/android/net.c
+++ b/native/android/net.c
@@ -162,11 +162,11 @@
resNetworkCancel(nsend_fd);
}
-int android_tag_socket_with_uid(int sockfd, int tag, uid_t uid) {
+int android_tag_socket_with_uid(int sockfd, uint32_t tag, uid_t uid) {
return tagSocket(sockfd, tag, uid);
}
-int android_tag_socket(int sockfd, int tag) {
+int android_tag_socket(int sockfd, uint32_t tag) {
return tagSocket(sockfd, tag, -1);
}
diff --git a/omapi/java/android/se/omapi/SEService.java b/omapi/java/android/se/omapi/SEService.java
index f42ca36..306c09a 100644
--- a/omapi/java/android/se/omapi/SEService.java
+++ b/omapi/java/android/se/omapi/SEService.java
@@ -118,6 +118,16 @@
});
}
}
+
+ @Override
+ public String getInterfaceHash() {
+ return ISecureElementListener.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return ISecureElementListener.VERSION;
+ }
}
private SEListener mSEListener = new SEListener();
diff --git a/packages/CarrierDefaultApp/OWNERS b/packages/CarrierDefaultApp/OWNERS
index a2352e2..447cd51 100644
--- a/packages/CarrierDefaultApp/OWNERS
+++ b/packages/CarrierDefaultApp/OWNERS
@@ -1,18 +1,3 @@
set noparent
-tgunn@google.com
-breadley@google.com
-rgreenwalt@google.com
-amitmahajan@google.com
-fionaxu@google.com
-jackyu@google.com
-jminjie@google.com
-satk@google.com
-shuoq@google.com
-sarahchin@google.com
-xiaotonj@google.com
-huiwang@google.com
-jayachandranc@google.com
-chinmayd@google.com
-amruthr@google.com
-sasindran@google.com
+file:platform/frameworks/base:/telephony/OWNERS
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 3a35659..5562684 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -110,6 +110,7 @@
private static final int EVENT_DSU_PROGRESS_UPDATE = 120000;
private static final int EVENT_DSU_INSTALL_COMPLETE = 120001;
private static final int EVENT_DSU_INSTALL_FAILED = 120002;
+ private static final int EVENT_DSU_INSTALL_INSUFFICIENT_SPACE = 120003;
protected static void logEventProgressUpdate(
String partitionName,
@@ -136,6 +137,10 @@
EventLog.writeEvent(EVENT_DSU_INSTALL_FAILED, cause);
}
+ protected static void logEventInsufficientSpace() {
+ EventLog.writeEvent(EVENT_DSU_INSTALL_INSUFFICIENT_SPACE);
+ }
+
/*
* IPC
*/
@@ -258,6 +263,8 @@
if (result == RESULT_CANCELLED) {
logEventFailed("Dynamic System installation task is canceled by the user.");
+ } else if (detail instanceof InstallationAsyncTask.InsufficientSpaceException) {
+ logEventInsufficientSpace();
} else {
logEventFailed("error: " + detail);
}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/EventLogTags.logtags b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/EventLogTags.logtags
index 4073143..8d8be10 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/EventLogTags.logtags
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/EventLogTags.logtags
@@ -5,3 +5,4 @@
120000 dsu_progress_update (partition_name|3),(installed_bytes|2|5),(total_bytes|2|5),(partition_number|1|5),(total_partition_number|1|5),(total_progress_percentage|1|5)
120001 dsu_install_complete
120002 dsu_install_failed (cause|3)
+120003 dsu_install_insufficient_space
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 2af7e00..a41399f 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -16,17 +16,20 @@
package com.android.dynsystem;
+import android.annotation.NonNull;
import android.content.Context;
import android.gsi.AvbPublicKey;
+import android.gsi.IGsiService;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
-import android.os.MemoryFile;
-import android.os.ParcelFileDescriptor;
+import android.os.SharedMemory;
import android.os.SystemProperties;
import android.os.image.DynamicSystemManager;
import android.service.persistentdata.PersistentDataBlockManager;
+import android.system.ErrnoException;
import android.util.Log;
+import android.util.Pair;
import android.util.Range;
import android.webkit.URLUtil;
@@ -37,10 +40,15 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
+import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -51,8 +59,8 @@
private static final String TAG = "InstallationAsyncTask";
private static final int MIN_SHARED_MEMORY_SIZE = 8 << 10; // 8KiB
- private static final int MAX_SHARED_MEMORY_SIZE = 1024 << 10; // 1MiB
- private static final int DEFAULT_SHARED_MEMORY_SIZE = 64 << 10; // 64KiB
+ private static final int MAX_SHARED_MEMORY_SIZE = 8 << 20; // 8MiB
+ private static final int DEFAULT_SHARED_MEMORY_SIZE = 512 << 10; // 512KiB
private static final String SHARED_MEMORY_SIZE_PROP =
"dynamic_system.data_transfer.shared_memory.size";
@@ -102,8 +110,15 @@
}
}
+ static class InsufficientSpaceException extends IOException {
+ InsufficientSpaceException(String message) {
+ super(message);
+ }
+ }
+
/** UNSET means the installation is not completed */
static final int RESULT_UNSET = 0;
+
static final int RESULT_OK = 1;
static final int RESULT_CANCELLED = 2;
static final int RESULT_ERROR_IO = 3;
@@ -141,6 +156,22 @@
void onResult(int resultCode, Throwable detail);
}
+ private static class MappedMemoryBuffer implements AutoCloseable {
+ public ByteBuffer mBuffer;
+
+ MappedMemoryBuffer(@NonNull ByteBuffer buffer) {
+ mBuffer = buffer;
+ }
+
+ @Override
+ public void close() {
+ if (mBuffer != null) {
+ SharedMemory.unmap(mBuffer);
+ mBuffer = null;
+ }
+ }
+ }
+
private final int mSharedMemorySize;
private final String mUrl;
private final String mDsuSlot;
@@ -153,6 +184,7 @@
private final boolean mIsNetworkUrl;
private final boolean mIsDeviceBootloaderUnlocked;
private final boolean mWantScratchPartition;
+ private int mCreatePartitionStatus;
private DynamicSystemManager.Session mInstallationSession;
private KeyRevocationList mKeyRevocationList;
@@ -360,7 +392,7 @@
mIsZip = true;
} else {
throw new UnsupportedFormatException(
- String.format(Locale.US, "Unsupported file format: %s", mUrl));
+ String.format(Locale.US, "Unsupported file format: %s", mUrl));
}
if (mIsNetworkUrl) {
@@ -431,14 +463,19 @@
throws IOException {
Log.d(TAG, "Creating writable partition: " + partitionName + ", size: " + partitionSize);
- Thread thread = new Thread() {
- @Override
- public void run() {
- mInstallationSession =
- mDynSystem.createPartition(
- partitionName, partitionSize, /* readOnly= */ false);
- }
- };
+ mCreatePartitionStatus = 0;
+ mInstallationSession = null;
+ Thread thread =
+ new Thread() {
+ @Override
+ public void run() {
+ Pair<Integer, DynamicSystemManager.Session> result =
+ mDynSystem.createPartition(
+ partitionName, partitionSize, /* readOnly = */ false);
+ mCreatePartitionStatus = result.first;
+ mInstallationSession = result.second;
+ }
+ };
initPartitionProgress(partitionName, partitionSize, /* readonly = */ false);
publishProgress(/* installedSize = */ 0L);
@@ -464,13 +501,17 @@
}
}
- if (prevInstalledSize != partitionSize) {
- publishProgress(partitionSize);
- }
-
if (mInstallationSession == null) {
- throw new IOException(
- "Failed to start installation with requested size: " + partitionSize);
+ if (mCreatePartitionStatus == IGsiService.INSTALL_ERROR_NO_SPACE
+ || mCreatePartitionStatus == IGsiService.INSTALL_ERROR_FILE_SYSTEM_CLUTTERED) {
+ throw new InsufficientSpaceException(
+ "Failed to create "
+ + partitionName
+ + " partition: storage media has insufficient free space");
+ } else {
+ throw new IOException(
+ "Failed to start installation with requested size: " + partitionSize);
+ }
}
// Reset installation session and verify that installation completes successfully.
@@ -478,6 +519,11 @@
if (!mDynSystem.closePartition()) {
throw new IOException("Failed to complete partition installation: " + partitionName);
}
+
+ // Ensure a 100% mark is published.
+ if (prevInstalledSize != partitionSize) {
+ publishProgress(partitionSize);
+ }
}
private void installScratch() throws IOException {
@@ -488,7 +534,7 @@
installWritablePartition("userdata", mUserdataSize);
}
- private void installImages() throws IOException, ImageValidationException {
+ private void installImages() throws ExecutionException, IOException, ImageValidationException {
if (mStream != null) {
if (mIsZip) {
installStreamingZipUpdate();
@@ -500,7 +546,8 @@
}
}
- private void installStreamingGzUpdate() throws IOException, ImageValidationException {
+ private void installStreamingGzUpdate()
+ throws ExecutionException, IOException, ImageValidationException {
Log.d(TAG, "To install a streaming GZ update");
installImage("system", mSystemSize, new GZIPInputStream(mStream));
}
@@ -528,7 +575,8 @@
return total;
}
- private void installStreamingZipUpdate() throws IOException, ImageValidationException {
+ private void installStreamingZipUpdate()
+ throws ExecutionException, IOException, ImageValidationException {
Log.d(TAG, "To install a streaming ZIP update");
ZipInputStream zis = new ZipInputStream(mStream);
@@ -548,7 +596,8 @@
}
}
- private void installLocalZipUpdate() throws IOException, ImageValidationException {
+ private void installLocalZipUpdate()
+ throws ExecutionException, IOException, ImageValidationException {
Log.d(TAG, "To install a local ZIP update");
Enumeration<? extends ZipEntry> entries = mZipFile.entries();
@@ -569,7 +618,7 @@
}
private void installImageFromAnEntry(ZipEntry entry, InputStream is)
- throws IOException, ImageValidationException {
+ throws ExecutionException, IOException, ImageValidationException {
String name = entry.getName();
Log.d(TAG, "ZipEntry: " + name);
@@ -581,7 +630,7 @@
}
private void installImage(String partitionName, long uncompressedSize, InputStream is)
- throws IOException, ImageValidationException {
+ throws ExecutionException, IOException, ImageValidationException {
SparseInputStream sis = new SparseInputStream(new BufferedInputStream(is));
@@ -599,10 +648,19 @@
throw new IOException("Cannot get raw size for " + partitionName);
}
- Thread thread = new Thread(() -> {
- mInstallationSession =
- mDynSystem.createPartition(partitionName, partitionSize, true);
- });
+ mCreatePartitionStatus = 0;
+ mInstallationSession = null;
+ Thread thread =
+ new Thread() {
+ @Override
+ public void run() {
+ Pair<Integer, DynamicSystemManager.Session> result =
+ mDynSystem.createPartition(
+ partitionName, partitionSize, /* readOnly = */ true);
+ mCreatePartitionStatus = result.first;
+ mInstallationSession = result.second;
+ }
+ };
Log.d(TAG, "Start creating partition: " + partitionName);
thread.start();
@@ -620,46 +678,80 @@
}
if (mInstallationSession == null) {
- throw new IOException(
- "Failed to start installation with requested size: " + partitionSize);
+ if (mCreatePartitionStatus == IGsiService.INSTALL_ERROR_NO_SPACE
+ || mCreatePartitionStatus == IGsiService.INSTALL_ERROR_FILE_SYSTEM_CLUTTERED) {
+ throw new InsufficientSpaceException(
+ "Failed to create "
+ + partitionName
+ + " partition: storage media has insufficient free space");
+ } else {
+ throw new IOException(
+ "Failed to start installation with requested size: " + partitionSize);
+ }
}
Log.d(TAG, "Start installing: " + partitionName);
- MemoryFile memoryFile = new MemoryFile("dsu_" + partitionName, mSharedMemorySize);
- ParcelFileDescriptor pfd = new ParcelFileDescriptor(memoryFile.getFileDescriptor());
-
- mInstallationSession.setAshmem(pfd, memoryFile.length());
-
- initPartitionProgress(partitionName, partitionSize, /* readonly = */ true);
- publishProgress(/* installedSize = */ 0L);
-
long prevInstalledSize = 0;
- long installedSize = 0;
- byte[] bytes = new byte[memoryFile.length()];
- int numBytesRead;
+ try (SharedMemory sharedMemory =
+ SharedMemory.create("dsu_buffer_" + partitionName, mSharedMemorySize);
+ MappedMemoryBuffer mappedBuffer =
+ new MappedMemoryBuffer(sharedMemory.mapReadWrite())) {
+ mInstallationSession.setAshmem(sharedMemory.getFdDup(), sharedMemory.getSize());
- while ((numBytesRead = sis.read(bytes, 0, bytes.length)) != -1) {
- if (isCancelled()) {
- return;
+ initPartitionProgress(partitionName, partitionSize, /* readonly = */ true);
+ publishProgress(/* installedSize = */ 0L);
+
+ long installedSize = 0;
+ byte[] readBuffer = new byte[sharedMemory.getSize()];
+ ByteBuffer buffer = mappedBuffer.mBuffer;
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ Future<Boolean> submitPromise = null;
+
+ while (true) {
+ final int numBytesRead = sis.read(readBuffer, 0, readBuffer.length);
+
+ if (submitPromise != null) {
+ // Wait until the previous submit task is complete.
+ while (true) {
+ try {
+ if (!submitPromise.get()) {
+ throw new IOException("Failed submitFromAshmem() to DynamicSystem");
+ }
+ break;
+ } catch (InterruptedException e) {
+ // Ignore.
+ }
+ }
+
+ // Publish the progress of the previous submit task.
+ if (installedSize > prevInstalledSize + MIN_PROGRESS_TO_PUBLISH) {
+ publishProgress(installedSize);
+ prevInstalledSize = installedSize;
+ }
+ }
+
+ // Ensure the previous submit task (submitPromise) is complete before exiting the
+ // loop.
+ if (numBytesRead < 0) {
+ break;
+ }
+
+ if (isCancelled()) {
+ return;
+ }
+
+ buffer.position(0);
+ buffer.put(readBuffer, 0, numBytesRead);
+ submitPromise =
+ executor.submit(() -> mInstallationSession.submitFromAshmem(numBytesRead));
+
+ // Even though we update the bytes counter here, the actual progress is updated only
+ // after the submit task (submitPromise) is complete.
+ installedSize += numBytesRead;
}
-
- memoryFile.writeBytes(bytes, 0, 0, numBytesRead);
-
- if (!mInstallationSession.submitFromAshmem(numBytesRead)) {
- throw new IOException("Failed write() to DynamicSystem");
- }
-
- installedSize += numBytesRead;
-
- if (installedSize > prevInstalledSize + MIN_PROGRESS_TO_PUBLISH) {
- publishProgress(installedSize);
- prevInstalledSize = installedSize;
- }
- }
-
- if (prevInstalledSize != partitionSize) {
- publishProgress(partitionSize);
+ } catch (ErrnoException e) {
+ e.rethrowAsIOException();
}
AvbPublicKey avbPublicKey = new AvbPublicKey();
@@ -677,6 +769,11 @@
if (!mDynSystem.closePartition()) {
throw new IOException("Failed to complete partition installation: " + partitionName);
}
+
+ // Ensure a 100% mark is published.
+ if (prevInstalledSize != partitionSize) {
+ publishProgress(partitionSize);
+ }
}
private static String toHexString(byte[] bytes) {
diff --git a/packages/SettingsLib/res/drawable/ic_4g_lte_mobiledata.xml b/packages/SettingsLib/res/drawable/ic_4g_lte_mobiledata.xml
new file mode 100644
index 0000000..43a82fa
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_4g_lte_mobiledata.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="42.3281dp"
+ android:height="24dp"
+ android:viewportWidth="42.3281"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M10.7871,12.959H11.8477V14.1016H10.7871V16H9.3633V14.1016H5.6777L5.6367,13.2344L9.3164,7.4688H10.7871V12.959ZM7.1309,12.959H9.3633V9.3965L9.2578,9.584L7.1309,12.959ZM19.5586,14.8926C19.25,15.2949 18.8223,15.5996 18.2754,15.8066C17.7285,16.0137 17.1074,16.1172 16.4121,16.1172C15.6973,16.1172 15.0645,15.9551 14.5137,15.6309C13.9629,15.3066 13.5371,14.8438 13.2363,14.2422C12.9395,13.6367 12.7852,12.9316 12.7734,12.127V11.459C12.7734,10.1699 13.082,9.1641 13.6992,8.4414C14.3164,7.7148 15.1777,7.3516 16.2832,7.3516C17.2324,7.3516 17.9863,7.5859 18.5449,8.0547C19.1035,8.5234 19.4395,9.1992 19.5527,10.082H18.0996C17.9355,9.0547 17.3398,8.541 16.3125,8.541C15.6484,8.541 15.1426,8.7813 14.7949,9.2617C14.4512,9.7383 14.2734,10.4395 14.2617,11.3652V12.0215C14.2617,12.9434 14.4551,13.6602 14.8418,14.1719C15.2324,14.6797 15.7734,14.9336 16.4648,14.9336C17.2227,14.9336 17.7617,14.7617 18.082,14.418V12.748H16.3242V11.623H19.5586V14.8926ZM25.6582,14.8164H29.5312V16H24.1758V7.4688H25.6582V14.8164ZM35.625,8.6641H32.9648V16H31.4941V8.6641H28.8574V7.4688H35.625V8.6641ZM41.7363,12.1914H38.2324V14.8164H42.3281V16H36.75V7.4688H42.2871V8.6641H38.2324V11.0195H41.7363V12.1914Z" />
+
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_4g_lte_plus_mobiledata.xml b/packages/SettingsLib/res/drawable/ic_4g_lte_plus_mobiledata.xml
new file mode 100644
index 0000000..b80c47a
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_4g_lte_plus_mobiledata.xml
@@ -0,0 +1,29 @@
+<!--
+ Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="49.65dp"
+ android:height="24dp"
+ android:viewportWidth="49.65"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M10.7871,12.959H11.8477V14.1016H10.7871V16H9.3633V14.1016H5.6777L5.6367,13.2344L9.3164,7.4688H10.7871V12.959ZM7.1309,12.959H9.3633V9.3965L9.2578,9.584L7.1309,12.959ZM19.5586,14.8926C19.25,15.2949 18.8223,15.5996 18.2754,15.8066C17.7285,16.0137 17.1074,16.1172 16.4121,16.1172C15.6973,16.1172 15.0645,15.9551 14.5137,15.6309C13.9629,15.3066 13.5371,14.8438 13.2363,14.2422C12.9395,13.6367 12.7852,12.9316 12.7734,12.127V11.459C12.7734,10.1699 13.082,9.1641 13.6992,8.4414C14.3164,7.7148 15.1777,7.3516 16.2832,7.3516C17.2324,7.3516 17.9863,7.5859 18.5449,8.0547C19.1035,8.5234 19.4395,9.1992 19.5527,10.082H18.0996C17.9355,9.0547 17.3398,8.541 16.3125,8.541C15.6484,8.541 15.1426,8.7813 14.7949,9.2617C14.4512,9.7383 14.2734,10.4395 14.2617,11.3652V12.0215C14.2617,12.9434 14.4551,13.6602 14.8418,14.1719C15.2324,14.6797 15.7734,14.9336 16.4648,14.9336C17.2227,14.9336 17.7617,14.7617 18.082,14.418V12.748H16.3242V11.623H19.5586V14.8926ZM25.6582,14.8164H29.5312V16H24.1758V7.4688H25.6582V14.8164ZM35.625,8.6641H32.9648V16H31.4941V8.6641H28.8574V7.4688H35.625V8.6641ZM41.7363,12.1914H38.2324V14.8164H42.3281V16H36.75V7.4688H42.2871V8.6641H38.2324V11.0195H41.7363V12.1914Z" />
+
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M 47.3 9.74L 47.3 7.39L 46 7.39L 46 9.74L 43.65 9.74L 43.65 11.04L 46 11.04L 46 13.39L 47.3 13.39L 47.3 11.04L 49.65 11.04L 49.65 9.74Z" />
+
+</vector>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 534e41c..6ca15c1 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1478,6 +1478,12 @@
<!-- Content description of the data connection type LTE+. [CHAR LIMIT=NONE] -->
<string name="data_connection_lte_plus">LTE+</string>
+ <!-- Content description of the data connection type 4G LTE . [CHAR LIMIT=NONE] -->
+ <string name="data_connection_4g_lte" translatable="false">4G LTE</string>
+
+ <!-- Content description of the data connection type 4G LTE+ . [CHAR LIMIT=NONE] -->
+ <string name="data_connection_4g_lte_plus" translatable="false">4G LTE+</string>
+
<!-- Content description of the data connection type 5Ge with HTML styling. DO NOT TRANSLATE [CHAR LIMIT=NONE] -->
<string name="data_connection_5ge_html" translate="false"> <i>5G <small>E</small></i> </string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java
index 5e91a14..c941954 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileMappings.java
@@ -160,6 +160,19 @@
TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA),
TelephonyIcons.FOUR_G_PLUS);
}
+ } else if (config.show4glteForLte) {
+ networkToIconLookup.put(toIconKey(
+ TelephonyManager.NETWORK_TYPE_LTE),
+ TelephonyIcons.FOUR_G_LTE);
+ if (config.hideLtePlus) {
+ networkToIconLookup.put(toDisplayIconKey(
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA),
+ TelephonyIcons.FOUR_G_LTE);
+ } else {
+ networkToIconLookup.put(toDisplayIconKey(
+ TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA),
+ TelephonyIcons.FOUR_G_LTE_PLUS);
+ }
} else {
networkToIconLookup.put(toIconKey(
TelephonyManager.NETWORK_TYPE_LTE),
@@ -200,6 +213,7 @@
public boolean show4gFor3g = false;
public boolean alwaysShowCdmaRssi = false;
public boolean show4gForLte = false;
+ public boolean show4glteForLte = false;
public boolean hideLtePlus = false;
public boolean hspaDataDistinguishable;
public boolean alwaysShowDataRatIcon = false;
@@ -228,6 +242,8 @@
CarrierConfigManager.KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL);
config.show4gForLte = b.getBoolean(
CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL);
+ config.show4glteForLte = b.getBoolean(
+ CarrierConfigManager.KEY_SHOW_4GLTE_FOR_LTE_DATA_ICON_BOOL);
config.show4gFor3g = b.getBoolean(
CarrierConfigManager.KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL);
config.hideLtePlus = b.getBoolean(
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
index d4e58f7..23e0923 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/TelephonyIcons.java
@@ -39,6 +39,8 @@
public static final int ICON_3G = R.drawable.ic_3g_mobiledata;
public static final int ICON_4G = R.drawable.ic_4g_mobiledata;
public static final int ICON_4G_PLUS = R.drawable.ic_4g_plus_mobiledata;
+ public static final int ICON_4G_LTE = R.drawable.ic_4g_lte_mobiledata;
+ public static final int ICON_4G_LTE_PLUS = R.drawable.ic_4g_lte_plus_mobiledata;
public static final int ICON_5G_E = R.drawable.ic_5g_e_mobiledata;
public static final int ICON_1X = R.drawable.ic_1x_mobiledata;
public static final int ICON_5G = R.drawable.ic_5g_mobiledata;
@@ -225,6 +227,34 @@
TelephonyIcons.ICON_LTE_PLUS
);
+ public static final MobileIconGroup FOUR_G_LTE = new MobileIconGroup(
+ "4G LTE",
+ null,
+ null,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
+ 0,
+ 0,
+ 0,
+ 0,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
+ R.string.data_connection_4g_lte,
+ TelephonyIcons.ICON_4G_LTE
+ );
+
+ public static final MobileIconGroup FOUR_G_LTE_PLUS = new MobileIconGroup(
+ "4G LTE+",
+ null,
+ null,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
+ 0,
+ 0,
+ 0,
+ 0,
+ AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
+ R.string.data_connection_4g_lte_plus,
+ TelephonyIcons.ICON_4G_LTE_PLUS
+ );
+
public static final MobileIconGroup LTE_CA_5G_E = new MobileIconGroup(
"5Ge",
null,
@@ -327,6 +357,8 @@
ICON_NAME_TO_ICON.put("h+", H_PLUS);
ICON_NAME_TO_ICON.put("4g", FOUR_G);
ICON_NAME_TO_ICON.put("4g+", FOUR_G_PLUS);
+ ICON_NAME_TO_ICON.put("4glte", FOUR_G_LTE);
+ ICON_NAME_TO_ICON.put("4glte+", FOUR_G_LTE_PLUS);
ICON_NAME_TO_ICON.put("5ge", LTE_CA_5G_E);
ICON_NAME_TO_ICON.put("lte", LTE);
ICON_NAME_TO_ICON.put("lte+", LTE_PLUS);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index feee4a1..208bfbbf 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -471,6 +471,9 @@
<!-- Permission needed for CTS test - MatchContentFrameRateTest -->
<uses-permission android:name="android.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE" />
+ <!-- Permissions needed for manual testing telephony time zone detector behavior -->
+ <uses-permission android:name="android.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE" />
+
<!-- Permissions needed for CTS test - TimeManagerTest -->
<uses-permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" />
<uses-permission android:name="android.permission.SUGGEST_EXTERNAL_TIME" />
@@ -606,6 +609,9 @@
<!-- Permission required for ATS test - CarDevicePolicyManagerTest -->
<uses-permission android:name="android.permission.LOCK_DEVICE" />
+ <!-- Permission required for CTS test - CtsKeystoreTestCases -->
+ <uses-permission android:name="android.permission.REQUEST_UNIQUE_ID_ATTESTATION" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 6c8a92d..4b07eaf 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -73,6 +73,7 @@
zakcohen@google.com
jernej@google.com
jglazier@google.com
+peskal@google.com
#Android Auto
hseog@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 96ae646..290bf0d 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -41,26 +41,23 @@
import javax.inject.Inject;
-import dagger.Lazy;
-
public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks {
private final Context mContext;
- private final Lazy<GlobalActionsDialogLite> mGlobalActionsDialogLazy;
private final KeyguardStateController mKeyguardStateController;
private final DeviceProvisionedController mDeviceProvisionedController;
private final BlurUtils mBlurUtils;
private final CommandQueue mCommandQueue;
- private GlobalActionsDialogLite mGlobalActionsDialog;
+ private final GlobalActionsDialogLite mGlobalActionsDialog;
private boolean mDisabled;
@Inject
public GlobalActionsImpl(Context context, CommandQueue commandQueue,
- Lazy<GlobalActionsDialogLite> globalActionsDialogLazy, BlurUtils blurUtils,
+ GlobalActionsDialogLite globalActionsDialog, BlurUtils blurUtils,
KeyguardStateController keyguardStateController,
DeviceProvisionedController deviceProvisionedController) {
mContext = context;
- mGlobalActionsDialogLazy = globalActionsDialogLazy;
+ mGlobalActionsDialog = globalActionsDialog;
mKeyguardStateController = keyguardStateController;
mDeviceProvisionedController = deviceProvisionedController;
mCommandQueue = commandQueue;
@@ -71,16 +68,12 @@
@Override
public void destroy() {
mCommandQueue.removeCallback(this);
- if (mGlobalActionsDialog != null) {
- mGlobalActionsDialog.destroy();
- mGlobalActionsDialog = null;
- }
+ mGlobalActionsDialog.destroy();
}
@Override
public void showGlobalActions(GlobalActionsManager manager) {
if (mDisabled) return;
- mGlobalActionsDialog = mGlobalActionsDialogLazy.get();
mGlobalActionsDialog.showOrHideDialog(mKeyguardStateController.isShowing(),
mDeviceProvisionedController.isDeviceProvisioned(), null /* view */);
}
@@ -189,7 +182,7 @@
final boolean disabled = (state2 & DISABLE2_GLOBAL_ACTIONS) != 0;
if (displayId != mContext.getDisplayId() || disabled == mDisabled) return;
mDisabled = disabled;
- if (disabled && mGlobalActionsDialog != null) {
+ if (disabled) {
mGlobalActionsDialog.dismissDialog();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index e9dea65..d558336 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -237,7 +237,7 @@
String subjectDate = DateFormat.getDateTimeInstance().format(new Date(mImageTime));
String subject = String.format(SCREENSHOT_SHARE_SUBJECT_TEMPLATE, subjectDate);
Intent sharingIntent = new Intent(Intent.ACTION_SEND);
- sharingIntent.setType("image/png");
+ sharingIntent.setDataAndType(uri, "image/png");
sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
// Include URI in ClipData also, so that grantPermission picks it up.
// We don't use setData here because some apps interpret this as "to:".
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index f14cc93c..9f44060 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -279,11 +279,11 @@
@Override
public void onThemeChanged() {
- updateShowEmptyShadeView();
mView.updateCornerRadius();
mView.updateBgColor();
mView.updateDecorViews();
mView.reinflateViews();
+ updateShowEmptyShadeView();
updateFooter();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 2c70a5f..c4567df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -376,6 +376,11 @@
final float stackHeight = ambientState.getStackHeight() - shelfHeight - scrimPadding;
final float stackEndHeight = ambientState.getStackEndHeight() - shelfHeight - scrimPadding;
+ if (stackEndHeight == 0f) {
+ // This should not happen, since even when the shade is empty we show EmptyShadeView
+ // but check just in case, so we don't return infinity or NaN.
+ return 0f;
+ }
return stackHeight / stackEndHeight;
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 9d19008..e4a0097 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -112,7 +112,6 @@
"java/com/android/server/am/EventLogTags.logtags",
"java/com/android/server/wm/EventLogTags.logtags",
"java/com/android/server/policy/EventLogTags.logtags",
- ":services.connectivity-tiramisu-sources",
],
libs: [
@@ -154,7 +153,6 @@
"android.hardware.biometrics.fingerprint-V1-java",
"android.hardware.oemlock-V1.0-java",
"android.hardware.configstore-V1.1-java",
- "android.hardware.contexthub-V1.0-java",
"android.hardware.ir-V1-java",
"android.hardware.rebootescrow-V1-java",
"android.hardware.soundtrigger-V2.3-java",
@@ -166,9 +164,6 @@
"overlayable_policy_aidl-java",
"SurfaceFlingerProperties",
"com.android.sysprop.watchdog",
- // This is used for services.connectivity-tiramisu-sources.
- // TODO: delete when NetworkStatsService is moved to the mainline module.
- "net-utils-device-common-bpf",
],
javac_shard_size: 50,
}
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index 26d76a84..aac1035 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -31,7 +31,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
-import android.os.storage.StorageManager;
import android.provider.Downloads;
import android.system.ErrnoException;
import android.system.Os;
@@ -281,14 +280,8 @@
HashMap<String, Long> timestamps = readTimestamps();
if (SystemProperties.getLong("ro.runtime.firstboot", 0) == 0) {
- if (StorageManager.inCryptKeeperBounce()) {
- // Encrypted, first boot to get PIN/pattern/password so data is tmpfs
- // Don't set ro.runtime.firstboot so that we will do this again
- // when data is properly mounted
- } else {
- String now = Long.toString(System.currentTimeMillis());
- SystemProperties.set("ro.runtime.firstboot", now);
- }
+ String now = Long.toString(System.currentTimeMillis());
+ SystemProperties.set("ro.runtime.firstboot", now);
if (db != null) db.addText("SYSTEM_BOOT", headers);
// Negative sizes mean to take the *tail* of the file (see FileUtils.readTextFile())
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index e924012..ce0e69c 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -91,6 +91,10 @@
if (!volume.isMountedWritable()) {
continue;
}
+ // gsid only supports vfat external storage.
+ if (!"vfat".equalsIgnoreCase(volume.fsType)) {
+ continue;
+ }
DiskInfo disk = volume.getDisk();
long mega = disk.size >> 20;
Slog.i(TAG, volume.getPath() + ": " + mega + " MB");
@@ -119,14 +123,13 @@
@Override
@EnforcePermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
- public boolean createPartition(String name, long size, boolean readOnly)
- throws RemoteException {
+ public int createPartition(String name, long size, boolean readOnly) throws RemoteException {
IGsiService service = getGsiService();
- if (service.createPartition(name, size, readOnly) != 0) {
- Slog.i(TAG, "Failed to install " + name);
- return false;
+ int status = service.createPartition(name, size, readOnly);
+ if (status != IGsiService.INSTALL_OK) {
+ Slog.i(TAG, "Failed to create partition: " + name);
}
- return true;
+ return status;
}
@Override
diff --git a/services/core/java/com/android/server/EntropyMixer.java b/services/core/java/com/android/server/EntropyMixer.java
index a83c981..d08d90c 100644
--- a/services/core/java/com/android/server/EntropyMixer.java
+++ b/services/core/java/com/android/server/EntropyMixer.java
@@ -25,41 +25,63 @@
import android.os.Handler;
import android.os.Message;
import android.os.SystemProperties;
+import android.util.AtomicFile;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.PrintWriter;
+import java.nio.ByteBuffer;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
/**
- * A service designed to load and periodically save "randomness"
- * for the Linux kernel RNG.
+ * A service that loads and periodically saves "randomness" for the
+ * Linux kernel RNG.
*
- * <p>When a Linux system starts up, the entropy pool associated with
- * {@code /dev/random} may be in a fairly predictable state. Applications which
- * depend strongly on randomness may find {@code /dev/random} or
- * {@code /dev/urandom} returning predictable data. In order to counteract
- * this effect, it's helpful to carry the entropy pool information across
- * shutdowns and startups.
- *
- * <p>This class was modeled after the script in the
- * <a href="https://man7.org/linux/man-pages/man4/random.4.html">
- * random(4) manual page</a>.
+ * <p>When a Linux system starts up, the entropy pool associated with {@code
+ * /dev/urandom}, {@code /dev/random}, and {@code getrandom()} may be in a
+ * fairly predictable state, depending on the entropy sources available to the
+ * kernel. Applications that depend on randomness may find these APIs returning
+ * predictable data. To counteract this effect, this service maintains a seed
+ * file across shutdowns and startups, and also mixes some device and
+ * boot-specific information into the pool.
*/
public class EntropyMixer extends Binder {
private static final String TAG = "EntropyMixer";
- private static final int ENTROPY_WHAT = 1;
- private static final int ENTROPY_WRITE_PERIOD = 3 * 60 * 60 * 1000; // 3 hrs
+ private static final int UPDATE_SEED_MSG = 1;
+ private static final int SEED_UPDATE_PERIOD = 3 * 60 * 60 * 1000; // 3 hrs
private static final long START_TIME = System.currentTimeMillis();
private static final long START_NANOTIME = System.nanoTime();
- private final String randomDevice;
- private final String entropyFile;
+ /*
+ * The size of the seed file in bytes. This must be at least the size of a
+ * SHA-256 digest (32 bytes). It *should* also be at least the size of the
+ * kernel's entropy pool (/proc/sys/kernel/random/poolsize divided by 8),
+ * which historically was 512 bytes, but changed to 32 bytes in Linux v5.18.
+ * There's actually no real need for more than a 32-byte seed, even with
+ * older kernels; however, we take the conservative approach of staying with
+ * the 512-byte size for now, as the cost is very small.
+ */
+ @VisibleForTesting
+ static final int SEED_FILE_SIZE = 512;
+
+ @VisibleForTesting
+ static final String DEVICE_SPECIFIC_INFO_HEADER =
+ "Copyright (C) 2009 The Android Open Source Project\n" +
+ "All Your Randomness Are Belong To Us\n";
+
+ private final AtomicFile seedFile;
+ private final File randomReadDevice;
+ private final File randomWriteDevice; // separate from randomReadDevice only for testing
/**
- * Handler that periodically updates the entropy on disk.
+ * Handler that periodically updates the seed file.
*/
private final Handler mHandler = new Handler(IoThread.getHandler().getLooper()) {
// IMPLEMENTATION NOTE: This handler runs on the I/O thread to avoid I/O on the main thread.
@@ -67,40 +89,36 @@
// own ID space for the "what" parameter of messages seen by the handler.
@Override
public void handleMessage(Message msg) {
- if (msg.what != ENTROPY_WHAT) {
+ if (msg.what != UPDATE_SEED_MSG) {
Slog.e(TAG, "Will not process invalid message");
return;
}
- writeEntropy();
- scheduleEntropyWriter();
+ updateSeedFile();
+ scheduleSeedUpdater();
}
};
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- writeEntropy();
+ updateSeedFile();
}
};
public EntropyMixer(Context context) {
- this(context, getSystemDir() + "/entropy.dat", "/dev/urandom");
+ this(context, new File(getSystemDir(), "entropy.dat"),
+ new File("/dev/urandom"), new File("/dev/urandom"));
}
- /** Test only interface, not for public use */
- public EntropyMixer(
- Context context,
- String entropyFile,
- String randomDevice) {
- if (randomDevice == null) { throw new NullPointerException("randomDevice"); }
- if (entropyFile == null) { throw new NullPointerException("entropyFile"); }
+ @VisibleForTesting
+ EntropyMixer(Context context, File seedFile, File randomReadDevice, File randomWriteDevice) {
+ this.seedFile = new AtomicFile(Preconditions.checkNotNull(seedFile));
+ this.randomReadDevice = Preconditions.checkNotNull(randomReadDevice);
+ this.randomWriteDevice = Preconditions.checkNotNull(randomWriteDevice);
- this.randomDevice = randomDevice;
- this.entropyFile = entropyFile;
loadInitialEntropy();
- addDeviceSpecificEntropy();
- writeEntropy();
- scheduleEntropyWriter();
+ updateSeedFile();
+ scheduleSeedUpdater();
IntentFilter broadcastFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
broadcastFilter.addAction(Intent.ACTION_POWER_CONNECTED);
broadcastFilter.addAction(Intent.ACTION_REBOOT);
@@ -112,76 +130,147 @@
);
}
- private void scheduleEntropyWriter() {
- mHandler.removeMessages(ENTROPY_WHAT);
- mHandler.sendEmptyMessageDelayed(ENTROPY_WHAT, ENTROPY_WRITE_PERIOD);
+ private void scheduleSeedUpdater() {
+ mHandler.removeMessages(UPDATE_SEED_MSG);
+ mHandler.sendEmptyMessageDelayed(UPDATE_SEED_MSG, SEED_UPDATE_PERIOD);
}
private void loadInitialEntropy() {
- try {
- RandomBlock.fromFile(entropyFile).toFile(randomDevice, false);
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "No existing entropy file -- first boot?");
+ byte[] seed = readSeedFile();
+ try (FileOutputStream out = new FileOutputStream(randomWriteDevice)) {
+ if (seed.length != 0) {
+ out.write(seed);
+ Slog.i(TAG, "Loaded existing seed file");
+ }
+ out.write(getDeviceSpecificInformation());
} catch (IOException e) {
- Slog.w(TAG, "Failure loading existing entropy file", e);
+ Slog.e(TAG, "Error writing to " + randomWriteDevice, e);
}
}
- private void writeEntropy() {
+ private byte[] readSeedFile() {
try {
- Slog.i(TAG, "Writing entropy...");
- RandomBlock.fromFile(randomDevice).toFile(entropyFile, true);
+ return seedFile.readFully();
+ } catch (FileNotFoundException e) {
+ return new byte[0];
} catch (IOException e) {
- Slog.w(TAG, "Unable to write entropy", e);
+ Slog.e(TAG, "Error reading " + seedFile.getBaseFile(), e);
+ return new byte[0];
}
}
/**
- * Add additional information to the kernel entropy pool. The
- * information isn't necessarily "random", but that's ok. Even
- * sending non-random information to {@code /dev/urandom} is useful
- * because, while it doesn't increase the "quality" of the entropy pool,
- * it mixes more bits into the pool, which gives us a higher degree
- * of uncertainty in the generated randomness. Like nature, writes to
- * the random device can only cause the quality of the entropy in the
- * kernel to stay the same or increase.
+ * Update (or create) the seed file.
*
- * <p>For maximum effect, we try to target information which varies
- * on a per-device basis, and is not easily observable to an
- * attacker.
+ * <p>Traditionally, the recommended way to update a seed file on Linux was
+ * to simply copy some bytes from /dev/urandom. However, that isn't
+ * actually a good way to do it, because writes to /dev/urandom aren't
+ * guaranteed to immediately affect reads from /dev/urandom. This can cause
+ * the new seed file to contain less entropy than the old one!
+ *
+ * <p>Instead, we generate the new seed by hashing the old seed together
+ * with some bytes from /dev/urandom, following the example of <a
+ * href="https://git.zx2c4.com/seedrng/tree/README.md">SeedRNG</a>. This
+ * ensures that the new seed is at least as entropic as the old seed.
*/
- private void addDeviceSpecificEntropy() {
- PrintWriter out = null;
- try {
- out = new PrintWriter(new FileOutputStream(randomDevice));
- out.println("Copyright (C) 2009 The Android Open Source Project");
- out.println("All Your Randomness Are Belong To Us");
- out.println(START_TIME);
- out.println(START_NANOTIME);
- out.println(SystemProperties.get("ro.serialno"));
- out.println(SystemProperties.get("ro.bootmode"));
- out.println(SystemProperties.get("ro.baseband"));
- out.println(SystemProperties.get("ro.carrier"));
- out.println(SystemProperties.get("ro.bootloader"));
- out.println(SystemProperties.get("ro.hardware"));
- out.println(SystemProperties.get("ro.revision"));
- out.println(SystemProperties.get("ro.build.fingerprint"));
- out.println(new Object().hashCode());
- out.println(System.currentTimeMillis());
- out.println(System.nanoTime());
- } catch (IOException e) {
- Slog.w(TAG, "Unable to add device specific data to the entropy pool", e);
- } finally {
- if (out != null) {
- out.close();
+ private void updateSeedFile() {
+ byte[] oldSeed = readSeedFile();
+ byte[] newSeed = new byte[SEED_FILE_SIZE];
+
+ try (FileInputStream in = new FileInputStream(randomReadDevice)) {
+ if (in.read(newSeed) != newSeed.length) {
+ throw new IOException("unexpected EOF");
}
+ } catch (IOException e) {
+ Slog.e(TAG, "Error reading " + randomReadDevice +
+ "; seed file won't be properly updated", e);
+ // Continue on; at least we'll have new timestamps...
+ }
+
+ // newSeed = newSeed[:-32] ||
+ // SHA-256(fixed_prefix || real_time || boot_time ||
+ // old_seed_len || old_seed || new_seed_len || new_seed)
+ MessageDigest sha256;
+ try {
+ sha256 = MessageDigest.getInstance("SHA-256");
+ } catch (NoSuchAlgorithmException e) {
+ Slog.wtf(TAG, "SHA-256 algorithm not found; seed file won't be updated", e);
+ return;
+ }
+ // This fixed prefix should be changed if the fields that are hashed change.
+ sha256.update("Android EntropyMixer v1".getBytes());
+ sha256.update(longToBytes(System.currentTimeMillis()));
+ sha256.update(longToBytes(System.nanoTime()));
+ sha256.update(longToBytes(oldSeed.length));
+ sha256.update(oldSeed);
+ sha256.update(longToBytes(newSeed.length));
+ sha256.update(newSeed);
+ byte[] digest = sha256.digest();
+ System.arraycopy(digest, 0, newSeed, newSeed.length - digest.length, digest.length);
+
+ writeNewSeed(newSeed);
+ if (oldSeed.length == 0) {
+ Slog.i(TAG, "Created seed file");
+ } else {
+ Slog.i(TAG, "Updated seed file");
}
}
- private static String getSystemDir() {
+ private void writeNewSeed(byte[] newSeed) {
+ FileOutputStream out = null;
+ try {
+ out = seedFile.startWrite();
+ out.write(newSeed);
+ seedFile.finishWrite(out);
+ } catch (IOException e) {
+ Slog.e(TAG, "Error writing " + seedFile.getBaseFile(), e);
+ seedFile.failWrite(out);
+ }
+ }
+
+ private static byte[] longToBytes(long x) {
+ ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
+ buffer.putLong(x);
+ return buffer.array();
+ }
+
+ /**
+ * Get some device and boot-specific information to mix into the kernel's
+ * entropy pool. This information probably won't contain much actual
+ * entropy, but that's fine because we don't ask the kernel to credit it.
+ * Writes to {@code /dev/urandom} can only increase or have no effect on the
+ * quality of random numbers, never decrease it.
+ *
+ * <p>The main goal here is just to initialize the entropy pool differently
+ * on devices that might otherwise be identical and have very little other
+ * entropy available. Therefore, we include various system properties that
+ * can vary on a per-device and/or per-build basis. We also include some
+ * timestamps, as these might vary on a per-boot basis and be not easily
+ * observable or guessable by an attacker.
+ */
+ private byte[] getDeviceSpecificInformation() {
+ StringBuilder b = new StringBuilder();
+ b.append(DEVICE_SPECIFIC_INFO_HEADER);
+ b.append(START_TIME).append('\n');
+ b.append(START_NANOTIME).append('\n');
+ b.append(SystemProperties.get("ro.serialno")).append('\n');
+ b.append(SystemProperties.get("ro.bootmode")).append('\n');
+ b.append(SystemProperties.get("ro.baseband")).append('\n');
+ b.append(SystemProperties.get("ro.carrier")).append('\n');
+ b.append(SystemProperties.get("ro.bootloader")).append('\n');
+ b.append(SystemProperties.get("ro.hardware")).append('\n');
+ b.append(SystemProperties.get("ro.revision")).append('\n');
+ b.append(SystemProperties.get("ro.build.fingerprint")).append('\n');
+ b.append(new Object().hashCode()).append('\n');
+ b.append(System.currentTimeMillis()).append('\n');
+ b.append(System.nanoTime()).append('\n');
+ return b.toString().getBytes();
+ }
+
+ private static File getSystemDir() {
File dataDir = Environment.getDataDirectory();
File systemDir = new File(dataDir, "system");
systemDir.mkdirs();
- return systemDir.toString();
+ return systemDir;
}
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 1a39ffa..73d9cc7 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1528,7 +1528,7 @@
if (updateFirewallUidRuleLocked(chain, uid, rule)) {
final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
try {
- cm.updateFirewallRule(chain, uid, isFirewallRuleAllow(chain, rule));
+ cm.setUidFirewallRule(chain, uid, rule);
} catch (RuntimeException e) {
throw new IllegalStateException(e);
}
@@ -1601,14 +1601,6 @@
}
}
- // There are only two type of firewall rule: FIREWALL_RULE_ALLOW or FIREWALL_RULE_DENY.
- private boolean isFirewallRuleAllow(int chain, int rule) {
- if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) {
- return getFirewallType(chain) == FIREWALL_DENYLIST;
- }
- return rule == INetd.FIREWALL_RULE_ALLOW;
- }
-
private void enforceSystemUid() {
final int uid = mDeps.getCallingUid();
if (uid != Process.SYSTEM_UID) {
diff --git a/services/core/java/com/android/server/RandomBlock.java b/services/core/java/com/android/server/RandomBlock.java
deleted file mode 100644
index 6d6d901..0000000
--- a/services/core/java/com/android/server/RandomBlock.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2009 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;
-
-import android.util.Slog;
-
-import java.io.Closeable;
-import java.io.DataOutput;
-import java.io.EOFException;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.RandomAccessFile;
-
-/**
- * A block of 512 random {@code byte}s.
- */
-class RandomBlock {
-
- private static final String TAG = "RandomBlock";
- private static final boolean DEBUG = false;
- private static final int BLOCK_SIZE = 512;
- private byte[] block = new byte[BLOCK_SIZE];
-
- private RandomBlock() { }
-
- static RandomBlock fromFile(String filename) throws IOException {
- if (DEBUG) Slog.v(TAG, "reading from file " + filename);
- InputStream stream = null;
- try {
- stream = new FileInputStream(filename);
- return fromStream(stream);
- } finally {
- close(stream);
- }
- }
-
- private static RandomBlock fromStream(InputStream in) throws IOException {
- RandomBlock retval = new RandomBlock();
- int total = 0;
- while(total < BLOCK_SIZE) {
- int result = in.read(retval.block, total, BLOCK_SIZE - total);
- if (result == -1) {
- throw new EOFException();
- }
- total += result;
- }
- return retval;
- }
-
- void toFile(String filename, boolean sync) throws IOException {
- if (DEBUG) Slog.v(TAG, "writing to file " + filename);
- RandomAccessFile out = null;
- try {
- out = new RandomAccessFile(filename, sync ? "rws" : "rw");
- toDataOut(out);
- truncateIfPossible(out);
- } finally {
- close(out);
- }
- }
-
- private static void truncateIfPossible(RandomAccessFile f) {
- try {
- f.setLength(BLOCK_SIZE);
- } catch (IOException e) {
- // ignore this exception. Sometimes, the file we're trying to
- // write is a character device, such as /dev/urandom, and
- // these character devices do not support setting the length.
- }
- }
-
- private void toDataOut(DataOutput out) throws IOException {
- out.write(block);
- }
-
- private static void close(Closeable c) {
- try {
- if (c == null) {
- return;
- }
- c.close();
- } catch (IOException e) {
- Slog.w(TAG, "IOException thrown while closing Closeable", e);
- }
- }
-}
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 81627a0..2ab477f 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -1803,7 +1803,10 @@
mComputedNightMode = false;
return;
}
- resetNightModeOverrideLocked();
+ if (mNightMode != MODE_NIGHT_AUTO || (mTwilightManager != null
+ && mTwilightManager.getLastTwilightState() != null)) {
+ resetNightModeOverrideLocked();
+ }
}
private boolean resetNightModeOverrideLocked() {
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 2d328d8..f26d9f9 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -52,6 +52,7 @@
import android.net.vcn.VcnUnderlyingNetworkPolicy;
import android.net.wifi.WifiInfo;
import android.os.Binder;
+import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -82,11 +83,13 @@
import com.android.server.vcn.VcnNetworkProvider;
import com.android.server.vcn.util.PersistableBundleUtils;
+import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -153,7 +156,7 @@
public class VcnManagementService extends IVcnManagementService.Stub {
@NonNull private static final String TAG = VcnManagementService.class.getSimpleName();
private static final long DUMP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(5);
- private static final int LOCAL_LOG_LINE_COUNT = 128;
+ private static final int LOCAL_LOG_LINE_COUNT = 512;
// Public for use in all other VCN classes
@NonNull public static final LocalLog LOCAL_LOG = new LocalLog(LOCAL_LOG_LINE_COUNT);
@@ -161,7 +164,8 @@
public static final boolean VDBG = false; // STOPSHIP: if true
@VisibleForTesting(visibility = Visibility.PRIVATE)
- static final String VCN_CONFIG_FILE = "/data/system/vcn/configs.xml";
+ static final String VCN_CONFIG_FILE =
+ new File(Environment.getDataSystemDirectory(), "vcn/configs.xml").getPath();
/* Binder context for this service */
@NonNull private final Context mContext;
@@ -172,7 +176,7 @@
@NonNull private final VcnNetworkProvider mNetworkProvider;
@NonNull private final TelephonySubscriptionTrackerCallback mTelephonySubscriptionTrackerCb;
@NonNull private final TelephonySubscriptionTracker mTelephonySubscriptionTracker;
- @NonNull private final BroadcastReceiver mPkgChangeReceiver;
+ @NonNull private final BroadcastReceiver mVcnBroadcastReceiver;
@NonNull
private final TrackingNetworkCallback mTrackingNetworkCallback = new TrackingNetworkCallback();
@@ -217,28 +221,17 @@
mConfigDiskRwHelper = mDeps.newPersistableBundleLockingReadWriteHelper(VCN_CONFIG_FILE);
- mPkgChangeReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
-
- if (Intent.ACTION_PACKAGE_ADDED.equals(action)
- || Intent.ACTION_PACKAGE_REPLACED.equals(action)
- || Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
- mTelephonySubscriptionTracker.handleSubscriptionsChanged();
- } else {
- Log.wtf(TAG, "received unexpected intent: " + action);
- }
- }
- };
+ mVcnBroadcastReceiver = new VcnBroadcastReceiver();
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
intentFilter.addDataScheme("package");
mContext.registerReceiver(
- mPkgChangeReceiver, intentFilter, null /* broadcastPermission */, mHandler);
+ mVcnBroadcastReceiver, intentFilter, null /* broadcastPermission */, mHandler);
// Run on handler to ensure I/O does not block system server startup
mHandler.post(() -> {
@@ -443,6 +436,53 @@
return Objects.equals(subGrp, snapshot.getActiveDataSubscriptionGroup());
}
+ private class VcnBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+
+ switch (action) {
+ case Intent.ACTION_PACKAGE_ADDED: // Fallthrough
+ case Intent.ACTION_PACKAGE_REPLACED: // Fallthrough
+ case Intent.ACTION_PACKAGE_REMOVED:
+ // Reevaluate subscriptions
+ mTelephonySubscriptionTracker.handleSubscriptionsChanged();
+
+ break;
+ case Intent.ACTION_PACKAGE_FULLY_REMOVED:
+ case Intent.ACTION_PACKAGE_DATA_CLEARED:
+ final String pkgName = intent.getData().getSchemeSpecificPart();
+
+ if (pkgName == null || pkgName.isEmpty()) {
+ logWtf("Package name was empty or null for intent with action" + action);
+ return;
+ }
+
+ // Clear configs for the packages that had data cleared, or removed.
+ synchronized (mLock) {
+ final List<ParcelUuid> toRemove = new ArrayList<>();
+ for (Entry<ParcelUuid, VcnConfig> entry : mConfigs.entrySet()) {
+ if (pkgName.equals(entry.getValue().getProvisioningPackageName())) {
+ toRemove.add(entry.getKey());
+ }
+ }
+
+ for (ParcelUuid subGrp : toRemove) {
+ stopAndClearVcnConfigInternalLocked(subGrp);
+ }
+
+ if (!toRemove.isEmpty()) {
+ writeConfigsToDiskLocked();
+ }
+ }
+
+ break;
+ default:
+ Slog.wtf(TAG, "received unexpected intent: " + action);
+ }
+ }
+ }
+
private class VcnSubscriptionTrackerCallback implements TelephonySubscriptionTrackerCallback {
/**
* Handles subscription group changes, as notified by {@link TelephonySubscriptionTracker}
@@ -456,7 +496,7 @@
synchronized (mLock) {
final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot;
mLastSnapshot = snapshot;
- logDbg("new snapshot: " + mLastSnapshot);
+ logInfo("new snapshot: " + mLastSnapshot);
// Start any VCN instances as necessary
for (Entry<ParcelUuid, VcnConfig> entry : mConfigs.entrySet()) {
@@ -504,6 +544,7 @@
final Map<ParcelUuid, Set<Integer>> currSubGrpMappings =
getSubGroupToSubIdMappings(mLastSnapshot);
if (!currSubGrpMappings.equals(oldSubGrpMappings)) {
+ garbageCollectAndWriteVcnConfigsLocked();
notifyAllPolicyListenersLocked();
}
}
@@ -522,6 +563,8 @@
@GuardedBy("mLock")
private void stopVcnLocked(@NonNull ParcelUuid uuidToTeardown) {
+ logInfo("Stopping VCN config for subGrp: " + uuidToTeardown);
+
// Remove in 2 steps. Make sure teardownAsync is triggered before removing from the map.
final Vcn vcnToTeardown = mVcns.get(uuidToTeardown);
if (vcnToTeardown == null) {
@@ -567,7 +610,7 @@
@GuardedBy("mLock")
private void startVcnLocked(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) {
- logDbg("Starting VCN config for subGrp: " + subscriptionGroup);
+ logInfo("Starting VCN config for subGrp: " + subscriptionGroup);
// TODO(b/193687515): Support multiple VCNs active at the same time
if (!mVcns.isEmpty()) {
@@ -626,7 +669,7 @@
if (!config.getProvisioningPackageName().equals(opPkgName)) {
throw new IllegalArgumentException("Mismatched caller and VcnConfig creator");
}
- logDbg("VCN config updated for subGrp: " + subscriptionGroup);
+ logInfo("VCN config updated for subGrp: " + subscriptionGroup);
mContext.getSystemService(AppOpsManager.class)
.checkPackage(mDeps.getBinderCallingUid(), config.getProvisioningPackageName());
@@ -643,6 +686,39 @@
});
}
+ private void enforceCarrierPrivilegeOrProvisioningPackage(
+ @NonNull ParcelUuid subscriptionGroup, @NonNull String pkg) {
+ // Only apps running in the primary (system) user are allowed to configure the VCN. This is
+ // in line with Telephony's behavior with regards to binding to a Carrier App provided
+ // CarrierConfigService.
+ enforcePrimaryUser();
+
+ if (isProvisioningPackageForConfig(subscriptionGroup, pkg)) {
+ return;
+ }
+
+ // Must NOT be called from cleared binder identity, since this checks user calling identity
+ enforceCallingUserAndCarrierPrivilege(subscriptionGroup, pkg);
+ }
+
+ private boolean isProvisioningPackageForConfig(
+ @NonNull ParcelUuid subscriptionGroup, @NonNull String pkg) {
+ // Try-finally to return early if matching owned subscription found.
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ final VcnConfig config = mConfigs.get(subscriptionGroup);
+ if (config != null && pkg.equals(config.getProvisioningPackageName())) {
+ return true;
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ return false;
+ }
+
/**
* Clears the VcnManagementService for a given subscription group.
*
@@ -652,35 +728,60 @@
public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull String opPkgName) {
requireNonNull(subscriptionGroup, "subscriptionGroup was null");
requireNonNull(opPkgName, "opPkgName was null");
- logDbg("VCN config cleared for subGrp: " + subscriptionGroup);
+ logInfo("VCN config cleared for subGrp: " + subscriptionGroup);
mContext.getSystemService(AppOpsManager.class)
.checkPackage(mDeps.getBinderCallingUid(), opPkgName);
- enforceCallingUserAndCarrierPrivilege(subscriptionGroup, opPkgName);
+ enforceCarrierPrivilegeOrProvisioningPackage(subscriptionGroup, opPkgName);
Binder.withCleanCallingIdentity(() -> {
synchronized (mLock) {
- mConfigs.remove(subscriptionGroup);
- final boolean vcnExists = mVcns.containsKey(subscriptionGroup);
-
- stopVcnLocked(subscriptionGroup);
-
- if (vcnExists) {
- // TODO(b/181789060): invoke asynchronously after Vcn notifies through
- // VcnCallback
- notifyAllPermissionedStatusCallbacksLocked(
- subscriptionGroup, VCN_STATUS_CODE_NOT_CONFIGURED);
- }
-
+ stopAndClearVcnConfigInternalLocked(subscriptionGroup);
writeConfigsToDiskLocked();
}
});
}
+ private void stopAndClearVcnConfigInternalLocked(@NonNull ParcelUuid subscriptionGroup) {
+ mConfigs.remove(subscriptionGroup);
+ final boolean vcnExists = mVcns.containsKey(subscriptionGroup);
+
+ stopVcnLocked(subscriptionGroup);
+
+ if (vcnExists) {
+ // TODO(b/181789060): invoke asynchronously after Vcn notifies through
+ // VcnCallback
+ notifyAllPermissionedStatusCallbacksLocked(
+ subscriptionGroup, VCN_STATUS_CODE_NOT_CONFIGURED);
+ }
+ }
+
+ private void garbageCollectAndWriteVcnConfigsLocked() {
+ final SubscriptionManager subMgr = mContext.getSystemService(SubscriptionManager.class);
+
+ boolean shouldWrite = false;
+
+ final Iterator<ParcelUuid> configsIterator = mConfigs.keySet().iterator();
+ while (configsIterator.hasNext()) {
+ final ParcelUuid subGrp = configsIterator.next();
+
+ final List<SubscriptionInfo> subscriptions = subMgr.getSubscriptionsInGroup(subGrp);
+ if (subscriptions == null || subscriptions.isEmpty()) {
+ // Trim subGrps with no more subscriptions; must have moved to another subGrp
+ configsIterator.remove();
+ shouldWrite = true;
+ }
+ }
+
+ if (shouldWrite) {
+ writeConfigsToDiskLocked();
+ }
+ }
+
/**
* Retrieves the list of subscription groups with configured VcnConfigs
*
- * <p>Limited to subscription groups for which the caller is carrier privileged.
+ * <p>Limited to subscription groups for which the caller had configured.
*
* <p>Implements the IVcnManagementService Binder interface.
*/
@@ -696,7 +797,8 @@
final List<ParcelUuid> result = new ArrayList<>();
synchronized (mLock) {
for (ParcelUuid subGrp : mConfigs.keySet()) {
- if (mLastSnapshot.packageHasPermissionsForSubscriptionGroup(subGrp, opPkgName)) {
+ if (mLastSnapshot.packageHasPermissionsForSubscriptionGroup(subGrp, opPkgName)
+ || isProvisioningPackageForConfig(subGrp, opPkgName)) {
result.add(subGrp);
}
}
@@ -1050,24 +1152,34 @@
Slog.d(TAG, msg, tr);
}
+ private void logInfo(String msg) {
+ Slog.i(TAG, msg);
+ LOCAL_LOG.log("[INFO] [" + TAG + "] " + msg);
+ }
+
+ private void logInfo(String msg, Throwable tr) {
+ Slog.i(TAG, msg, tr);
+ LOCAL_LOG.log("[INFO] [" + TAG + "] " + msg + tr);
+ }
+
private void logErr(String msg) {
Slog.e(TAG, msg);
- LOCAL_LOG.log(TAG + " ERR: " + msg);
+ LOCAL_LOG.log("[ERR] [" + TAG + "] " + msg);
}
private void logErr(String msg, Throwable tr) {
Slog.e(TAG, msg, tr);
- LOCAL_LOG.log(TAG + " ERR: " + msg + tr);
+ LOCAL_LOG.log("[ERR ] [" + TAG + "] " + msg + tr);
}
private void logWtf(String msg) {
Slog.wtf(TAG, msg);
- LOCAL_LOG.log(TAG + " WTF: " + msg);
+ LOCAL_LOG.log("[WTF] [" + TAG + "] " + msg);
}
private void logWtf(String msg, Throwable tr) {
Slog.wtf(TAG, msg, tr);
- LOCAL_LOG.log(TAG + " WTF: " + msg + tr);
+ LOCAL_LOG.log("[WTF ] [" + TAG + "] " + msg + tr);
}
/**
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index f591b26..297d28d 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -18,6 +18,7 @@
import static com.android.internal.util.dump.DumpUtils.writeStringIfNotNull;
+import android.annotation.NonNull;
import android.annotation.TestApi;
import android.app.ActivityManager;
import android.app.Notification;
@@ -170,6 +171,12 @@
mAdbConnectionInfo = new AdbConnectionInfo();
}
+ static void sendBroadcastWithDebugPermission(@NonNull Context context, @NonNull Intent intent,
+ @NonNull UserHandle userHandle) {
+ context.sendBroadcastAsUser(intent, userHandle,
+ android.Manifest.permission.MANAGE_DEBUGGING);
+ }
+
class PairingThread extends Thread implements NsdManager.RegistrationListener {
private NsdManager mNsdManager;
private String mPublicKey;
@@ -1278,7 +1285,7 @@
? AdbManager.WIRELESS_STATUS_CONNECTED
: AdbManager.WIRELESS_STATUS_DISCONNECTED);
intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
}
private void onAdbdWifiServerConnected(int port) {
@@ -1350,7 +1357,8 @@
if (publicKey == null) {
Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, AdbManager.WIRELESS_STATUS_FAIL);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent,
+ UserHandle.ALL);
} else {
Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
@@ -1366,7 +1374,8 @@
device.guid = hostname;
device.connected = false;
intent.putExtra(AdbManager.WIRELESS_PAIR_DEVICE_EXTRA, device);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent,
+ UserHandle.ALL);
// Add the key into the keystore
mAdbKeyStore.setLastConnectionTime(publicKey,
System.currentTimeMillis());
@@ -1380,14 +1389,14 @@
intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
AdbManager.WIRELESS_STATUS_CONNECTED);
intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
}
private void sendPairedDevicesToUI(Map<String, PairDevice> devices) {
Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION);
// Map is not serializable, so need to downcast
intent.putExtra(AdbManager.WIRELESS_DEVICES_EXTRA, (HashMap) devices);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
}
private void updateUIPairCode(String code) {
@@ -1397,7 +1406,7 @@
intent.putExtra(AdbManager.WIRELESS_PAIRING_CODE_EXTRA, code);
intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
AdbManager.WIRELESS_STATUS_PAIRING_CODE);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
}
}
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index 7a4d2ce..2845fbf 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -459,7 +459,7 @@
? AdbManager.WIRELESS_STATUS_CONNECTED
: AdbManager.WIRELESS_STATUS_DISCONNECTED);
intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
Slog.i(TAG, "sent port broadcast port=" + port);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0f450e1..ec9f1fe 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -28,6 +28,7 @@
import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS;
import static android.app.ActivityManager.INSTR_FLAG_NO_RESTART;
import static android.app.ActivityManager.INTENT_SENDER_ACTIVITY;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
@@ -45,6 +46,13 @@
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER;
+import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED;
+import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY;
+import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER;
+import static android.net.ConnectivityManager.BLOCKED_REASON_DOZE;
+import static android.net.ConnectivityManager.BLOCKED_REASON_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
import static android.os.FactoryTest.FACTORY_TEST_OFF;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
@@ -1415,6 +1423,10 @@
final ActivityThread mSystemThread;
final UidObserverController mUidObserverController;
+ private volatile IUidObserver mNetworkPolicyUidObserver;
+
+ @GuardedBy("mUidNetworkBlockedReasons")
+ private final SparseIntArray mUidNetworkBlockedReasons = new SparseIntArray();
private final class AppDeathRecipient implements IBinder.DeathRecipient {
final ProcessRecord mApp;
@@ -6784,6 +6796,9 @@
}
synchronized (mProcLock) {
+ if (mPendingStartActivityUids.isPendingTopUid(uid)) {
+ return PROCESS_STATE_TOP;
+ }
return mProcessList.getUidProcStateLOSP(uid);
}
}
@@ -10375,7 +10390,7 @@
"Native",
"System", "Persistent", "Persistent Service", "Foreground",
"Visible", "Perceptible", "Perceptible Low", "Perceptible Medium",
- "Heavy Weight", "Backup",
+ "Backup", "Heavy Weight",
"A Services", "Home",
"Previous", "B Services", "Cached"
};
@@ -10383,7 +10398,7 @@
"native",
"sys", "pers", "persvc", "fore",
"vis", "percept", "perceptl", "perceptm",
- "heavy", "backup",
+ "backup", "heavy",
"servicea", "home",
"prev", "serviceb", "cached"
};
@@ -14447,7 +14462,6 @@
uid, change, procState, procStateSeq, capability, ephemeral);
if (uidRec != null) {
uidRec.setLastReportedChange(enqueuedChange);
- uidRec.updateLastDispatchedProcStateSeq(enqueuedChange);
}
// Directly update the power manager, since we sit on top of it and it is critical
@@ -15585,18 +15599,13 @@
return;
}
record.lastNetworkUpdatedProcStateSeq = procStateSeq;
- if (record.curProcStateSeq > procStateSeq) {
- if (DEBUG_NETWORK) {
- Slog.d(TAG_NETWORK, "No need to handle older seq no., Uid: " + uid
- + ", curProcstateSeq: " + record.curProcStateSeq
- + ", procStateSeq: " + procStateSeq);
- }
- return;
- }
- if (record.waitingForNetwork) {
+ if (record.procStateSeqWaitingForNetwork != 0
+ && procStateSeq >= record.procStateSeqWaitingForNetwork) {
if (DEBUG_NETWORK) {
Slog.d(TAG_NETWORK, "Notifying all blocking threads for uid: " + uid
- + ", procStateSeq: " + procStateSeq);
+ + ", procStateSeq: " + procStateSeq
+ + ", procStateSeqWaitingForNetwork: "
+ + record.procStateSeqWaitingForNetwork);
}
record.networkStateLock.notifyAll();
}
@@ -15604,6 +15613,17 @@
}
@Override
+ public void onUidBlockedReasonsChanged(int uid, int blockedReasons) {
+ synchronized (mUidNetworkBlockedReasons) {
+ if (blockedReasons == BLOCKED_REASON_NONE) {
+ mUidNetworkBlockedReasons.delete(uid);
+ } else {
+ mUidNetworkBlockedReasons.put(uid, blockedReasons);
+ }
+ }
+ }
+
+ @Override
public boolean isRuntimeRestarted() {
return mSystemServiceManager.isRuntimeRestarted();
}
@@ -16301,8 +16321,51 @@
}
@Override
- public void addPendingTopUid(int uid, int pid) {
- mPendingStartActivityUids.add(uid, pid);
+ public void addPendingTopUid(int uid, int pid, @Nullable IApplicationThread thread) {
+ final boolean isNewPending = mPendingStartActivityUids.add(uid, pid);
+ // We need to update the network rules for the app coming to the top state so that
+ // it can access network when the device or the app is in a restricted state
+ // (e.g. battery/data saver) but since waiting for updateOomAdj to complete and then
+ // informing NetworkPolicyManager might get delayed, informing the state change as soon
+ // as we know app is going to come to the top state.
+ if (isNewPending && mNetworkPolicyUidObserver != null) {
+ try {
+ final long procStateSeq = mProcessList.getNextProcStateSeq();
+ mNetworkPolicyUidObserver.onUidStateChanged(uid, PROCESS_STATE_TOP,
+ procStateSeq, PROCESS_CAPABILITY_ALL);
+ if (thread != null && isNetworkingBlockedForUid(uid)) {
+ thread.setNetworkBlockSeq(procStateSeq);
+ }
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Error calling setNetworkBlockSeq", e);
+ }
+ }
+ }
+
+ private boolean isNetworkingBlockedForUid(int uid) {
+ synchronized (mUidNetworkBlockedReasons) {
+ // TODO: We can consider only those blocked reasons that will be overridden
+ // by the TOP state. For other ones, there is no point in waiting.
+ // TODO: We can reuse this data in
+ // ProcessList#incrementProcStateSeqAndNotifyAppsLOSP instead of calling into
+ // NetworkManagementService.
+ final int uidBlockedReasons = mUidNetworkBlockedReasons.get(
+ uid, BLOCKED_REASON_NONE);
+ if (uidBlockedReasons == BLOCKED_REASON_NONE) {
+ return false;
+ }
+ final int topExemptedBlockedReasons = BLOCKED_REASON_BATTERY_SAVER
+ | BLOCKED_REASON_DOZE
+ | BLOCKED_REASON_APP_STANDBY
+ | BLOCKED_REASON_LOW_POWER_STANDBY
+ | BLOCKED_METERED_REASON_DATA_SAVER
+ | BLOCKED_METERED_REASON_USER_RESTRICTED;
+ final int effectiveBlockedReasons =
+ uidBlockedReasons & ~topExemptedBlockedReasons;
+ // Only consider it as blocked if it is not blocked by a reason
+ // that is not exempted by app being in the top state.
+ return effectiveBlockedReasons == BLOCKED_REASON_NONE;
+ }
}
@Override
@@ -16449,6 +16512,14 @@
public void setStopUserOnSwitch(int value) {
ActivityManagerService.this.setStopUserOnSwitch(value);
}
+
+ @Override
+ public void registerNetworkPolicyUidObserver(@NonNull IUidObserver observer,
+ int which, int cutpoint, @NonNull String callingPackage) {
+ mNetworkPolicyUidObserver = observer;
+ mUidObserverController.register(observer, which, cutpoint, callingPackage,
+ Binder.getCallingUid());
+ }
}
long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
@@ -16528,23 +16599,6 @@
}
}
synchronized (record.networkStateLock) {
- if (record.lastDispatchedProcStateSeq < procStateSeq) {
- if (DEBUG_NETWORK) {
- Slog.d(TAG_NETWORK, "Uid state change for seq no. " + procStateSeq + " is not "
- + "dispatched to NPMS yet, so don't wait. Uid: " + callingUid
- + " lastProcStateSeqDispatchedToObservers: "
- + record.lastDispatchedProcStateSeq);
- }
- return;
- }
- if (record.curProcStateSeq > procStateSeq) {
- if (DEBUG_NETWORK) {
- Slog.d(TAG_NETWORK, "Ignore the wait requests for older seq numbers. Uid: "
- + callingUid + ", curProcStateSeq: " + record.curProcStateSeq
- + ", procStateSeq: " + procStateSeq);
- }
- return;
- }
if (record.lastNetworkUpdatedProcStateSeq >= procStateSeq) {
if (DEBUG_NETWORK) {
Slog.d(TAG_NETWORK, "Network rules have been already updated for seq no. "
@@ -16560,9 +16614,9 @@
+ " Uid: " + callingUid + " procStateSeq: " + procStateSeq);
}
final long startTime = SystemClock.uptimeMillis();
- record.waitingForNetwork = true;
+ record.procStateSeqWaitingForNetwork = procStateSeq;
record.networkStateLock.wait(mWaitForNetworkTimeoutMs);
- record.waitingForNetwork = false;
+ record.procStateSeqWaitingForNetwork = 0;
final long totalTime = SystemClock.uptimeMillis() - startTime;
if (totalTime >= mWaitForNetworkTimeoutMs || DEBUG_NETWORK) {
Slog.w(TAG_NETWORK, "Total time waited for network rules to get updated: "
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 0bf0fe2..98412bd 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -577,12 +577,14 @@
mPackageWatchdog.onPackageFailure(r.getPackageListWithVersionCode(),
PackageWatchdog.FAILURE_REASON_APP_CRASH);
- mService.mProcessList.noteAppKill(r, (crashInfo != null
- && "Native crash".equals(crashInfo.exceptionClassName))
- ? ApplicationExitInfo.REASON_CRASH_NATIVE
- : ApplicationExitInfo.REASON_CRASH,
- ApplicationExitInfo.SUBREASON_UNKNOWN,
- "crash");
+ synchronized (mService) {
+ mService.mProcessList.noteAppKill(r, (crashInfo != null
+ && "Native crash".equals(crashInfo.exceptionClassName))
+ ? ApplicationExitInfo.REASON_CRASH_NATIVE
+ : ApplicationExitInfo.REASON_CRASH,
+ ApplicationExitInfo.SUBREASON_UNKNOWN,
+ "crash");
+ }
}
final int relaunchReason = r != null
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index b3b74a1..c5ee259 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -556,8 +556,31 @@
// We were asked to fetch Bluetooth data.
final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null) {
- bluetoothReceiver = new SynchronousResultReceiver("bluetooth");
- adapter.requestControllerActivityEnergyInfo(bluetoothReceiver);
+ SynchronousResultReceiver resultReceiver =
+ new SynchronousResultReceiver("bluetooth");
+ adapter.requestControllerActivityEnergyInfo(
+ Runnable::run,
+ new BluetoothAdapter.OnBluetoothActivityEnergyInfoCallback() {
+ @Override
+ public void onBluetoothActivityEnergyInfoAvailable(
+ BluetoothActivityEnergyInfo info) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(
+ BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info);
+ resultReceiver.send(0, bundle);
+ }
+
+ @Override
+ public void onBluetoothActivityEnergyInfoError(int errorCode) {
+ Slog.w(TAG, "error reading Bluetooth stats: " + errorCode);
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(
+ BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, null);
+ resultReceiver.send(0, bundle);
+ }
+ }
+ );
+ bluetoothReceiver = resultReceiver;
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 2030b19..dedd898 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1978,7 +1978,7 @@
mHandler.post(() -> {
synchronized (mStats) {
mStats.noteBluetoothScanStoppedFromSourceLocked(localWs, isUnoptimized,
- uptime, elapsedRealtime);
+ elapsedRealtime, uptime);
}
});
}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 9e04410..ce40f85 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2911,8 +2911,7 @@
// To avoid some abuse patterns, we are going to be careful about what we consider
// to be an app interaction. Being the top activity doesn't count while the display
// is sleeping, nor do short foreground services.
- if (state.getCurProcState() <= PROCESS_STATE_TOP
- || state.getCurProcState() == PROCESS_STATE_BOUND_TOP) {
+ if (ActivityManager.isProcStateConsideredInteraction(state.getCurProcState())) {
isInteraction = true;
state.setFgInteractionTime(0);
} else if (state.getCurProcState() <= PROCESS_STATE_FOREGROUND_SERVICE) {
diff --git a/services/core/java/com/android/server/am/PendingStartActivityUids.java b/services/core/java/com/android/server/am/PendingStartActivityUids.java
index 20f6bb2..5beda31 100644
--- a/services/core/java/com/android/server/am/PendingStartActivityUids.java
+++ b/services/core/java/com/android/server/am/PendingStartActivityUids.java
@@ -44,10 +44,12 @@
mContext = context;
}
- synchronized void add(int uid, int pid) {
+ synchronized boolean add(int uid, int pid) {
if (mPendingUids.get(uid) == null) {
mPendingUids.put(uid, new Pair<>(pid, SystemClock.elapsedRealtime()));
+ return true;
}
+ return false;
}
synchronized void delete(int uid, long nowElapsed) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 1e66ed4..ca16f57 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -482,9 +482,8 @@
* Having a global counter ensures that seq numbers are monotonically increasing for a
* particular uid even when the uidRecord is re-created.
*/
- @GuardedBy("mService")
@VisibleForTesting
- long mProcStateSeqCounter = 0;
+ volatile long mProcStateSeqCounter = 0;
/**
* A global counter for generating sequence numbers to uniquely identify pending process starts.
@@ -2426,8 +2425,8 @@
if (!regularZygote) {
// webview and app zygote don't have the permission to create the nodes
if (Process.createProcessGroup(uid, startResult.pid) < 0) {
- Slog.e(ActivityManagerService.TAG, "Unable to create process group for "
- + app.processName + " (" + startResult.pid + ")");
+ throw new AssertionError("Unable to create process group for " + app.processName
+ + " (" + startResult.pid + ")");
}
}
@@ -4878,13 +4877,17 @@
}
/**
- * Checks if any uid is coming from background to foreground or vice versa and if so, increments
- * the {@link UidRecord#curProcStateSeq} corresponding to that uid using global seq counter
- * {@link ProcessList#mProcStateSeqCounter} and notifies the app if it needs to block.
+ * Increments the {@link UidRecord#curProcStateSeq} for all uids using global seq counter
+ * {@link ProcessList#mProcStateSeqCounter} and checks if any uid is coming
+ * from background to foreground or vice versa and if so, notifies the app if it needs to block.
*/
@VisibleForTesting
@GuardedBy(anyOf = {"mService", "mProcLock"})
void incrementProcStateSeqAndNotifyAppsLOSP(ActiveUids activeUids) {
+ for (int i = activeUids.size() - 1; i >= 0; --i) {
+ final UidRecord uidRec = activeUids.valueAt(i);
+ uidRec.curProcStateSeq = getNextProcStateSeq();
+ }
if (mService.mWaitForNetworkTimeoutMs <= 0) {
return;
}
@@ -4911,7 +4914,6 @@
continue;
}
synchronized (uidRec.networkStateLock) {
- uidRec.curProcStateSeq = ++mProcStateSeqCounter; // TODO: use method
if (blockState == NETWORK_STATE_BLOCK) {
if (blockingUids == null) {
blockingUids = new ArrayList<>();
@@ -4922,7 +4924,7 @@
Slog.d(TAG_NETWORK, "uid going to background, notifying all blocking"
+ " threads for uid: " + uidRec);
}
- if (uidRec.waitingForNetwork) {
+ if (uidRec.procStateSeqWaitingForNetwork != 0) {
uidRec.networkStateLock.notifyAll();
}
}
@@ -4956,6 +4958,10 @@
}
}
+ long getNextProcStateSeq() {
+ return ++mProcStateSeqCounter;
+ }
+
/**
* Create a server socket in system_server, zygote will connect to it
* in order to send unsolicited messages to system_server.
diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java
index c1bfe25..67b9ae6 100644
--- a/services/core/java/com/android/server/am/UidObserverController.java
+++ b/services/core/java/com/android/server/am/UidObserverController.java
@@ -219,7 +219,6 @@
validateUid.setCurProcState(item.procState);
validateUid.setSetCapability(item.capability);
validateUid.setCurCapability(item.capability);
- validateUid.lastDispatchedProcStateSeq = item.procStateSeq;
}
}
}
diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java
index 4ba59fa..2ec08a4 100644
--- a/services/core/java/com/android/server/am/UidRecord.java
+++ b/services/core/java/com/android/server/am/UidRecord.java
@@ -95,16 +95,9 @@
long lastNetworkUpdatedProcStateSeq;
/**
- * Last seq number for which AcitivityManagerService dispatched uid state change to
- * NetworkPolicyManagerService.
+ * Indicates if any thread is waiting for network rules to get updated for {@link #mUid}.
*/
- @GuardedBy("networkStateUpdate")
- long lastDispatchedProcStateSeq;
-
- /**
- * Indicates if any thread is waiting for network rules to get updated for {@link #uid}.
- */
- volatile boolean waitingForNetwork;
+ volatile long procStateSeqWaitingForNetwork;
/**
* Indicates whether this uid has internet permission or not.
@@ -309,18 +302,6 @@
mUid) == PackageManager.PERMISSION_GRANTED;
}
- /**
- * If the change being dispatched is not CHANGE_GONE (not interested in
- * these changes), then update the {@link #lastDispatchedProcStateSeq} with
- * {@link #curProcStateSeq}.
- */
- public void updateLastDispatchedProcStateSeq(int changeToDispatch) {
- if ((changeToDispatch & CHANGE_GONE) == 0) {
- lastDispatchedProcStateSeq = curProcStateSeq;
- }
- }
-
-
void dumpDebug(ProtoOutputStream proto, long fieldId) {
long token = proto.start(fieldId);
proto.write(UidRecordProto.UID, mUid);
@@ -341,7 +322,6 @@
proto.write(UidRecordProto.ProcStateSequence.CURURENT, curProcStateSeq);
proto.write(UidRecordProto.ProcStateSequence.LAST_NETWORK_UPDATED,
lastNetworkUpdatedProcStateSeq);
- proto.write(UidRecordProto.ProcStateSequence.LAST_DISPATCHED, lastDispatchedProcStateSeq);
proto.end(seqToken);
proto.end(token);
@@ -412,8 +392,6 @@
sb.append(curProcStateSeq);
sb.append(",");
sb.append(lastNetworkUpdatedProcStateSeq);
- sb.append(",");
- sb.append(lastDispatchedProcStateSeq);
sb.append(")}");
return sb.toString();
}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index a0c5aa3..863be98 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -582,7 +582,7 @@
void setPrimaryClipInternal(PerUserClipboard clipboard, @Nullable ClipData clip,
int uid) {
- synchronized ("mLock") {
+ synchronized (mLock) {
setPrimaryClipInternalLocked(clipboard, clip, uid, null);
}
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index a6da4a6..b49654e 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -20,6 +20,7 @@
import static android.Manifest.permission.CONTROL_VPN;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN;
@@ -84,6 +85,7 @@
import android.net.ipsec.ike.IkeSession;
import android.net.ipsec.ike.IkeSessionCallback;
import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.IkeTunnelConnectionParams;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
@@ -207,7 +209,6 @@
private final NetworkInfo mNetworkInfo;
private int mLegacyState;
@VisibleForTesting protected String mPackage;
- private String mSessionKey;
private int mOwnerUID;
private boolean mIsPackageTargetingAtLeastQ;
@VisibleForTesting
@@ -1989,9 +1990,7 @@
public synchronized int getActiveVpnType() {
if (!mNetworkInfo.isConnectedOrConnecting()) return VpnManager.TYPE_VPN_NONE;
if (mVpnRunner == null) return VpnManager.TYPE_VPN_SERVICE;
- return mVpnRunner instanceof IkeV2VpnRunner
- ? VpnManager.TYPE_VPN_PLATFORM
- : VpnManager.TYPE_VPN_LEGACY;
+ return isIkev2VpnRunner() ? VpnManager.TYPE_VPN_PLATFORM : VpnManager.TYPE_VPN_LEGACY;
}
private void updateAlwaysOnNotification(DetailedState networkState) {
@@ -2266,6 +2265,11 @@
profile.setAllowedAlgorithms(Ikev2VpnProfile.DEFAULT_ALGORITHMS);
startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN);
return;
+ case VpnProfile.TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS:
+ // All the necessary IKE options should come from IkeTunnelConnectionParams in the
+ // profile.
+ startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN);
+ return;
case VpnProfile.TYPE_L2TP_IPSEC_PSK:
racoon = new String[] {
iface, profile.server, "udppsk", profile.ipsecIdentifier,
@@ -2517,12 +2521,15 @@
@Nullable private IpSecTunnelInterface mTunnelIface;
@Nullable private IkeSession mSession;
@Nullable private Network mActiveNetwork;
+ private final String mSessionKey;
IkeV2VpnRunner(@NonNull Ikev2VpnProfile profile) {
super(TAG);
mProfile = profile;
mIpSecManager = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
- mNetworkCallback = new VpnIkev2Utils.Ikev2VpnNetworkCallback(TAG, this);
+ // Pass mExecutor into Ikev2VpnNetworkCallback and make sure that IkeV2VpnRunnerCallback
+ // will be called by the mExecutor thread.
+ mNetworkCallback = new VpnIkev2Utils.Ikev2VpnNetworkCallback(TAG, this, mExecutor);
mSessionKey = UUID.randomUUID().toString();
}
@@ -2542,6 +2549,7 @@
req = new NetworkRequest.Builder()
.clearCapabilities()
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+ .addCapability(NET_CAPABILITY_NOT_VPN)
.build();
} else {
// Basically, the request here is referring to the default request which is defined
@@ -2676,57 +2684,68 @@
* <p>The Ikev2VpnRunner will unconditionally switch to the new network, killing the old IKE
* state in the process, and starting a new IkeSession instance.
*
- * <p>This method is called multiple times over the lifetime of the Ikev2VpnRunner, and is
- * called on the ConnectivityService thread. Thus, the actual work MUST be proxied to the
- * mExecutor thread in order to ensure consistency of the Ikev2VpnRunner fields.
+ * <p>This method MUST always be called on the mExecutor thread in order to ensure
+ * consistency of the Ikev2VpnRunner fields.
*/
public void onDefaultNetworkChanged(@NonNull Network network) {
Log.d(TAG, "Starting IKEv2/IPsec session on new network: " + network);
- // Proxy to the Ikev2VpnRunner (single-thread) executor to ensure consistency in lieu
- // of locking.
- mExecutor.execute(() -> {
- try {
- if (!mIsRunning) {
- Log.d(TAG, "onDefaultNetworkChanged after exit");
- return; // VPN has been shut down.
- }
-
- // Without MOBIKE, we have no way to seamlessly migrate. Close on old
- // (non-default) network, and start the new one.
- resetIkeState();
- mActiveNetwork = network;
-
- final IkeSessionParams ikeSessionParams =
- VpnIkev2Utils.buildIkeSessionParams(mContext, mProfile, network);
- final ChildSessionParams childSessionParams =
- VpnIkev2Utils.buildChildSessionParams(mProfile.getAllowedAlgorithms());
-
- // TODO: Remove the need for adding two unused addresses with
- // IPsec tunnels.
- final InetAddress address = InetAddress.getLocalHost();
- mTunnelIface =
- mIpSecManager.createIpSecTunnelInterface(
- address /* unused */,
- address /* unused */,
- network);
- NetdUtils.setInterfaceUp(mNetd, mTunnelIface.getInterfaceName());
-
- mSession = mIkev2SessionCreator.createIkeSession(
- mContext,
- ikeSessionParams,
- childSessionParams,
- mExecutor,
- new VpnIkev2Utils.IkeSessionCallbackImpl(
- TAG, IkeV2VpnRunner.this, network),
- new VpnIkev2Utils.ChildSessionCallbackImpl(
- TAG, IkeV2VpnRunner.this, network));
- Log.d(TAG, "Ike Session started for network " + network);
- } catch (Exception e) {
- Log.i(TAG, "Setup failed for network " + network + ". Aborting", e);
- onSessionLost(network, e);
+ try {
+ if (!mIsRunning) {
+ Log.d(TAG, "onDefaultNetworkChanged after exit");
+ return; // VPN has been shut down.
}
- });
+
+ // Clear mInterface to prevent Ikev2VpnRunner being cleared when
+ // interfaceRemoved() is called.
+ mInterface = null;
+ // Without MOBIKE, we have no way to seamlessly migrate. Close on old
+ // (non-default) network, and start the new one.
+ resetIkeState();
+ mActiveNetwork = network;
+
+ // Get Ike options from IkeTunnelConnectionParams if it's available in the
+ // profile.
+ final IkeTunnelConnectionParams ikeTunConnParams =
+ mProfile.getIkeTunnelConnectionParams();
+ final IkeSessionParams ikeSessionParams;
+ final ChildSessionParams childSessionParams;
+ if (ikeTunConnParams != null) {
+ final IkeSessionParams.Builder builder = new IkeSessionParams.Builder(
+ ikeTunConnParams.getIkeSessionParams()).setNetwork(network);
+ ikeSessionParams = builder.build();
+ childSessionParams = ikeTunConnParams.getTunnelModeChildSessionParams();
+ } else {
+ ikeSessionParams = VpnIkev2Utils.buildIkeSessionParams(
+ mContext, mProfile, network);
+ childSessionParams = VpnIkev2Utils.buildChildSessionParams(
+ mProfile.getAllowedAlgorithms());
+ }
+
+ // TODO: Remove the need for adding two unused addresses with
+ // IPsec tunnels.
+ final InetAddress address = InetAddress.getLocalHost();
+ mTunnelIface =
+ mIpSecManager.createIpSecTunnelInterface(
+ address /* unused */,
+ address /* unused */,
+ network);
+ NetdUtils.setInterfaceUp(mNetd, mTunnelIface.getInterfaceName());
+
+ mSession = mIkev2SessionCreator.createIkeSession(
+ mContext,
+ ikeSessionParams,
+ childSessionParams,
+ mExecutor,
+ new VpnIkev2Utils.IkeSessionCallbackImpl(
+ TAG, IkeV2VpnRunner.this, network),
+ new VpnIkev2Utils.ChildSessionCallbackImpl(
+ TAG, IkeV2VpnRunner.this, network));
+ Log.d(TAG, "Ike Session started for network " + network);
+ } catch (Exception e) {
+ Log.i(TAG, "Setup failed for network " + network + ". Aborting", e);
+ onSessionLost(network, e);
+ }
}
/** Marks the state as FAILED, and disconnects. */
@@ -2845,7 +2864,6 @@
*/
private void disconnectVpnRunner() {
mActiveNetwork = null;
- mSessionKey = null;
mIsRunning = false;
resetIkeState();
@@ -3221,6 +3239,7 @@
case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
+ case VpnProfile.TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS:
if (!mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_IPSEC_TUNNELS)) {
throw new UnsupportedOperationException(
@@ -3274,7 +3293,7 @@
}
private boolean isCurrentIkev2VpnLocked(@NonNull String packageName) {
- return isCurrentPreparedPackage(packageName) && mVpnRunner instanceof IkeV2VpnRunner;
+ return isCurrentPreparedPackage(packageName) && isIkev2VpnRunner();
}
/**
@@ -3328,6 +3347,16 @@
return VpnProfile.decode("" /* Key unused */, encoded);
}
+ private boolean isIkev2VpnRunner() {
+ return (mVpnRunner instanceof IkeV2VpnRunner);
+ }
+
+ @GuardedBy("this")
+ @Nullable
+ private String getSessionKeyLocked() {
+ return isIkev2VpnRunner() ? ((IkeV2VpnRunner) mVpnRunner).mSessionKey : null;
+ }
+
/**
* Starts an already provisioned VPN Profile, keyed by package name.
*
@@ -3355,7 +3384,11 @@
}
startVpnProfilePrivileged(profile, packageName);
- return mSessionKey;
+ if (!isIkev2VpnRunner()) {
+ throw new IllegalStateException("mVpnRunner shouldn't be null and should also be "
+ + "an instance of Ikev2VpnRunner");
+ }
+ return getSessionKeyLocked();
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -3394,6 +3427,7 @@
case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
+ case VpnProfile.TYPE_IKEV2_FROM_IKE_TUN_CONN_PARAMS:
mVpnRunner =
new IkeV2VpnRunner(Ikev2VpnProfile.fromVpnProfile(profile));
mVpnRunner.start();
@@ -3457,11 +3491,8 @@
}
private VpnProfileState makeVpnProfileState() {
- // TODO: mSessionKey will be moved to Ikev2VpnRunner once aosp/2007077 is merged, so after
- // merging aosp/2007077, here should check Ikev2VpnRunner is null or not. Session key will
- // be null if Ikev2VpnRunner is null.
- return new VpnProfileState(getStateFromLegacyState(mLegacyState), mSessionKey, mAlwaysOn,
- mLockdown);
+ return new VpnProfileState(getStateFromLegacyState(mLegacyState),
+ isIkev2VpnRunner() ? getSessionKeyLocked() : null, mAlwaysOn, mLockdown);
}
/**
diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
index a0a596d..6982d60 100644
--- a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
+++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
@@ -86,6 +86,7 @@
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
+import java.util.concurrent.ExecutorService;
/**
* Utility class to build and convert IKEv2/IPsec parameters.
@@ -376,22 +377,25 @@
static class Ikev2VpnNetworkCallback extends NetworkCallback {
private final String mTag;
private final Vpn.IkeV2VpnRunnerCallback mCallback;
+ private final ExecutorService mExecutor;
- Ikev2VpnNetworkCallback(String tag, Vpn.IkeV2VpnRunnerCallback callback) {
+ Ikev2VpnNetworkCallback(String tag, Vpn.IkeV2VpnRunnerCallback callback,
+ ExecutorService executor) {
mTag = tag;
mCallback = callback;
+ mExecutor = executor;
}
@Override
public void onAvailable(@NonNull Network network) {
Log.d(mTag, "Starting IKEv2/IPsec session on new network: " + network);
- mCallback.onDefaultNetworkChanged(network);
+ mExecutor.execute(() -> mCallback.onDefaultNetworkChanged(network));
}
@Override
public void onLost(@NonNull Network network) {
Log.d(mTag, "Tearing down; lost network: " + network);
- mCallback.onSessionLost(network, null);
+ mExecutor.execute(() -> mCallback.onSessionLost(network, null));
}
}
diff --git a/services/core/java/com/android/server/display/OWNERS b/services/core/java/com/android/server/display/OWNERS
index 09d96be..8e34601 100644
--- a/services/core/java/com/android/server/display/OWNERS
+++ b/services/core/java/com/android/server/display/OWNERS
@@ -3,5 +3,6 @@
hackbod@google.com
ogunwale@google.com
santoscordon@google.com
+flc@google.com
per-file ColorDisplayService.java=christyfranks@google.com
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 4071858..7ff65f5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -127,6 +127,7 @@
// We'll not invalidate the active source on the hotplug event to pass CETC 11.2.2-2 ~ 3.
if (!connected) {
getWakeLock().release();
+ mService.getHdmiCecNetwork().removeDevicesConnectedToPort(portId);
}
}
@@ -274,7 +275,9 @@
try {
String iso3Language = new String(message.getParams(), 0, 3, "US-ASCII");
Locale currentLocale = mService.getContext().getResources().getConfiguration().locale;
- if (currentLocale.getISO3Language().equals(iso3Language)) {
+ String curIso3Language = mService.localeToMenuLanguage(currentLocale);
+ HdmiLogger.debug("handleSetMenuLanguage " + iso3Language + " cur:" + curIso3Language);
+ if (curIso3Language.equals(iso3Language)) {
// Do not switch language if the new language is the same as the current one.
// This helps avoid accidental country variant switching from en_US to en_AU
// due to the limitation of CEC. See the warning below.
@@ -286,7 +289,7 @@
final List<LocaleInfo> localeInfos = LocalePicker.getAllAssetLocales(
mService.getContext(), false);
for (LocaleInfo localeInfo : localeInfos) {
- if (localeInfo.getLocale().getISO3Language().equals(iso3Language)) {
+ if (mService.localeToMenuLanguage(localeInfo.getLocale()).equals(iso3Language)) {
// WARNING: CEC adopts ISO/FDIS-2 for language code, while Android requires
// additional country variant to pinpoint the locale. This keeps the right
// locale from being chosen. 'eng' in the CEC command, for instance,
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 3d218cf..b1ffbda 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1218,6 +1218,11 @@
@ServiceThreadOnly
void onHotplug(int portId, boolean connected) {
assertRunOnServiceThread();
+
+ if (!connected) {
+ mService.getHdmiCecNetwork().removeCecSwitches(portId);
+ }
+
// Turning System Audio Mode off when the AVR is unlugged or standby.
// When the device is not unplugged but reawaken from standby, we check if the System
// Audio Control Feature is enabled or not then decide if turning SAM on/off accordingly.
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
index 7ceaa95..72b79f4 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java
@@ -269,7 +269,10 @@
// The addition of a local device should not notify listeners
return;
}
- if (old == null) {
+ if (info.getPhysicalAddress() == HdmiDeviceInfo.PATH_INVALID) {
+ // Don't notify listeners of devices that haven't reported their physical address yet
+ return;
+ } else if (old == null || old.getPhysicalAddress() == HdmiDeviceInfo.PATH_INVALID) {
invokeDeviceEventListener(info,
HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
} else if (!old.equals(info)) {
@@ -296,7 +299,10 @@
assertRunOnServiceThread();
HdmiDeviceInfo old = addDeviceInfo(info);
- if (old == null) {
+ if (info.getPhysicalAddress() == HdmiDeviceInfo.PATH_INVALID) {
+ // Don't notify listeners of devices that haven't reported their physical address yet
+ return;
+ } else if (old == null || old.getPhysicalAddress() == HdmiDeviceInfo.PATH_INVALID) {
invokeDeviceEventListener(info,
HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
} else if (!old.equals(info)) {
@@ -370,10 +376,12 @@
// This only applies to TV devices.
// Returns true if the policy is set to true, and the device to check does not have
// a parent CEC device (which should be the CEC-enabled switch) in the list.
+ // Devices with an invalid physical address are assumed to NOT be connected to a legacy switch.
private boolean hideDevicesBehindLegacySwitch(HdmiDeviceInfo info) {
return isLocalDeviceAddress(Constants.ADDR_TV)
&& HdmiConfig.HIDE_DEVICES_BEHIND_LEGACY_SWITCH
- && !isConnectedToCecSwitch(info.getPhysicalAddress(), getCecSwitches());
+ && !isConnectedToCecSwitch(info.getPhysicalAddress(), getCecSwitches())
+ && info.getPhysicalAddress() != HdmiDeviceInfo.PATH_INVALID;
}
/**
@@ -387,6 +395,10 @@
HdmiDeviceInfo info = removeDeviceInfo(HdmiDeviceInfo.idForCecDevice(address));
localDevice.mCecMessageCache.flushMessagesFrom(address);
+ if (info.getPhysicalAddress() == HdmiDeviceInfo.PATH_INVALID) {
+ // Don't notify listeners of devices that haven't reported their physical address yet
+ return;
+ }
invokeDeviceEventListener(info,
HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
}
@@ -498,6 +510,34 @@
}
/**
+ * Attempts to deduce the device type of a device given its logical address.
+ * If multiple types are possible, returns {@link HdmiDeviceInfo#DEVICE_RESERVED}.
+ */
+ private static int logicalAddressToDeviceType(int logicalAddress) {
+ switch (logicalAddress) {
+ case Constants.ADDR_TV:
+ return HdmiDeviceInfo.DEVICE_TV;
+ case Constants.ADDR_RECORDER_1:
+ case Constants.ADDR_RECORDER_2:
+ case Constants.ADDR_RECORDER_3:
+ return HdmiDeviceInfo.DEVICE_RECORDER;
+ case Constants.ADDR_TUNER_1:
+ case Constants.ADDR_TUNER_2:
+ case Constants.ADDR_TUNER_3:
+ case Constants.ADDR_TUNER_4:
+ return HdmiDeviceInfo.DEVICE_TUNER;
+ case Constants.ADDR_PLAYBACK_1:
+ case Constants.ADDR_PLAYBACK_2:
+ case Constants.ADDR_PLAYBACK_3:
+ return HdmiDeviceInfo.DEVICE_PLAYBACK;
+ case Constants.ADDR_AUDIO_SYSTEM:
+ return HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
+ default:
+ return HdmiDeviceInfo.DEVICE_RESERVED;
+ }
+ }
+
+ /**
* Passively listen to incoming CEC messages.
*
* This shall not result in any CEC messages being sent.
@@ -510,7 +550,7 @@
if (getCecDeviceInfo(sourceAddress) == null) {
HdmiDeviceInfo newDevice = new HdmiDeviceInfo(sourceAddress,
HdmiDeviceInfo.PATH_INVALID, HdmiDeviceInfo.PORT_INVALID,
- HdmiDeviceInfo.DEVICE_RESERVED, Constants.UNKNOWN_VENDOR_ID,
+ logicalAddressToDeviceType(sourceAddress), Constants.UNKNOWN_VENDOR_ID,
HdmiUtils.getDefaultDeviceName(sourceAddress));
addCecDevice(newDevice);
}
@@ -668,7 +708,7 @@
return mCecSwitches;
}
- void removeDevicesConnectedToPort(int portId) {
+ void removeCecSwitches(int portId) {
Iterator<Integer> it = mCecSwitches.iterator();
while (it.hasNext()) {
int path = it.next();
@@ -677,6 +717,11 @@
it.remove();
}
}
+ }
+
+ void removeDevicesConnectedToPort(int portId) {
+ removeCecSwitches(portId);
+
List<Integer> toRemove = new ArrayList<>();
for (int i = 0; i < mDeviceInfos.size(); i++) {
int key = mDeviceInfos.keyAt(i);
@@ -788,7 +833,10 @@
public void clearDeviceList() {
assertRunOnServiceThread();
for (HdmiDeviceInfo info : HdmiUtils.sparseArrayToList(mDeviceInfos)) {
- if (info.getPhysicalAddress() == getPhysicalAddress()) {
+ if (info.getPhysicalAddress() == getPhysicalAddress()
+ || info.getPhysicalAddress() == HdmiDeviceInfo.PATH_INVALID) {
+ // Don't notify listeners of local devices or devices that haven't reported their
+ // physical address yet
continue;
}
invokeDeviceEventListener(info,
@@ -835,10 +883,13 @@
* on the current device.
*/
int physicalAddressToPortId(int path) {
+ int physicalAddress = getPhysicalAddress();
+ if (path == physicalAddress) {
+ // The local device isn't connected to any port; assign portId 0
+ return Constants.CEC_SWITCH_HOME;
+ }
int mask = 0xF000;
int finalMask = 0xF000;
- int physicalAddress;
- physicalAddress = getPhysicalAddress();
int maskedAddress = physicalAddress;
while (maskedAddress != 0) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
index 1c296e5..8647680 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
@@ -83,7 +83,9 @@
private final HdmiCecLocalDevice mDevice;
private final SparseArray<CecMessageHandler> mCecMessageHandlers = new SparseArray<>();
- private final CecMessageHandler mDefaultHandler = new Aborter(
+ private final CecMessageHandler mDefaultHandler;
+
+ private final CecMessageHandler mAborterUnrecognizedOpcode = new Aborter(
Constants.ABORT_UNRECOGNIZED_OPCODE);
private final CecMessageHandler mAborterIncorrectMode = new Aborter(
Constants.ABORT_NOT_IN_CORRECT_MODE);
@@ -95,6 +97,10 @@
mUserControlProcessedHandler = new UserControlProcessedHandler();
private void addCommonHandlers() {
+ addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler);
+ }
+
+ private void addTvHandlers() {
addHandler(Constants.MESSAGE_ACTIVE_SOURCE, mBystander);
addHandler(Constants.MESSAGE_REQUEST_ACTIVE_SOURCE, mBystander);
addHandler(Constants.MESSAGE_ROUTING_CHANGE, mBystander);
@@ -118,17 +124,13 @@
addHandler(Constants.MESSAGE_REPORT_POWER_STATUS, mBypasser);
addHandler(Constants.MESSAGE_GIVE_FEATURES, mBypasser);
- addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler);
-
addHandler(Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS, mBypasser);
addHandler(Constants.MESSAGE_ABORT, mBypasser);
addHandler(Constants.MESSAGE_GET_CEC_VERSION, mBypasser);
addHandler(Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, mAborterIncorrectMode);
addHandler(Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE, mAborterIncorrectMode);
- }
- private void addTvHandlers() {
addHandler(Constants.MESSAGE_IMAGE_VIEW_ON, mAutoOnHandler);
addHandler(Constants.MESSAGE_TEXT_VIEW_ON, mAutoOnHandler);
@@ -153,6 +155,9 @@
addCommonHandlers();
if (mDevice.getType() == HdmiDeviceInfo.DEVICE_TV) {
addTvHandlers();
+ mDefaultHandler = mAborterUnrecognizedOpcode;
+ } else {
+ mDefaultHandler = mBypasser;
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 8dadf5a..a16bd3b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1251,10 +1251,6 @@
device.onHotplug(portId, connected);
}
- if (!connected) {
- mHdmiCecNetwork.removeDevicesConnectedToPort(portId);
- }
-
announceHotplugEvent(portId, connected);
}
diff --git a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
index 4c4c978..3d77164 100644
--- a/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
+++ b/services/core/java/com/android/server/hdmi/HotplugDetectionAction.java
@@ -36,9 +36,9 @@
final class HotplugDetectionAction extends HdmiCecFeatureAction {
private static final String TAG = "HotPlugDetectionAction";
- private static final int POLLING_INTERVAL_MS = 5000;
- private static final int TIMEOUT_COUNT = 3;
- private static final int AVR_COUNT_MAX = 3;
+ public static final int POLLING_INTERVAL_MS = 5000;
+ public static final int TIMEOUT_COUNT = 3;
+ public static final int AVR_COUNT_MAX = 3;
// State in which waits for next polling
private static final int STATE_WAIT_FOR_NEXT_POLLING = 1;
diff --git a/services/core/java/com/android/server/health/HealthRegCallbackAidl.java b/services/core/java/com/android/server/health/HealthRegCallbackAidl.java
index 629011a..90a2f48 100644
--- a/services/core/java/com/android/server/health/HealthRegCallbackAidl.java
+++ b/services/core/java/com/android/server/health/HealthRegCallbackAidl.java
@@ -115,5 +115,13 @@
public void healthInfoChanged(HealthInfo healthInfo) throws RemoteException {
mServiceInfoCallback.update(healthInfo);
}
+ @Override
+ public String getInterfaceHash() {
+ return IHealthInfoCallback.HASH;
+ }
+ @Override
+ public int getInterfaceVersion() {
+ return IHealthInfoCallback.VERSION;
+ }
}
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index a73c8e0..0e4bbbb 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -18,12 +18,14 @@
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import android.app.ActivityManager;
import android.app.admin.PasswordMetrics;
import android.content.Context;
import android.os.ShellCommand;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Slog;
@@ -48,6 +50,8 @@
private static final String COMMAND_REMOVE_CACHE = "remove-cache";
private static final String COMMAND_SET_ROR_PROVIDER_PACKAGE =
"set-resume-on-reboot-provider-package";
+ private static final String COMMAND_REQUIRE_STRONG_AUTH =
+ "require-strong-auth";
private static final String COMMAND_HELP = "help";
private int mCurrentUserId;
@@ -97,6 +101,9 @@
case COMMAND_SET_ROR_PROVIDER_PACKAGE:
runSetResumeOnRebootProviderPackage();
return 0;
+ case COMMAND_REQUIRE_STRONG_AUTH:
+ runRequireStrongAuth();
+ return 0;
case COMMAND_HELP:
onHelp();
return 0;
@@ -192,6 +199,10 @@
pw.println(" Sets the package name for server based resume on reboot service "
+ "provider.");
pw.println("");
+ pw.println(" require-strong-auth [--user USER_ID] <reason>");
+ pw.println(" Requires the strong authentication. The current supported reasons: "
+ + "STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN.");
+ pw.println("");
}
}
@@ -288,6 +299,24 @@
return true;
}
+ private boolean runRequireStrongAuth() {
+ final String reason = mNew;
+ int strongAuthReason;
+ switch (reason) {
+ case "STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN":
+ strongAuthReason = STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+ mCurrentUserId = UserHandle.USER_ALL;
+ break;
+ default:
+ getErrPrintWriter().println("Unsupported reason: " + reason);
+ return false;
+ }
+ mLockPatternUtils.requireStrongAuth(strongAuthReason, mCurrentUserId);
+ getOutPrintWriter().println("Require strong auth for USER_ID "
+ + mCurrentUserId + " because of " + mNew);
+ return true;
+ }
+
private boolean runClear() {
LockscreenCredential none = LockscreenCredential.createNone();
if (!isNewCredentialSufficient(none)) {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 33ac6cd..c963154 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -77,6 +77,8 @@
private static final int EVENT_FIREWALL_CHAIN_ENABLED = 12;
private static final int EVENT_UPDATE_METERED_RESTRICTED_PKGS = 13;
private static final int EVENT_APP_IDLE_WL_CHANGED = 14;
+ private static final int EVENT_METERED_ALLOWLIST_CHANGED = 15;
+ private static final int EVENT_METERED_DENYLIST_CHANGED = 16;
private final LogBuffer mNetworkBlockedBuffer = new LogBuffer(MAX_NETWORK_BLOCKED_LOG_SIZE);
private final LogBuffer mUidStateChangeBuffer = new LogBuffer(MAX_LOG_SIZE);
@@ -89,7 +91,7 @@
void networkBlocked(int uid, @Nullable UidBlockedState uidBlockedState) {
synchronized (mLock) {
if (LOGD || uid == mDebugUid) {
- Slog.d(TAG, "Blocked state of uid: " + uidBlockedState.toString());
+ Slog.d(TAG, "Blocked state of " + uid + ": " + uidBlockedState.toString());
}
if (uidBlockedState == null) {
mNetworkBlockedBuffer.networkBlocked(uid, BLOCKED_REASON_NONE, ALLOWED_REASON_NONE,
@@ -245,6 +247,24 @@
}
}
+ void meteredAllowlistChanged(int uid, boolean added) {
+ synchronized (mLock) {
+ if (LOGD || mDebugUid == uid) {
+ Slog.d(TAG, getMeteredAllowlistChangedLog(uid, added));
+ }
+ mEventsBuffer.meteredAllowlistChanged(uid, added);
+ }
+ }
+
+ void meteredDenylistChanged(int uid, boolean added) {
+ synchronized (mLock) {
+ if (LOGD || mDebugUid == uid) {
+ Slog.d(TAG, getMeteredDenylistChangedLog(uid, added));
+ }
+ mEventsBuffer.meteredDenylistChanged(uid, added);
+ }
+ }
+
void setDebugUid(int uid) {
mDebugUid = uid;
}
@@ -320,6 +340,14 @@
return "Firewall chain " + getFirewallChainName(chain) + " state: " + enabled;
}
+ private static String getMeteredAllowlistChangedLog(int uid, boolean added) {
+ return "metered-allowlist for " + uid + " changed to " + added;
+ }
+
+ private static String getMeteredDenylistChangedLog(int uid, boolean added) {
+ return "metered-denylist for " + uid + " changed to " + added;
+ }
+
private static String getFirewallChainName(int chain) {
switch (chain) {
case FIREWALL_CHAIN_DOZABLE:
@@ -520,6 +548,28 @@
data.timeStamp = System.currentTimeMillis();
}
+ public void meteredAllowlistChanged(int uid, boolean added) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_METERED_ALLOWLIST_CHANGED;
+ data.ifield1 = uid;
+ data.bfield1 = added;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
+ public void meteredDenylistChanged(int uid, boolean added) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ data.type = EVENT_METERED_DENYLIST_CHANGED;
+ data.ifield1 = uid;
+ data.bfield1 = added;
+ data.timeStamp = System.currentTimeMillis();
+ }
+
public void reverseDump(IndentingPrintWriter pw) {
final Data[] allData = toArray();
for (int i = allData.length - 1; i >= 0; --i) {
@@ -567,6 +617,10 @@
return getUidFirewallRuleChangedLog(data.ifield1, data.ifield2, data.ifield3);
case EVENT_FIREWALL_CHAIN_ENABLED:
return getFirewallChainEnabledLog(data.ifield1, data.bfield1);
+ case EVENT_METERED_ALLOWLIST_CHANGED:
+ return getMeteredAllowlistChangedLog(data.ifield1, data.bfield1);
+ case EVENT_METERED_DENYLIST_CHANGED:
+ return getMeteredDenylistChangedLog(data.ifield1, data.bfield1);
default:
return String.valueOf(data.type);
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 8ef42ff..3cb5878 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -91,4 +91,10 @@
*/
public abstract void setMeteredRestrictedPackagesAsync(
Set<String> packageNames, int userId);
+
+ /** Informs that Low Power Standby has become active */
+ public abstract void setLowPowerStandbyActive(boolean active);
+
+ /** Informs that the Low Power Standby allowlist has changed */
+ public abstract void setLowPowerStandbyAllowlist(int[] uids);
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 67b4469..3858d7a 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -26,6 +26,9 @@
import static android.Manifest.permission.OBSERVE_NETWORK_POLICY;
import static android.Manifest.permission.READ_PHONE_STATE;
import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
+import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
+import static android.app.ActivityManager.isProcStateConsideredInteraction;
+import static android.app.ActivityManager.procStateToString;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import static android.content.Intent.ACTION_PACKAGE_ADDED;
@@ -46,17 +49,19 @@
import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY;
import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER;
import static android.net.ConnectivityManager.BLOCKED_REASON_DOZE;
+import static android.net.ConnectivityManager.BLOCKED_REASON_LOW_POWER_STANDBY;
import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
import static android.net.ConnectivityManager.BLOCKED_REASON_RESTRICTED_MODE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_DOZABLE;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
-import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED;
-import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
import static android.net.INetd.FIREWALL_RULE_ALLOW;
import static android.net.INetd.FIREWALL_RULE_DENY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
@@ -69,11 +74,13 @@
import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_SYSTEM;
import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_USER_EXEMPTED;
import static android.net.NetworkPolicyManager.ALLOWED_REASON_FOREGROUND;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST;
import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE;
import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_ALLOWLIST;
import static android.net.NetworkPolicyManager.ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST;
import static android.net.NetworkPolicyManager.ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS;
import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_TOP;
import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
@@ -86,7 +93,10 @@
import static android.net.NetworkPolicyManager.RULE_REJECT_RESTRICTED_MODE;
import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
import static android.net.NetworkPolicyManager.SUBSCRIPTION_OVERRIDE_UNMETERED;
+import static android.net.NetworkPolicyManager.allowedReasonsToString;
+import static android.net.NetworkPolicyManager.blockedReasonsToString;
import static android.net.NetworkPolicyManager.isProcStateAllowedWhileIdleOrPowerSaveMode;
+import static android.net.NetworkPolicyManager.isProcStateAllowedWhileInLowPowerStandby;
import static android.net.NetworkPolicyManager.isProcStateAllowedWhileOnRestrictBackground;
import static android.net.NetworkPolicyManager.resolveNetworkId;
import static android.net.NetworkPolicyManager.uidPoliciesToString;
@@ -155,6 +165,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -163,6 +174,7 @@
import android.net.INetworkManagementEventObserver;
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
+import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkIdentity;
@@ -225,6 +237,7 @@
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
+import android.util.SparseSetArray;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
@@ -413,6 +426,7 @@
private static final int MSG_SET_NETWORK_TEMPLATE_ENABLED = 18;
private static final int MSG_SUBSCRIPTION_PLANS_CHANGED = 19;
private static final int MSG_STATS_PROVIDER_WARNING_OR_LIMIT_REACHED = 20;
+
// TODO: Add similar docs for other messages.
/**
* Message to indicate that reasons for why an uid is blocked changed.
@@ -420,7 +434,8 @@
* arg2 = newBlockedReasons
* obj = oldBlockedReasons
*/
- private static final int MSG_BLOCKED_REASON_CHANGED = 21;
+ private static final int MSG_UID_BLOCKED_REASON_CHANGED = 21;
+
/**
* Message to indicate that subscription plans expired and should be cleared.
* arg1 = subId
@@ -429,6 +444,15 @@
*/
private static final int MSG_CLEAR_SUBSCRIPTION_PLANS = 22;
+ /**
+ * Message to indicate that reasons for why some uids are blocked changed.
+ * obj = SparseArray<SomeArgs> where key = uid and value = SomeArgs object with
+ * value.argi1 = oldEffectiveBlockedReasons,
+ * value.argi2 = newEffectiveBlockedReasons,
+ * value.argi3 = uidRules
+ */
+ private static final int MSG_UIDS_BLOCKED_REASONS_CHANGED = 23;
+
private static final int UID_MSG_STATE_CHANGED = 100;
private static final int UID_MSG_GONE = 101;
@@ -466,7 +490,7 @@
final Object mUidRulesFirstLock = new Object();
final Object mNetworkPoliciesSecondLock = new Object();
- @GuardedBy({"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
+ @GuardedBy(anyOf = {"mUidRulesFirstLock", "mNetworkPoliciesSecondLock"})
volatile boolean mSystemReady;
@GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictBackground;
@@ -477,6 +501,8 @@
volatile boolean mRestrictBackgroundChangedInBsm;
@GuardedBy("mUidRulesFirstLock")
volatile boolean mRestrictedNetworkingMode;
+ @GuardedBy("mUidRulesFirstLock")
+ volatile boolean mLowPowerStandbyActive;
private final boolean mSuppressDefaultPolicy;
@@ -516,6 +542,8 @@
final SparseIntArray mUidFirewallPowerSaveRules = new SparseIntArray();
@GuardedBy("mUidRulesFirstLock")
final SparseIntArray mUidFirewallRestrictedModeRules = new SparseIntArray();
+ @GuardedBy("mUidRulesFirstLock")
+ final SparseIntArray mUidFirewallLowPowerStandbyModeRules = new SparseIntArray();
/** Set of states for the child firewall chains. True if the chain is active. */
@GuardedBy("mUidRulesFirstLock")
@@ -544,6 +572,9 @@
@GuardedBy("mUidRulesFirstLock")
private final SparseBooleanArray mPowerSaveTempWhitelistAppIds = new SparseBooleanArray();
+ @GuardedBy("mUidRulesFirstLock")
+ private final SparseBooleanArray mLowPowerStandbyAllowlistUids = new SparseBooleanArray();
+
/**
* UIDs that have been allowlisted temporarily to be able to have network access despite being
* idle. Other power saving restrictions still apply.
@@ -582,7 +613,7 @@
@GuardedBy("mUidRulesFirstLock")
private final SparseArray<UidState> mUidState = new SparseArray<>();
- @GuardedBy("mUidRulesFirstLock")
+ @GuardedBy("mUidBlockedState")
private final SparseArray<UidBlockedState> mUidBlockedState = new SparseArray<>();
/** Objects used temporarily while computing the new blocked state for each uid. */
@@ -595,6 +626,9 @@
/** Map from network ID to last observed roaming state */
@GuardedBy("mNetworkPoliciesSecondLock")
private final SparseBooleanArray mNetworkRoaming = new SparseBooleanArray();
+ /** Map from network ID to the last ifaces on it */
+ @GuardedBy("mNetworkPoliciesSecondLock")
+ private SparseSetArray<String> mNetworkToIfaces = new SparseSetArray<>();
/** Map from netId to subId as of last update */
@GuardedBy("mNetworkPoliciesSecondLock")
@@ -897,6 +931,7 @@
mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
mAppStandby = LocalServices.getService(AppStandbyInternal.class);
+ mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
synchronized (mUidRulesFirstLock) {
synchronized (mNetworkPoliciesSecondLock) {
@@ -974,12 +1009,11 @@
}
}
- mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
try {
final int changes = ActivityManager.UID_OBSERVER_PROCSTATE
| ActivityManager.UID_OBSERVER_GONE
| ActivityManager.UID_OBSERVER_CAPABILITY;
- mActivityManager.registerUidObserver(mUidObserver, changes,
+ mActivityManagerInternal.registerNetworkPolicyUidObserver(mUidObserver, changes,
NetworkPolicyManager.FOREGROUND_THRESHOLD_STATE, "android");
mNetworkManager.registerObserver(mAlertObserver);
} catch (RemoteException e) {
@@ -1095,7 +1129,9 @@
callbackInfo = new UidStateCallbackInfo();
mUidStateCallbackInfos.put(uid, callbackInfo);
}
- callbackInfo.update(uid, procState, procStateSeq, capability);
+ if (callbackInfo.procStateSeq == -1 || procStateSeq > callbackInfo.procStateSeq) {
+ callbackInfo.update(uid, procState, procStateSeq, capability);
+ }
if (!callbackInfo.isPending) {
mUidEventHandler.obtainMessage(UID_MSG_STATE_CHANGED, callbackInfo)
.sendToTarget();
@@ -1119,8 +1155,8 @@
private static final class UidStateCallbackInfo {
public int uid;
- public int procState;
- public long procStateSeq;
+ public int procState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+ public long procStateSeq = -1;
@ProcessCapability
public int capability;
public boolean isPending;
@@ -1312,11 +1348,28 @@
return changed;
}
+ @GuardedBy("mNetworkPoliciesSecondLock")
+ private boolean updateNetworkToIfacesNL(int netId, @NonNull ArraySet<String> newIfaces) {
+ // TODO: Add a facility SparseSetArray.contains(key) to return whether the key exists.
+ final ArraySet<String> lastIfaces = mNetworkToIfaces.get(netId);
+ final boolean changed = lastIfaces == null ? true : !lastIfaces.equals(newIfaces);
+
+ if (changed) {
+ // Changed on the same network should remove last ifaces and add new ifaces.
+ // TODO: Add a facility SparseSetArray.put(key, value) for replacing the
+ // value for a given key.
+ mNetworkToIfaces.remove(netId);
+ for (String iface : newIfaces) {
+ mNetworkToIfaces.add(netId, iface);
+ }
+ }
+ return changed;
+ }
+
private final NetworkCallback mNetworkCallback = new NetworkCallback() {
@Override
- public void onCapabilitiesChanged(Network network,
- NetworkCapabilities networkCapabilities) {
- if (network == null || networkCapabilities == null) return;
+ public void onCapabilitiesChanged(@NonNull Network network,
+ @NonNull NetworkCapabilities networkCapabilities) {
synchronized (mNetworkPoliciesSecondLock) {
final boolean newMetered = !networkCapabilities
@@ -1335,6 +1388,25 @@
}
}
}
+
+ @Override
+ public void onLinkPropertiesChanged(@NonNull Network network, @NonNull LinkProperties lp) {
+ synchronized (mNetworkPoliciesSecondLock) {
+ final ArraySet<String> newIfaces = new ArraySet<>(lp.getAllInterfaceNames());
+ final boolean ifacesChanged = updateNetworkToIfacesNL(network.getNetId(),
+ newIfaces);
+ if (ifacesChanged) {
+ updateNetworkRulesNL();
+ }
+ }
+ }
+
+ @Override
+ public void onLost(@NonNull Network network) {
+ synchronized (mNetworkPoliciesSecondLock) {
+ mNetworkToIfaces.remove(network.getNetId());
+ }
+ }
};
/**
@@ -3286,7 +3358,7 @@
if (mSystemReady) {
// Device idle change means we need to rebuild rules for all
// known apps, so do a global refresh.
- updateRulesForRestrictPowerUL();
+ handleDeviceIdleModeChangedUL(enabled);
}
}
if (enabled) {
@@ -3785,6 +3857,7 @@
fout.print("Restrict power: "); fout.println(mRestrictPower);
fout.print("Device idle: "); fout.println(mDeviceIdleMode);
fout.print("Restricted networking mode: "); fout.println(mRestrictedNetworkingMode);
+ fout.print("Low Power Standby mode: "); fout.println(mLowPowerStandbyActive);
synchronized (mMeteredIfacesLock) {
fout.print("Metered ifaces: ");
fout.println(mMeteredIfaces);
@@ -3920,9 +3993,23 @@
fout.decreaseIndent();
}
+ size = mLowPowerStandbyAllowlistUids.size();
+ if (size > 0) {
+ fout.println("Low Power Standby allowlist uids:");
+ fout.increaseIndent();
+ for (int i = 0; i < size; i++) {
+ fout.print("UID=");
+ fout.print(mLowPowerStandbyAllowlistUids.keyAt(i));
+ fout.println();
+ }
+ fout.decreaseIndent();
+ }
+
final SparseBooleanArray knownUids = new SparseBooleanArray();
collectKeys(mUidState, knownUids);
- collectKeys(mUidBlockedState, knownUids);
+ synchronized (mUidBlockedState) {
+ collectKeys(mUidBlockedState, knownUids);
+ }
fout.println("Status for all known UIDs:");
fout.increaseIndent();
@@ -3940,12 +4027,14 @@
fout.print(uidState.toString());
}
- final UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
- if (uidBlockedState == null) {
- fout.print(" blocked_state={null}");
- } else {
- fout.print(" blocked_state=");
- fout.print(uidBlockedState.toString());
+ synchronized (mUidBlockedState) {
+ final UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
+ if (uidBlockedState == null) {
+ fout.print(" blocked_state={null}");
+ } else {
+ fout.print(" blocked_state=");
+ fout.print(uidBlockedState);
+ }
}
fout.println();
}
@@ -4001,32 +4090,60 @@
return isProcStateAllowedWhileIdleOrPowerSaveMode(uidState);
}
+ @GuardedBy("mUidRulesFirstLock")
+ private boolean isUidTop(int uid) {
+ final UidState uidState = mUidState.get(uid);
+ return isProcStateAllowedWhileInLowPowerStandby(uidState);
+ }
+
/**
* Process state of UID changed; if needed, will trigger
* {@link #updateRulesForDataUsageRestrictionsUL(int)} and
* {@link #updateRulesForPowerRestrictionsUL(int)}. Returns true if the state was updated.
*/
@GuardedBy("mUidRulesFirstLock")
- private boolean updateUidStateUL(int uid, int procState, @ProcessCapability int capability) {
+ private boolean updateUidStateUL(int uid, int procState, long procStateSeq,
+ @ProcessCapability int capability) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateUidStateUL");
try {
final UidState oldUidState = mUidState.get(uid);
+ if (oldUidState != null && procStateSeq < oldUidState.procStateSeq) {
+ if (LOGV) {
+ Slog.v(TAG, "Ignoring older uid state updates; uid=" + uid
+ + ",procState=" + procStateToString(procState) + ",seq=" + procStateSeq
+ + ",cap=" + capability + ",oldUidState=" + oldUidState);
+ }
+ return false;
+ }
if (oldUidState == null || oldUidState.procState != procState
|| oldUidState.capability != capability) {
- final UidState newUidState = new UidState(uid, procState, capability);
+ final UidState newUidState = new UidState(uid, procState, procStateSeq, capability);
// state changed, push updated rules
mUidState.put(uid, newUidState);
updateRestrictBackgroundRulesOnUidStatusChangedUL(uid, oldUidState, newUidState);
- if (isProcStateAllowedWhileIdleOrPowerSaveMode(oldUidState)
- != isProcStateAllowedWhileIdleOrPowerSaveMode(newUidState)) {
- updateRuleForAppIdleUL(uid);
+ boolean allowedWhileIdleOrPowerSaveModeChanged =
+ isProcStateAllowedWhileIdleOrPowerSaveMode(oldUidState)
+ != isProcStateAllowedWhileIdleOrPowerSaveMode(newUidState);
+ if (allowedWhileIdleOrPowerSaveModeChanged) {
+ updateRuleForAppIdleUL(uid, procState);
if (mDeviceIdleMode) {
updateRuleForDeviceIdleUL(uid);
}
if (mRestrictPower) {
updateRuleForRestrictPowerUL(uid);
}
- updateRulesForPowerRestrictionsUL(uid);
+ updateRulesForPowerRestrictionsUL(uid, procState);
+ }
+ if (mLowPowerStandbyActive) {
+ boolean allowedInLpsChanged =
+ isProcStateAllowedWhileInLowPowerStandby(oldUidState)
+ != isProcStateAllowedWhileInLowPowerStandby(newUidState);
+ if (allowedInLpsChanged) {
+ if (!allowedWhileIdleOrPowerSaveModeChanged) {
+ updateRulesForPowerRestrictionsUL(uid, procState);
+ }
+ updateRuleForLowPowerStandbyUL(uid);
+ }
}
return true;
}
@@ -4051,6 +4168,9 @@
updateRuleForRestrictPowerUL(uid);
}
updateRulesForPowerRestrictionsUL(uid);
+ if (mLowPowerStandbyActive) {
+ updateRuleForLowPowerStandbyUL(uid);
+ }
return true;
}
}
@@ -4098,9 +4218,9 @@
mUidFirewallRestrictedModeRules.clear();
forEachUid("updateRestrictedModeAllowlist", uid -> {
synchronized (mUidRulesFirstLock) {
- final UidBlockedState uidBlockedState = updateBlockedReasonsForRestrictedModeUL(
+ final int effectiveBlockedReasons = updateBlockedReasonsForRestrictedModeUL(
uid);
- final int newFirewallRule = getRestrictedModeFirewallRule(uidBlockedState);
+ final int newFirewallRule = getRestrictedModeFirewallRule(effectiveBlockedReasons);
// setUidFirewallRulesUL will allowlist all uids that are passed to it, so only add
// non-default rules.
@@ -4120,7 +4240,7 @@
@VisibleForTesting
@GuardedBy("mUidRulesFirstLock")
void updateRestrictedModeForUidUL(int uid) {
- final UidBlockedState uidBlockedState = updateBlockedReasonsForRestrictedModeUL(uid);
+ final int effectiveBlockedReasons = updateBlockedReasonsForRestrictedModeUL(uid);
// if restricted networking mode is on, and the app has an access exemption, the uid rule
// will not change, but the firewall rule will have to be updated.
@@ -4128,37 +4248,48 @@
// Note: setUidFirewallRule also updates mUidFirewallRestrictedModeRules.
// In this case, default firewall rules can also be added.
setUidFirewallRuleUL(FIREWALL_CHAIN_RESTRICTED, uid,
- getRestrictedModeFirewallRule(uidBlockedState));
+ getRestrictedModeFirewallRule(effectiveBlockedReasons));
}
}
@GuardedBy("mUidRulesFirstLock")
- private UidBlockedState updateBlockedReasonsForRestrictedModeUL(int uid) {
- final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid(
- mUidBlockedState, uid);
- final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
- if (mRestrictedNetworkingMode) {
- uidBlockedState.blockedReasons |= BLOCKED_REASON_RESTRICTED_MODE;
- } else {
- uidBlockedState.blockedReasons &= ~BLOCKED_REASON_RESTRICTED_MODE;
- }
- if (hasRestrictedModeAccess(uid)) {
- uidBlockedState.allowedReasons |= ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS;
- } else {
- uidBlockedState.allowedReasons &= ~ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS;
- }
- uidBlockedState.updateEffectiveBlockedReasons();
- if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) {
- postBlockedReasonsChangedMsg(uid,
- uidBlockedState.effectiveBlockedReasons, oldEffectiveBlockedReasons);
+ private int updateBlockedReasonsForRestrictedModeUL(int uid) {
+ final boolean hasRestrictedModeAccess = hasRestrictedModeAccess(uid);
+ final int oldEffectiveBlockedReasons;
+ final int newEffectiveBlockedReasons;
+ final int uidRules;
+ synchronized (mUidBlockedState) {
+ final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid(
+ mUidBlockedState, uid);
+ oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+ if (mRestrictedNetworkingMode) {
+ uidBlockedState.blockedReasons |= BLOCKED_REASON_RESTRICTED_MODE;
+ } else {
+ uidBlockedState.blockedReasons &= ~BLOCKED_REASON_RESTRICTED_MODE;
+ }
+ if (hasRestrictedModeAccess) {
+ uidBlockedState.allowedReasons |= ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS;
+ } else {
+ uidBlockedState.allowedReasons &= ~ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS;
+ }
+ uidBlockedState.updateEffectiveBlockedReasons();
- postUidRulesChangedMsg(uid, uidBlockedState.deriveUidRules());
+ newEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+ uidRules = oldEffectiveBlockedReasons == newEffectiveBlockedReasons
+ ? RULE_NONE
+ : uidBlockedState.deriveUidRules();
}
- return uidBlockedState;
+ if (oldEffectiveBlockedReasons != newEffectiveBlockedReasons) {
+ handleBlockedReasonsChanged(uid,
+ newEffectiveBlockedReasons, oldEffectiveBlockedReasons);
+
+ postUidRulesChangedMsg(uid, uidRules);
+ }
+ return newEffectiveBlockedReasons;
}
- private static int getRestrictedModeFirewallRule(UidBlockedState uidBlockedState) {
- if ((uidBlockedState.effectiveBlockedReasons & BLOCKED_REASON_RESTRICTED_MODE) != 0) {
+ private static int getRestrictedModeFirewallRule(int effectiveBlockedReasons) {
+ if ((effectiveBlockedReasons & BLOCKED_REASON_RESTRICTED_MODE) != 0) {
// rejected in restricted mode, this is the default behavior.
return FIREWALL_RULE_DEFAULT;
} else {
@@ -4254,6 +4385,47 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
+ void updateRulesForLowPowerStandbyUL() {
+ Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForLowPowerStandbyUL");
+ try {
+ if (mLowPowerStandbyActive) {
+ mUidFirewallLowPowerStandbyModeRules.clear();
+ for (int i = mUidState.size() - 1; i >= 0; i--) {
+ final int uid = mUidState.keyAt(i);
+ final int effectiveBlockedReasons = getEffectiveBlockedReasons(uid);
+ if (hasInternetPermissionUL(uid) && (effectiveBlockedReasons
+ & BLOCKED_REASON_LOW_POWER_STANDBY) == 0) {
+ mUidFirewallLowPowerStandbyModeRules.put(uid, FIREWALL_RULE_ALLOW);
+ }
+ }
+ setUidFirewallRulesUL(FIREWALL_CHAIN_LOW_POWER_STANDBY,
+ mUidFirewallLowPowerStandbyModeRules, CHAIN_TOGGLE_ENABLE);
+ } else {
+ setUidFirewallRulesUL(FIREWALL_CHAIN_LOW_POWER_STANDBY, null, CHAIN_TOGGLE_DISABLE);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
+ }
+ }
+
+ @GuardedBy("mUidRulesFirstLock")
+ void updateRuleForLowPowerStandbyUL(int uid) {
+ if (!hasInternetPermissionUL(uid)) {
+ return;
+ }
+
+ final int effectiveBlockedReasons = getEffectiveBlockedReasons(uid);
+ if (mUidState.contains(uid)
+ && (effectiveBlockedReasons & BLOCKED_REASON_LOW_POWER_STANDBY) == 0) {
+ mUidFirewallLowPowerStandbyModeRules.put(uid, FIREWALL_RULE_ALLOW);
+ setUidFirewallRuleUL(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, FIREWALL_RULE_ALLOW);
+ } else {
+ mUidFirewallLowPowerStandbyModeRules.delete(uid);
+ setUidFirewallRuleUL(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid, FIREWALL_RULE_DEFAULT);
+ }
+ }
+
/**
* Returns whether a uid is allowlisted from power saving restrictions (eg: Battery Saver, Doze
* mode, and app idle).
@@ -4283,6 +4455,14 @@
return mPowerSaveWhitelistExceptIdleAppIds.get(appId);
}
+ /**
+ * Returns whether a uid is allowlisted from low power standby restrictions.
+ */
+ @GuardedBy("mUidRulesFirstLock")
+ private boolean isAllowlistedFromLowPowerStandbyUL(int uid) {
+ return mLowPowerStandbyAllowlistUids.get(uid);
+ }
+
// NOTE: since both fw_dozable and fw_powersave uses the same map
// (mPowerSaveTempWhitelistAppIds) for allowlisting, we can reuse their logic in this method.
@GuardedBy("mUidRulesFirstLock")
@@ -4329,7 +4509,7 @@
}
@GuardedBy("mUidRulesFirstLock")
- void updateRuleForAppIdleUL(int uid) {
+ void updateRuleForAppIdleUL(int uid, int uidProcessState) {
if (!isUidValidForDenylistRulesUL(uid)) return;
if (Trace.isTagEnabled(Trace.TRACE_TAG_NETWORK)) {
@@ -4337,7 +4517,7 @@
}
try {
int appId = UserHandle.getAppId(uid);
- if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid)
+ if (!mPowerSaveTempWhitelistAppIds.get(appId) && isUidIdle(uid, uidProcessState)
&& !isUidForegroundOnRestrictPowerUL(uid)) {
setUidFirewallRuleUL(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DENY);
if (LOGD) Log.d(TAG, "updateRuleForAppIdleUL DENY " + uid);
@@ -4366,9 +4546,8 @@
if (!isUidValidForDenylistRulesUL(uid)) {
continue;
}
- final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid(
- mUidBlockedState, uid);
- if (!enableChain && (uidBlockedState.blockedReasons & ~BLOCKED_METERED_REASON_MASK)
+ final int blockedReasons = getBlockedReasons(uid);
+ if (!enableChain && (blockedReasons & ~BLOCKED_METERED_REASON_MASK)
== BLOCKED_REASON_NONE) {
// Chain isn't enabled and the uid had no restrictions to begin with.
continue;
@@ -4413,6 +4592,71 @@
}
}
+ @GuardedBy("mUidRulesFirstLock")
+ private void handleDeviceIdleModeChangedUL(boolean enabled) {
+ Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "updateRulesForRestrictPowerUL");
+ try {
+ updateRulesForDeviceIdleUL();
+ if (enabled) {
+ forEachUid("updateRulesForRestrictPower", uid -> {
+ synchronized (mUidRulesFirstLock) {
+ updateRulesForPowerRestrictionsUL(uid);
+ }
+ });
+ } else {
+ // TODO: Note that we could handle the case of enabling-doze state similar
+ // to this but first, we need to update how we listen to uid state changes
+ // so that we always get a callback when a process moves from a NONEXISTENT state
+ // to a "background" state.
+ handleDeviceIdleModeDisabledUL();
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
+ }
+ }
+
+ @GuardedBy("mUidRulesFirstLock")
+ private void handleDeviceIdleModeDisabledUL() {
+ Trace.traceBegin(TRACE_TAG_NETWORK, "handleDeviceIdleModeDisabledUL");
+ try {
+ final SparseArray<SomeArgs> uidStateUpdates = new SparseArray<>();
+ synchronized (mUidBlockedState) {
+ final int size = mUidBlockedState.size();
+ for (int i = 0; i < size; ++i) {
+ final int uid = mUidBlockedState.keyAt(i);
+ final UidBlockedState uidBlockedState = mUidBlockedState.valueAt(i);
+ if ((uidBlockedState.blockedReasons & BLOCKED_REASON_DOZE) == 0) {
+ continue;
+ }
+ uidBlockedState.blockedReasons &= ~BLOCKED_REASON_DOZE;
+ final int oldEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+ uidBlockedState.updateEffectiveBlockedReasons();
+ if (LOGV) {
+ Log.v(TAG, "handleDeviceIdleModeDisabled(" + uid + "); "
+ + "newUidBlockedState=" + uidBlockedState
+ + ", oldEffectiveBlockedReasons=" + oldEffectiveBlockedReasons);
+ }
+ if (oldEffectiveBlockedReasons != uidBlockedState.effectiveBlockedReasons) {
+ final SomeArgs someArgs = SomeArgs.obtain();
+ someArgs.argi1 = oldEffectiveBlockedReasons;
+ someArgs.argi2 = uidBlockedState.effectiveBlockedReasons;
+ someArgs.argi3 = uidBlockedState.deriveUidRules();
+ uidStateUpdates.append(uid, someArgs);
+ // TODO: Update the state for all changed uids together.
+ mActivityManagerInternal.onUidBlockedReasonsChanged(uid,
+ uidBlockedState.effectiveBlockedReasons);
+ }
+ }
+ }
+ if (uidStateUpdates.size() != 0) {
+ mHandler.obtainMessage(MSG_UIDS_BLOCKED_REASONS_CHANGED, uidStateUpdates)
+ .sendToTarget();
+ }
+ } finally {
+ Trace.traceEnd(TRACE_TAG_NETWORK);
+ }
+ }
+
// TODO: rename / document to make it clear these are global (not app-specific) rules
@GuardedBy("mUidRulesFirstLock")
private void updateRulesForRestrictPowerUL() {
@@ -4444,9 +4688,7 @@
}
try {
// update rules for all installed applications
- final PackageManager pm = mContext.getPackageManager();
final List<UserInfo> users;
- final List<ApplicationInfo> apps;
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "list-users");
try {
@@ -4454,26 +4696,30 @@
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
- Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "list-uids");
+ Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "iterate-uids");
try {
- apps = pm.getInstalledApplications(
- PackageManager.MATCH_ANY_USER | PackageManager.MATCH_DISABLED_COMPONENTS
- | PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+ final PackageManagerInternal packageManagerInternal = LocalServices.getService(
+ PackageManagerInternal.class);
+ final int usersSize = users.size();
+ for (int i = 0; i < usersSize; ++i) {
+ final int userId = users.get(i).id;
+ final SparseBooleanArray sharedAppIdsHandled = new SparseBooleanArray();
+ packageManagerInternal.forEachInstalledPackage(androidPackage -> {
+ final int appId = androidPackage.getUid();
+ if (androidPackage.getSharedUserId() != null) {
+ if (sharedAppIdsHandled.indexOfKey(appId) < 0) {
+ sharedAppIdsHandled.put(appId, true);
+ } else {
+ return;
+ }
+ }
+ final int uid = UserHandle.getUid(userId, appId);
+ consumer.accept(uid);
+ }, userId);
+ }
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
-
- final int usersSize = users.size();
- final int appsSize = apps.size();
- for (int i = 0; i < usersSize; i++) {
- final UserInfo user = users.get(i);
- for (int j = 0; j < appsSize; j++) {
- final ApplicationInfo app = apps.get(j);
- final int uid = UserHandle.getUid(user.id, app.uid);
- consumer.accept(uid);
- }
- }
} finally {
Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
}
@@ -4487,7 +4733,7 @@
final UserInfo user = users.get(i);
int uid = UserHandle.getUid(user.id, appId);
// Update external firewall rules.
- updateRuleForAppIdleUL(uid);
+ updateRuleForAppIdleUL(uid, PROCESS_STATE_UNKNOWN);
updateRuleForDeviceIdleUL(uid);
updateRuleForRestrictPowerUL(uid);
// Update internal rules.
@@ -4535,7 +4781,7 @@
} else {
mAppIdleTempWhitelistAppIds.delete(uid);
}
- updateRuleForAppIdleUL(uid);
+ updateRuleForAppIdleUL(uid, PROCESS_STATE_UNKNOWN);
updateRulesForPowerRestrictionsUL(uid);
} finally {
Binder.restoreCallingIdentity(token);
@@ -4561,7 +4807,15 @@
/** Returns if the UID is currently considered idle. */
@VisibleForTesting
boolean isUidIdle(int uid) {
+ return isUidIdle(uid, PROCESS_STATE_UNKNOWN);
+ }
+
+ private boolean isUidIdle(int uid, int uidProcessState) {
synchronized (mUidRulesFirstLock) {
+ if (uidProcessState != PROCESS_STATE_UNKNOWN && isProcStateConsideredInteraction(
+ uidProcessState)) {
+ return false;
+ }
if (mAppIdleTempWhitelistAppIds.get(uid)) {
// UID is temporarily allowlisted.
return false;
@@ -4610,7 +4864,11 @@
@GuardedBy("mUidRulesFirstLock")
private void onUidDeletedUL(int uid) {
// First cleanup in-memory state synchronously...
- mUidBlockedState.delete(uid);
+ synchronized (mUidBlockedState) {
+ mUidBlockedState.delete(uid);
+ }
+ mUidState.delete(uid);
+ mActivityManagerInternal.onUidBlockedReasonsChanged(uid, BLOCKED_REASON_NONE);
mUidPolicy.delete(uid);
mUidFirewallStandbyRules.delete(uid);
mUidFirewallDozableRules.delete(uid);
@@ -4620,6 +4878,7 @@
mPowerSaveTempWhitelistAppIds.delete(uid);
mAppIdleTempWhitelistAppIds.delete(uid);
mUidFirewallRestrictedModeRules.delete(uid);
+ mUidFirewallLowPowerStandbyModeRules.delete(uid);
synchronized (mUidStateCallbackInfos) {
mUidStateCallbackInfos.remove(uid);
}
@@ -4645,7 +4904,7 @@
private void updateRestrictionRulesForUidUL(int uid) {
// Methods below only changes the firewall rules for the power-related modes.
updateRuleForDeviceIdleUL(uid);
- updateRuleForAppIdleUL(uid);
+ updateRuleForAppIdleUL(uid, PROCESS_STATE_UNKNOWN);
updateRuleForRestrictPowerUL(uid);
// If the uid has the necessary permissions, then it should be added to the restricted mode
@@ -4721,11 +4980,6 @@
final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
final boolean isForeground = isUidForegroundOnRestrictBackgroundUL(uid);
final boolean isRestrictedByAdmin = isRestrictedByAdminUL(uid);
- final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid(
- mUidBlockedState, uid);
- final UidBlockedState previousUidBlockedState = getOrCreateUidBlockedStateForUid(
- mTmpUidBlockedState, uid);
- previousUidBlockedState.copyFrom(uidBlockedState);
final boolean isDenied = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
final boolean isAllowed = (uidPolicy & POLICY_ALLOW_METERED_BACKGROUND) != 0;
@@ -4740,18 +4994,47 @@
newAllowedReasons |= (isForeground ? ALLOWED_METERED_REASON_FOREGROUND : 0);
newAllowedReasons |= (isAllowed ? ALLOWED_METERED_REASON_USER_EXEMPTED : 0);
- uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
- & ~BLOCKED_METERED_REASON_MASK) | newBlockedReasons;
- uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons
- & ~ALLOWED_METERED_REASON_MASK) | newAllowedReasons;
- uidBlockedState.updateEffectiveBlockedReasons();
- final int oldEffectiveBlockedReasons = previousUidBlockedState.effectiveBlockedReasons;
- final int newEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+ final int oldEffectiveBlockedReasons;
+ final int newEffectiveBlockedReasons;
+ final int oldAllowedReasons;
+ final int uidRules;
+ synchronized (mUidBlockedState) {
+ final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid(
+ mUidBlockedState, uid);
+ final UidBlockedState previousUidBlockedState = getOrCreateUidBlockedStateForUid(
+ mTmpUidBlockedState, uid);
+ previousUidBlockedState.copyFrom(uidBlockedState);
+
+ uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
+ & ~BLOCKED_METERED_REASON_MASK) | newBlockedReasons;
+ uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons
+ & ~ALLOWED_METERED_REASON_MASK) | newAllowedReasons;
+ uidBlockedState.updateEffectiveBlockedReasons();
+
+ oldEffectiveBlockedReasons = previousUidBlockedState.effectiveBlockedReasons;
+ newEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+ oldAllowedReasons = previousUidBlockedState.allowedReasons;
+ uidRules = (oldEffectiveBlockedReasons == newEffectiveBlockedReasons)
+ ? RULE_NONE : uidBlockedState.deriveUidRules();
+
+ if (LOGV) {
+ Log.v(TAG, "updateRuleForRestrictBackgroundUL(" + uid + ")"
+ + ": isForeground=" + isForeground
+ + ", isDenied=" + isDenied
+ + ", isAllowed=" + isAllowed
+ + ", isRestrictedByAdmin=" + isRestrictedByAdmin
+ + ", oldBlockedState=" + previousUidBlockedState
+ + ", newBlockedState=" + uidBlockedState
+ + ", newBlockedMeteredReasons=" + blockedReasonsToString(newBlockedReasons)
+ + ", newAllowedMeteredReasons=" + allowedReasonsToString(
+ newAllowedReasons));
+ }
+ }
if (oldEffectiveBlockedReasons != newEffectiveBlockedReasons) {
- postBlockedReasonsChangedMsg(uid,
+ handleBlockedReasonsChanged(uid,
newEffectiveBlockedReasons, oldEffectiveBlockedReasons);
- postUidRulesChangedMsg(uid, uidBlockedState.deriveUidRules());
+ postUidRulesChangedMsg(uid, uidRules);
}
// Note that the conditionals below are for avoiding unnecessary calls to netd.
@@ -4767,29 +5050,11 @@
}
final int allowlistReasons = ALLOWED_METERED_REASON_FOREGROUND
| ALLOWED_METERED_REASON_USER_EXEMPTED;
- final int oldAllowedReasons = previousUidBlockedState.allowedReasons;
if ((oldAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE
|| (newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE) {
setMeteredNetworkAllowlist(uid,
(newAllowedReasons & allowlistReasons) != ALLOWED_REASON_NONE);
}
-
- if (LOGV) {
- Log.v(TAG, "updateRuleForRestrictBackgroundUL(" + uid + ")"
- + ": isForeground=" +isForeground
- + ", isDenied=" + isDenied
- + ", isAllowed=" + isAllowed
- + ", isRestrictedByAdmin=" + isRestrictedByAdmin
- + ", oldBlockedState=" + previousUidBlockedState.toString()
- + ", newBlockedState="
- + ", oldBlockedMeteredReasons=" + NetworkPolicyManager.blockedReasonsToString(
- uidBlockedState.blockedReasons & BLOCKED_METERED_REASON_MASK)
- + ", oldBlockedMeteredEffectiveReasons="
- + NetworkPolicyManager.blockedReasonsToString(
- uidBlockedState.effectiveBlockedReasons & BLOCKED_METERED_REASON_MASK)
- + ", oldAllowedMeteredReasons=" + NetworkPolicyManager.blockedReasonsToString(
- uidBlockedState.allowedReasons & BLOCKED_METERED_REASON_MASK));
- }
}
/**
@@ -4813,7 +5078,12 @@
*/
@GuardedBy("mUidRulesFirstLock")
private void updateRulesForPowerRestrictionsUL(int uid) {
- updateRulesForPowerRestrictionsUL(uid, isUidIdle(uid));
+ updateRulesForPowerRestrictionsUL(uid, PROCESS_STATE_UNKNOWN);
+ }
+
+ @GuardedBy("mUidRulesFirstLock")
+ private void updateRulesForPowerRestrictionsUL(int uid, int uidProcState) {
+ updateRulesForPowerRestrictionsUL(uid, isUidIdle(uid, uidProcState));
}
/**
@@ -4845,54 +5115,70 @@
}
final boolean isForeground = isUidForegroundOnRestrictPowerUL(uid);
+ final boolean isTop = isUidTop(uid);
final boolean isWhitelisted = isWhitelistedFromPowerSaveUL(uid, mDeviceIdleMode);
- final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid(
- mUidBlockedState, uid);
- final UidBlockedState previousUidBlockedState = getOrCreateUidBlockedStateForUid(
- mTmpUidBlockedState, uid);
- previousUidBlockedState.copyFrom(uidBlockedState);
+ final int oldEffectiveBlockedReasons;
+ final int newEffectiveBlockedReasons;
+ final int uidRules;
+ synchronized (mUidBlockedState) {
+ final UidBlockedState uidBlockedState = getOrCreateUidBlockedStateForUid(
+ mUidBlockedState, uid);
+ final UidBlockedState previousUidBlockedState = getOrCreateUidBlockedStateForUid(
+ mTmpUidBlockedState, uid);
+ previousUidBlockedState.copyFrom(uidBlockedState);
- int newBlockedReasons = BLOCKED_REASON_NONE;
- int newAllowedReasons = ALLOWED_REASON_NONE;
- newBlockedReasons |= (mRestrictPower ? BLOCKED_REASON_BATTERY_SAVER : 0);
- newBlockedReasons |= (mDeviceIdleMode ? BLOCKED_REASON_DOZE : 0);
- newBlockedReasons |= (isUidIdle ? BLOCKED_REASON_APP_STANDBY : 0);
- newBlockedReasons |= (uidBlockedState.blockedReasons & BLOCKED_REASON_RESTRICTED_MODE);
+ int newBlockedReasons = BLOCKED_REASON_NONE;
+ int newAllowedReasons = ALLOWED_REASON_NONE;
+ newBlockedReasons |= (mRestrictPower ? BLOCKED_REASON_BATTERY_SAVER : 0);
+ newBlockedReasons |= (mDeviceIdleMode ? BLOCKED_REASON_DOZE : 0);
+ newBlockedReasons |= (mLowPowerStandbyActive ? BLOCKED_REASON_LOW_POWER_STANDBY : 0);
+ newBlockedReasons |= (isUidIdle ? BLOCKED_REASON_APP_STANDBY : 0);
+ newBlockedReasons |= (uidBlockedState.blockedReasons & BLOCKED_REASON_RESTRICTED_MODE);
- newAllowedReasons |= (isSystem(uid) ? ALLOWED_REASON_SYSTEM : 0);
- newAllowedReasons |= (isForeground ? ALLOWED_REASON_FOREGROUND : 0);
- newAllowedReasons |= (isWhitelistedFromPowerSaveUL(uid, true)
- ? ALLOWED_REASON_POWER_SAVE_ALLOWLIST : 0);
- newAllowedReasons |= (isWhitelistedFromPowerSaveExceptIdleUL(uid)
- ? ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST : 0);
- newAllowedReasons |= (uidBlockedState.allowedReasons
- & ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS);
+ newAllowedReasons |= (isSystem(uid) ? ALLOWED_REASON_SYSTEM : 0);
+ newAllowedReasons |= (isForeground ? ALLOWED_REASON_FOREGROUND : 0);
+ newAllowedReasons |= (isTop ? ALLOWED_REASON_TOP : 0);
+ newAllowedReasons |= (isWhitelistedFromPowerSaveUL(uid, true)
+ ? ALLOWED_REASON_POWER_SAVE_ALLOWLIST : 0);
+ newAllowedReasons |= (isWhitelistedFromPowerSaveExceptIdleUL(uid)
+ ? ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST : 0);
+ newAllowedReasons |= (uidBlockedState.allowedReasons
+ & ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS);
+ newAllowedReasons |= (isAllowlistedFromLowPowerStandbyUL(uid))
+ ? ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST : 0;
- uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
- & BLOCKED_METERED_REASON_MASK) | newBlockedReasons;
- uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons
- & ALLOWED_METERED_REASON_MASK) | newAllowedReasons;
- uidBlockedState.updateEffectiveBlockedReasons();
- if (previousUidBlockedState.effectiveBlockedReasons
- != uidBlockedState.effectiveBlockedReasons) {
- postBlockedReasonsChangedMsg(uid,
- uidBlockedState.effectiveBlockedReasons,
- previousUidBlockedState.effectiveBlockedReasons);
+ uidBlockedState.blockedReasons = (uidBlockedState.blockedReasons
+ & BLOCKED_METERED_REASON_MASK) | newBlockedReasons;
+ uidBlockedState.allowedReasons = (uidBlockedState.allowedReasons
+ & ALLOWED_METERED_REASON_MASK) | newAllowedReasons;
+ uidBlockedState.updateEffectiveBlockedReasons();
- postUidRulesChangedMsg(uid, uidBlockedState.deriveUidRules());
+ if (LOGV) {
+ Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")"
+ + ", isIdle: " + isUidIdle
+ + ", mRestrictPower: " + mRestrictPower
+ + ", mDeviceIdleMode: " + mDeviceIdleMode
+ + ", isForeground=" + isForeground
+ + ", isTop=" + isTop
+ + ", isWhitelisted=" + isWhitelisted
+ + ", oldUidBlockedState=" + previousUidBlockedState
+ + ", newUidBlockedState=" + uidBlockedState);
+ }
+
+ oldEffectiveBlockedReasons = previousUidBlockedState.effectiveBlockedReasons;
+ newEffectiveBlockedReasons = uidBlockedState.effectiveBlockedReasons;
+ uidRules = oldEffectiveBlockedReasons == newEffectiveBlockedReasons
+ ? RULE_NONE
+ : uidBlockedState.deriveUidRules();
}
+ if (oldEffectiveBlockedReasons != newEffectiveBlockedReasons) {
+ handleBlockedReasonsChanged(uid,
+ newEffectiveBlockedReasons,
+ oldEffectiveBlockedReasons);
- if (LOGV) {
- Log.v(TAG, "updateRulesForPowerRestrictionsUL(" + uid + ")"
- + ", isIdle: " + isUidIdle
- + ", mRestrictPower: " + mRestrictPower
- + ", mDeviceIdleMode: " + mDeviceIdleMode
- + ", isForeground=" + isForeground
- + ", isWhitelisted=" + isWhitelisted
- + ", oldUidBlockedState=" + previousUidBlockedState.toString()
- + ", newUidBlockedState=" + uidBlockedState.toString());
+ postUidRulesChangedMsg(uid, uidRules);
}
}
@@ -4905,7 +5191,7 @@
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
synchronized (mUidRulesFirstLock) {
mLogger.appIdleStateChanged(uid, idle);
- updateRuleForAppIdleUL(uid);
+ updateRuleForAppIdleUL(uid, PROCESS_STATE_UNKNOWN);
updateRulesForPowerRestrictionsUL(uid);
}
} catch (NameNotFoundException nnfe) {
@@ -4921,9 +5207,15 @@
}
}
+ private void handleBlockedReasonsChanged(int uid, int newEffectiveBlockedReasons,
+ int oldEffectiveBlockedReasons) {
+ mActivityManagerInternal.onUidBlockedReasonsChanged(uid, newEffectiveBlockedReasons);
+ postBlockedReasonsChangedMsg(uid, newEffectiveBlockedReasons, oldEffectiveBlockedReasons);
+ }
+
private void postBlockedReasonsChangedMsg(int uid, int newEffectiveBlockedReasons,
int oldEffectiveBlockedReasons) {
- mHandler.obtainMessage(MSG_BLOCKED_REASON_CHANGED, uid,
+ mHandler.obtainMessage(MSG_UID_BLOCKED_REASON_CHANGED, uid,
newEffectiveBlockedReasons, oldEffectiveBlockedReasons)
.sendToTarget();
}
@@ -5170,7 +5462,7 @@
}
return true;
}
- case MSG_BLOCKED_REASON_CHANGED: {
+ case MSG_UID_BLOCKED_REASON_CHANGED: {
final int uid = msg.arg1;
final int newBlockedReasons = msg.arg2;
final int oldBlockedReasons = (int) msg.obj;
@@ -5183,6 +5475,35 @@
mListeners.finishBroadcast();
return true;
}
+ case MSG_UIDS_BLOCKED_REASONS_CHANGED: {
+ final SparseArray<SomeArgs> uidStateUpdates = (SparseArray<SomeArgs>) msg.obj;
+ final int uidsSize = uidStateUpdates.size();
+ final int listenersSize = mListeners.beginBroadcast();
+ for (int i = 0; i < listenersSize; ++i) {
+ final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
+ for (int uidIndex = 0; uidIndex < uidsSize; ++uidIndex) {
+ final int uid = uidStateUpdates.keyAt(uidIndex);
+ final SomeArgs someArgs = uidStateUpdates.valueAt(uidIndex);
+ final int oldBlockedReasons = someArgs.argi1;
+ final int newBlockedReasons = someArgs.argi2;
+ final int uidRules = someArgs.argi3;
+
+ dispatchBlockedReasonChanged(listener, uid,
+ oldBlockedReasons, newBlockedReasons);
+ if (LOGV) {
+ Slog.v(TAG, "Dispatching rules=" + uidRulesToString(uidRules)
+ + " for uid=" + uid);
+ }
+ dispatchUidRulesChanged(listener, uid, uidRules);
+ }
+ }
+ mListeners.finishBroadcast();
+
+ for (int uidIndex = 0; uidIndex < uidsSize; ++uidIndex) {
+ uidStateUpdates.valueAt(uidIndex).recycle();
+ }
+ return true;
+ }
default: {
return false;
}
@@ -5195,21 +5516,7 @@
public boolean handleMessage(Message msg) {
switch (msg.what) {
case UID_MSG_STATE_CHANGED: {
- final UidStateCallbackInfo uidStateCallbackInfo =
- (UidStateCallbackInfo) msg.obj;
- final int uid;
- final int procState;
- final long procStateSeq;
- final int capability;
- synchronized (mUidStateCallbackInfos) {
- uid = uidStateCallbackInfo.uid;
- procState = uidStateCallbackInfo.procState;
- procStateSeq = uidStateCallbackInfo.procStateSeq;
- capability = uidStateCallbackInfo.capability;
- uidStateCallbackInfo.isPending = false;
- }
-
- handleUidChanged(uid, procState, procStateSeq, capability);
+ handleUidChanged((UidStateCallbackInfo) msg.obj);
return true;
}
case UID_MSG_GONE: {
@@ -5224,17 +5531,28 @@
}
};
- void handleUidChanged(int uid, int procState, long procStateSeq,
- @ProcessCapability int capability) {
+ void handleUidChanged(@NonNull UidStateCallbackInfo uidStateCallbackInfo) {
Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "onUidStateChanged");
try {
boolean updated;
+ final int uid;
+ final int procState;
+ final long procStateSeq;
+ final int capability;
synchronized (mUidRulesFirstLock) {
+ synchronized (mUidStateCallbackInfos) {
+ uid = uidStateCallbackInfo.uid;
+ procState = uidStateCallbackInfo.procState;
+ procStateSeq = uidStateCallbackInfo.procStateSeq;
+ capability = uidStateCallbackInfo.capability;
+ uidStateCallbackInfo.isPending = false;
+ }
+
// We received a uid state change callback, add it to the history so that it
// will be useful for debugging.
mLogger.uidStateChanged(uid, procState, procStateSeq, capability);
// Now update the network policy rules as per the updated uid state.
- updated = updateUidStateUL(uid, procState, capability);
+ updated = updateUidStateUL(uid, procState, procStateSeq, capability);
// Updating the network rules is done, so notify AMS about this.
mActivityManagerInternal.notifyNetworkPolicyRulesUpdated(uid, procStateSeq);
}
@@ -5331,6 +5649,12 @@
if (LOGV) Slog.v(TAG, "setMeteredNetworkDenylist " + uid + ": " + enable);
try {
mNetworkManager.setUidOnMeteredNetworkDenylist(uid, enable);
+ mLogger.meteredDenylistChanged(uid, enable);
+ if (Process.isApplicationUid(uid)) {
+ final int sdkSandboxUid = Process.toSdkSandboxUid(uid);
+ mNetworkManager.setUidOnMeteredNetworkDenylist(sdkSandboxUid, enable);
+ mLogger.meteredDenylistChanged(sdkSandboxUid, enable);
+ }
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem setting denylist (" + enable + ") rules for " + uid, e);
} catch (RemoteException e) {
@@ -5342,6 +5666,12 @@
if (LOGV) Slog.v(TAG, "setMeteredNetworkAllowlist " + uid + ": " + enable);
try {
mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, enable);
+ mLogger.meteredAllowlistChanged(uid, enable);
+ if (Process.isApplicationUid(uid)) {
+ final int sdkSandboxUid = Process.toSdkSandboxUid(uid);
+ mNetworkManager.setUidOnMeteredNetworkAllowlist(sdkSandboxUid, enable);
+ mLogger.meteredAllowlistChanged(sdkSandboxUid, enable);
+ }
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem setting allowlist (" + enable + ") rules for " + uid, e);
} catch (RemoteException e) {
@@ -5380,12 +5710,31 @@
}
}
+ private void addSdkSandboxUidsIfNeeded(SparseIntArray uidRules) {
+ final int size = uidRules.size();
+ final SparseIntArray sdkSandboxUids = new SparseIntArray();
+ for (int index = 0; index < size; index++) {
+ final int uid = uidRules.keyAt(index);
+ final int rule = uidRules.valueAt(index);
+ if (Process.isApplicationUid(uid)) {
+ sdkSandboxUids.put(Process.toSdkSandboxUid(uid), rule);
+ }
+ }
+
+ for (int index = 0; index < sdkSandboxUids.size(); index++) {
+ final int uid = sdkSandboxUids.keyAt(index);
+ final int rule = sdkSandboxUids.valueAt(index);
+ uidRules.put(uid, rule);
+ }
+ }
+
/**
* Set uid rules on a particular firewall chain. This is going to synchronize the rules given
* here to netd. It will clean up dead rules and make sure the target chain only contains rules
* specified here.
*/
private void setUidFirewallRulesUL(int chain, SparseIntArray uidRules) {
+ addSdkSandboxUidsIfNeeded(uidRules);
try {
int size = uidRules.size();
int[] uids = new int[size];
@@ -5421,11 +5770,18 @@
mUidFirewallPowerSaveRules.put(uid, rule);
} else if (chain == FIREWALL_CHAIN_RESTRICTED) {
mUidFirewallRestrictedModeRules.put(uid, rule);
+ } else if (chain == FIREWALL_CHAIN_LOW_POWER_STANDBY) {
+ mUidFirewallLowPowerStandbyModeRules.put(uid, rule);
}
try {
mNetworkManager.setFirewallUidRule(chain, uid, rule);
mLogger.uidFirewallRuleChanged(chain, uid, rule);
+ if (Process.isApplicationUid(uid)) {
+ final int sdkSandboxUid = Process.toSdkSandboxUid(uid);
+ mNetworkManager.setFirewallUidRule(chain, sdkSandboxUid, rule);
+ mLogger.uidFirewallRuleChanged(chain, sdkSandboxUid, rule);
+ }
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem setting firewall uid rules", e);
} catch (RemoteException e) {
@@ -5462,19 +5818,28 @@
*/
private void resetUidFirewallRules(int uid) {
try {
- mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_DOZABLE, uid, FIREWALL_RULE_DEFAULT);
- mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT);
- mNetworkManager
- .setFirewallUidRule(FIREWALL_CHAIN_POWERSAVE, uid, FIREWALL_RULE_DEFAULT);
- mNetworkManager
- .setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, uid, FIREWALL_RULE_DEFAULT);
+ mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_DOZABLE, uid,
+ FIREWALL_RULE_DEFAULT);
+ mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_STANDBY, uid,
+ FIREWALL_RULE_DEFAULT);
+ mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_POWERSAVE, uid,
+ FIREWALL_RULE_DEFAULT);
+ mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_RESTRICTED, uid,
+ FIREWALL_RULE_DEFAULT);
+ mNetworkManager.setFirewallUidRule(FIREWALL_CHAIN_LOW_POWER_STANDBY, uid,
+ FIREWALL_RULE_DEFAULT);
mNetworkManager.setUidOnMeteredNetworkAllowlist(uid, false);
+ mLogger.meteredAllowlistChanged(uid, false);
mNetworkManager.setUidOnMeteredNetworkDenylist(uid, false);
+ mLogger.meteredDenylistChanged(uid, false);
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem resetting firewall uid rules for " + uid, e);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
+ if (Process.isApplicationUid(uid)) {
+ resetUidFirewallRules(Process.toSdkSandboxUid(uid));
+ }
}
@Deprecated
@@ -5634,7 +5999,7 @@
mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
int blockedReasons;
- synchronized (mUidRulesFirstLock) {
+ synchronized (mUidBlockedState) {
final UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
blockedReasons = uidBlockedState == null
? BLOCKED_REASON_NONE : uidBlockedState.effectiveBlockedReasons;
@@ -5652,7 +6017,7 @@
@Override
public boolean isUidRestrictedOnMeteredNetworks(int uid) {
mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
- synchronized (mUidRulesFirstLock) {
+ synchronized (mUidBlockedState) {
final UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
int blockedReasons = uidBlockedState == null
? BLOCKED_REASON_NONE : uidBlockedState.effectiveBlockedReasons;
@@ -5747,6 +6112,67 @@
mHandler.obtainMessage(MSG_METERED_RESTRICTED_PACKAGES_CHANGED,
userId, 0, packageNames).sendToTarget();
}
+
+ @Override
+ public void setLowPowerStandbyActive(boolean active) {
+ Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "setLowPowerStandbyActive");
+ try {
+ synchronized (mUidRulesFirstLock) {
+ if (mLowPowerStandbyActive == active) {
+ return;
+ }
+ mLowPowerStandbyActive = active;
+ synchronized (mNetworkPoliciesSecondLock) {
+ if (!mSystemReady) return;
+ }
+
+ forEachUid("updateRulesForRestrictPower",
+ uid -> updateRulesForPowerRestrictionsUL(uid));
+ updateRulesForLowPowerStandbyUL();
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
+ }
+ }
+
+ @Override
+ public void setLowPowerStandbyAllowlist(int[] uids) {
+ synchronized (mUidRulesFirstLock) {
+ final SparseBooleanArray changedUids = new SparseBooleanArray();
+ for (int i = 0; i < mLowPowerStandbyAllowlistUids.size(); i++) {
+ final int oldUid = mLowPowerStandbyAllowlistUids.keyAt(i);
+ if (!ArrayUtils.contains(uids, oldUid)) {
+ changedUids.put(oldUid, true);
+ }
+ }
+
+ for (int i = 0; i < changedUids.size(); i++) {
+ final int deletedUid = changedUids.keyAt(i);
+ mLowPowerStandbyAllowlistUids.delete(deletedUid);
+ }
+
+ for (int newUid : uids) {
+ if (mLowPowerStandbyAllowlistUids.indexOfKey(newUid) < 0) {
+ changedUids.append(newUid, true);
+ mLowPowerStandbyAllowlistUids.append(newUid, true);
+ }
+ }
+
+ if (!mLowPowerStandbyActive) {
+ return;
+ }
+
+ synchronized (mNetworkPoliciesSecondLock) {
+ if (!mSystemReady) return;
+ }
+
+ for (int i = 0; i < changedUids.size(); i++) {
+ final int changedUid = changedUids.keyAt(i);
+ updateRulesForPowerRestrictionsUL(changedUid);
+ updateRuleForLowPowerStandbyUL(changedUid);
+ }
+ }
+ }
}
private void setMeteredRestrictedPackagesInternal(Set<String> packageNames, int userId) {
@@ -5839,10 +6265,6 @@
return restrictedUids != null && restrictedUids.contains(uid);
}
- private static boolean hasRule(int uidRules, int rule) {
- return (uidRules & rule) != 0;
- }
-
private static boolean getBooleanDefeatingNullable(@Nullable PersistableBundle bundle,
String key, boolean defaultValue) {
return (bundle != null) ? bundle.getBoolean(key, defaultValue) : defaultValue;
@@ -5858,16 +6280,39 @@
return uidBlockedState;
}
+ private int getEffectiveBlockedReasons(int uid) {
+ synchronized (mUidBlockedState) {
+ final UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
+ return uidBlockedState == null
+ ? BLOCKED_REASON_NONE
+ : uidBlockedState.effectiveBlockedReasons;
+ }
+ }
+
+ private int getBlockedReasons(int uid) {
+ synchronized (mUidBlockedState) {
+ final UidBlockedState uidBlockedState = mUidBlockedState.get(uid);
+ return uidBlockedState == null
+ ? BLOCKED_REASON_NONE
+ : uidBlockedState.blockedReasons;
+ }
+ }
+
@VisibleForTesting
static final class UidBlockedState {
public int blockedReasons;
public int allowedReasons;
public int effectiveBlockedReasons;
+ private UidBlockedState(int blockedReasons, int allowedReasons,
+ int effectiveBlockedReasons) {
+ this.blockedReasons = blockedReasons;
+ this.allowedReasons = allowedReasons;
+ this.effectiveBlockedReasons = effectiveBlockedReasons;
+ }
+
UidBlockedState() {
- blockedReasons = BLOCKED_REASON_NONE;
- allowedReasons = ALLOWED_REASON_NONE;
- effectiveBlockedReasons = BLOCKED_REASON_NONE;
+ this(BLOCKED_REASON_NONE, ALLOWED_REASON_NONE, BLOCKED_REASON_NONE);
}
void updateEffectiveBlockedReasons() {
@@ -5904,6 +6349,9 @@
effectiveBlockedReasons &= ~BLOCKED_METERED_REASON_DATA_SAVER;
effectiveBlockedReasons &= ~BLOCKED_METERED_REASON_USER_RESTRICTED;
}
+ if ((allowedReasons & ALLOWED_REASON_TOP) != 0) {
+ effectiveBlockedReasons &= ~BLOCKED_REASON_LOW_POWER_STANDBY;
+ }
if ((allowedReasons & ALLOWED_REASON_POWER_SAVE_ALLOWLIST) != 0) {
effectiveBlockedReasons &= ~BLOCKED_REASON_BATTERY_SAVER;
effectiveBlockedReasons &= ~BLOCKED_REASON_DOZE;
@@ -5919,6 +6367,10 @@
if ((allowedReasons & ALLOWED_METERED_REASON_USER_EXEMPTED) != 0) {
effectiveBlockedReasons &= ~BLOCKED_METERED_REASON_DATA_SAVER;
}
+ if ((allowedReasons & ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST) != 0) {
+ effectiveBlockedReasons &= ~BLOCKED_REASON_LOW_POWER_STANDBY;
+ }
+
return effectiveBlockedReasons;
}
@@ -5943,6 +6395,7 @@
BLOCKED_REASON_DOZE,
BLOCKED_REASON_APP_STANDBY,
BLOCKED_REASON_RESTRICTED_MODE,
+ BLOCKED_REASON_LOW_POWER_STANDBY,
BLOCKED_METERED_REASON_DATA_SAVER,
BLOCKED_METERED_REASON_USER_RESTRICTED,
BLOCKED_METERED_REASON_ADMIN_DISABLED,
@@ -5951,9 +6404,11 @@
private static final int[] ALLOWED_REASONS = {
ALLOWED_REASON_SYSTEM,
ALLOWED_REASON_FOREGROUND,
+ ALLOWED_REASON_TOP,
ALLOWED_REASON_POWER_SAVE_ALLOWLIST,
ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST,
ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS,
+ ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST,
ALLOWED_METERED_REASON_USER_EXEMPTED,
ALLOWED_METERED_REASON_SYSTEM,
ALLOWED_METERED_REASON_FOREGROUND,
@@ -5971,6 +6426,8 @@
return "APP_STANDBY";
case BLOCKED_REASON_RESTRICTED_MODE:
return "RESTRICTED_MODE";
+ case BLOCKED_REASON_LOW_POWER_STANDBY:
+ return "LOW_POWER_STANDBY";
case BLOCKED_METERED_REASON_DATA_SAVER:
return "DATA_SAVER";
case BLOCKED_METERED_REASON_USER_RESTRICTED:
@@ -5991,12 +6448,16 @@
return "SYSTEM";
case ALLOWED_REASON_FOREGROUND:
return "FOREGROUND";
+ case ALLOWED_REASON_TOP:
+ return "TOP";
case ALLOWED_REASON_POWER_SAVE_ALLOWLIST:
return "POWER_SAVE_ALLOWLIST";
case ALLOWED_REASON_POWER_SAVE_EXCEPT_IDLE_ALLOWLIST:
return "POWER_SAVE_EXCEPT_IDLE_ALLOWLIST";
case ALLOWED_REASON_RESTRICTED_MODE_PERMISSIONS:
return "RESTRICTED_MODE_PERMISSIONS";
+ case ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST:
+ return "LOW_POWER_STANDBY_ALLOWLIST";
case ALLOWED_METERED_REASON_USER_EXEMPTED:
return "METERED_USER_EXEMPTED";
case ALLOWED_METERED_REASON_SYSTEM:
@@ -6063,7 +6524,8 @@
int powerBlockedReasons = BLOCKED_REASON_APP_STANDBY
| BLOCKED_REASON_DOZE
- | BLOCKED_REASON_BATTERY_SAVER;
+ | BLOCKED_REASON_BATTERY_SAVER
+ | BLOCKED_REASON_LOW_POWER_STANDBY;
if ((effectiveBlockedReasons & powerBlockedReasons) != 0) {
uidRule |= RULE_REJECT_ALL;
} else if ((blockedReasons & powerBlockedReasons) != 0) {
@@ -6087,7 +6549,7 @@
}
}
if (LOGV) {
- Slog.v(TAG, "uidBlockedState=" + this.toString()
+ Slog.v(TAG, "uidBlockedState=" + this
+ " -> uidRule=" + uidRulesToString(uidRule));
}
return uidRule;
diff --git a/services/core/java/com/android/server/net/TEST_MAPPING b/services/core/java/com/android/server/net/TEST_MAPPING
index 02095eb..4ccf09e 100644
--- a/services/core/java/com/android/server/net/TEST_MAPPING
+++ b/services/core/java/com/android/server/net/TEST_MAPPING
@@ -2,12 +2,8 @@
"presubmit-large": [
{
"name": "CtsHostsideNetworkTests",
- "file_patterns": ["(/|^)NetworkPolicy[^/]*\\.java"],
"options": [
{
- "include-filter": "com.android.cts.net.HostsideRestrictBackgroundNetworkTests"
- },
- {
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
index 9cb8a01..4f26809 100644
--- a/services/core/java/com/android/server/notification/GroupHelper.java
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -82,7 +82,7 @@
}
String combinedKey = generatePackageGroupKey(userId, sbn.getPackageName(), group);
boolean needsOngoingFlag = notifications.size() > 0;
- mCallback.updateAutogroupSummary(sbn.getKey(), needsOngoingFlag);
+ mCallback.updateAutogroupSummary(userId, sbn.getPackageName(), needsOngoingFlag);
}
public void onNotificationUpdated(StatusBarNotification childSbn,
@@ -211,6 +211,6 @@
void removeAutoGroup(String key);
void addAutoGroupSummary(int userId, String pkg, String triggeringKey);
void removeAutoGroupSummary(int user, String pkg);
- void updateAutogroupSummary(String key, boolean needsOngoingFlag);
+ void updateAutogroupSummary(int userId, String pkg, boolean needsOngoingFlag);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 211f8d6..d9f2e97 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2498,19 +2498,11 @@
}
@Override
- public void updateAutogroupSummary(String key, boolean needsOngoingFlag) {
- String pkg;
- synchronized (mNotificationLock) {
- NotificationRecord r = mNotificationsByKey.get(key);
- pkg = r != null && r.getSbn() != null ? r.getSbn().getPackageName() : null;
- }
+ public void updateAutogroupSummary(int userId, String pkg, boolean needsOngoingFlag) {
boolean isAppForeground = pkg != null
&& mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
synchronized (mNotificationLock) {
- NotificationRecord r = mNotificationsByKey.get(key);
- if (r == null) return;
- updateAutobundledSummaryFlags(r.getUser().getIdentifier(),
- r.getSbn().getPackageName(), needsOngoingFlag, isAppForeground);
+ updateAutobundledSummaryFlags(userId, pkg, needsOngoingFlag, isAppForeground);
}
}
});
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 419b726..24b9f48 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -815,7 +815,7 @@
PendingIntent injectCreatePendingIntent(int requestCode, @NonNull Intent[] intents,
int flags, Bundle options, String ownerPackage, int ownerUserId) {
return mActivityManagerInternal.getPendingIntentActivityAsApp(requestCode, intents,
- flags, options, ownerPackage, ownerUserId);
+ flags, null /* options */, ownerPackage, ownerUserId);
}
@Override
@@ -1117,7 +1117,7 @@
// calling identity to mirror the startActivityAsUser() call which does not validate
// the calling user
return PendingIntent.getActivityAsUser(mContext, 0 /* requestCode */, launchIntent,
- FLAG_IMMUTABLE, opts, user);
+ FLAG_IMMUTABLE, null /* options */, user);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index fad18d2..7d1dbc5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -13257,7 +13257,8 @@
// Don't use profiles since that may cause compilation to be skipped.
final int res = performDexOptInternalWithDependenciesLI(pkg, pkgSetting,
new DexoptOptions(packageName,
- getDefaultCompilerFilter(),
+ REASON_CMDLINE,
+ getDefaultCompilerFilter(), null /* splitName */,
DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE));
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 265e606..d01f962 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3516,7 +3516,7 @@
}
List<PermissionInfo> ps = mPermissionManager
.queryPermissionsByGroup(groupList.get(i), 0 /*flags*/);
- final int count = ps.size();
+ final int count = (ps == null ? 0 : ps.size());
boolean first = true;
for (int p = 0 ; p < count ; p++) {
PermissionInfo pi = ps.get(p);
diff --git a/services/core/java/com/android/server/pm/dex/OdsignStatsLogger.java b/services/core/java/com/android/server/pm/dex/OdsignStatsLogger.java
new file mode 100644
index 0000000..fa08add
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/OdsignStatsLogger.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2022 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.pm.dex;
+
+import android.util.Slog;
+
+import com.android.internal.art.ArtStatsLog;
+import com.android.internal.os.BackgroundThread;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ * This class is responsible for reading metrics files generated by odsign and sending them to
+ * statsd. odsign can't send the stats directly to statsd, because statsd can't run until after
+ * odsign has completed. The code here is intended to run once per boot, since odsign runs at boot
+ * time.
+ */
+public class OdsignStatsLogger {
+ private static final String TAG = "OdsignStatsLogger";
+
+ // These need to be kept in sync with system/security/ondevice-signing/StatsReporter.{h, cpp}.
+ private static final String METRICS_FILE = "/data/misc/odsign/metrics/odsign-metrics.txt";
+ private static final String COMPOS_METRIC_NAME = "comp_os_artifacts_check_record";
+
+ /**
+ * Arrange for stats to be uploaded in the background.
+ */
+ public static void triggerStatsWrite() {
+ BackgroundThread.getExecutor().execute(OdsignStatsLogger::writeStats);
+ }
+
+ private static void writeStats() {
+ try {
+ String lines = IoUtils.readFileAsString(METRICS_FILE);
+
+ // Delete the file now so we don't upload it more than once, and don't keep trying
+ // to re-parse it if there is a problem.
+ if (!new File(METRICS_FILE).delete()) {
+ Slog.w(TAG, "Failed to delete metrics file");
+ }
+
+ // The format is simple - each line is a series of space separated tokens. The first is
+ // the metric name and subsequent ones are the metric values. The logic here must be
+ // kept in sync with system/security/ondevice-signing/StatsReporter.cpp.
+
+ for (String line : lines.split("\n")) {
+ String[] metrics = line.split(" ");
+
+ if (metrics.length != 4 || !metrics[0].equals(COMPOS_METRIC_NAME)) {
+ Slog.w(TAG, "Malformed metrics file");
+ break;
+ }
+
+ boolean currentArtifactsOk = metrics[1].equals("1");
+ boolean compOsPendingArtifactsExists = metrics[2].equals("1");
+ boolean useCompOsGeneratedArtifacts = metrics[3].equals("1");
+
+ ArtStatsLog.write(ArtStatsLog.EARLY_BOOT_COMP_OS_ARTIFACTS_CHECK_REPORTED,
+ currentArtifactsOk, compOsPendingArtifactsExists,
+ useCompOsGeneratedArtifacts);
+ }
+ } catch (FileNotFoundException e) {
+ // This is normal and probably means no new metrics have been generated.
+ } catch (IOException e) {
+ Slog.w(TAG, "Reading metrics file failed", e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 99c675d..48d464c 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -1618,7 +1618,28 @@
if (adapter != null) {
SynchronousResultReceiver bluetoothReceiver =
new SynchronousResultReceiver("bluetooth");
- adapter.requestControllerActivityEnergyInfo(bluetoothReceiver);
+ adapter.requestControllerActivityEnergyInfo(
+ Runnable::run,
+ new BluetoothAdapter.OnBluetoothActivityEnergyInfoCallback() {
+ @Override
+ public void onBluetoothActivityEnergyInfoAvailable(
+ BluetoothActivityEnergyInfo info) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(
+ BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info);
+ bluetoothReceiver.send(0, bundle);
+ }
+
+ @Override
+ public void onBluetoothActivityEnergyInfoError(int errorCode) {
+ Slog.w(TAG, "error reading Bluetooth stats: " + errorCode);
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(
+ BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, null);
+ bluetoothReceiver.send(0, bundle);
+ }
+ }
+ );
return awaitControllerInfo(bluetoothReceiver);
} else {
Slog.e(TAG, "Failed to get bluetooth adapter!");
diff --git a/services/core/java/com/android/server/telecom/OWNERS b/services/core/java/com/android/server/telecom/OWNERS
index 39be2c1..cad25a4 100644
--- a/services/core/java/com/android/server/telecom/OWNERS
+++ b/services/core/java/com/android/server/telecom/OWNERS
@@ -1,6 +1 @@
-breadley@google.com
-hallliu@google.com
-tgunn@google.com
-xiaotonj@google.com
-shuoq@google.com
-rgreenwalt@google.com
+file:platform/frameworks/base:/telecomm/OWNERS
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index 89470ec..5c305c6 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -29,6 +29,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.vcn.VcnManager;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.ParcelUuid;
@@ -47,6 +48,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.vcn.util.PersistableBundleUtils;
+import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import java.util.ArrayList;
import java.util.Collections;
@@ -95,6 +98,10 @@
// TODO (Android T+): Add ability to handle multiple subIds per slot.
@NonNull private final Map<Integer, Integer> mReadySubIdsBySlotId = new HashMap<>();
+
+ @NonNull
+ private final Map<Integer, PersistableBundleWrapper> mSubIdToCarrierConfigMap = new HashMap<>();
+
@NonNull private final OnSubscriptionsChangedListener mSubscriptionChangedListener;
@NonNull
@@ -250,7 +257,10 @@
final TelephonySubscriptionSnapshot newSnapshot =
new TelephonySubscriptionSnapshot(
- mDeps.getActiveDataSubscriptionId(), newSubIdToInfoMap, privilegedPackages);
+ mDeps.getActiveDataSubscriptionId(),
+ newSubIdToInfoMap,
+ mSubIdToCarrierConfigMap,
+ privilegedPackages);
// If snapshot was meaningfully updated, fire the callback
if (!newSnapshot.equals(mCurrentSnapshot)) {
@@ -311,47 +321,77 @@
}
if (SubscriptionManager.isValidSubscriptionId(subId)) {
- final PersistableBundle carrierConfigs = mCarrierConfigManager.getConfigForSubId(subId);
- if (mDeps.isConfigForIdentifiedCarrier(carrierConfigs)) {
+ final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId);
+ if (mDeps.isConfigForIdentifiedCarrier(carrierConfig)) {
mReadySubIdsBySlotId.put(slotId, subId);
+
+ final PersistableBundle minimized =
+ PersistableBundleUtils.minimizeBundle(
+ carrierConfig, VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
+ if (minimized != null) {
+ mSubIdToCarrierConfigMap.put(subId, new PersistableBundleWrapper(minimized));
+ }
handleSubscriptionsChanged();
}
} else {
- mReadySubIdsBySlotId.remove(slotId);
+ final Integer oldSubid = mReadySubIdsBySlotId.remove(slotId);
+ if (oldSubid != null) {
+ mSubIdToCarrierConfigMap.remove(oldSubid);
+ }
handleSubscriptionsChanged();
}
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
void setReadySubIdsBySlotId(Map<Integer, Integer> readySubIdsBySlotId) {
+ mReadySubIdsBySlotId.clear();
mReadySubIdsBySlotId.putAll(readySubIdsBySlotId);
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
+ void setSubIdToCarrierConfigMap(
+ Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap) {
+ mSubIdToCarrierConfigMap.clear();
+ mSubIdToCarrierConfigMap.putAll(subIdToCarrierConfigMap);
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
Map<Integer, Integer> getReadySubIdsBySlotId() {
return Collections.unmodifiableMap(mReadySubIdsBySlotId);
}
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ Map<Integer, PersistableBundleWrapper> getSubIdToCarrierConfigMap() {
+ return Collections.unmodifiableMap(mSubIdToCarrierConfigMap);
+ }
+
/** TelephonySubscriptionSnapshot is a class containing info about active subscriptions */
public static class TelephonySubscriptionSnapshot {
private final int mActiveDataSubId;
private final Map<Integer, SubscriptionInfo> mSubIdToInfoMap;
+ private final Map<Integer, PersistableBundleWrapper> mSubIdToCarrierConfigMap;
private final Map<ParcelUuid, Set<String>> mPrivilegedPackages;
public static final TelephonySubscriptionSnapshot EMPTY_SNAPSHOT =
new TelephonySubscriptionSnapshot(
- INVALID_SUBSCRIPTION_ID, Collections.emptyMap(), Collections.emptyMap());
+ INVALID_SUBSCRIPTION_ID,
+ Collections.emptyMap(),
+ Collections.emptyMap(),
+ Collections.emptyMap());
@VisibleForTesting(visibility = Visibility.PRIVATE)
TelephonySubscriptionSnapshot(
int activeDataSubId,
@NonNull Map<Integer, SubscriptionInfo> subIdToInfoMap,
+ @NonNull Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap,
@NonNull Map<ParcelUuid, Set<String>> privilegedPackages) {
mActiveDataSubId = activeDataSubId;
Objects.requireNonNull(subIdToInfoMap, "subIdToInfoMap was null");
Objects.requireNonNull(privilegedPackages, "privilegedPackages was null");
+ Objects.requireNonNull(subIdToCarrierConfigMap, "subIdToCarrierConfigMap was null");
mSubIdToInfoMap = Collections.unmodifiableMap(subIdToInfoMap);
+ mSubIdToCarrierConfigMap = Collections.unmodifiableMap(subIdToCarrierConfigMap);
final Map<ParcelUuid, Set<String>> unmodifiableInnerSets = new ArrayMap<>();
for (Entry<ParcelUuid, Set<String>> entry : privilegedPackages.entrySet()) {
@@ -423,9 +463,40 @@
: false;
}
+ /**
+ * Retrieves a carrier config for a subscription in the provided group.
+ *
+ * <p>This method will prioritize non-opportunistic subscriptions, but will use the a
+ * carrier config for an opportunistic subscription if no other subscriptions are found.
+ */
+ @Nullable
+ public PersistableBundleWrapper getCarrierConfigForSubGrp(@NonNull ParcelUuid subGrp) {
+ PersistableBundleWrapper result = null;
+
+ for (int subId : getAllSubIdsInGroup(subGrp)) {
+ final PersistableBundleWrapper config = mSubIdToCarrierConfigMap.get(subId);
+ if (config != null) {
+ result = config;
+
+ // Attempt to use (any) non-opportunistic subscription. If this subscription is
+ // opportunistic, continue and try to find a non-opportunistic subscription,
+ // using the opportunistic ones as a last resort.
+ if (!isOpportunistic(subId)) {
+ return config;
+ }
+ }
+ }
+
+ return result;
+ }
+
@Override
public int hashCode() {
- return Objects.hash(mActiveDataSubId, mSubIdToInfoMap, mPrivilegedPackages);
+ return Objects.hash(
+ mActiveDataSubId,
+ mSubIdToInfoMap,
+ mSubIdToCarrierConfigMap,
+ mPrivilegedPackages);
}
@Override
@@ -438,6 +509,7 @@
return mActiveDataSubId == other.mActiveDataSubId
&& mSubIdToInfoMap.equals(other.mSubIdToInfoMap)
+ && mSubIdToCarrierConfigMap.equals(other.mSubIdToCarrierConfigMap)
&& mPrivilegedPackages.equals(other.mPrivilegedPackages);
}
@@ -448,6 +520,7 @@
pw.println("mActiveDataSubId: " + mActiveDataSubId);
pw.println("mSubIdToInfoMap: " + mSubIdToInfoMap);
+ pw.println("mSubIdToCarrierConfigMap: " + mSubIdToCarrierConfigMap);
pw.println("mPrivilegedPackages: " + mPrivilegedPackages);
pw.decreaseIndent();
@@ -458,6 +531,7 @@
return "TelephonySubscriptionSnapshot{ "
+ "mActiveDataSubId=" + mActiveDataSubId
+ ", mSubIdToInfoMap=" + mSubIdToInfoMap
+ + ", mSubIdToCarrierConfigMap=" + mSubIdToCarrierConfigMap
+ ", mPrivilegedPackages=" + mPrivilegedPackages
+ " }";
}
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index f29c40f..37f0450 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -341,6 +341,9 @@
if (gatewayConnection == null) {
logWtf("Found gatewayConnectionConfig without GatewayConnection");
} else {
+ logInfo(
+ "Config updated, restarting gateway "
+ + gatewayConnection.getLogPrefix());
gatewayConnection.teardownAsynchronously();
}
}
@@ -397,7 +400,7 @@
// If preexisting VcnGatewayConnection(s) satisfy request, return
for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) {
if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
- logDbg("Request already satisfied by existing VcnGatewayConnection: " + request);
+ logVdbg("Request already satisfied by existing VcnGatewayConnection: " + request);
return;
}
}
@@ -407,8 +410,6 @@
for (VcnGatewayConnectionConfig gatewayConnectionConfig :
mConfig.getGatewayConnectionConfigs()) {
if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
- logDbg("Bringing up new VcnGatewayConnection for request " + request);
-
if (getExposedCapabilitiesForMobileDataState(gatewayConnectionConfig).isEmpty()) {
// Skip; this network does not provide any services if mobile data is disabled.
continue;
@@ -424,6 +425,7 @@
return;
}
+ logInfo("Bringing up new VcnGatewayConnection for request " + request);
final VcnGatewayConnection vcnGatewayConnection =
mDeps.newVcnGatewayConnection(
mVcnContext,
@@ -455,7 +457,7 @@
}
private void handleGatewayConnectionQuit(VcnGatewayConnectionConfig config) {
- logDbg("VcnGatewayConnection quit: " + config);
+ logInfo("VcnGatewayConnection quit: " + config);
mVcnGatewayConnections.remove(config);
// Trigger a re-evaluation of all NetworkRequests (to make sure any that can be satisfied
@@ -534,7 +536,7 @@
// Trigger re-evaluation of all requests; mobile data state impacts supported caps.
mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
- logDbg("Mobile data " + (mIsMobileDataEnabled ? "enabled" : "disabled"));
+ logInfo("Mobile data " + (mIsMobileDataEnabled ? "enabled" : "disabled"));
}
}
@@ -569,11 +571,11 @@
}
private String getLogPrefix() {
- return "["
+ return "("
+ LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup)
+ "-"
+ System.identityHashCode(this)
- + "] ";
+ + ") ";
}
private void logVdbg(String msg) {
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 597f7f2..05df22f 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -61,6 +61,7 @@
import android.net.ipsec.ike.IkeSession;
import android.net.ipsec.ike.IkeSessionCallback;
import android.net.ipsec.ike.IkeSessionConfiguration;
+import android.net.ipsec.ike.IkeSessionConnectionInfo;
import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.IkeTunnelConnectionParams;
import android.net.ipsec.ike.exceptions.IkeException;
@@ -509,6 +510,42 @@
}
}
+ /**
+ * Sent when an IKE session connection information has changed.
+ *
+ * <p>This signal is always fired before EVENT_SETUP_COMPLETED and EVENT_MIGRATION_COMPLETED.
+ *
+ * <p>Only relevant in the Connecting and Connected state.
+ *
+ * @param arg1 The session token for the IKE Session whose connection information has changed,
+ * used to prevent out-of-date signals from propagating.
+ * @param obj @NonNull An EventIkeConnectionInfoChangedInfo instance with relevant data.
+ */
+ private static final int EVENT_IKE_CONNECTION_INFO_CHANGED = 12;
+
+ private static class EventIkeConnectionInfoChangedInfo implements EventInfo {
+ @NonNull public final IkeSessionConnectionInfo ikeConnectionInfo;
+
+ EventIkeConnectionInfoChangedInfo(@NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
+ this.ikeConnectionInfo = ikeConnectionInfo;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(ikeConnectionInfo);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (!(other instanceof EventIkeConnectionInfoChangedInfo)) {
+ return false;
+ }
+
+ final EventIkeConnectionInfoChangedInfo rhs = (EventIkeConnectionInfoChangedInfo) other;
+ return Objects.equals(ikeConnectionInfo, rhs.ikeConnectionInfo);
+ }
+ }
+
@VisibleForTesting(visibility = Visibility.PRIVATE)
@NonNull
final DisconnectedState mDisconnectedState = new DisconnectedState();
@@ -544,6 +581,7 @@
private final boolean mIsMobileDataEnabled;
@NonNull private final IpSecManager mIpSecManager;
+ @NonNull private final ConnectivityManager mConnectivityManager;
@Nullable private IpSecTunnelInterface mTunnelIface = null;
@@ -623,6 +661,14 @@
private UnderlyingNetworkRecord mUnderlying;
/**
+ * The current IKE Session connection information
+ *
+ * <p>Set in Connected and Migrating states, always @NonNull in Connected, Migrating
+ * states, @Nullable otherwise.
+ */
+ private IkeSessionConnectionInfo mIkeConnectionInfo;
+
+ /**
* The active IKE session.
*
* <p>Set in Connecting or Migrating States, always @NonNull in Connecting, Connected, and
@@ -701,6 +747,7 @@
mLastSnapshot,
mUnderlyingNetworkControllerCallback);
mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class);
+ mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class);
addState(mDisconnectedState);
addState(mDisconnectingState);
@@ -730,14 +777,11 @@
logDbg("Triggering async teardown");
sendDisconnectRequestedAndAcquireWakelock(
DISCONNECT_REASON_TEARDOWN, true /* shouldQuit */);
-
- // TODO: Notify VcnInstance (via callbacks) of permanent teardown of this tunnel, since this
- // is also called asynchronously when a NetworkAgent becomes unwanted
}
@Override
protected void onQuitting() {
- logDbg("Quitting VcnGatewayConnection");
+ logInfo("Quitting VcnGatewayConnection");
if (mNetworkAgent != null) {
logWtf("NetworkAgent was non-null in onQuitting");
@@ -792,7 +836,7 @@
// TODO(b/180132994): explore safely removing this Thread check
mVcnContext.ensureRunningOnLooperThread();
- logDbg(
+ logInfo(
"Selected underlying network changed: "
+ (underlying == null ? null : underlying.network));
@@ -1198,6 +1242,14 @@
exceptionMessage);
}
+ private void ikeConnectionInfoChanged(
+ int token, @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
+ sendMessageAndAcquireWakeLock(
+ EVENT_IKE_CONNECTION_INFO_CHANGED,
+ token,
+ new EventIkeConnectionInfoChangedInfo(ikeConnectionInfo));
+ }
+
private void sessionClosed(int token, @Nullable Exception exception) {
if (exception != null) {
notifyStatusCallbackForSessionClosed(exception);
@@ -1314,7 +1366,8 @@
case EVENT_TEARDOWN_TIMEOUT_EXPIRED: // Fallthrough
case EVENT_SUBSCRIPTIONS_CHANGED: // Fallthrough
case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: // Fallthrough
- case EVENT_MIGRATION_COMPLETED:
+ case EVENT_MIGRATION_COMPLETED: // Fallthrough
+ case EVENT_IKE_CONNECTION_INFO_CHANGED:
logUnexpectedEvent(msg.what);
break;
default:
@@ -1333,7 +1386,7 @@
protected void handleDisconnectRequested(EventDisconnectRequestedInfo info) {
// TODO(b/180526152): notify VcnStatusCallback for Network loss
- logDbg("Tearing down. Cause: " + info.reason);
+ logInfo("Tearing down. Cause: " + info.reason + "; quitting = " + info.shouldQuit);
if (info.shouldQuit) {
mIsQuitting.setTrue();
}
@@ -1351,7 +1404,7 @@
protected void handleSafeModeTimeoutExceeded() {
mSafeModeTimeoutAlarm = null;
- logDbg("Entering safe mode after timeout exceeded");
+ logInfo("Entering safe mode after timeout exceeded");
// Connectivity for this GatewayConnection is broken; tear down the Network.
teardownNetwork();
@@ -1360,7 +1413,7 @@
}
protected void logUnexpectedEvent(int what) {
- logDbg(
+ logVdbg(
"Unexpected event code "
+ what
+ " in state "
@@ -1593,6 +1646,7 @@
transitionTo(mDisconnectingState);
break;
case EVENT_SETUP_COMPLETED: // fallthrough
+ case EVENT_IKE_CONNECTION_INFO_CHANGED: // fallthrough
case EVENT_TRANSFORM_CREATED:
// Child setup complete; move to ConnectedState for NetworkAgent registration
deferMessage(msg);
@@ -1615,12 +1669,17 @@
protected void updateNetworkAgent(
@NonNull IpSecTunnelInterface tunnelIface,
@NonNull VcnNetworkAgent agent,
- @NonNull VcnChildSessionConfiguration childConfig) {
+ @NonNull VcnChildSessionConfiguration childConfig,
+ @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
final NetworkCapabilities caps =
buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled);
final LinkProperties lp =
buildConnectedLinkProperties(
- mConnectionConfig, tunnelIface, childConfig, mUnderlying);
+ mConnectionConfig,
+ tunnelIface,
+ childConfig,
+ mUnderlying,
+ ikeConnectionInfo);
agent.sendNetworkCapabilities(caps);
agent.sendLinkProperties(lp);
@@ -1631,12 +1690,17 @@
protected VcnNetworkAgent buildNetworkAgent(
@NonNull IpSecTunnelInterface tunnelIface,
- @NonNull VcnChildSessionConfiguration childConfig) {
+ @NonNull VcnChildSessionConfiguration childConfig,
+ @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
final NetworkCapabilities caps =
buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled);
final LinkProperties lp =
buildConnectedLinkProperties(
- mConnectionConfig, tunnelIface, childConfig, mUnderlying);
+ mConnectionConfig,
+ tunnelIface,
+ childConfig,
+ mUnderlying,
+ ikeConnectionInfo);
final NetworkAgentConfig nac =
new NetworkAgentConfig.Builder()
.setLegacyType(ConnectivityManager.TYPE_MOBILE)
@@ -1670,7 +1734,7 @@
return;
}
- logDbg("NetworkAgent was unwanted");
+ logInfo("NetworkAgent was unwanted");
teardownAsynchronously();
} /* networkUnwantedCallback */,
(status) -> {
@@ -1683,6 +1747,14 @@
clearFailedAttemptCounterAndSafeModeAlarm();
break;
case NetworkAgent.VALIDATION_STATUS_NOT_VALID:
+ // Trigger re-validation of underlying networks; if it
+ // fails, the VCN will attempt to migrate away.
+ if (mUnderlying != null) {
+ mConnectivityManager.reportNetworkConnectivity(
+ mUnderlying.network,
+ false /* hasConnectivity */);
+ }
+
// Will only set a new alarm if no safe mode alarm is
// currently scheduled.
setSafeModeAlarm();
@@ -1738,7 +1810,7 @@
tunnelIface, IpSecManager.DIRECTION_FWD, transform);
}
} catch (IOException e) {
- logDbg("Transform application failed for network " + token, e);
+ logInfo("Transform application failed for network " + token, e);
sessionLost(token, e);
}
}
@@ -1772,7 +1844,7 @@
tunnelIface.removeAddress(address.getAddress(), address.getPrefixLength());
}
} catch (IOException e) {
- logDbg("Adding address to tunnel failed for token " + token, e);
+ logInfo("Adding address to tunnel failed for token " + token, e);
sessionLost(token, e);
}
}
@@ -1831,7 +1903,11 @@
mChildConfig = ((EventSetupCompletedInfo) msg.obj).childSessionConfig;
setupInterfaceAndNetworkAgent(
- mCurrentToken, mTunnelIface, mChildConfig, oldChildConfig);
+ mCurrentToken,
+ mTunnelIface,
+ mChildConfig,
+ oldChildConfig,
+ mIkeConnectionInfo);
break;
case EVENT_DISCONNECT_REQUESTED:
handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
@@ -1845,6 +1921,10 @@
handleMigrationCompleted(migrationCompletedInfo);
break;
+ case EVENT_IKE_CONNECTION_INFO_CHANGED:
+ mIkeConnectionInfo =
+ ((EventIkeConnectionInfoChangedInfo) msg.obj).ikeConnectionInfo;
+ break;
default:
logUnhandledMessage(msg);
break;
@@ -1852,7 +1932,7 @@
}
private void handleMigrationCompleted(EventMigrationCompletedInfo migrationCompletedInfo) {
- logDbg("Migration completed: " + mUnderlying.network);
+ logInfo("Migration completed: " + mUnderlying.network);
applyTransform(
mCurrentToken,
@@ -1868,7 +1948,11 @@
migrationCompletedInfo.outTransform,
IpSecManager.DIRECTION_OUT);
- updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig);
+ updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig, mIkeConnectionInfo);
+
+ // Trigger re-validation after migration events.
+ mConnectivityManager.reportNetworkConnectivity(
+ mNetworkAgent.getNetwork(), false /* hasConnectivity */);
}
private void handleUnderlyingNetworkChanged(@NonNull Message msg) {
@@ -1876,7 +1960,7 @@
mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
if (mUnderlying == null) {
- logDbg("Underlying network lost");
+ logInfo("Underlying network lost");
// Ignored for now; a new network may be coming up. If none does, the delayed
// NETWORK_LOST disconnect will be fired, and tear down the session + network.
@@ -1886,7 +1970,7 @@
// mUnderlying assumed non-null, given check above.
// If network changed, migrate. Otherwise, update any existing networkAgent.
if (oldUnderlying == null || !oldUnderlying.network.equals(mUnderlying.network)) {
- logDbg("Migrating to new network: " + mUnderlying.network);
+ logInfo("Migrating to new network: " + mUnderlying.network);
mIkeSession.setNetwork(mUnderlying.network);
} else {
// oldUnderlying is non-null & underlying network itself has not changed
@@ -1895,7 +1979,8 @@
// Network not yet set up, or child not yet connected.
if (mNetworkAgent != null && mChildConfig != null) {
// If only network properties changed and agent is active, update properties
- updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig);
+ updateNetworkAgent(
+ mTunnelIface, mNetworkAgent, mChildConfig, mIkeConnectionInfo);
}
}
}
@@ -1904,13 +1989,14 @@
int token,
@NonNull IpSecTunnelInterface tunnelIface,
@NonNull VcnChildSessionConfiguration childConfig,
- @NonNull VcnChildSessionConfiguration oldChildConfig) {
+ @NonNull VcnChildSessionConfiguration oldChildConfig,
+ @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
setupInterface(token, tunnelIface, childConfig, oldChildConfig);
if (mNetworkAgent == null) {
- mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig);
+ mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig, ikeConnectionInfo);
} else {
- updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig);
+ updateNetworkAgent(tunnelIface, mNetworkAgent, childConfig, ikeConnectionInfo);
// mNetworkAgent not null, so the VCN Network has already been established. Clear
// the failed attempt counter and safe mode alarm since this transition is complete.
@@ -2087,7 +2173,8 @@
@NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
@NonNull IpSecTunnelInterface tunnelIface,
@NonNull VcnChildSessionConfiguration childConfig,
- @Nullable UnderlyingNetworkRecord underlying) {
+ @Nullable UnderlyingNetworkRecord underlying,
+ @NonNull IkeSessionConnectionInfo ikeConnectionInfo) {
final IkeTunnelConnectionParams ikeTunnelParams =
gatewayConnectionConfig.getTunnelConnectionParams();
final LinkProperties lp = new LinkProperties();
@@ -2128,7 +2215,8 @@
MtuUtils.getMtu(
ikeTunnelParams.getTunnelModeChildSessionParams().getSaProposals(),
gatewayConnectionConfig.getMaxMtu(),
- underlyingMtu));
+ underlyingMtu,
+ ikeConnectionInfo.getLocalAddress() instanceof Inet4Address));
return lp;
}
@@ -2143,7 +2231,7 @@
@Override
public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) {
logDbg("IkeOpened for token " + mToken);
- // Nothing to do here.
+ ikeConnectionInfoChanged(mToken, ikeSessionConfig.getIkeSessionConnectionInfo());
}
@Override
@@ -2154,15 +2242,22 @@
@Override
public void onClosedExceptionally(@NonNull IkeException exception) {
- logDbg("IkeClosedExceptionally for token " + mToken, exception);
+ logInfo("IkeClosedExceptionally for token " + mToken, exception);
sessionClosed(mToken, exception);
}
@Override
public void onError(@NonNull IkeProtocolException exception) {
- logDbg("IkeError for token " + mToken, exception);
+ logInfo("IkeError for token " + mToken, exception);
// Non-fatal, log and continue.
}
+
+ @Override
+ public void onIkeSessionConnectionInfoChanged(
+ @NonNull IkeSessionConnectionInfo connectionInfo) {
+ logDbg("onIkeSessionConnectionInfoChanged for token " + mToken);
+ ikeConnectionInfoChanged(mToken, connectionInfo);
+ }
}
/** Implementation of ChildSessionCallback, exposed for testing. */
@@ -2194,7 +2289,7 @@
@Override
public void onClosedExceptionally(@NonNull IkeException exception) {
- logDbg("ChildClosedExceptionally for token " + mToken, exception);
+ logInfo("ChildClosedExceptionally for token " + mToken, exception);
sessionLost(mToken, exception);
}
@@ -2220,14 +2315,19 @@
}
}
- private String getLogPrefix() {
- return "["
+ // Used in Vcn.java, but must be public for mockito to mock this.
+ public String getLogPrefix() {
+ return "("
+ LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup)
+ "-"
+ mConnectionConfig.getGatewayConnectionName()
+ "-"
+ System.identityHashCode(this)
- + "] ";
+ + ") ";
+ }
+
+ private String getTagLogPrefix() {
+ return "[ " + TAG + " " + getLogPrefix() + "]";
}
private void logVdbg(String msg) {
@@ -2244,34 +2344,44 @@
Slog.d(TAG, getLogPrefix() + msg, tr);
}
+ private void logInfo(String msg) {
+ Slog.i(TAG, getLogPrefix() + msg);
+ LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg);
+ }
+
+ private void logInfo(String msg, Throwable tr) {
+ Slog.i(TAG, getLogPrefix() + msg, tr);
+ LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg + tr);
+ }
+
private void logWarn(String msg) {
Slog.w(TAG, getLogPrefix() + msg);
- LOCAL_LOG.log(getLogPrefix() + "WARN: " + msg);
+ LOCAL_LOG.log("[WARN] " + getTagLogPrefix() + msg);
}
private void logWarn(String msg, Throwable tr) {
Slog.w(TAG, getLogPrefix() + msg, tr);
- LOCAL_LOG.log(getLogPrefix() + "WARN: " + msg + tr);
+ LOCAL_LOG.log("[WARN] " + getTagLogPrefix() + msg + tr);
}
private void logErr(String msg) {
Slog.e(TAG, getLogPrefix() + msg);
- LOCAL_LOG.log(getLogPrefix() + "ERR: " + msg);
+ LOCAL_LOG.log("[ERR ] " + getTagLogPrefix() + msg);
}
private void logErr(String msg, Throwable tr) {
Slog.e(TAG, getLogPrefix() + msg, tr);
- LOCAL_LOG.log(getLogPrefix() + "ERR: " + msg + tr);
+ LOCAL_LOG.log("[ERR ] " + getTagLogPrefix() + msg + tr);
}
private void logWtf(String msg) {
Slog.wtf(TAG, getLogPrefix() + msg);
- LOCAL_LOG.log(getLogPrefix() + "WTF: " + msg);
+ LOCAL_LOG.log("[WTF ] " + msg);
}
private void logWtf(String msg, Throwable tr) {
Slog.wtf(TAG, getLogPrefix() + msg, tr);
- LOCAL_LOG.log(getLogPrefix() + "WTF: " + msg + tr);
+ LOCAL_LOG.log("[WTF ] " + msg + tr);
}
/**
@@ -2324,6 +2434,11 @@
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
+ IkeSessionConnectionInfo getIkeConnectionInfo() {
+ return mIkeConnectionInfo;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
boolean isQuitting() {
return mIsQuitting.getValue();
}
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
index c96c1ee..2f84fdd 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkPriorityClassifier.java
@@ -24,6 +24,7 @@
import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_REQUIRED;
import static com.android.server.VcnManagementService.LOCAL_LOG;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -34,7 +35,6 @@
import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
import android.os.ParcelUuid;
-import android.os.PersistableBundle;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Slog;
@@ -81,7 +81,7 @@
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
- PersistableBundle carrierConfig) {
+ PersistableBundleWrapper carrierConfig) {
// mRouteSelectionNetworkRequest requires a network be both VALIDATED and NOT_SUSPENDED
if (networkRecord.isBlocked) {
@@ -119,7 +119,7 @@
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
- PersistableBundle carrierConfig) {
+ PersistableBundleWrapper carrierConfig) {
final NetworkCapabilities caps = networkRecord.networkCapabilities;
final boolean isSelectedUnderlyingNetwork =
currentlySelected != null
@@ -181,7 +181,7 @@
VcnWifiUnderlyingNetworkTemplate networkPriority,
UnderlyingNetworkRecord networkRecord,
UnderlyingNetworkRecord currentlySelected,
- PersistableBundle carrierConfig) {
+ PersistableBundleWrapper carrierConfig) {
final NetworkCapabilities caps = networkRecord.networkCapabilities;
if (!caps.hasTransport(TRANSPORT_WIFI)) {
@@ -204,7 +204,7 @@
private static boolean isWifiRssiAcceptable(
UnderlyingNetworkRecord networkRecord,
UnderlyingNetworkRecord currentlySelected,
- PersistableBundle carrierConfig) {
+ PersistableBundleWrapper carrierConfig) {
final NetworkCapabilities caps = networkRecord.networkCapabilities;
final boolean isSelectedNetwork =
currentlySelected != null
@@ -314,7 +314,7 @@
return false;
}
- static int getWifiEntryRssiThreshold(@Nullable PersistableBundle carrierConfig) {
+ static int getWifiEntryRssiThreshold(@Nullable PersistableBundleWrapper carrierConfig) {
if (carrierConfig != null) {
return carrierConfig.getInt(
VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
@@ -323,7 +323,7 @@
return WIFI_ENTRY_RSSI_THRESHOLD_DEFAULT;
}
- static int getWifiExitRssiThreshold(@Nullable PersistableBundle carrierConfig) {
+ static int getWifiExitRssiThreshold(@Nullable PersistableBundleWrapper carrierConfig) {
if (carrierConfig != null) {
return carrierConfig.getInt(
VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
index ca2e449..d474c5d 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java
@@ -21,7 +21,7 @@
import static com.android.server.VcnManagementService.LOCAL_LOG;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiEntryRssiThreshold;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.getWifiExitRssiThreshold;
-import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.isOpportunistic;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -37,8 +37,6 @@
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.ParcelUuid;
-import android.os.PersistableBundle;
-import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
@@ -48,9 +46,9 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.VcnContext;
+import com.android.server.vcn.util.LogUtils;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -86,7 +84,7 @@
@Nullable private UnderlyingNetworkListener mRouteSelectionCallback;
@NonNull private TelephonySubscriptionSnapshot mLastSnapshot;
- @Nullable private PersistableBundle mCarrierConfig;
+ @Nullable private PersistableBundleWrapper mCarrierConfig;
private boolean mIsQuitting = false;
@Nullable private UnderlyingNetworkRecord mCurrentRecord;
@@ -123,25 +121,7 @@
.getSystemService(TelephonyManager.class)
.registerTelephonyCallback(new HandlerExecutor(mHandler), mActiveDataSubIdListener);
- // TODO: Listen for changes in carrier config that affect this.
- for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
- PersistableBundle config =
- mVcnContext
- .getContext()
- .getSystemService(CarrierConfigManager.class)
- .getConfigForSubId(subId);
-
- if (config != null) {
- mCarrierConfig = config;
-
- // Attempt to use (any) non-opportunistic subscription. If this subscription is
- // opportunistic, continue and try to find a non-opportunistic subscription, using
- // the opportunistic ones as a last resort.
- if (!isOpportunistic(mLastSnapshot, Collections.singleton(subId))) {
- break;
- }
- }
- }
+ mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup);
registerOrUpdateNetworkRequests();
}
@@ -333,6 +313,9 @@
final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot;
mLastSnapshot = newSnapshot;
+ // Update carrier config
+ mCarrierConfig = mLastSnapshot.getCarrierConfigForSubGrp(mSubscriptionGroup);
+
// Only trigger re-registration if subIds in this group have changed
if (oldSnapshot
.getAllSubIdsInGroup(mSubscriptionGroup)
@@ -368,6 +351,18 @@
return;
}
+ String allNetworkPriorities = "";
+ for (UnderlyingNetworkRecord record : sorted) {
+ if (!allNetworkPriorities.isEmpty()) {
+ allNetworkPriorities += ", ";
+ }
+ allNetworkPriorities += record.network + ": " + record.getPriorityClass();
+ }
+ logInfo(
+ "Selected network changed to "
+ + (candidate == null ? null : candidate.network)
+ + ", selected from list: "
+ + allNetworkPriorities);
mCurrentRecord = candidate;
mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord);
}
@@ -478,14 +473,38 @@
}
}
- private static void logWtf(String msg) {
- Slog.wtf(TAG, msg);
- LOCAL_LOG.log(TAG + " WTF: " + msg);
+ private String getLogPrefix() {
+ return "("
+ + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup)
+ + "-"
+ + mConnectionConfig.getGatewayConnectionName()
+ + "-"
+ + System.identityHashCode(this)
+ + ") ";
}
- private static void logWtf(String msg, Throwable tr) {
+ private String getTagLogPrefix() {
+ return "[ " + TAG + " " + getLogPrefix() + "]";
+ }
+
+ private void logInfo(String msg) {
+ Slog.i(TAG, getLogPrefix() + msg);
+ LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg);
+ }
+
+ private void logInfo(String msg, Throwable tr) {
+ Slog.i(TAG, getLogPrefix() + msg, tr);
+ LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg + tr);
+ }
+
+ private void logWtf(String msg) {
+ Slog.wtf(TAG, msg);
+ LOCAL_LOG.log(TAG + "[WTF ] " + getTagLogPrefix() + msg);
+ }
+
+ private void logWtf(String msg, Throwable tr) {
Slog.wtf(TAG, msg, tr);
- LOCAL_LOG.log(TAG + " WTF: " + msg + tr);
+ LOCAL_LOG.log(TAG + "[WTF ] " + getTagLogPrefix() + msg + tr);
}
/** Dumps the state of this record for logging and debugging purposes. */
diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
index c0488b1..319680e 100644
--- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
+++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java
@@ -16,6 +16,8 @@
package com.android.server.vcn.routeselection;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.LinkProperties;
@@ -23,7 +25,6 @@
import android.net.NetworkCapabilities;
import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.os.ParcelUuid;
-import android.os.PersistableBundle;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
@@ -41,11 +42,15 @@
* @hide
*/
public class UnderlyingNetworkRecord {
+ private static final int PRIORITY_CLASS_INVALID = Integer.MAX_VALUE;
+
@NonNull public final Network network;
@NonNull public final NetworkCapabilities networkCapabilities;
@NonNull public final LinkProperties linkProperties;
public final boolean isBlocked;
+ private int mPriorityClass = PRIORITY_CLASS_INVALID;
+
@VisibleForTesting(visibility = Visibility.PRIVATE)
public UnderlyingNetworkRecord(
@NonNull Network network,
@@ -58,6 +63,34 @@
this.isBlocked = isBlocked;
}
+ private int getOrCalculatePriorityClass(
+ VcnContext vcnContext,
+ List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates,
+ ParcelUuid subscriptionGroup,
+ TelephonySubscriptionSnapshot snapshot,
+ UnderlyingNetworkRecord currentlySelected,
+ PersistableBundleWrapper carrierConfig) {
+ // Never changes after the underlying network record is created.
+ if (mPriorityClass == PRIORITY_CLASS_INVALID) {
+ mPriorityClass =
+ NetworkPriorityClassifier.calculatePriorityClass(
+ vcnContext,
+ this,
+ underlyingNetworkTemplates,
+ subscriptionGroup,
+ snapshot,
+ currentlySelected,
+ carrierConfig);
+ }
+
+ return mPriorityClass;
+ }
+
+ // Used in UnderlyingNetworkController
+ int getPriorityClass() {
+ return mPriorityClass;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -81,21 +114,19 @@
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
- PersistableBundle carrierConfig) {
+ PersistableBundleWrapper carrierConfig) {
return (left, right) -> {
final int leftIndex =
- NetworkPriorityClassifier.calculatePriorityClass(
+ left.getOrCalculatePriorityClass(
vcnContext,
- left,
underlyingNetworkTemplates,
subscriptionGroup,
snapshot,
currentlySelected,
carrierConfig);
final int rightIndex =
- NetworkPriorityClassifier.calculatePriorityClass(
+ right.getOrCalculatePriorityClass(
vcnContext,
- right,
underlyingNetworkTemplates,
subscriptionGroup,
snapshot,
@@ -137,21 +168,20 @@
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
UnderlyingNetworkRecord currentlySelected,
- PersistableBundle carrierConfig) {
+ PersistableBundleWrapper carrierConfig) {
pw.println("UnderlyingNetworkRecord:");
pw.increaseIndent();
final int priorityIndex =
- NetworkPriorityClassifier.calculatePriorityClass(
+ getOrCalculatePriorityClass(
vcnContext,
- this,
underlyingNetworkTemplates,
subscriptionGroup,
snapshot,
currentlySelected,
carrierConfig);
- pw.println("Priority index:" + priorityIndex);
+ pw.println("Priority index: " + priorityIndex);
pw.println("mNetwork: " + network);
pw.println("mNetworkCapabilities: " + networkCapabilities);
pw.println("mLinkProperties: " + linkProperties);
diff --git a/services/core/java/com/android/server/vcn/util/MtuUtils.java b/services/core/java/com/android/server/vcn/util/MtuUtils.java
index 5d40cca..356c71f 100644
--- a/services/core/java/com/android/server/vcn/util/MtuUtils.java
+++ b/services/core/java/com/android/server/vcn/util/MtuUtils.java
@@ -51,9 +51,20 @@
/**
* Max ESP overhead possible
*
- * <p>60 (Outer IPv4 + options) + 8 (UDP encap) + 4 (SPI) + 4 (Seq) + 2 (Pad + NextHeader)
+ * <p>60 (Outer IPv4 + options) + 8 (UDP encap) + 4 (SPI) + 4 (Seq) + 2 (Pad Length + Next
+ * Header). Note: Payload data, Pad Length and Next Header will need to be padded to be multiple
+ * of the block size of a cipher, and at the same time be aligned on a 4-byte boundary.
*/
- private static final int GENERIC_ESP_OVERHEAD_MAX = 78;
+ private static final int GENERIC_ESP_OVERHEAD_MAX_V4 = 78;
+
+ /**
+ * Max ESP overhead possible
+ *
+ * <p>40 (Outer IPv6) + 4 (SPI) + 4 (Seq) + 2 (Pad Length + Next Header). Note: Payload data,
+ * Pad Length and Next Header will need to be padded to be multiple of the block size of a
+ * cipher, and at the same time be aligned on a 4-byte boundary.
+ */
+ private static final int GENERIC_ESP_OVERHEAD_MAX_V6 = 50;
/** Maximum overheads of authentication algorithms, keyed on IANA-defined constants */
private static final Map<Integer, Integer> AUTH_ALGORITHM_OVERHEAD;
@@ -108,7 +119,10 @@
* </ul>
*/
public static int getMtu(
- @NonNull List<ChildSaProposal> childProposals, int maxMtu, int underlyingMtu) {
+ @NonNull List<ChildSaProposal> childProposals,
+ int maxMtu,
+ int underlyingMtu,
+ boolean isIpv4) {
if (underlyingMtu <= 0) {
return IPV6_MIN_MTU;
}
@@ -145,10 +159,13 @@
}
}
+ final int genericEspOverheadMax =
+ isIpv4 ? GENERIC_ESP_OVERHEAD_MAX_V4 : GENERIC_ESP_OVERHEAD_MAX_V6;
+
// Return minimum of maxMtu, and the adjusted MTUs based on algorithms.
- final int combinedModeMtu = underlyingMtu - maxAuthCryptOverhead - GENERIC_ESP_OVERHEAD_MAX;
+ final int combinedModeMtu = underlyingMtu - maxAuthCryptOverhead - genericEspOverheadMax;
final int normalModeMtu =
- underlyingMtu - maxCryptOverhead - maxAuthOverhead - GENERIC_ESP_OVERHEAD_MAX;
+ underlyingMtu - maxCryptOverhead - maxAuthOverhead - genericEspOverheadMax;
return Math.min(Math.min(maxMtu, combinedModeMtu), normalModeMtu);
}
}
diff --git a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
index 1c675c2..08e8eebb 100644
--- a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
+++ b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
@@ -28,11 +28,13 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.TreeSet;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -354,4 +356,182 @@
}
}
}
+
+ /**
+ * Returns a copy of the persistable bundle with only the specified keys
+ *
+ * <p>This allows for holding minimized copies for memory-saving purposes.
+ */
+ @NonNull
+ public static PersistableBundle minimizeBundle(
+ @NonNull PersistableBundle bundle, String... keys) {
+ final PersistableBundle minimized = new PersistableBundle();
+
+ if (bundle == null) {
+ return minimized;
+ }
+
+ for (String key : keys) {
+ if (bundle.containsKey(key)) {
+ final Object value = bundle.get(key);
+ if (value == null) {
+ continue;
+ }
+
+ if (value instanceof Boolean) {
+ minimized.putBoolean(key, (Boolean) value);
+ } else if (value instanceof boolean[]) {
+ minimized.putBooleanArray(key, (boolean[]) value);
+ } else if (value instanceof Double) {
+ minimized.putDouble(key, (Double) value);
+ } else if (value instanceof double[]) {
+ minimized.putDoubleArray(key, (double[]) value);
+ } else if (value instanceof Integer) {
+ minimized.putInt(key, (Integer) value);
+ } else if (value instanceof int[]) {
+ minimized.putIntArray(key, (int[]) value);
+ } else if (value instanceof Long) {
+ minimized.putLong(key, (Long) value);
+ } else if (value instanceof long[]) {
+ minimized.putLongArray(key, (long[]) value);
+ } else if (value instanceof String) {
+ minimized.putString(key, (String) value);
+ } else if (value instanceof String[]) {
+ minimized.putStringArray(key, (String[]) value);
+ } else if (value instanceof PersistableBundle) {
+ minimized.putPersistableBundle(key, (PersistableBundle) value);
+ } else {
+ continue;
+ }
+ }
+ }
+
+ return minimized;
+ }
+
+ /** Builds a stable hashcode */
+ public static int getHashCode(@Nullable PersistableBundle bundle) {
+ if (bundle == null) {
+ return -1;
+ }
+
+ int iterativeHashcode = 0;
+ TreeSet<String> treeSet = new TreeSet<>(bundle.keySet());
+ for (String key : treeSet) {
+ Object val = bundle.get(key);
+ if (val instanceof PersistableBundle) {
+ iterativeHashcode =
+ Objects.hash(iterativeHashcode, key, getHashCode((PersistableBundle) val));
+ } else {
+ iterativeHashcode = Objects.hash(iterativeHashcode, key, val);
+ }
+ }
+
+ return iterativeHashcode;
+ }
+
+ /** Checks for persistable bundle equality */
+ public static boolean isEqual(
+ @Nullable PersistableBundle left, @Nullable PersistableBundle right) {
+ // Check for pointer equality & null equality
+ if (Objects.equals(left, right)) {
+ return true;
+ }
+
+ // If only one of the two is null, but not the other, not equal by definition.
+ if (Objects.isNull(left) != Objects.isNull(right)) {
+ return false;
+ }
+
+ if (!left.keySet().equals(right.keySet())) {
+ return false;
+ }
+
+ for (String key : left.keySet()) {
+ Object leftVal = left.get(key);
+ Object rightVal = right.get(key);
+
+ // Check for equality
+ if (Objects.equals(leftVal, rightVal)) {
+ continue;
+ } else if (Objects.isNull(leftVal) != Objects.isNull(rightVal)) {
+ // If only one of the two is null, but not the other, not equal by definition.
+ return false;
+ } else if (!Objects.equals(leftVal.getClass(), rightVal.getClass())) {
+ // If classes are different, not equal by definition.
+ return false;
+ }
+ if (leftVal instanceof PersistableBundle) {
+ if (!isEqual((PersistableBundle) leftVal, (PersistableBundle) rightVal)) {
+ return false;
+ }
+ } else if (leftVal.getClass().isArray()) {
+ if (leftVal instanceof boolean[]) {
+ if (!Arrays.equals((boolean[]) leftVal, (boolean[]) rightVal)) {
+ return false;
+ }
+ } else if (leftVal instanceof double[]) {
+ if (!Arrays.equals((double[]) leftVal, (double[]) rightVal)) {
+ return false;
+ }
+ } else if (leftVal instanceof int[]) {
+ if (!Arrays.equals((int[]) leftVal, (int[]) rightVal)) {
+ return false;
+ }
+ } else if (leftVal instanceof long[]) {
+ if (!Arrays.equals((long[]) leftVal, (long[]) rightVal)) {
+ return false;
+ }
+ } else if (!Arrays.equals((Object[]) leftVal, (Object[]) rightVal)) {
+ return false;
+ }
+ } else {
+ if (!Objects.equals(leftVal, rightVal)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Wrapper class around PersistableBundles to allow equality comparisons
+ *
+ * <p>This class exposes the minimal getters to retrieve values.
+ */
+ public static class PersistableBundleWrapper {
+ @NonNull private final PersistableBundle mBundle;
+
+ public PersistableBundleWrapper(@NonNull PersistableBundle bundle) {
+ mBundle = Objects.requireNonNull(bundle, "Bundle was null");
+ }
+
+ /**
+ * Retrieves the integer associated with the provided key.
+ *
+ * @param key the string key to query
+ * @param defaultValue the value to return if key does not exist
+ * @return the int value, or the default
+ */
+ public int getInt(String key, int defaultValue) {
+ return mBundle.getInt(key, defaultValue);
+ }
+
+ @Override
+ public int hashCode() {
+ return getHashCode(mBundle);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof PersistableBundleWrapper)) {
+ return false;
+ }
+
+ final PersistableBundleWrapper other = (PersistableBundleWrapper) obj;
+
+ return isEqual(mBundle, other.mBundle);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index f580bd0..e70517f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -7380,7 +7380,8 @@
mSizeCompatBounds = null;
mCompatDisplayInsets = null;
- onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
+ // Clear config override in #updateCompatDisplayInsets().
+ onRequestedOverrideConfigurationChanged(EMPTY);
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 3ccb06c..ab8527e 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -1123,7 +1123,7 @@
/** Makes the process have top state before oom-adj is computed from a posted message. */
void addToPendingTop() {
- mAtm.mAmInternal.addPendingTopUid(mUid, mPid);
+ mAtm.mAmInternal.addPendingTopUid(mUid, mPid, mThread);
}
void updateServiceConnectionActivities() {
@@ -1176,7 +1176,7 @@
}
// update ActivityManagerService.PendingStartActivityUids list.
if (topProcessState == ActivityManager.PROCESS_STATE_TOP) {
- mAtm.mAmInternal.addPendingTopUid(mUid, mPid);
+ mAtm.mAmInternal.addPendingTopUid(mUid, mPid, mThread);
}
prepareOomAdjustment();
// Posting the message at the front of queue so WM lock isn't held when we call into AM,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
index cc385c7..623e411 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java
@@ -30,7 +30,6 @@
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.os.storage.StorageManager;
import android.provider.Settings;
import android.security.Credentials;
import android.security.KeyChain;
@@ -129,9 +128,6 @@
private final BroadcastReceiver mRootCaReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (StorageManager.inCryptKeeperBounce()) {
- return;
- }
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId());
updateInstalledCertificates(UserHandle.of(userId));
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 738e0b4..5e95a785 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1497,23 +1497,6 @@
return StorageManager.isFileEncryptedNativeOnly();
}
- boolean storageManagerIsNonDefaultBlockEncrypted() {
- final long identity = Binder.clearCallingIdentity();
- try {
- return StorageManager.isNonDefaultBlockEncrypted();
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- boolean storageManagerIsEncrypted() {
- return StorageManager.isEncrypted();
- }
-
- boolean storageManagerIsEncryptable() {
- return StorageManager.isEncryptable();
- }
-
Looper getMyLooper() {
return Looper.myLooper();
}
@@ -2295,11 +2278,6 @@
}
private void setDeviceOwnershipSystemPropertyLocked() {
- // Still at the first stage of CryptKeeper double bounce, nothing can be learnt about
- // the real system at this point.
- if (StorageManager.inCryptKeeperBounce()) {
- return;
- }
final boolean deviceProvisioned =
mInjector.settingsGlobalGetInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0;
final boolean hasDeviceOwner = mOwners.hasDeviceOwner();
@@ -7678,21 +7656,12 @@
/**
* Hook to low-levels: Reporting the current status of encryption.
- * @return A value such as {@link DevicePolicyManager#ENCRYPTION_STATUS_UNSUPPORTED},
- * {@link DevicePolicyManager#ENCRYPTION_STATUS_INACTIVE},
- * {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY},
- * {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE_PER_USER}, or
- * {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE}.
+ * @return Either {@link DevicePolicyManager#ENCRYPTION_STATUS_UNSUPPORTED}
+ * or {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE_PER_USER}.
*/
private int getEncryptionStatus() {
if (mInjector.storageManagerIsFileBasedEncryptionEnabled()) {
return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER;
- } else if (mInjector.storageManagerIsNonDefaultBlockEncrypted()) {
- return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE;
- } else if (mInjector.storageManagerIsEncrypted()) {
- return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY;
- } else if (mInjector.storageManagerIsEncryptable()) {
- return DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE;
} else {
return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 77d40d2..dc729f2 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -159,6 +159,7 @@
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.ShortcutService;
import com.android.server.pm.UserManagerService;
+import com.android.server.pm.dex.OdsignStatsLogger;
import com.android.server.pm.dex.SystemServerDexLoadReporter;
import com.android.server.pm.verify.domain.DomainVerificationService;
import com.android.server.policy.AppOpsPolicy;
@@ -1381,8 +1382,9 @@
Slog.i(TAG, SECONDARY_ZYGOTE_PRELOAD);
TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
traceLog.traceBegin(SECONDARY_ZYGOTE_PRELOAD);
- if (!Process.ZYGOTE_PROCESS.preloadDefault(Build.SUPPORTED_32_BIT_ABIS[0])) {
- Slog.e(TAG, "Unable to preload default resources");
+ String[] abis32 = Build.SUPPORTED_32_BIT_ABIS;
+ if (abis32.length > 0 && !Process.ZYGOTE_PROCESS.preloadDefault(abis32[0])) {
+ Slog.e(TAG, "Unable to preload default resources for secondary");
}
traceLog.traceEnd();
} catch (Exception ex) {
@@ -2887,6 +2889,14 @@
setIncrementalServiceSystemReady(mIncrementalServiceHandle);
t.traceEnd();
}
+
+ t.traceBegin("OdsignStatsLogger");
+ try {
+ OdsignStatsLogger.triggerStatsWrite();
+ } catch (Throwable e) {
+ reportWtf("Triggering OdsignStatsLogger", e);
+ }
+ t.traceEnd();
}, t);
t.traceBegin("StartSystemUI");
diff --git a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
index 58d6dae..68dcc7d 100644
--- a/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
+++ b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java
@@ -16,26 +16,116 @@
package com.android.server;
+import static org.junit.Assert.assertArrayEquals;
+
import android.content.Context;
-import android.os.FileUtils;
import android.test.AndroidTestCase;
+import org.junit.Test;
+
import java.io.File;
+import java.nio.file.Files;
+import java.util.Arrays;
/**
* Tests for {@link com.android.server.EntropyMixer}
*/
public class EntropyMixerTest extends AndroidTestCase {
- public void testInitialWrite() throws Exception {
- File dir = getContext().getDir("testInitialWrite", Context.MODE_PRIVATE);
- File file = File.createTempFile("testInitialWrite", "dat", dir);
+ private static final int SEED_FILE_SIZE = EntropyMixer.SEED_FILE_SIZE;
+
+ private File dir;
+ private File seedFile;
+ private File randomReadDevice;
+ private File randomWriteDevice;
+
+ @Override
+ public void setUp() throws Exception {
+ dir = getContext().getDir("test", Context.MODE_PRIVATE);
+ seedFile = createTempFile(dir, "entropy.dat");
+ randomReadDevice = createTempFile(dir, "urandomRead");
+ randomWriteDevice = createTempFile(dir, "urandomWrite");
+ }
+
+ private File createTempFile(File dir, String prefix) throws Exception {
+ File file = File.createTempFile(prefix, null, dir);
file.deleteOnExit();
- assertEquals(0, FileUtils.readTextFile(file, 0, null).length());
+ return file;
+ }
- // The constructor has the side effect of writing to file
- new EntropyMixer(getContext(), "/dev/null", file.getCanonicalPath());
+ private byte[] repeatByte(byte b, int length) {
+ byte[] data = new byte[length];
+ Arrays.fill(data, b);
+ return data;
+ }
- assertTrue(FileUtils.readTextFile(file, 0, null).length() > 0);
+ // Test initializing the EntropyMixer when the seed file doesn't exist yet.
+ @Test
+ public void testInitFirstBoot() throws Exception {
+ seedFile.delete();
+
+ byte[] urandomInjectedData = repeatByte((byte) 0x01, SEED_FILE_SIZE);
+ Files.write(randomReadDevice.toPath(), urandomInjectedData);
+
+ // The constructor should have the side effect of writing to
+ // randomWriteDevice and creating seedFile.
+ new EntropyMixer(getContext(), seedFile, randomReadDevice, randomWriteDevice);
+
+ // Since there was no old seed file, the data that was written to
+ // randomWriteDevice should contain only device-specific information.
+ assertTrue(isDeviceSpecificInfo(Files.readAllBytes(randomWriteDevice.toPath())));
+
+ // The seed file should have been created.
+ validateSeedFile(seedFile, new byte[0], urandomInjectedData);
+ }
+
+ // Test initializing the EntropyMixer when the seed file already exists.
+ @Test
+ public void testInitNonFirstBoot() throws Exception {
+ byte[] previousSeed = repeatByte((byte) 0x01, SEED_FILE_SIZE);
+ Files.write(seedFile.toPath(), previousSeed);
+
+ byte[] urandomInjectedData = repeatByte((byte) 0x02, SEED_FILE_SIZE);
+ Files.write(randomReadDevice.toPath(), urandomInjectedData);
+
+ // The constructor should have the side effect of writing to
+ // randomWriteDevice and updating seedFile.
+ new EntropyMixer(getContext(), seedFile, randomReadDevice, randomWriteDevice);
+
+ // The data that was written to randomWriteDevice should consist of the
+ // previous seed followed by the device-specific information.
+ byte[] dataWrittenToUrandom = Files.readAllBytes(randomWriteDevice.toPath());
+ byte[] firstPartWritten = Arrays.copyOf(dataWrittenToUrandom, SEED_FILE_SIZE);
+ byte[] secondPartWritten =
+ Arrays.copyOfRange(
+ dataWrittenToUrandom, SEED_FILE_SIZE, dataWrittenToUrandom.length);
+ assertArrayEquals(previousSeed, firstPartWritten);
+ assertTrue(isDeviceSpecificInfo(secondPartWritten));
+
+ // The seed file should have been updated.
+ validateSeedFile(seedFile, previousSeed, urandomInjectedData);
+ }
+
+ private boolean isDeviceSpecificInfo(byte[] data) {
+ return new String(data).startsWith(EntropyMixer.DEVICE_SPECIFIC_INFO_HEADER);
+ }
+
+ private void validateSeedFile(File seedFile, byte[] previousSeed, byte[] urandomInjectedData)
+ throws Exception {
+ final int unhashedLen = SEED_FILE_SIZE - 32;
+ byte[] newSeed = Files.readAllBytes(seedFile.toPath());
+ assertEquals(SEED_FILE_SIZE, newSeed.length);
+ assertEquals(SEED_FILE_SIZE, urandomInjectedData.length);
+ assertFalse(Arrays.equals(newSeed, previousSeed));
+ // The new seed should consist of the first SEED_FILE_SIZE - 32 bytes
+ // that were read from urandom, followed by a 32-byte hash that should
+ // *not* be the same as the last 32 bytes that were read from urandom.
+ byte[] firstPart = Arrays.copyOf(newSeed, unhashedLen);
+ byte[] secondPart = Arrays.copyOfRange(newSeed, unhashedLen, SEED_FILE_SIZE);
+ byte[] firstPartInjected = Arrays.copyOf(urandomInjectedData, unhashedLen);
+ byte[] secondPartInjected =
+ Arrays.copyOfRange(urandomInjectedData, unhashedLen, SEED_FILE_SIZE);
+ assertArrayEquals(firstPart, firstPartInjected);
+ assertFalse(Arrays.equals(secondPart, secondPartInjected));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
index cffff66..02cf971 100644
--- a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
@@ -23,7 +23,14 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.debug.AdbManager;
+import android.debug.IAdbManager;
+import android.os.ServiceManager;
import android.provider.Settings;
import android.util.Log;
@@ -105,6 +112,7 @@
public void tearDown() throws Exception {
mKeyStore.deleteKeyStore();
setAllowedConnectionTime(mOriginalAllowedConnectionTime);
+ dropShellPermissionIdentity();
}
/**
@@ -813,6 +821,108 @@
return hasAtLeastOneLetter;
}
+ CountDownLatch mAdbActionLatch = new CountDownLatch(1);
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ Log.i(TAG, "Received intent action=" + action);
+ if (AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION.equals(action)) {
+ assertEquals("Received broadcast without MANAGE_DEBUGGING permission.",
+ context.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING),
+ PackageManager.PERMISSION_GRANTED);
+ Log.i(TAG, "action=" + action + " paired_device=" + intent.getSerializableExtra(
+ AdbManager.WIRELESS_DEVICES_EXTRA).toString());
+ mAdbActionLatch.countDown();
+ } else if (AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION.equals(action)) {
+ assertEquals("Received broadcast without MANAGE_DEBUGGING permission.",
+ context.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING),
+ PackageManager.PERMISSION_GRANTED);
+ int status = intent.getIntExtra(AdbManager.WIRELESS_STATUS_EXTRA,
+ AdbManager.WIRELESS_STATUS_DISCONNECTED);
+ Log.i(TAG, "action=" + action + " status=" + status);
+ mAdbActionLatch.countDown();
+ } else if (AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION.equals(action)) {
+ assertEquals("Received broadcast without MANAGE_DEBUGGING permission.",
+ context.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING),
+ PackageManager.PERMISSION_GRANTED);
+ Integer res = intent.getIntExtra(
+ AdbManager.WIRELESS_STATUS_EXTRA,
+ AdbManager.WIRELESS_STATUS_FAIL);
+ Log.i(TAG, "action=" + action + " result=" + res);
+
+ if (res.equals(AdbManager.WIRELESS_STATUS_PAIRING_CODE)) {
+ String pairingCode = intent.getStringExtra(
+ AdbManager.WIRELESS_PAIRING_CODE_EXTRA);
+ Log.i(TAG, "pairingCode=" + pairingCode);
+ } else if (res.equals(AdbManager.WIRELESS_STATUS_CONNECTED)) {
+ int port = intent.getIntExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, 0);
+ Log.i(TAG, "port=" + port);
+ }
+ mAdbActionLatch.countDown();
+ }
+ }
+ };
+
+ private void adoptShellPermissionIdentity() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(android.Manifest.permission.MANAGE_DEBUGGING);
+ }
+
+ private void dropShellPermissionIdentity() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ @Test
+ public void testBroadcastReceiverWithPermissions() throws Exception {
+ adoptShellPermissionIdentity();
+ final IAdbManager mAdbManager = IAdbManager.Stub.asInterface(
+ ServiceManager.getService(Context.ADB_SERVICE));
+ IntentFilter intentFilter =
+ new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION);
+ intentFilter.addAction(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
+ intentFilter.addAction(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+ assertEquals("Context does not have MANAGE_DEBUGGING permission.",
+ mContext.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING),
+ PackageManager.PERMISSION_GRANTED);
+ try {
+ mContext.registerReceiver(mReceiver, intentFilter);
+ mAdbManager.enablePairingByPairingCode();
+ if (!mAdbActionLatch.await(TIMEOUT, TIMEOUT_TIME_UNIT)) {
+ fail("Receiver did not receive adb intent action within the timeout duration");
+ }
+ } finally {
+ mContext.unregisterReceiver(mReceiver);
+ }
+ }
+
+ @Test
+ public void testBroadcastReceiverWithoutPermissions() throws Exception {
+ adoptShellPermissionIdentity();
+ final IAdbManager mAdbManager = IAdbManager.Stub.asInterface(
+ ServiceManager.getService(Context.ADB_SERVICE));
+ IntentFilter intentFilter =
+ new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION);
+ intentFilter.addAction(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
+ intentFilter.addAction(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+ mAdbManager.enablePairingByPairingCode();
+
+ dropShellPermissionIdentity();
+ assertEquals("Context has MANAGE_DEBUGGING permission.",
+ mContext.checkSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING),
+ PackageManager.PERMISSION_DENIED);
+ try {
+ mContext.registerReceiver(mReceiver, intentFilter);
+
+ if (mAdbActionLatch.await(TIMEOUT, TIMEOUT_TIME_UNIT)) {
+ fail("Broadcast receiver received adb action intent without debug permissions");
+ }
+ } finally {
+ mContext.unregisterReceiver(mReceiver);
+ }
+ }
+
/**
* Runs an adb test with the provided configuration.
*
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
index 8c21a39..16406bc 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerInternalTest.java
@@ -129,7 +129,7 @@
thread2.assertWaiting("Unexpected state for " + record2);
thread2.interrupt();
- mAms.mProcessList.mActiveUids.clear();
+ clearActiveUids();
}
private UidRecord addActiveUidRecord(int uid, long curProcStateSeq,
@@ -137,11 +137,21 @@
final UidRecord record = new UidRecord(uid, mAms);
record.lastNetworkUpdatedProcStateSeq = lastNetworkUpdatedProcStateSeq;
record.curProcStateSeq = curProcStateSeq;
- record.waitingForNetwork = true;
- mAms.mProcessList.mActiveUids.put(uid, record);
+ record.procStateSeqWaitingForNetwork = 1;
+ addActiveUidRecord(uid, record);
return record;
}
+ @SuppressWarnings("GuardedBy")
+ private void addActiveUidRecord(int uid, UidRecord record) {
+ mAms.mProcessList.mActiveUids.put(uid, record);
+ }
+
+ @SuppressWarnings("GuardedBy")
+ private void clearActiveUids() {
+ mAms.mProcessList.mActiveUids.clear();
+ }
+
static class CustomThread extends Thread {
private static final long WAIT_TIMEOUT_MS = 1000;
private static final long WAIT_INTERVAL_MS = 100;
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 12e0d8b..968a3e2 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -191,8 +191,6 @@
verifySeqCounterAndInteractions(uidRec,
PROCESS_STATE_TOP, // prevState
PROCESS_STATE_TOP, // curState
- 0, // expectedGlobalCounter
- 0, // exptectedCurProcStateSeq
NETWORK_STATE_NO_CHANGE, // expectedBlockState
false); // expectNotify
@@ -200,8 +198,6 @@
verifySeqCounterAndInteractions(uidRec,
PROCESS_STATE_FOREGROUND_SERVICE, // prevState
PROCESS_STATE_SERVICE, // curState
- 1, // expectedGlobalCounter
- 1, // exptectedCurProcStateSeq
NETWORK_STATE_UNBLOCK, // expectedBlockState
true); // expectNotify
@@ -213,8 +209,6 @@
verifySeqCounterAndInteractions(uidRec,
PROCESS_STATE_TRANSIENT_BACKGROUND, // prevState
PROCESS_STATE_IMPORTANT_BACKGROUND, // curState
- 42, // expectedGlobalCounter
- 1, // exptectedCurProcStateSeq
NETWORK_STATE_NO_CHANGE, // expectedBlockState
false); // expectNotify
@@ -222,73 +216,22 @@
verifySeqCounterAndInteractions(uidRec,
PROCESS_STATE_LAST_ACTIVITY, // prevState
PROCESS_STATE_TOP, // curState
- 43, // expectedGlobalCounter
- 43, // exptectedCurProcStateSeq
NETWORK_STATE_BLOCK, // expectedBlockState
false); // expectNotify
// verify waiting threads are not notified.
- uidRec.waitingForNetwork = false;
+ uidRec.procStateSeqWaitingForNetwork = 0;
// Uid state is moving from foreground to background.
verifySeqCounterAndInteractions(uidRec,
PROCESS_STATE_FOREGROUND_SERVICE, // prevState
PROCESS_STATE_SERVICE, // curState
- 44, // expectedGlobalCounter
- 44, // exptectedCurProcStateSeq
NETWORK_STATE_UNBLOCK, // expectedBlockState
false); // expectNotify
-
- // Verify when uid is not restricted, procStateSeq is not incremented.
- uidRec.waitingForNetwork = true;
- mInjector.setNetworkRestrictedForUid(false);
- verifySeqCounterAndInteractions(uidRec,
- PROCESS_STATE_IMPORTANT_BACKGROUND, // prevState
- PROCESS_STATE_TOP, // curState
- 44, // expectedGlobalCounter
- 44, // exptectedCurProcStateSeq
- -1, // expectedBlockState, -1 to verify there are no interactions with main thread.
- false); // expectNotify
-
- // Verify when waitForNetworkTimeout is 0, then procStateSeq is not incremented.
- mAms.mWaitForNetworkTimeoutMs = 0;
- mInjector.setNetworkRestrictedForUid(true);
- verifySeqCounterAndInteractions(uidRec,
- PROCESS_STATE_TOP, // prevState
- PROCESS_STATE_IMPORTANT_BACKGROUND, // curState
- 44, // expectedGlobalCounter
- 44, // exptectedCurProcStateSeq
- -1, // expectedBlockState, -1 to verify there are no interactions with main thread.
- false); // expectNotify
-
- // Verify when the uid doesn't have internet permission, then procStateSeq is not
- // incremented.
- uidRec.hasInternetPermission = false;
- mAms.mWaitForNetworkTimeoutMs = 111;
- mInjector.setNetworkRestrictedForUid(true);
- verifySeqCounterAndInteractions(uidRec,
- PROCESS_STATE_CACHED_ACTIVITY, // prevState
- PROCESS_STATE_FOREGROUND_SERVICE, // curState
- 44, // expectedGlobalCounter
- 44, // exptectedCurProcStateSeq
- -1, // expectedBlockState, -1 to verify there are no interactions with main thread.
- false); // expectNotify
-
- // Verify procStateSeq is not incremented when the uid is not an application, regardless
- // of the process state.
- final int notAppUid = 111;
- final UidRecord uidRec2 = addUidRecord(notAppUid);
- verifySeqCounterAndInteractions(uidRec2,
- PROCESS_STATE_CACHED_EMPTY, // prevState
- PROCESS_STATE_TOP, // curState
- 44, // expectedGlobalCounter
- 0, // exptectedCurProcStateSeq
- -1, // expectedBlockState, -1 to verify there are no interactions with main thread.
- false); // expectNotify
}
private UidRecord addUidRecord(int uid) {
final UidRecord uidRec = new UidRecord(uid, mAms);
- uidRec.waitingForNetwork = true;
+ uidRec.procStateSeqWaitingForNetwork = 1;
uidRec.hasInternetPermission = true;
mAms.mProcessList.mActiveUids.put(uid, uidRec);
@@ -305,18 +248,26 @@
@SuppressWarnings("GuardedBy")
private void verifySeqCounterAndInteractions(UidRecord uidRec, int prevState, int curState,
- int expectedGlobalCounter, int expectedCurProcStateSeq, int expectedBlockState,
- boolean expectNotify) throws Exception {
+ int expectedBlockState, boolean expectNotify) throws Exception {
CustomThread thread = new CustomThread(uidRec.networkStateLock);
thread.startAndWait("Unexpected state for " + uidRec);
uidRec.setSetProcState(prevState);
uidRec.setCurProcState(curState);
+ final long beforeProcStateSeq = mAms.mProcessList.mProcStateSeqCounter;
+
mAms.mProcessList.incrementProcStateSeqAndNotifyAppsLOSP(mAms.mProcessList.mActiveUids);
- // @SuppressWarnings("GuardedBy")
- assertEquals(expectedGlobalCounter, mAms.mProcessList.mProcStateSeqCounter);
- assertEquals(expectedCurProcStateSeq, uidRec.curProcStateSeq);
+ final long afterProcStateSeq = beforeProcStateSeq
+ + mAms.mProcessList.mActiveUids.size();
+ assertEquals("beforeProcStateSeq=" + beforeProcStateSeq
+ + ",activeUids.size=" + mAms.mProcessList.mActiveUids.size(),
+ afterProcStateSeq, mAms.mProcessList.mProcStateSeqCounter);
+ assertTrue("beforeProcStateSeq=" + beforeProcStateSeq
+ + ",afterProcStateSeq=" + afterProcStateSeq
+ + ",uidCurProcStateSeq=" + uidRec.curProcStateSeq,
+ uidRec.curProcStateSeq > beforeProcStateSeq
+ && uidRec.curProcStateSeq <= afterProcStateSeq);
for (int i = mAms.mProcessList.getLruSizeLOSP() - 1; i >= 0; --i) {
final ProcessRecord app = mAms.mProcessList.getLruProcessesLOSP().get(i);
@@ -815,48 +766,11 @@
}
@Test
- public void testEnqueueUidChangeLocked_procStateSeqUpdated() {
- final UidRecord uidRecord = new UidRecord(TEST_UID, mAms);
- uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ1;
-
- // Verify with no pending changes for TEST_UID.
- verifyLastProcStateSeqUpdated(uidRecord, -1, TEST_PROC_STATE_SEQ1);
-
- // Add a pending change for TEST_UID and verify enqueueUidChangeLocked still works as
- // expected.
- uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ2;
- verifyLastProcStateSeqUpdated(uidRecord, -1, TEST_PROC_STATE_SEQ2);
- }
-
- @Test
public void testEnqueueUidChangeLocked_nullUidRecord() {
// Use "null" uidRecord to make sure there is no crash.
mAms.enqueueUidChangeLocked(null, TEST_UID, UidRecord.CHANGE_ACTIVE);
}
- private void verifyLastProcStateSeqUpdated(UidRecord uidRecord, int uid, long curProcstateSeq) {
- // Test enqueueUidChangeLocked with every UidRecord.CHANGE_*
- for (int i = 0; i < UID_RECORD_CHANGES.length; ++i) {
- final int changeToDispatch = UID_RECORD_CHANGES[i];
- // Reset lastProcStateSeqDispatchToObservers after every test.
- uidRecord.lastDispatchedProcStateSeq = 0;
- mAms.enqueueUidChangeLocked(uidRecord, uid, changeToDispatch);
- // Verify there is no effect on curProcStateSeq.
- assertEquals(curProcstateSeq, uidRecord.curProcStateSeq);
- if ((changeToDispatch & UidRecord.CHANGE_GONE) != 0) {
- // Since the change is CHANGE_GONE or CHANGE_GONE_IDLE, verify that
- // lastProcStateSeqDispatchedToObservers is not updated.
- assertNotEquals(uidRecord.curProcStateSeq,
- uidRecord.lastDispatchedProcStateSeq);
- } else {
- // Since the change is neither CHANGE_GONE nor CHANGE_GONE_IDLE, verify that
- // lastProcStateSeqDispatchedToObservers has been updated to curProcStateSeq.
- assertEquals(uidRecord.curProcStateSeq,
- uidRecord.lastDispatchedProcStateSeq);
- }
- }
- }
-
@MediumTest
@Test
public void testEnqueueUidChangeLocked_dispatchUidsChanged() {
@@ -893,29 +807,10 @@
// Check there is no crash when there is no UidRecord for myUid
mAms.waitForNetworkStateUpdate(TEST_PROC_STATE_SEQ1);
- // Verify there is no waiting when UidRecord.curProcStateSeq is greater than
- // the procStateSeq in the request to wait.
- verifyWaitingForNetworkStateUpdate(
- TEST_PROC_STATE_SEQ1, // curProcStateSeq
- TEST_PROC_STATE_SEQ1, // lastDsipatchedProcStateSeq
- TEST_PROC_STATE_SEQ1 - 4, // lastNetworkUpdatedProcStateSeq
- TEST_PROC_STATE_SEQ1 - 2, // procStateSeqToWait
- false); // expectWait
-
- // Verify there is no waiting when the procStateSeq in the request to wait is
- // not dispatched to NPMS.
- verifyWaitingForNetworkStateUpdate(
- TEST_PROC_STATE_SEQ1, // curProcStateSeq
- TEST_PROC_STATE_SEQ1 - 1, // lastDsipatchedProcStateSeq
- TEST_PROC_STATE_SEQ1 - 1, // lastNetworkUpdatedProcStateSeq
- TEST_PROC_STATE_SEQ1, // procStateSeqToWait
- false); // expectWait
-
// Verify there is not waiting when the procStateSeq in the request already has
// an updated network state.
verifyWaitingForNetworkStateUpdate(
TEST_PROC_STATE_SEQ1, // curProcStateSeq
- TEST_PROC_STATE_SEQ1, // lastDsipatchedProcStateSeq
TEST_PROC_STATE_SEQ1, // lastNetworkUpdatedProcStateSeq
TEST_PROC_STATE_SEQ1, // procStateSeqToWait
false); // expectWait
@@ -923,18 +818,16 @@
// Verify waiting for network works
verifyWaitingForNetworkStateUpdate(
TEST_PROC_STATE_SEQ1, // curProcStateSeq
- TEST_PROC_STATE_SEQ1, // lastDsipatchedProcStateSeq
TEST_PROC_STATE_SEQ1 - 1, // lastNetworkUpdatedProcStateSeq
TEST_PROC_STATE_SEQ1, // procStateSeqToWait
true); // expectWait
}
private void verifyWaitingForNetworkStateUpdate(long curProcStateSeq,
- long lastDispatchedProcStateSeq, long lastNetworkUpdatedProcStateSeq,
+ long lastNetworkUpdatedProcStateSeq,
final long procStateSeqToWait, boolean expectWait) throws Exception {
final UidRecord record = new UidRecord(Process.myUid(), mAms);
record.curProcStateSeq = curProcStateSeq;
- record.lastDispatchedProcStateSeq = lastDispatchedProcStateSeq;
record.lastNetworkUpdatedProcStateSeq = lastNetworkUpdatedProcStateSeq;
mAms.mProcessList.mActiveUids.put(Process.myUid(), record);
@@ -953,7 +846,7 @@
}
thread.assertTerminated(errMsg);
assertTrue(thread.mNotified);
- assertFalse(record.waitingForNetwork);
+ assertEquals(0, record.procStateSeqWaitingForNetwork);
} else {
thread.start();
thread.assertTerminated(errMsg);
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/OWNERS b/services/tests/servicestests/src/com/android/server/appwidget/OWNERS
new file mode 100644
index 0000000..d724cac
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appwidget/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/appwidget/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index 61d7ede..59236dc 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -255,21 +255,6 @@
}
@Override
- boolean storageManagerIsNonDefaultBlockEncrypted() {
- return services.storageManager.isNonDefaultBlockEncrypted();
- }
-
- @Override
- boolean storageManagerIsEncrypted() {
- return services.storageManager.isEncrypted();
- }
-
- @Override
- boolean storageManagerIsEncryptable() {
- return services.storageManager.isEncryptable();
- }
-
- @Override
String getDevicePolicyFilePathForSystemUser() {
return services.systemUserDataDir.getAbsolutePath() + "/";
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 8a2919d..248af0e 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -492,18 +492,6 @@
public boolean isFileBasedEncryptionEnabled() {
return false;
}
-
- public boolean isNonDefaultBlockEncrypted() {
- return false;
- }
-
- public boolean isEncrypted() {
- return false;
- }
-
- public boolean isEncryptable() {
- return false;
- }
}
// We have to keep track of broadcast receivers registered for a given intent ourselves as the
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index 524ad62..77af225 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -1789,4 +1789,32 @@
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(featureAbortPressed);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(featureAbortReleased);
}
+
+ @Test
+ public void onHotplugClearsDevices() {
+ mHdmiControlService.getHdmiCecNetwork().clearDeviceList();
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+ .isEmpty();
+ // Add a device to the network and assert that this device is included in the list of
+ // devices.
+ HdmiDeviceInfo infoPlayback = new HdmiDeviceInfo(
+ Constants.ADDR_PLAYBACK_3,
+ 0x1000,
+ PORT_1,
+ HdmiDeviceInfo.DEVICE_PLAYBACK,
+ 0x1000,
+ "Playback 3",
+ HdmiControlManager.POWER_STATUS_ON);
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(infoPlayback);
+ mTestLooper.dispatchAll();
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+ .hasSize(1);
+
+ // HAL detects a hotplug out. Assert that this device gets removed from the list of devices.
+ mHdmiControlService.onHotplug(PORT_1, false);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+ .isEmpty();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index 59711a6..0afd27c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -19,6 +19,7 @@
import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
+import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_2;
import static com.android.server.hdmi.Constants.ADDR_RECORDER_1;
import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
@@ -56,12 +57,14 @@
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
+import java.util.List;
@SmallTest
@RunWith(JUnit4.class)
/** Tests for {@link HdmiCecLocalDeviceTv} class. */
public class HdmiCecLocalDeviceTvTest {
private static final int TIMEOUT_MS = HdmiConfig.TIMEOUT_MS + 1;
+ private static final int PORT_1 = 1;
private HdmiControlService mHdmiControlService;
private HdmiCecController mHdmiCecController;
@@ -73,6 +76,25 @@
private int mTvPhysicalAddress;
private int mTvLogicalAddress;
private boolean mWokenUp;
+ private List<DeviceEventListener> mDeviceEventListeners = new ArrayList<>();
+
+ private class DeviceEventListener {
+ private HdmiDeviceInfo mDevice;
+ private int mStatus;
+
+ DeviceEventListener(HdmiDeviceInfo device, int status) {
+ this.mDevice = device;
+ this.mStatus = status;
+ }
+
+ int getStatus() {
+ return mStatus;
+ }
+
+ HdmiDeviceInfo getDeviceInfo() {
+ return mDevice;
+ }
+ }
@Mock
private IPowerManager mIPowerManagerMock;
@@ -124,6 +146,11 @@
AudioManager getAudioManager() {
return mAudioManager;
}
+
+ @Override
+ void invokeDeviceEventListeners(HdmiDeviceInfo device, int status) {
+ mDeviceEventListeners.add(new DeviceEventListener(device, status));
+ }
};
mHdmiCecLocalDeviceTv = new HdmiCecLocalDeviceTv(mHdmiControlService);
@@ -596,4 +623,147 @@
verify(mAudioManager, never()).setStreamVolume(anyInt(), anyInt(), anyInt());
}
+
+ @Test
+ public void hotplugDetectionActionClearsDevices() {
+ mHdmiControlService.getHdmiCecNetwork().clearDeviceList();
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+ .isEmpty();
+ // Add a device to the network and assert that this device is included in the list of
+ // devices.
+ HdmiDeviceInfo infoPlayback = new HdmiDeviceInfo(
+ Constants.ADDR_PLAYBACK_2,
+ 0x1000,
+ PORT_1,
+ HdmiDeviceInfo.DEVICE_PLAYBACK,
+ 0x1000,
+ "Playback 2",
+ HdmiControlManager.POWER_STATUS_ON);
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(infoPlayback);
+ mTestLooper.dispatchAll();
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+ .hasSize(1);
+ mDeviceEventListeners.clear();
+ assertThat(mDeviceEventListeners.size()).isEqualTo(0);
+
+ // HAL detects a hotplug out. Assert that this device stays in the list of devices.
+ mHdmiControlService.onHotplug(PORT_1, false);
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+ .hasSize(1);
+ assertThat(mDeviceEventListeners).isEmpty();
+ mTestLooper.dispatchAll();
+ // Make the device not acknowledge the poll message sent by the HotplugDetectionAction.
+ // Assert that this device is removed from the list of devices.
+ mNativeWrapper.setPollAddressResponse(Constants.ADDR_PLAYBACK_2, SendMessageResult.NACK);
+ for (int pollCount = 0; pollCount < HotplugDetectionAction.TIMEOUT_COUNT; pollCount++) {
+ mTestLooper.moveTimeForward(HotplugDetectionAction.POLLING_INTERVAL_MS);
+ mTestLooper.dispatchAll();
+ }
+
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+ .isEmpty();
+ assertThat(mDeviceEventListeners.size()).isEqualTo(1);
+ assertThat(mDeviceEventListeners.get(0).getStatus())
+ .isEqualTo(HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
+ HdmiDeviceInfo removedDeviceInfo = mDeviceEventListeners.get(0).getDeviceInfo();
+ assertThat(removedDeviceInfo.getPortId()).isEqualTo(PORT_1);
+ assertThat(removedDeviceInfo.getLogicalAddress()).isEqualTo(Constants.ADDR_PLAYBACK_2);
+ assertThat(removedDeviceInfo.getPhysicalAddress()).isEqualTo(0x1000);
+ assertThat(removedDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_PLAYBACK);
+ }
+
+ @Test
+ public void hotplugDetectionActionClearsDevices_AudioSystem() {
+ mHdmiControlService.getHdmiCecNetwork().clearDeviceList();
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+ .isEmpty();
+ // Add a device to the network and assert that this device is included in the list of
+ // devices.
+ HdmiDeviceInfo infoAudioSystem = new HdmiDeviceInfo(
+ ADDR_AUDIO_SYSTEM,
+ 0x1000,
+ PORT_1,
+ HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM,
+ 0x1000,
+ "Audio System",
+ HdmiControlManager.POWER_STATUS_ON);
+ mHdmiControlService.getHdmiCecNetwork().addCecDevice(infoAudioSystem);
+ mTestLooper.dispatchAll();
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+ .hasSize(1);
+ mDeviceEventListeners.clear();
+ assertThat(mDeviceEventListeners.size()).isEqualTo(0);
+
+ // HAL detects a hotplug out. Assert that this device stays in the list of devices.
+ mHdmiControlService.onHotplug(PORT_1, false);
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+ .hasSize(1);
+ assertThat(mDeviceEventListeners).isEmpty();
+ mTestLooper.dispatchAll();
+ // Make the device not acknowledge the poll message sent by the HotplugDetectionAction.
+ // Assert that this device is removed from the list of devices.
+ mNativeWrapper.setPollAddressResponse(ADDR_AUDIO_SYSTEM, SendMessageResult.NACK);
+ for (int pollCount = 0; pollCount < HotplugDetectionAction.TIMEOUT_COUNT; pollCount++) {
+ mTestLooper.moveTimeForward(HotplugDetectionAction.POLLING_INTERVAL_MS);
+ mTestLooper.dispatchAll();
+ }
+
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+ .isEmpty();
+ assertThat(mDeviceEventListeners.size()).isEqualTo(1);
+ assertThat(mDeviceEventListeners.get(0).getStatus())
+ .isEqualTo(HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
+ HdmiDeviceInfo removedDeviceInfo = mDeviceEventListeners.get(0).getDeviceInfo();
+ assertThat(removedDeviceInfo.getPortId()).isEqualTo(PORT_1);
+ assertThat(removedDeviceInfo.getLogicalAddress()).isEqualTo(Constants.ADDR_AUDIO_SYSTEM);
+ assertThat(removedDeviceInfo.getPhysicalAddress()).isEqualTo(0x1000);
+ assertThat(removedDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+ }
+
+ @Test
+ public void listenerInvokedIfPhysicalAddressReported() {
+ mHdmiControlService.getHdmiCecNetwork().clearDeviceList();
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+ .isEmpty();
+ HdmiCecMessage reportPhysicalAddress =
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ ADDR_PLAYBACK_2, 0x1000, HdmiDeviceInfo.DEVICE_PLAYBACK);
+ mNativeWrapper.onCecMessage(reportPhysicalAddress);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+ .hasSize(1);
+ assertThat(mDeviceEventListeners.size()).isEqualTo(1);
+ assertThat(mDeviceEventListeners.get(0).getStatus())
+ .isEqualTo(HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
+ }
+
+ @Test
+ public void listenerNotInvokedIfPhysicalAddressUnknown() {
+ mHdmiControlService.getHdmiCecNetwork().clearDeviceList();
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+ .isEmpty();
+ HdmiCecMessage setOsdName = HdmiCecMessageBuilder.buildSetOsdNameCommand(
+ ADDR_PLAYBACK_2, ADDR_TV, "Playback 2");
+ mNativeWrapper.onCecMessage(setOsdName);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+ .hasSize(1);
+ assertThat(mDeviceEventListeners).isEmpty();
+
+ // When the device reports its physical address, the listener eventually is invoked.
+ HdmiCecMessage reportPhysicalAddress =
+ HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
+ ADDR_PLAYBACK_2, 0x1000, HdmiDeviceInfo.DEVICE_PLAYBACK);
+ mNativeWrapper.onCecMessage(reportPhysicalAddress);
+ mTestLooper.dispatchAll();
+
+ assertThat(mHdmiControlService.getHdmiCecNetwork().getDeviceInfoList(false))
+ .hasSize(1);
+ assertThat(mDeviceEventListeners.size()).isEqualTo(1);
+ assertThat(mDeviceEventListeners.get(0).getStatus())
+ .isEqualTo(HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
+
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
index b1998f5..c617d21 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecNetworkTest.java
@@ -100,6 +100,8 @@
new HdmiPortInfo(5, HdmiPortInfo.PORT_OUTPUT, 0x0000, true, false, false);
mNativeWrapper.setPortInfo(mHdmiPortInfo);
mHdmiCecNetwork.initPortInfo();
+
+ mHdmiCecNetwork = mHdmiControlService.getHdmiCecNetwork();
}
@Test
@@ -140,7 +142,24 @@
}
@Test
+ public void physicalAddressToPort_localDevice_weAreSourceDevice() {
+ mNativeWrapper.setPhysicalAddress(0x2000);
+ mHdmiCecNetwork.initPortInfo();
+ assertThat(mHdmiCecNetwork.physicalAddressToPortId(0x2000))
+ .isEqualTo(Constants.CEC_SWITCH_HOME);
+ }
+
+ @Test
+ public void physicalAddressToPort_localDevice_weAreTv() {
+ mNativeWrapper.setPhysicalAddress(0x0000);
+ mHdmiCecNetwork.initPortInfo();
+ assertThat(mHdmiCecNetwork.physicalAddressToPortId(0x0000))
+ .isEqualTo(Constants.CEC_SWITCH_HOME);
+ }
+
+ @Test
public void localDevices_verifyOne_tv() {
+ mHdmiCecNetwork.clearLocalDevices();
mHdmiCecNetwork.addLocalDevice(HdmiDeviceInfo.DEVICE_TV,
new HdmiCecLocalDeviceTv(mHdmiControlService));
@@ -153,6 +172,7 @@
@Test
public void localDevices_verifyOne_playback() {
+ mHdmiCecNetwork.clearLocalDevices();
mHdmiCecNetwork.addLocalDevice(HdmiDeviceInfo.DEVICE_PLAYBACK,
new HdmiCecLocalDevicePlayback(mHdmiControlService));
@@ -175,27 +195,26 @@
assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
Constants.INVALID_PHYSICAL_ADDRESS);
- assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
+ assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_PLAYBACK);
assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
HdmiUtils.getDefaultDeviceName(logicalAddress));
assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(
HdmiControlManager.POWER_STATUS_UNKNOWN);
- assertThat(mDeviceEventListenerStatuses).containsExactly(
- HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
+ assertThat(mDeviceEventListenerStatuses).isEmpty();
}
@Test
public void cecDevices_tracking_logicalAddressOnly_doesntNotifyAgain() throws Exception {
int logicalAddress = Constants.ADDR_PLAYBACK_1;
+ int physicalAddress = 0x1000;
mHdmiCecNetwork.handleCecMessage(
- HdmiCecMessageBuilder.buildActiveSource(logicalAddress, 0x1000));
+ HdmiCecMessageBuilder.buildActiveSource(logicalAddress, physicalAddress));
mHdmiCecNetwork.handleCecMessage(
- HdmiCecMessageBuilder.buildActiveSource(logicalAddress, 0x1000));
+ HdmiCecMessageBuilder.buildActiveSource(logicalAddress, physicalAddress));
- assertThat(mDeviceEventListenerStatuses).containsExactly(
- HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
+ assertThat(mDeviceEventListenerStatuses).isEmpty();
}
@Test
@@ -219,6 +238,9 @@
assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(
HdmiControlManager.POWER_STATUS_UNKNOWN);
+
+ assertThat(mDeviceEventListenerStatuses).containsExactly(
+ HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
}
@Test
@@ -236,11 +258,10 @@
physicalAddress, type));
- // ADD for logical address first detected
- // UPDATE for updating device with physical address
+ // ADD for physical address first detected
+ // no UPDATE, since physical address didn't change
assertThat(mDeviceEventListenerStatuses).containsExactly(
- HdmiControlManager.DEVICE_EVENT_ADD_DEVICE,
- HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
+ HdmiControlManager.DEVICE_EVENT_ADD_DEVICE);
}
@Test
@@ -257,11 +278,13 @@
assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
Constants.INVALID_PHYSICAL_ADDRESS);
- assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
+ assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_PLAYBACK);
assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
HdmiUtils.getDefaultDeviceName(logicalAddress));
assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(powerStatus);
+
+ assertThat(mDeviceEventListenerStatuses).isEmpty();
}
@Test
@@ -278,11 +301,13 @@
assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
Constants.INVALID_PHYSICAL_ADDRESS);
- assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
+ assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_PLAYBACK);
assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(osdName);
assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(
HdmiControlManager.POWER_STATUS_UNKNOWN);
+
+ assertThat(mDeviceEventListenerStatuses).isEmpty();
}
@Test
@@ -298,12 +323,14 @@
assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
Constants.INVALID_PHYSICAL_ADDRESS);
- assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
+ assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_PLAYBACK);
assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
HdmiUtils.getDefaultDeviceName(logicalAddress));
assertThat(cecDeviceInfo.getVendorId()).isEqualTo(vendorId);
assertThat(cecDeviceInfo.getDevicePowerStatus()).isEqualTo(
HdmiControlManager.POWER_STATUS_UNKNOWN);
+
+ assertThat(mDeviceEventListenerStatuses).isEmpty();
}
@Test
@@ -362,12 +389,10 @@
assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(updatedPhysicalAddress);
assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(type);
- // ADD for logical address first detected
- // UPDATE for updating device with physical address
+ // ADD for physical address first detected
// UPDATE for updating device with new physical address
assertThat(mDeviceEventListenerStatuses).containsExactly(
HdmiControlManager.DEVICE_EVENT_ADD_DEVICE,
- HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE,
HdmiControlManager.DEVICE_EVENT_UPDATE_DEVICE);
}
@@ -429,7 +454,7 @@
assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
Constants.INVALID_PHYSICAL_ADDRESS);
- assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
+ assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_PLAYBACK);
assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
HdmiUtils.getDefaultDeviceName(logicalAddress));
assertThat(cecDeviceInfo.getVendorId()).isEqualTo(updatedVendorId);
@@ -451,9 +476,8 @@
assertThat(mHdmiCecNetwork.getSafeCecDevicesLocked()).isEmpty();
- assertThat(mDeviceEventListenerStatuses).containsExactly(
- HdmiControlManager.DEVICE_EVENT_ADD_DEVICE,
- HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE);
+ // Physical address never got reported, so no listeners are triggered
+ assertThat(mDeviceEventListenerStatuses).isEmpty();
}
@Test
@@ -470,7 +494,7 @@
assertThat(cecDeviceInfo.getLogicalAddress()).isEqualTo(logicalAddress);
assertThat(cecDeviceInfo.getPhysicalAddress()).isEqualTo(
Constants.INVALID_PHYSICAL_ADDRESS);
- assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_RESERVED);
+ assertThat(cecDeviceInfo.getDeviceType()).isEqualTo(HdmiDeviceInfo.DEVICE_PLAYBACK);
assertThat(cecDeviceInfo.getVendorId()).isEqualTo(Constants.UNKNOWN_VENDOR_ID);
assertThat(cecDeviceInfo.getDisplayName()).isEqualTo(
HdmiUtils.getDefaultDeviceName(logicalAddress));
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
index 33ea710..b9ae670 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
@@ -25,6 +25,8 @@
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+
import static junit.framework.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -48,6 +50,7 @@
import android.os.Process;
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
@@ -370,6 +373,19 @@
mUserId);
}
+ @Test
+ public void testRequireStrongAuth_STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN() throws Exception {
+ when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true);
+
+ assertEquals(0, mCommand.exec(new Binder(), in, out, err,
+ new String[] { "require-strong-auth", "STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN"},
+ mShellCallback, mResultReceiver));
+
+ verify(mLockPatternUtils).requireStrongAuth(
+ STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
+ UserHandle.USER_ALL);
+ }
+
private List<LockPatternView.Cell> stringToPattern(String str) {
return LockPatternUtils.byteArrayToPattern(str.getBytes());
}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index f865a50..0f2fe44 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -23,11 +23,13 @@
import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY;
import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER;
import static android.net.ConnectivityManager.BLOCKED_REASON_DOZE;
+import static android.net.ConnectivityManager.BLOCKED_REASON_LOW_POWER_STANDBY;
import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_LOW_POWER_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.net.INetd.FIREWALL_CHAIN_RESTRICTED;
import static android.net.INetd.FIREWALL_RULE_ALLOW;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -38,8 +40,10 @@
import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_FOREGROUND;
import static android.net.NetworkPolicyManager.ALLOWED_METERED_REASON_SYSTEM;
import static android.net.NetworkPolicyManager.ALLOWED_REASON_FOREGROUND;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST;
import static android.net.NetworkPolicyManager.ALLOWED_REASON_NONE;
import static android.net.NetworkPolicyManager.ALLOWED_REASON_SYSTEM;
+import static android.net.NetworkPolicyManager.ALLOWED_REASON_TOP;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.POLICY_NONE;
@@ -114,6 +118,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.net.ConnectivityManager;
@@ -162,6 +167,7 @@
import com.android.internal.util.test.FsUtil;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.usage.AppStandbyInternal;
import com.google.common.util.concurrent.AbstractFuture;
@@ -200,6 +206,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
@@ -211,6 +218,7 @@
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
@@ -269,6 +277,7 @@
ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
private ActivityManagerInternal mActivityManagerInternal;
+ private PackageManagerInternal mPackageManagerInternal;
private IUidObserver mUidObserver;
private INetworkManagementEventObserver mNetworkObserver;
@@ -330,6 +339,7 @@
when(usageStats.getIdleUidsForUser(anyInt())).thenReturn(new int[]{});
mActivityManagerInternal = addLocalServiceMock(ActivityManagerInternal.class);
+ mPackageManagerInternal = addLocalServiceMock(PackageManagerInternal.class);
final PowerSaveState state = new PowerSaveState.Builder()
.setBatterySaverEnabled(false).build();
@@ -431,14 +441,14 @@
setNetpolicyXml(context);
doAnswer(new Answer<Void>() {
-
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
mUidObserver = (IUidObserver) invocation.getArguments()[0];
Log.d(TAG, "set mUidObserver to " + mUidObserver);
return null;
}
- }).when(mActivityManager).registerUidObserver(any(), anyInt(), anyInt(), any(String.class));
+ }).when(mActivityManagerInternal).registerNetworkPolicyUidObserver(any(),
+ anyInt(), anyInt(), any(String.class));
mFutureIntent = newRestrictBackgroundChangedFuture();
mDeps = new TestDependencies(mServiceContext);
@@ -478,8 +488,15 @@
.thenReturn(buildApplicationInfo(PKG_NAME_B, UID_B));
when(mPackageManager.getApplicationInfo(eq(PKG_NAME_C), anyInt()))
.thenReturn(buildApplicationInfo(PKG_NAME_C, UID_C));
- when(mPackageManager.getInstalledApplications(anyInt())).thenReturn(
- buildInstalledApplicationInfoList());
+ doAnswer(arg -> {
+ final Consumer<AndroidPackage> consumer =
+ (Consumer<AndroidPackage>) arg.getArguments()[0];
+ for (AndroidPackage androidPackage : buildInstalledPackageList()) {
+ consumer.accept(androidPackage);
+ }
+ return null;
+ }).when(mPackageManagerInternal).forEachInstalledPackage(
+ any(Consumer.class), anyInt());
when(mUserManager.getUsers()).thenReturn(buildUserInfoList());
when(mNetworkManager.isBandwidthControlEnabled()).thenReturn(true);
when(mNetworkManager.setDataSaverModeEnabled(anyBoolean())).thenReturn(true);
@@ -531,6 +548,7 @@
LocalServices.removeServiceForTest(DeviceIdleInternal.class);
LocalServices.removeServiceForTest(AppStandbyInternal.class);
LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
}
@After
@@ -973,19 +991,20 @@
@Test
public void testUidForeground() throws Exception {
// push all uids into background
- callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, 0);
- callOnUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_SERVICE, 0);
+ long procStateSeq = 0;
+ callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, procStateSeq++);
+ callOnUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_SERVICE, procStateSeq++);
assertFalse(mService.isUidForeground(UID_A));
assertFalse(mService.isUidForeground(UID_B));
// push one of the uids into foreground
- callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_TOP, 0);
+ callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_TOP, procStateSeq++);
assertTrue(mService.isUidForeground(UID_A));
assertFalse(mService.isUidForeground(UID_B));
// and swap another uid into foreground
- callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, 0);
- callOnUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_TOP, 0);
+ callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_SERVICE, procStateSeq++);
+ callOnUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_TOP, procStateSeq++);
assertFalse(mService.isUidForeground(UID_A));
assertTrue(mService.isUidForeground(UID_B));
}
@@ -1877,55 +1896,99 @@
}
@Test
+ public void testLowPowerStandbyAllowlist() throws Exception {
+ callOnUidStateChanged(UID_A, ActivityManager.PROCESS_STATE_TOP, 0);
+ callOnUidStateChanged(UID_B, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0);
+ callOnUidStateChanged(UID_C, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0);
+ expectHasInternetPermission(UID_A, true);
+ expectHasInternetPermission(UID_B, true);
+ expectHasInternetPermission(UID_C, true);
+
+ final NetworkPolicyManagerInternal internal = LocalServices
+ .getService(NetworkPolicyManagerInternal.class);
+
+ Map<Integer, Integer> firewallUidRules = new ArrayMap<>();
+ doAnswer(arg -> {
+ int[] uids = arg.getArgument(1);
+ int[] rules = arg.getArgument(2);
+ assertTrue(uids.length == rules.length);
+
+ for (int i = 0; i < uids.length; ++i) {
+ firewallUidRules.put(uids[i], rules[i]);
+ }
+ return null;
+ }).when(mNetworkManager).setFirewallUidRules(eq(FIREWALL_CHAIN_LOW_POWER_STANDBY),
+ any(int[].class), any(int[].class));
+
+ internal.setLowPowerStandbyAllowlist(new int[] { UID_B });
+ internal.setLowPowerStandbyActive(true);
+ assertEquals(FIREWALL_RULE_ALLOW, firewallUidRules.get(UID_A).intValue());
+ assertEquals(FIREWALL_RULE_ALLOW, firewallUidRules.get(UID_B).intValue());
+ assertFalse(mService.isUidNetworkingBlocked(UID_A, false));
+ assertFalse(mService.isUidNetworkingBlocked(UID_B, false));
+ assertTrue(mService.isUidNetworkingBlocked(UID_C, false));
+
+ internal.setLowPowerStandbyActive(false);
+ assertFalse(mService.isUidNetworkingBlocked(UID_A, false));
+ assertFalse(mService.isUidNetworkingBlocked(UID_B, false));
+ assertFalse(mService.isUidNetworkingBlocked(UID_C, false));
+ }
+
+ @Test
public void testUpdateEffectiveBlockedReasons() {
- final SparseArray<Pair<Integer, Integer>> effectiveBlockedReasons = new SparseArray<>();
- effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
- Pair.create(BLOCKED_REASON_NONE, ALLOWED_REASON_NONE));
+ final Map<Pair<Integer, Integer>, Integer> effectiveBlockedReasons = new HashMap<>();
+ effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_NONE, ALLOWED_REASON_NONE),
+ BLOCKED_REASON_NONE);
- effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
- Pair.create(BLOCKED_REASON_BATTERY_SAVER, ALLOWED_REASON_SYSTEM));
- effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
- Pair.create(BLOCKED_REASON_BATTERY_SAVER | BLOCKED_REASON_DOZE,
- ALLOWED_REASON_SYSTEM));
- effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
- Pair.create(BLOCKED_METERED_REASON_DATA_SAVER,
- ALLOWED_METERED_REASON_SYSTEM));
- effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
- Pair.create(BLOCKED_METERED_REASON_DATA_SAVER
- | BLOCKED_METERED_REASON_USER_RESTRICTED,
- ALLOWED_METERED_REASON_SYSTEM));
+ effectiveBlockedReasons.put(
+ Pair.create(BLOCKED_REASON_BATTERY_SAVER, ALLOWED_REASON_SYSTEM),
+ BLOCKED_REASON_NONE);
+ effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_BATTERY_SAVER | BLOCKED_REASON_DOZE,
+ ALLOWED_REASON_SYSTEM), BLOCKED_REASON_NONE);
+ effectiveBlockedReasons.put(
+ Pair.create(BLOCKED_METERED_REASON_DATA_SAVER, ALLOWED_METERED_REASON_SYSTEM),
+ BLOCKED_REASON_NONE);
+ effectiveBlockedReasons.put(Pair.create(BLOCKED_METERED_REASON_DATA_SAVER
+ | BLOCKED_METERED_REASON_USER_RESTRICTED,
+ ALLOWED_METERED_REASON_SYSTEM), BLOCKED_REASON_NONE);
- effectiveBlockedReasons.put(BLOCKED_METERED_REASON_DATA_SAVER,
+ effectiveBlockedReasons.put(
Pair.create(BLOCKED_REASON_BATTERY_SAVER | BLOCKED_METERED_REASON_DATA_SAVER,
- ALLOWED_REASON_SYSTEM));
- effectiveBlockedReasons.put(BLOCKED_REASON_APP_STANDBY,
+ ALLOWED_REASON_SYSTEM), BLOCKED_METERED_REASON_DATA_SAVER);
+ effectiveBlockedReasons.put(
Pair.create(BLOCKED_REASON_APP_STANDBY | BLOCKED_METERED_REASON_USER_RESTRICTED,
- ALLOWED_METERED_REASON_SYSTEM));
+ ALLOWED_METERED_REASON_SYSTEM), BLOCKED_REASON_APP_STANDBY);
- effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
- Pair.create(BLOCKED_REASON_BATTERY_SAVER, ALLOWED_REASON_FOREGROUND));
- effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
- Pair.create(BLOCKED_REASON_BATTERY_SAVER | BLOCKED_REASON_DOZE,
- ALLOWED_REASON_FOREGROUND));
- effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
- Pair.create(BLOCKED_METERED_REASON_DATA_SAVER, ALLOWED_METERED_REASON_FOREGROUND));
- effectiveBlockedReasons.put(BLOCKED_REASON_NONE,
- Pair.create(BLOCKED_METERED_REASON_DATA_SAVER
- | BLOCKED_METERED_REASON_USER_RESTRICTED,
- ALLOWED_METERED_REASON_FOREGROUND));
- effectiveBlockedReasons.put(BLOCKED_METERED_REASON_DATA_SAVER,
+ effectiveBlockedReasons.put(
+ Pair.create(BLOCKED_REASON_BATTERY_SAVER, ALLOWED_REASON_FOREGROUND),
+ BLOCKED_REASON_NONE);
+ effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_BATTERY_SAVER | BLOCKED_REASON_DOZE,
+ ALLOWED_REASON_FOREGROUND), BLOCKED_REASON_NONE);
+ effectiveBlockedReasons.put(
+ Pair.create(BLOCKED_METERED_REASON_DATA_SAVER, ALLOWED_METERED_REASON_FOREGROUND),
+ BLOCKED_REASON_NONE);
+ effectiveBlockedReasons.put(Pair.create(BLOCKED_METERED_REASON_DATA_SAVER
+ | BLOCKED_METERED_REASON_USER_RESTRICTED,
+ ALLOWED_METERED_REASON_FOREGROUND), BLOCKED_REASON_NONE);
+ effectiveBlockedReasons.put(
Pair.create(BLOCKED_REASON_BATTERY_SAVER | BLOCKED_METERED_REASON_DATA_SAVER,
- ALLOWED_REASON_FOREGROUND));
- effectiveBlockedReasons.put(BLOCKED_REASON_BATTERY_SAVER,
- Pair.create(BLOCKED_REASON_BATTERY_SAVER
- | BLOCKED_METERED_REASON_USER_RESTRICTED,
- ALLOWED_METERED_REASON_FOREGROUND));
+ ALLOWED_REASON_FOREGROUND), BLOCKED_METERED_REASON_DATA_SAVER);
+ effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_BATTERY_SAVER
+ | BLOCKED_METERED_REASON_USER_RESTRICTED,
+ ALLOWED_METERED_REASON_FOREGROUND), BLOCKED_REASON_BATTERY_SAVER);
+
+ effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_LOW_POWER_STANDBY,
+ ALLOWED_REASON_FOREGROUND), BLOCKED_REASON_LOW_POWER_STANDBY);
+ effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_LOW_POWER_STANDBY,
+ ALLOWED_REASON_TOP), BLOCKED_REASON_NONE);
+ effectiveBlockedReasons.put(Pair.create(BLOCKED_REASON_LOW_POWER_STANDBY,
+ ALLOWED_REASON_LOW_POWER_STANDBY_ALLOWLIST), BLOCKED_REASON_NONE);
// TODO: test more combinations of blocked reasons.
- for (int i = 0; i < effectiveBlockedReasons.size(); ++i) {
- final int expectedEffectiveBlockedReasons = effectiveBlockedReasons.keyAt(i);
- final int blockedReasons = effectiveBlockedReasons.valueAt(i).first;
- final int allowedReasons = effectiveBlockedReasons.valueAt(i).second;
+ for (Map.Entry<Pair<Integer, Integer>, Integer> test : effectiveBlockedReasons.entrySet()) {
+ final int expectedEffectiveBlockedReasons = test.getValue();
+ final int blockedReasons = test.getKey().first;
+ final int allowedReasons = test.getKey().second;
final String errorMsg = "Expected="
+ blockedReasonsToString(expectedEffectiveBlockedReasons)
+ "; blockedReasons=" + blockedReasonsToString(blockedReasons)
@@ -1988,14 +2051,20 @@
return ai;
}
- private List<ApplicationInfo> buildInstalledApplicationInfoList() {
- final List<ApplicationInfo> installedApps = new ArrayList<>();
- installedApps.add(buildApplicationInfo(PKG_NAME_A, UID_A));
- installedApps.add(buildApplicationInfo(PKG_NAME_B, UID_B));
- installedApps.add(buildApplicationInfo(PKG_NAME_C, UID_C));
+ private List<AndroidPackage> buildInstalledPackageList() {
+ final List<AndroidPackage> installedApps = new ArrayList<>();
+ installedApps.add(createPackageMock(UID_A));
+ installedApps.add(createPackageMock(UID_B));
+ installedApps.add(createPackageMock(UID_C));
return installedApps;
}
+ private AndroidPackage createPackageMock(int uid) {
+ final AndroidPackage androidPackage = mock(AndroidPackage.class);
+ when(androidPackage.getUid()).thenReturn(uid);
+ return androidPackage;
+ }
+
private List<UserInfo> buildUserInfoList() {
final List<UserInfo> users = new ArrayList<>();
users.add(new UserInfo(USER_ID, "user1", 0));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index 721641a..5458a5b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -174,7 +174,7 @@
}
verify(mCallback, times(AUTOGROUP_AT_COUNT + 1))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
@@ -203,7 +203,7 @@
mGroupHelper.onNotificationUpdated(notifications.get(0), true);
verify(mCallback, times(AUTOGROUP_AT_COUNT + 2))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
@@ -236,7 +236,7 @@
mGroupHelper.onNotificationUpdated(notifications.get(0), true);
verify(mCallback, times(AUTOGROUP_AT_COUNT + 3))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
@@ -263,7 +263,7 @@
mGroupHelper.onNotificationRemoved(notifications.get(0));
verify(mCallback, times(AUTOGROUP_AT_COUNT + 2))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
@@ -291,7 +291,7 @@
mGroupHelper.onNotificationUpdated(notifications.get(0), true);
verify(mCallback, times(1))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
@@ -315,7 +315,7 @@
}
verify(mCallback, times(1))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
@@ -339,7 +339,7 @@
}
verify(mCallback, times(0))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(userId, pkg, AUTOGROUP_KEY), 0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 9639aa7..dc378dc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -76,6 +76,7 @@
import android.content.pm.ActivityInfo.ScreenOrientation;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
@@ -2181,6 +2182,29 @@
.computeAspectRatio(sizeCompatAppBounds), delta);
}
+ @Test
+ public void testClearSizeCompat_resetOverrideConfig() {
+ final int origDensity = 480;
+ final int newDensity = 520;
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 600, 800)
+ .setDensityDpi(origDensity)
+ .build();
+ setUpApp(display);
+ prepareUnresizable(mActivity, -1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
+
+ // Activity should enter size compat with old density after display density change.
+ display.setForcedDensity(newDensity, UserHandle.USER_CURRENT);
+
+ assertScaled();
+ assertEquals(origDensity, mActivity.getConfiguration().densityDpi);
+
+ // Activity should exit size compat with new density.
+ mActivity.clearSizeCompatMode();
+
+ assertFitted();
+ assertEquals(newDensity, mActivity.getConfiguration().densityDpi);
+ }
+
private void assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
float letterboxHorizontalPositionMultiplier) {
// Set up a display in landscape and ignoring orientation request.
diff --git a/services/usb/Android.bp b/services/usb/Android.bp
index 01feacd..4dc5423 100644
--- a/services/usb/Android.bp
+++ b/services/usb/Android.bp
@@ -29,6 +29,7 @@
"android.hardware.usb-V1.1-java",
"android.hardware.usb-V1.2-java",
"android.hardware.usb-V1.3-java",
+ "android.hardware.usb-V1-java",
"android.hardware.usb.gadget-V1.0-java",
"android.hardware.usb.gadget-V1.1-java",
"android.hardware.usb.gadget-V1.2-java",
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index ec28040..d472639 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -16,6 +16,8 @@
package com.android.server.usb;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_PORT_MISMATCH;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
import static android.hardware.usb.UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
@@ -25,6 +27,12 @@
import static android.hardware.usb.UsbPortStatus.MODE_UFP;
import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_POWER_ROLE_SOURCE;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_POWER_ROLE_SINK;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_DATA_ROLE_HOST;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_DATA_ROLE_DEVICE;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_MODE_DFP;
+import static com.android.server.usb.hal.port.UsbPortHal.HAL_MODE_UFP;
import static com.android.internal.usb.DumpUtils.writePort;
import static com.android.internal.usb.DumpUtils.writePortStatus;
@@ -38,6 +46,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
+import android.hardware.usb.IUsbOperationInternal;
import android.hardware.usb.ParcelableUsbPort;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
@@ -74,9 +83,13 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.FgThread;
+import com.android.server.usb.hal.port.RawPortInfo;
+import com.android.server.usb.hal.port.UsbPortHal;
+import com.android.server.usb.hal.port.UsbPortHalInstance;
import java.util.ArrayList;
import java.util.NoSuchElementException;
+import java.util.Objects;
/**
* Allows trusted components to control the properties of physical USB ports
@@ -109,16 +122,9 @@
// The system context.
private final Context mContext;
- // Proxy object for the usb hal daemon.
- @GuardedBy("mLock")
- private IUsb mProxy = null;
-
// Callback when the UsbPort status is changed by the kernel.
// Mostly due a command sent by the remote Usb device.
- private HALCallback mHALCallback = new HALCallback(null, this);
-
- // Cookie sent for usb hal death notification.
- private static final int USB_HAL_DEATH_COOKIE = 1000;
+ //private HALCallback mHALCallback = new HALCallback(null, this);
// Used as the key while sending the bundle to Main thread.
private static final String PORT_INFO = "port_info";
@@ -156,36 +162,23 @@
*/
private int mIsPortContaminatedNotificationId;
- private boolean mEnableUsbDataSignaling;
- protected int mCurrentUsbHalVersion;
+ private UsbPortHal mUsbPortHal;
+
+ private long mTransactionId;
public UsbPortManager(Context context) {
mContext = context;
- try {
- ServiceNotification serviceNotification = new ServiceNotification();
-
- boolean ret = IServiceManager.getService()
- .registerForNotifications("android.hardware.usb@1.0::IUsb",
- "", serviceNotification);
- if (!ret) {
- logAndPrint(Log.ERROR, null,
- "Failed to register service start notification");
- }
- } catch (RemoteException e) {
- logAndPrintException(null,
- "Failed to register service start notification", e);
- return;
- }
- connectToProxy(null);
+ mUsbPortHal = UsbPortHalInstance.getInstance(this, null);
+ logAndPrint(Log.DEBUG, null, "getInstance done");
}
public void systemReady() {
- mSystemReady = true;
- if (mProxy != null) {
+ mSystemReady = true;
+ if (mUsbPortHal != null) {
+ mUsbPortHal.systemReady();
try {
- mProxy.queryPortStatus();
- mEnableUsbDataSignaling = true;
- } catch (RemoteException e) {
+ mUsbPortHal.queryPortStatus(++mTransactionId);
+ } catch (Exception e) {
logAndPrintException(null,
"ServiceStart: Failed to query port status", e);
}
@@ -340,13 +333,9 @@
}
try {
- // Oneway call into the hal. Use the castFrom method from HIDL.
- android.hardware.usb.V1_2.IUsb proxy = android.hardware.usb.V1_2.IUsb.castFrom(mProxy);
- proxy.enableContaminantPresenceDetection(portId, enable);
- } catch (RemoteException e) {
+ mUsbPortHal.enableContaminantPresenceDetection(portId, enable, ++mTransactionId);
+ } catch (Exception e) {
logAndPrintException(pw, "Failed to set contaminant detection", e);
- } catch (ClassCastException e) {
- logAndPrintException(pw, "Method only applicable to V1.2 or above implementation", e);
}
}
@@ -355,46 +344,79 @@
*
* @param enable enable or disable USB data signaling
*/
- public boolean enableUsbDataSignal(boolean enable) {
- try {
- mEnableUsbDataSignaling = enable;
- // Call into the hal. Use the castFrom method from HIDL.
- android.hardware.usb.V1_3.IUsb proxy = android.hardware.usb.V1_3.IUsb.castFrom(mProxy);
- return proxy.enableUsbDataSignal(enable);
- } catch (RemoteException e) {
- logAndPrintException(null, "Failed to set USB data signaling", e);
- return false;
- } catch (ClassCastException e) {
- logAndPrintException(null, "Method only applicable to V1.3 or above implementation", e);
+ public boolean enableUsbData(@NonNull String portId, boolean enable, int transactionId,
+ @NonNull IUsbOperationInternal callback, IndentingPrintWriter pw) {
+ Objects.requireNonNull(callback);
+ Objects.requireNonNull(portId);
+ final PortInfo portInfo = mPorts.get(portId);
+ if (portInfo == null) {
+ logAndPrint(Log.ERROR, pw, "enableUsbData: No such port: " + portId
+ + " opId:" + transactionId);
+ try {
+ callback.onOperationComplete(USB_OPERATION_ERROR_PORT_MISMATCH);
+ } catch (RemoteException e) {
+ logAndPrintException(pw,
+ "enableUsbData: Failed to call OperationComplete. opId:"
+ + transactionId, e);
+ }
return false;
}
+
+ try {
+ try {
+ return mUsbPortHal.enableUsbData(portId, enable, transactionId, callback);
+ } catch (Exception e) {
+ logAndPrintException(pw,
+ "enableUsbData: Failed to invoke enableUsbData. opId:"
+ + transactionId , e);
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(pw,
+ "enableUsbData: Failed to call onOperationComplete. opId:"
+ + transactionId, e);
+ }
+
+ return false;
}
/**
* Get USB HAL version
*
* @param none
+ * @return {@link UsbManager#USB_HAL_RETRY} returned when hal version
+ * is yet to be determined.
*/
public int getUsbHalVersion() {
- return mCurrentUsbHalVersion;
+ if (mUsbPortHal != null) {
+ try {
+ return mUsbPortHal.getUsbHalVersion();
+ } catch (RemoteException e) {
+ return UsbManager.USB_HAL_RETRY;
+ }
+ }
+ return UsbManager.USB_HAL_RETRY;
}
- /**
- * update USB HAL version
- *
- * @param none
- */
- private void updateUsbHalVersion() {
- if (android.hardware.usb.V1_3.IUsb.castFrom(mProxy) != null) {
- mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_3;
- } else if (android.hardware.usb.V1_2.IUsb.castFrom(mProxy) != null) {
- mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_2;
- } else if (android.hardware.usb.V1_1.IUsb.castFrom(mProxy) != null) {
- mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_1;
- } else {
- mCurrentUsbHalVersion = UsbManager.USB_HAL_V1_0;
- }
- logAndPrint(Log.INFO, null, "USB HAL version: " + mCurrentUsbHalVersion);
+ private int toHalUsbDataRole(int usbDataRole) {
+ if (usbDataRole == DATA_ROLE_DEVICE)
+ return HAL_DATA_ROLE_DEVICE;
+ else
+ return HAL_DATA_ROLE_HOST;
+ }
+
+ private int toHalUsbPowerRole(int usbPowerRole) {
+ if (usbPowerRole == POWER_ROLE_SINK)
+ return HAL_POWER_ROLE_SINK;
+ else
+ return HAL_POWER_ROLE_SOURCE;
+ }
+
+ private int toHalUsbMode(int usbMode) {
+ if (usbMode == MODE_UFP)
+ return HAL_MODE_UFP;
+ else
+ return HAL_MODE_DFP;
}
public void setPortRoles(String portId, int newPowerRole, int newDataRole,
@@ -473,7 +495,7 @@
sim.currentPowerRole = newPowerRole;
sim.currentDataRole = newDataRole;
updatePortsLocked(pw, null);
- } else if (mProxy != null) {
+ } else if (mUsbPortHal != null) {
if (currentMode != newMode) {
// Changing the mode will have the side-effect of also changing
// the power and data roles but it might take some time to apply
@@ -485,44 +507,37 @@
logAndPrint(Log.ERROR, pw, "Trying to set the USB port mode: "
+ "portId=" + portId
+ ", newMode=" + UsbPort.modeToString(newMode));
- PortRole newRole = new PortRole();
- newRole.type = PortRoleType.MODE;
- newRole.role = newMode;
try {
- mProxy.switchRole(portId, newRole);
- } catch (RemoteException e) {
+ mUsbPortHal.switchMode(portId, toHalUsbMode(newMode), ++mTransactionId);
+ } catch (Exception e) {
logAndPrintException(pw, "Failed to set the USB port mode: "
+ "portId=" + portId
- + ", newMode=" + UsbPort.modeToString(newRole.role), e);
+ + ", newMode=" + UsbPort.modeToString(newMode), e);
}
} else {
// Change power and data role independently as needed.
if (currentPowerRole != newPowerRole) {
- PortRole newRole = new PortRole();
- newRole.type = PortRoleType.POWER_ROLE;
- newRole.role = newPowerRole;
try {
- mProxy.switchRole(portId, newRole);
- } catch (RemoteException e) {
+ mUsbPortHal.switchPowerRole(portId, toHalUsbPowerRole(newPowerRole),
+ ++mTransactionId);
+ } catch (Exception e) {
logAndPrintException(pw, "Failed to set the USB port power role: "
+ "portId=" + portId
+ ", newPowerRole=" + UsbPort.powerRoleToString
- (newRole.role),
+ (newPowerRole),
e);
return;
}
}
if (currentDataRole != newDataRole) {
- PortRole newRole = new PortRole();
- newRole.type = PortRoleType.DATA_ROLE;
- newRole.role = newDataRole;
try {
- mProxy.switchRole(portId, newRole);
- } catch (RemoteException e) {
+ mUsbPortHal.switchDataRole(portId, toHalUsbDataRole(newDataRole),
+ ++mTransactionId);
+ } catch (Exception e) {
logAndPrintException(pw, "Failed to set the USB port data role: "
+ "portId=" + portId
- + ", newDataRole=" + UsbPort.dataRoleToString(newRole
- .role),
+ + ", newDataRole=" + UsbPort.dataRoleToString
+ (newDataRole),
e);
}
}
@@ -531,6 +546,15 @@
}
}
+ public void updatePorts(ArrayList<RawPortInfo> newPortInfo) {
+ Message message = mHandler.obtainMessage();
+ Bundle bundle = new Bundle();
+ bundle.putParcelableArrayList(PORT_INFO, newPortInfo);
+ message.what = MSG_UPDATE_PORTS;
+ message.setData(bundle);
+ mHandler.sendMessage(message);
+ }
+
public void addSimulatedPort(String portId, int supportedModes, IndentingPrintWriter pw) {
synchronized (mLock) {
if (mSimulatedPorts.containsKey(portId)) {
@@ -662,191 +686,12 @@
portInfo.dump(dump, "usb_ports", UsbPortManagerProto.USB_PORTS);
}
- dump.write("enable_usb_data_signaling", UsbPortManagerProto.ENABLE_USB_DATA_SIGNALING,
- mEnableUsbDataSignaling);
+ dump.write("usb_hal_version", UsbPortManagerProto.HAL_VERSION, getUsbHalVersion());
}
dump.end(token);
}
- private static class HALCallback extends IUsbCallback.Stub {
- public IndentingPrintWriter pw;
- public UsbPortManager portManager;
-
- HALCallback(IndentingPrintWriter pw, UsbPortManager portManager) {
- this.pw = pw;
- this.portManager = portManager;
- }
-
- public void notifyPortStatusChange(
- ArrayList<android.hardware.usb.V1_0.PortStatus> currentPortStatus, int retval) {
- if (!portManager.mSystemReady) {
- return;
- }
-
- if (retval != Status.SUCCESS) {
- logAndPrint(Log.ERROR, pw, "port status enquiry failed");
- return;
- }
-
- ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
-
- for (android.hardware.usb.V1_0.PortStatus current : currentPortStatus) {
- RawPortInfo temp = new RawPortInfo(current.portName,
- current.supportedModes, CONTAMINANT_PROTECTION_NONE,
- current.currentMode,
- current.canChangeMode, current.currentPowerRole,
- current.canChangePowerRole,
- current.currentDataRole, current.canChangeDataRole,
- false, CONTAMINANT_PROTECTION_NONE,
- false, CONTAMINANT_DETECTION_NOT_SUPPORTED);
- newPortInfo.add(temp);
- logAndPrint(Log.INFO, pw, "ClientCallback V1_0: " + current.portName);
- }
-
- Message message = portManager.mHandler.obtainMessage();
- Bundle bundle = new Bundle();
- bundle.putParcelableArrayList(PORT_INFO, newPortInfo);
- message.what = MSG_UPDATE_PORTS;
- message.setData(bundle);
- portManager.mHandler.sendMessage(message);
- }
-
-
- public void notifyPortStatusChange_1_1(ArrayList<PortStatus_1_1> currentPortStatus,
- int retval) {
- if (!portManager.mSystemReady) {
- return;
- }
-
- if (retval != Status.SUCCESS) {
- logAndPrint(Log.ERROR, pw, "port status enquiry failed");
- return;
- }
-
- ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
-
- int numStatus = currentPortStatus.size();
- for (int i = 0; i < numStatus; i++) {
- PortStatus_1_1 current = currentPortStatus.get(i);
- RawPortInfo temp = new RawPortInfo(current.status.portName,
- current.supportedModes, CONTAMINANT_PROTECTION_NONE,
- current.currentMode,
- current.status.canChangeMode, current.status.currentPowerRole,
- current.status.canChangePowerRole,
- current.status.currentDataRole, current.status.canChangeDataRole,
- false, CONTAMINANT_PROTECTION_NONE,
- false, CONTAMINANT_DETECTION_NOT_SUPPORTED);
- newPortInfo.add(temp);
- logAndPrint(Log.INFO, pw, "ClientCallback V1_1: " + current.status.portName);
- }
-
- Message message = portManager.mHandler.obtainMessage();
- Bundle bundle = new Bundle();
- bundle.putParcelableArrayList(PORT_INFO, newPortInfo);
- message.what = MSG_UPDATE_PORTS;
- message.setData(bundle);
- portManager.mHandler.sendMessage(message);
- }
-
- public void notifyPortStatusChange_1_2(
- ArrayList<PortStatus> currentPortStatus, int retval) {
- if (!portManager.mSystemReady) {
- return;
- }
-
- if (retval != Status.SUCCESS) {
- logAndPrint(Log.ERROR, pw, "port status enquiry failed");
- return;
- }
-
- ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
-
- int numStatus = currentPortStatus.size();
- for (int i = 0; i < numStatus; i++) {
- PortStatus current = currentPortStatus.get(i);
- RawPortInfo temp = new RawPortInfo(current.status_1_1.status.portName,
- current.status_1_1.supportedModes,
- current.supportedContaminantProtectionModes,
- current.status_1_1.currentMode,
- current.status_1_1.status.canChangeMode,
- current.status_1_1.status.currentPowerRole,
- current.status_1_1.status.canChangePowerRole,
- current.status_1_1.status.currentDataRole,
- current.status_1_1.status.canChangeDataRole,
- current.supportsEnableContaminantPresenceProtection,
- current.contaminantProtectionStatus,
- current.supportsEnableContaminantPresenceDetection,
- current.contaminantDetectionStatus);
- newPortInfo.add(temp);
- logAndPrint(Log.INFO, pw, "ClientCallback V1_2: "
- + current.status_1_1.status.portName);
- }
-
- Message message = portManager.mHandler.obtainMessage();
- Bundle bundle = new Bundle();
- bundle.putParcelableArrayList(PORT_INFO, newPortInfo);
- message.what = MSG_UPDATE_PORTS;
- message.setData(bundle);
- portManager.mHandler.sendMessage(message);
- }
-
- public void notifyRoleSwitchStatus(String portName, PortRole role, int retval) {
- if (retval == Status.SUCCESS) {
- logAndPrint(Log.INFO, pw, portName + " role switch successful");
- } else {
- logAndPrint(Log.ERROR, pw, portName + " role switch failed");
- }
- }
- }
-
- final class DeathRecipient implements HwBinder.DeathRecipient {
- public IndentingPrintWriter pw;
-
- DeathRecipient(IndentingPrintWriter pw) {
- this.pw = pw;
- }
-
- @Override
- public void serviceDied(long cookie) {
- if (cookie == USB_HAL_DEATH_COOKIE) {
- logAndPrint(Log.ERROR, pw, "Usb hal service died cookie: " + cookie);
- synchronized (mLock) {
- mProxy = null;
- }
- }
- }
- }
-
- final class ServiceNotification extends IServiceNotification.Stub {
- @Override
- public void onRegistration(String fqName, String name, boolean preexisting) {
- logAndPrint(Log.INFO, null, "Usb hal service started " + fqName + " " + name);
- connectToProxy(null);
- }
- }
-
- private void connectToProxy(IndentingPrintWriter pw) {
- synchronized (mLock) {
- if (mProxy != null) {
- return;
- }
-
- try {
- mProxy = IUsb.getService();
- mProxy.linkToDeath(new DeathRecipient(pw), USB_HAL_DEATH_COOKIE);
- mProxy.setCallback(mHALCallback);
- mProxy.queryPortStatus();
- updateUsbHalVersion();
- } catch (NoSuchElementException e) {
- logAndPrintException(pw, "connectToProxy: usb hal service not found."
- + " Did the service fail to start?", e);
- } catch (RemoteException e) {
- logAndPrintException(pw, "connectToProxy: usb hal service not responding", e);
- }
- }
- }
-
/**
* Simulated ports directly add the new roles to mSimulatedPorts before calling.
* USB hal callback populates and sends the newPortInfo.
@@ -869,7 +714,8 @@
portInfo.supportsEnableContaminantPresenceProtection,
portInfo.contaminantProtectionStatus,
portInfo.supportsEnableContaminantPresenceDetection,
- portInfo.contaminantDetectionStatus, pw);
+ portInfo.contaminantDetectionStatus,
+ portInfo.usbDataEnabled, pw);
}
} else {
for (RawPortInfo currentPortInfo : newPortInfo) {
@@ -881,7 +727,8 @@
currentPortInfo.supportsEnableContaminantPresenceProtection,
currentPortInfo.contaminantProtectionStatus,
currentPortInfo.supportsEnableContaminantPresenceDetection,
- currentPortInfo.contaminantDetectionStatus, pw);
+ currentPortInfo.contaminantDetectionStatus,
+ currentPortInfo.usbDataEnabled, pw);
}
}
@@ -917,6 +764,7 @@
int contaminantProtectionStatus,
boolean supportsEnableContaminantPresenceDetection,
int contaminantDetectionStatus,
+ boolean usbDataEnabled,
IndentingPrintWriter pw) {
// Only allow mode switch capability for dual role ports.
// Validate that the current mode matches the supported modes we expect.
@@ -975,7 +823,7 @@
currentPowerRole, canChangePowerRole,
currentDataRole, canChangeDataRole,
supportedRoleCombinations, contaminantProtectionStatus,
- contaminantDetectionStatus);
+ contaminantDetectionStatus, usbDataEnabled);
mPorts.put(portId, portInfo);
} else {
// Validate that ports aren't changing definition out from under us.
@@ -1012,7 +860,7 @@
currentPowerRole, canChangePowerRole,
currentDataRole, canChangeDataRole,
supportedRoleCombinations, contaminantProtectionStatus,
- contaminantDetectionStatus)) {
+ contaminantDetectionStatus, usbDataEnabled)) {
portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED;
} else {
portInfo.mDisposition = PortInfo.DISPOSITION_READY;
@@ -1141,14 +989,14 @@
}
}
- private static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) {
+ public static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) {
Slog.println(priority, TAG, msg);
if (pw != null) {
pw.println(msg);
}
}
- private static void logAndPrintException(IndentingPrintWriter pw, String msg, Exception e) {
+ public static void logAndPrintException(IndentingPrintWriter pw, String msg, Exception e) {
Slog.e(TAG, msg, e);
if (pw != null) {
pw.println(msg + e);
@@ -1179,7 +1027,7 @@
/**
* Describes a USB port.
*/
- private static final class PortInfo {
+ public static final class PortInfo {
public static final int DISPOSITION_ADDED = 0;
public static final int DISPOSITION_CHANGED = 1;
public static final int DISPOSITION_READY = 2;
@@ -1224,7 +1072,7 @@
!= supportedRoleCombinations) {
mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
supportedRoleCombinations, UsbPortStatus.CONTAMINANT_PROTECTION_NONE,
- UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED);
+ UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED, true);
dispositionChanged = true;
}
@@ -1243,7 +1091,7 @@
int currentPowerRole, boolean canChangePowerRole,
int currentDataRole, boolean canChangeDataRole,
int supportedRoleCombinations, int contaminantProtectionStatus,
- int contaminantDetectionStatus) {
+ int contaminantDetectionStatus, boolean usbDataEnabled) {
boolean dispositionChanged = false;
mCanChangeMode = canChangeMode;
@@ -1258,10 +1106,12 @@
|| mUsbPortStatus.getContaminantProtectionStatus()
!= contaminantProtectionStatus
|| mUsbPortStatus.getContaminantDetectionStatus()
- != contaminantDetectionStatus) {
+ != contaminantDetectionStatus
+ || mUsbPortStatus.getUsbDataStatus()
+ != usbDataEnabled){
mUsbPortStatus = new UsbPortStatus(currentMode, currentPowerRole, currentDataRole,
supportedRoleCombinations, contaminantProtectionStatus,
- contaminantDetectionStatus);
+ contaminantDetectionStatus, usbDataEnabled);
dispositionChanged = true;
}
@@ -1290,7 +1140,6 @@
UsbPortInfoProto.CONNECTED_AT_MILLIS, mConnectedAtMillis);
dump.write("last_connect_duration_millis",
UsbPortInfoProto.LAST_CONNECT_DURATION_MILLIS, mLastConnectDurationMillis);
-
dump.end(token);
}
@@ -1304,115 +1153,4 @@
+ ", lastConnectDurationMillis=" + mLastConnectDurationMillis;
}
}
-
- /**
- * Used for storing the raw data from the kernel
- * Values of the member variables mocked directly incase of emulation.
- */
- private static final class RawPortInfo implements Parcelable {
- public final String portId;
- public final int supportedModes;
- public final int supportedContaminantProtectionModes;
- public int currentMode;
- public boolean canChangeMode;
- public int currentPowerRole;
- public boolean canChangePowerRole;
- public int currentDataRole;
- public boolean canChangeDataRole;
- public boolean supportsEnableContaminantPresenceProtection;
- public int contaminantProtectionStatus;
- public boolean supportsEnableContaminantPresenceDetection;
- public int contaminantDetectionStatus;
-
- RawPortInfo(String portId, int supportedModes) {
- this.portId = portId;
- this.supportedModes = supportedModes;
- this.supportedContaminantProtectionModes = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
- this.supportsEnableContaminantPresenceProtection = false;
- this.contaminantProtectionStatus = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
- this.supportsEnableContaminantPresenceDetection = false;
- this.contaminantDetectionStatus = UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
- }
-
- RawPortInfo(String portId, int supportedModes, int supportedContaminantProtectionModes,
- int currentMode, boolean canChangeMode,
- int currentPowerRole, boolean canChangePowerRole,
- int currentDataRole, boolean canChangeDataRole,
- boolean supportsEnableContaminantPresenceProtection,
- int contaminantProtectionStatus,
- boolean supportsEnableContaminantPresenceDetection,
- int contaminantDetectionStatus) {
- this.portId = portId;
- this.supportedModes = supportedModes;
- this.supportedContaminantProtectionModes = supportedContaminantProtectionModes;
- this.currentMode = currentMode;
- this.canChangeMode = canChangeMode;
- this.currentPowerRole = currentPowerRole;
- this.canChangePowerRole = canChangePowerRole;
- this.currentDataRole = currentDataRole;
- this.canChangeDataRole = canChangeDataRole;
- this.supportsEnableContaminantPresenceProtection =
- supportsEnableContaminantPresenceProtection;
- this.contaminantProtectionStatus = contaminantProtectionStatus;
- this.supportsEnableContaminantPresenceDetection =
- supportsEnableContaminantPresenceDetection;
- this.contaminantDetectionStatus = contaminantDetectionStatus;
- }
-
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(portId);
- dest.writeInt(supportedModes);
- dest.writeInt(supportedContaminantProtectionModes);
- dest.writeInt(currentMode);
- dest.writeByte((byte) (canChangeMode ? 1 : 0));
- dest.writeInt(currentPowerRole);
- dest.writeByte((byte) (canChangePowerRole ? 1 : 0));
- dest.writeInt(currentDataRole);
- dest.writeByte((byte) (canChangeDataRole ? 1 : 0));
- dest.writeBoolean(supportsEnableContaminantPresenceProtection);
- dest.writeInt(contaminantProtectionStatus);
- dest.writeBoolean(supportsEnableContaminantPresenceDetection);
- dest.writeInt(contaminantDetectionStatus);
- }
-
- public static final Parcelable.Creator<RawPortInfo> CREATOR =
- new Parcelable.Creator<RawPortInfo>() {
- @Override
- public RawPortInfo createFromParcel(Parcel in) {
- String id = in.readString();
- int supportedModes = in.readInt();
- int supportedContaminantProtectionModes = in.readInt();
- int currentMode = in.readInt();
- boolean canChangeMode = in.readByte() != 0;
- int currentPowerRole = in.readInt();
- boolean canChangePowerRole = in.readByte() != 0;
- int currentDataRole = in.readInt();
- boolean canChangeDataRole = in.readByte() != 0;
- boolean supportsEnableContaminantPresenceProtection = in.readBoolean();
- int contaminantProtectionStatus = in.readInt();
- boolean supportsEnableContaminantPresenceDetection = in.readBoolean();
- int contaminantDetectionStatus = in.readInt();
- return new RawPortInfo(id, supportedModes,
- supportedContaminantProtectionModes, currentMode, canChangeMode,
- currentPowerRole, canChangePowerRole,
- currentDataRole, canChangeDataRole,
- supportsEnableContaminantPresenceProtection,
- contaminantProtectionStatus,
- supportsEnableContaminantPresenceDetection,
- contaminantDetectionStatus);
- }
-
- @Override
- public RawPortInfo[] newArray(int size) {
- return new RawPortInfo[size];
- }
- };
- }
}
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 3d3538d..28227fc 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -16,6 +16,7 @@
package com.android.server.usb;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
import static android.hardware.usb.UsbPortStatus.MODE_DFP;
@@ -35,6 +36,7 @@
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.hardware.usb.IUsbManager;
+import android.hardware.usb.IUsbOperationInternal;
import android.hardware.usb.ParcelableUsbPort;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
@@ -44,6 +46,7 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.usb.UsbServiceDumpProto;
@@ -762,19 +765,30 @@
}
@Override
- public boolean enableUsbDataSignal(boolean enable) {
+ public boolean enableUsbData(String portId, boolean enable, int operationId,
+ IUsbOperationInternal callback) {
+ Objects.requireNonNull(portId, "enableUsbData: portId must not be null. opId:"
+ + operationId);
+ Objects.requireNonNull(callback, "enableUsbData: callback must not be null. opId:"
+ + operationId);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
-
final long ident = Binder.clearCallingIdentity();
+ boolean wait;
try {
if (mPortManager != null) {
- return mPortManager.enableUsbDataSignal(enable);
+ wait = mPortManager.enableUsbData(portId, enable, operationId, callback, null);
} else {
- return false;
+ wait = false;
+ try {
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "enableUsbData: Failed to call onOperationComplete", e);
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
}
+ return wait;
}
@Override
diff --git a/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java b/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java
new file mode 100644
index 0000000..9c6cbbd
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/RawPortInfo.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.hal.port;
+
+import android.hardware.usb.UsbPortStatus;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Used for storing the raw data from the HAL.
+ * Values of the member variables mocked directly in case of emulation.
+ */
+public final class RawPortInfo implements Parcelable {
+ public final String portId;
+ public final int supportedModes;
+ public final int supportedContaminantProtectionModes;
+ public int currentMode;
+ public boolean canChangeMode;
+ public int currentPowerRole;
+ public boolean canChangePowerRole;
+ public int currentDataRole;
+ public boolean canChangeDataRole;
+ public boolean supportsEnableContaminantPresenceProtection;
+ public int contaminantProtectionStatus;
+ public boolean supportsEnableContaminantPresenceDetection;
+ public int contaminantDetectionStatus;
+ public boolean usbDataEnabled;
+
+ public RawPortInfo(String portId, int supportedModes) {
+ this.portId = portId;
+ this.supportedModes = supportedModes;
+ this.supportedContaminantProtectionModes = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+ this.supportsEnableContaminantPresenceProtection = false;
+ this.contaminantProtectionStatus = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+ this.supportsEnableContaminantPresenceDetection = false;
+ this.contaminantDetectionStatus = UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
+ this.usbDataEnabled = true;
+ }
+
+ public RawPortInfo(String portId, int supportedModes, int supportedContaminantProtectionModes,
+ int currentMode, boolean canChangeMode,
+ int currentPowerRole, boolean canChangePowerRole,
+ int currentDataRole, boolean canChangeDataRole,
+ boolean supportsEnableContaminantPresenceProtection,
+ int contaminantProtectionStatus,
+ boolean supportsEnableContaminantPresenceDetection,
+ int contaminantDetectionStatus,
+ boolean usbDataEnabled) {
+ this.portId = portId;
+ this.supportedModes = supportedModes;
+ this.supportedContaminantProtectionModes = supportedContaminantProtectionModes;
+ this.currentMode = currentMode;
+ this.canChangeMode = canChangeMode;
+ this.currentPowerRole = currentPowerRole;
+ this.canChangePowerRole = canChangePowerRole;
+ this.currentDataRole = currentDataRole;
+ this.canChangeDataRole = canChangeDataRole;
+ this.supportsEnableContaminantPresenceProtection =
+ supportsEnableContaminantPresenceProtection;
+ this.contaminantProtectionStatus = contaminantProtectionStatus;
+ this.supportsEnableContaminantPresenceDetection =
+ supportsEnableContaminantPresenceDetection;
+ this.contaminantDetectionStatus = contaminantDetectionStatus;
+ this.usbDataEnabled = usbDataEnabled;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(portId);
+ dest.writeInt(supportedModes);
+ dest.writeInt(supportedContaminantProtectionModes);
+ dest.writeInt(currentMode);
+ dest.writeByte((byte) (canChangeMode ? 1 : 0));
+ dest.writeInt(currentPowerRole);
+ dest.writeByte((byte) (canChangePowerRole ? 1 : 0));
+ dest.writeInt(currentDataRole);
+ dest.writeByte((byte) (canChangeDataRole ? 1 : 0));
+ dest.writeBoolean(supportsEnableContaminantPresenceProtection);
+ dest.writeInt(contaminantProtectionStatus);
+ dest.writeBoolean(supportsEnableContaminantPresenceDetection);
+ dest.writeInt(contaminantDetectionStatus);
+ dest.writeBoolean(usbDataEnabled);
+ }
+
+ public static final Parcelable.Creator<RawPortInfo> CREATOR =
+ new Parcelable.Creator<RawPortInfo>() {
+ @Override
+ public RawPortInfo createFromParcel(Parcel in) {
+ String id = in.readString();
+ int supportedModes = in.readInt();
+ int supportedContaminantProtectionModes = in.readInt();
+ int currentMode = in.readInt();
+ boolean canChangeMode = in.readByte() != 0;
+ int currentPowerRole = in.readInt();
+ boolean canChangePowerRole = in.readByte() != 0;
+ int currentDataRole = in.readInt();
+ boolean canChangeDataRole = in.readByte() != 0;
+ boolean supportsEnableContaminantPresenceProtection = in.readBoolean();
+ int contaminantProtectionStatus = in.readInt();
+ boolean supportsEnableContaminantPresenceDetection = in.readBoolean();
+ int contaminantDetectionStatus = in.readInt();
+ boolean usbDataEnabled = in.readBoolean();
+ return new RawPortInfo(id, supportedModes,
+ supportedContaminantProtectionModes, currentMode, canChangeMode,
+ currentPowerRole, canChangePowerRole,
+ currentDataRole, canChangeDataRole,
+ supportsEnableContaminantPresenceProtection,
+ contaminantProtectionStatus,
+ supportsEnableContaminantPresenceDetection,
+ contaminantDetectionStatus, usbDataEnabled);
+ }
+
+ @Override
+ public RawPortInfo[] newArray(int size) {
+ return new RawPortInfo[size];
+ }
+ };
+}
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
new file mode 100644
index 0000000..1efcd9c
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.hal.port;
+
+import static android.hardware.usb.UsbManager.USB_HAL_V2_0;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_SUCCESS;
+
+import static com.android.server.usb.UsbPortManager.logAndPrint;
+import static com.android.server.usb.UsbPortManager.logAndPrintException;
+
+import android.annotation.Nullable;
+import android.hardware.usb.ContaminantProtectionStatus;
+import android.hardware.usb.IUsb;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbManager.UsbHalVersion;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.UsbPortStatus;
+import android.hardware.usb.PortMode;
+import android.hardware.usb.Status;
+import android.hardware.usb.IUsbCallback;
+import android.hardware.usb.PortRole;
+import android.hardware.usb.PortStatus;
+import android.os.ServiceManager;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.LongSparseArray;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.usb.UsbPortManager;
+import com.android.server.usb.hal.port.RawPortInfo;
+
+import java.util.ArrayList;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+
+/**
+ * Implements the methods to interact with AIDL USB HAL.
+ */
+public final class UsbPortAidl implements UsbPortHal {
+ private static final String TAG = UsbPortAidl.class.getSimpleName();
+ private static final String USB_AIDL_SERVICE =
+ "android.hardware.usb.IUsb/default";
+ private static final LongSparseArray<IUsbOperationInternal>
+ sCallbacks = new LongSparseArray<>();
+ // Proxy object for the usb hal daemon.
+ @GuardedBy("mLock")
+ private IUsb mProxy;
+ private UsbPortManager mPortManager;
+ public IndentingPrintWriter mPw;
+ // Mutex for all mutable shared state.
+ private final Object mLock = new Object();
+ // Callback when the UsbPort status is changed by the kernel.
+ private HALCallback mHALCallback;
+ private IBinder mBinder;
+ private boolean mSystemReady;
+ private long mTransactionId;
+
+ public @UsbHalVersion int getUsbHalVersion() throws RemoteException {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ throw new RemoteException("IUsb not initialized yet");
+ }
+ }
+ logAndPrint(Log.INFO, null, "USB HAL AIDL version: USB_HAL_V2_0");
+ return USB_HAL_V2_0;
+ }
+
+ @Override
+ public void systemReady() {
+ mSystemReady = true;
+ }
+
+ public void serviceDied() {
+ logAndPrint(Log.ERROR, mPw, "Usb AIDL hal service died");
+ synchronized (mLock) {
+ mProxy = null;
+ }
+ connectToProxy(null);
+ }
+
+ private void connectToProxy(IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ if (mProxy != null) {
+ return;
+ }
+
+ try {
+ mBinder = ServiceManager.waitForService(USB_AIDL_SERVICE);
+ mProxy = IUsb.Stub.asInterface(mBinder);
+ mBinder.linkToDeath(this::serviceDied, 0);
+ mProxy.setCallback(mHALCallback);
+ mProxy.queryPortStatus(++mTransactionId);
+ } catch (NoSuchElementException e) {
+ logAndPrintException(pw, "connectToProxy: usb hal service not found."
+ + " Did the service fail to start?", e);
+ } catch (RemoteException e) {
+ logAndPrintException(pw, "connectToProxy: usb hal service not responding", e);
+ }
+ }
+ }
+
+ static boolean isServicePresent(IndentingPrintWriter pw) {
+ try {
+ return ServiceManager.isDeclared(USB_AIDL_SERVICE);
+ } catch (NoSuchElementException e) {
+ logAndPrintException(pw, "connectToProxy: usb Aidl hal service not found.", e);
+ }
+
+ return false;
+ }
+
+ public UsbPortAidl(UsbPortManager portManager, IndentingPrintWriter pw) {
+ mPortManager = Objects.requireNonNull(portManager);
+ mPw = pw;
+ mHALCallback = new HALCallback(null, mPortManager, this);
+ connectToProxy(mPw);
+ }
+
+ @Override
+ public void enableContaminantPresenceDetection(String portName, boolean enable,
+ long operationID) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID: "
+ + operationID);
+ return;
+ }
+
+ try {
+ // Oneway call into the hal. Use the castFrom method from HIDL.
+ mProxy.enableContaminantPresenceDetection(portName, enable, operationID);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set contaminant detection. opID:"
+ + operationID, e);
+ }
+ }
+ }
+
+ @Override
+ public void queryPortStatus(long operationID) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:"
+ + operationID);
+ return;
+ }
+
+ try {
+ mProxy.queryPortStatus(operationID);
+ } catch (RemoteException e) {
+ logAndPrintException(null, "ServiceStart: Failed to query port status. opID:"
+ + operationID, e);
+ }
+ }
+ }
+
+ @Override
+ public void switchMode(String portId, @HalUsbPortMode int newMode, long operationID) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:"
+ + operationID);
+ return;
+ }
+
+ PortRole newRole = new PortRole();
+ newRole.setMode((byte)newMode);
+ try {
+ mProxy.switchRole(portId, newRole, operationID);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set the USB port mode: "
+ + "portId=" + portId
+ + ", newMode=" + UsbPort.modeToString(newMode)
+ + "opID:" + operationID, e);
+ }
+ }
+ }
+
+ @Override
+ public void switchPowerRole(String portId, @HalUsbPowerRole int newPowerRole,
+ long operationID) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:"
+ + operationID);
+ return;
+ }
+
+ PortRole newRole = new PortRole();
+ newRole.setPowerRole((byte)newPowerRole);
+ try {
+ mProxy.switchRole(portId, newRole, operationID);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set the USB power role: portId=" + portId
+ + ", newPowerRole=" + UsbPort.powerRoleToString(newPowerRole)
+ + "opID:" + operationID, e);
+ }
+ }
+ }
+
+ @Override
+ public void switchDataRole(String portId, @HalUsbDataRole int newDataRole, long operationID) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry ! opID:"
+ + operationID);
+ return;
+ }
+
+ PortRole newRole = new PortRole();
+ newRole.setDataRole((byte)newDataRole);
+ try {
+ mProxy.switchRole(portId, newRole, operationID);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set the USB data role: portId=" + portId
+ + ", newDataRole=" + UsbPort.dataRoleToString(newDataRole)
+ + "opID:" + operationID, e);
+ }
+ }
+ }
+
+ @Override
+ public boolean enableUsbData(String portName, boolean enable, long operationID,
+ IUsbOperationInternal callback) {
+ Objects.requireNonNull(portName);
+ Objects.requireNonNull(callback);
+ long key = operationID;
+ synchronized (mLock) {
+ try {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw,
+ "enableUsbData: Proxy is null. Retry !opID:"
+ + operationID);
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ return false;
+ }
+ while (sCallbacks.get(key) != null) {
+ key = ThreadLocalRandom.current().nextInt();
+ }
+ if (key != operationID) {
+ logAndPrint(Log.INFO, mPw, "enableUsbData: operationID exists ! opID:"
+ + operationID + " key:" + key);
+ }
+ try {
+ sCallbacks.put(key, callback);
+ mProxy.enableUsbData(portName, enable, key);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw,
+ "enableUsbData: Failed to invoke enableUsbData: portID="
+ + portName + "opID:" + operationID, e);
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ sCallbacks.remove(key);
+ return false;
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(mPw,
+ "enableUsbData: Failed to call onOperationComplete portID="
+ + portName + "opID:" + operationID, e);
+ sCallbacks.remove(key);
+ return false;
+ }
+ return true;
+ }
+ }
+
+ private static class HALCallback extends IUsbCallback.Stub {
+ public IndentingPrintWriter mPw;
+ public UsbPortManager mPortManager;
+ public UsbPortAidl mUsbPortAidl;
+
+ HALCallback(IndentingPrintWriter pw, UsbPortManager portManager, UsbPortAidl usbPortAidl) {
+ this.mPw = pw;
+ this.mPortManager = portManager;
+ this.mUsbPortAidl = usbPortAidl;
+ }
+
+ /**
+ * Converts from AIDL defined mode constants to UsbPortStatus constants.
+ * AIDL does not gracefully support bitfield when combined with enums.
+ */
+ private int toPortMode(byte aidlPortMode) {
+ switch (aidlPortMode) {
+ case PortMode.NONE:
+ return UsbPortStatus.MODE_NONE;
+ case PortMode.UFP:
+ return UsbPortStatus.MODE_UFP;
+ case PortMode.DFP:
+ return UsbPortStatus.MODE_DFP;
+ case PortMode.DRP:
+ return UsbPortStatus.MODE_DUAL;
+ case PortMode.AUDIO_ACCESSORY:
+ return UsbPortStatus.MODE_AUDIO_ACCESSORY;
+ case PortMode.DEBUG_ACCESSORY:
+ return UsbPortStatus.MODE_DEBUG_ACCESSORY;
+ default:
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, "Unrecognized aidlPortMode:"
+ + aidlPortMode);
+ return UsbPortStatus.MODE_NONE;
+ }
+ }
+
+ private int toSupportedModes(byte[] aidlPortModes) {
+ int supportedModes = UsbPortStatus.MODE_NONE;
+
+ for (byte aidlPortMode : aidlPortModes) {
+ supportedModes |= toPortMode(aidlPortMode);
+ }
+
+ return supportedModes;
+ }
+
+ /**
+ * Converts from AIDL defined contaminant protection constants to UsbPortStatus constants.
+ * AIDL does not gracefully support bitfield when combined with enums.
+ * Common to both ContaminantProtectionMode and ContaminantProtectionStatus.
+ */
+ private int toContaminantProtectionStatus(byte aidlContaminantProtection) {
+ switch (aidlContaminantProtection) {
+ case ContaminantProtectionStatus.NONE:
+ return UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+ case ContaminantProtectionStatus.FORCE_SINK:
+ return UsbPortStatus.CONTAMINANT_PROTECTION_SINK;
+ case ContaminantProtectionStatus.FORCE_SOURCE:
+ return UsbPortStatus.CONTAMINANT_PROTECTION_SOURCE;
+ case ContaminantProtectionStatus.FORCE_DISABLE:
+ return UsbPortStatus.CONTAMINANT_PROTECTION_FORCE_DISABLE;
+ case ContaminantProtectionStatus.DISABLED:
+ return UsbPortStatus.CONTAMINANT_PROTECTION_DISABLED;
+ default:
+ UsbPortManager.logAndPrint(Log.ERROR, mPw,
+ "Unrecognized aidlContaminantProtection:"
+ + aidlContaminantProtection);
+ return UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+ }
+ }
+
+ private int toSupportedContaminantProtectionModes(byte[] aidlModes) {
+ int supportedContaminantProtectionModes = UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+
+ for (byte aidlMode : aidlModes) {
+ supportedContaminantProtectionModes |= toContaminantProtectionStatus(aidlMode);
+ }
+
+ return supportedContaminantProtectionModes;
+ }
+
+ @Override
+ public void notifyPortStatusChange(
+ android.hardware.usb.PortStatus[] currentPortStatus, int retval) {
+ if (!mUsbPortAidl.mSystemReady) {
+ return;
+ }
+
+ if (retval != Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed");
+ return;
+ }
+
+ ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
+
+ int numStatus = currentPortStatus.length;
+ for (int i = 0; i < numStatus; i++) {
+ PortStatus current = currentPortStatus[i];
+ RawPortInfo temp = new RawPortInfo(current.portName,
+ toSupportedModes(current.supportedModes),
+ toSupportedContaminantProtectionModes(current
+ .supportedContaminantProtectionModes),
+ toPortMode(current.currentMode),
+ current.canChangeMode,
+ current.currentPowerRole,
+ current.canChangePowerRole,
+ current.currentDataRole,
+ current.canChangeDataRole,
+ current.supportsEnableContaminantPresenceProtection,
+ toContaminantProtectionStatus(current.contaminantProtectionStatus),
+ current.supportsEnableContaminantPresenceDetection,
+ current.contaminantDetectionStatus,
+ current.usbDataEnabled);
+ newPortInfo.add(temp);
+ UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback AIDL V1: "
+ + current.portName);
+ }
+ mPortManager.updatePorts(newPortInfo);
+ }
+
+ @Override
+ public void notifyRoleSwitchStatus(String portName, PortRole role, int retval,
+ long operationID) {
+ if (retval == Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.INFO, mPw, portName
+ + " role switch successful. opID:"
+ + operationID);
+ } else {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, portName + " role switch failed. err:"
+ + retval
+ + "opID:" + operationID);
+ }
+ }
+
+ @Override
+ public void notifyQueryPortStatus(String portName, int retval, long operationID) {
+ if (retval == Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.INFO, mPw, portName + ": opID:"
+ + operationID + " successful");
+ } else {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, portName + ": opID:"
+ + operationID + " failed. err:" + retval);
+ }
+ }
+
+ @Override
+ public void notifyEnableUsbDataStatus(String portName, boolean enable, int retval,
+ long operationID) {
+ if (retval == Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.INFO, mPw, "notifyEnableUsbDataStatus:"
+ + portName + ": opID:"
+ + operationID + " enable:" + enable);
+ } else {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, portName
+ + "notifyEnableUsbDataStatus: opID:"
+ + operationID + " failed. err:" + retval);
+ }
+ try {
+ sCallbacks.get(operationID).onOperationComplete(retval == Status.SUCCESS
+ ? USB_OPERATION_SUCCESS
+ : USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw,
+ "notifyEnableUsbDataStatus: Failed to call onOperationComplete",
+ e);
+ }
+ }
+
+ @Override
+ public void notifyContaminantEnabledStatus(String portName, boolean enable, int retval,
+ long operationID) {
+ if (retval == Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.INFO, mPw, "notifyContaminantEnabledStatus:"
+ + portName + ": opID:"
+ + operationID + " enable:" + enable);
+ } else {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, portName
+ + "notifyContaminantEnabledStatus: opID:"
+ + operationID + " failed. err:" + retval);
+ }
+ }
+
+ @Override
+ public String getInterfaceHash() {
+ return IUsbCallback.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return IUsbCallback.VERSION;
+ }
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHal.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHal.java
new file mode 100644
index 0000000..e7f9bc2
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHal.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.hal.port;
+
+import android.annotation.IntDef;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbManager.UsbHalVersion;
+import android.os.RemoteException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.String;
+
+/**
+ * @hide
+ */
+public interface UsbPortHal {
+ /**
+ * Power role: This USB port can act as a source (provide power).
+ * @hide
+ */
+ public static final int HAL_POWER_ROLE_SOURCE = 1;
+
+ /**
+ * Power role: This USB port can act as a sink (receive power).
+ * @hide
+ */
+ public static final int HAL_POWER_ROLE_SINK = 2;
+
+ @IntDef(prefix = { "HAL_POWER_ROLE_" }, value = {
+ HAL_POWER_ROLE_SOURCE,
+ HAL_POWER_ROLE_SINK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface HalUsbPowerRole{}
+
+ /**
+ * Data role: This USB port can act as a host (access data services).
+ * @hide
+ */
+ public static final int HAL_DATA_ROLE_HOST = 1;
+
+ /**
+ * Data role: This USB port can act as a device (offer data services).
+ * @hide
+ */
+ public static final int HAL_DATA_ROLE_DEVICE = 2;
+
+ @IntDef(prefix = { "HAL_DATA_ROLE_" }, value = {
+ HAL_DATA_ROLE_HOST,
+ HAL_DATA_ROLE_DEVICE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface HalUsbDataRole{}
+
+ /**
+ * This USB port can act as a downstream facing port (host).
+ *
+ * @hide
+ */
+ public static final int HAL_MODE_DFP = 1;
+
+ /**
+ * This USB port can act as an upstream facing port (device).
+ *
+ * @hide
+ */
+ public static final int HAL_MODE_UFP = 2;
+ @IntDef(prefix = { "HAL_MODE_" }, value = {
+ HAL_MODE_DFP,
+ HAL_MODE_UFP,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface HalUsbPortMode{}
+
+ /**
+ * UsbPortManager would call this when the system is done booting.
+ */
+ public void systemReady();
+
+ /**
+ * Invoked to enable/disable contaminant presence detection on the USB port.
+ *
+ * @param portName Port Identifier.
+ * @param enable Enable contaminant presence detection when true.
+ * Disable when false.
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ */
+ public void enableContaminantPresenceDetection(String portName, boolean enable,
+ long transactionId);
+
+ /**
+ * Invoked to query port status of all the ports.
+ *
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ */
+ public void queryPortStatus(long transactionId);
+
+ /**
+ * Invoked to switch USB port mode.
+ *
+ * @param portName Port Identifier.
+ * @param mode New mode that the port is switching into.
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ */
+ public void switchMode(String portName, @HalUsbPortMode int mode, long transactionId);
+
+ /**
+ * Invoked to switch USB port power role.
+ *
+ * @param portName Port Identifier.
+ * @param powerRole New power role that the port is switching into.
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ */
+ public void switchPowerRole(String portName, @HalUsbPowerRole int powerRole,
+ long transactionId);
+
+ /**
+ * Invoked to switch USB port data role.
+ *
+ * @param portName Port Identifier.
+ * @param dataRole New data role that the port is switching into.
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ */
+ public void switchDataRole(String portName, @HalUsbDataRole int dataRole, long transactionId);
+
+ /**
+ * Invoked to query the version of current hal implementation.
+ */
+ public @UsbHalVersion int getUsbHalVersion() throws RemoteException;
+
+ /**
+ * Invoked to enable/disable UsbData on the specified port.
+ *
+ * @param portName Port Identifier.
+ * @param enable Enable USB data when true.
+ * Disable when false.
+ * @param transactionId Used for tracking the current request and is passed down to the HAL
+ * implementation as needed.
+ * @param callback callback object to be invoked to invoke the status of the operation upon
+ * completion.
+ * @param callback callback object to be invoked when the operation is complete.
+ * @return True when the operation is asynchronous. The caller of
+ * {@link UsbOperationCallbackInternal} must therefore call
+ * {@link UsbOperationCallbackInternal#waitForOperationComplete} for processing
+ * the result.
+ * False when the operation is synchronous. Caller can proceed reading the result
+ * through {@link UsbOperationCallbackInternal#getStatus}
+ */
+ public boolean enableUsbData(String portName, boolean enable, long transactionId,
+ IUsbOperationInternal callback);
+}
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
new file mode 100644
index 0000000..41f9fae
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.hal.port;
+
+import static com.android.server.usb.UsbPortManager.logAndPrint;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.usb.hal.port.UsbPortHidl;
+import com.android.server.usb.hal.port.UsbPortAidl;
+import com.android.server.usb.UsbPortManager;
+
+import android.util.Log;
+/**
+ * Helper class that queries the underlying hal layer to populate UsbPortHal instance.
+ */
+public final class UsbPortHalInstance {
+
+ public static UsbPortHal getInstance(UsbPortManager portManager, IndentingPrintWriter pw) {
+
+ logAndPrint(Log.DEBUG, null, "Querying USB HAL version");
+ if (UsbPortHidl.isServicePresent(null)) {
+ logAndPrint(Log.INFO, null, "USB HAL HIDL present");
+ return new UsbPortHidl(portManager, pw);
+ }
+ if (UsbPortAidl.isServicePresent(null)) {
+ logAndPrint(Log.INFO, null, "USB HAL AIDL present");
+ return new UsbPortAidl(portManager, pw);
+ }
+
+ return null;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java
new file mode 100644
index 0000000..00d0d06
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHidl.java
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.hal.port;
+
+import static android.hardware.usb.UsbManager.USB_HAL_NOT_SUPPORTED;
+import static android.hardware.usb.UsbManager.USB_HAL_V1_0;
+import static android.hardware.usb.UsbManager.USB_HAL_V1_1;
+import static android.hardware.usb.UsbManager.USB_HAL_V1_2;
+import static android.hardware.usb.UsbManager.USB_HAL_V1_3;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_INTERNAL;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_ERROR_NOT_SUPPORTED;
+import static android.hardware.usb.UsbOperationInternal.USB_OPERATION_SUCCESS;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_DETECTION_NOT_SUPPORTED;
+import static android.hardware.usb.UsbPortStatus.CONTAMINANT_PROTECTION_NONE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
+import static android.hardware.usb.UsbPortStatus.DATA_ROLE_HOST;
+import static android.hardware.usb.UsbPortStatus.MODE_DFP;
+import static android.hardware.usb.UsbPortStatus.MODE_DUAL;
+import static android.hardware.usb.UsbPortStatus.MODE_UFP;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SINK;
+import static android.hardware.usb.UsbPortStatus.POWER_ROLE_SOURCE;
+
+import static com.android.server.usb.UsbPortManager.logAndPrint;
+import static com.android.server.usb.UsbPortManager.logAndPrintException;
+
+import android.annotation.Nullable;
+import android.hardware.usb.IUsbOperationInternal;
+import android.hardware.usb.UsbManager.UsbHalVersion;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.V1_0.IUsb;
+import android.hardware.usb.V1_0.PortRoleType;
+import android.hardware.usb.V1_0.Status;
+import android.hardware.usb.V1_1.PortStatus_1_1;
+import android.hardware.usb.V1_2.IUsbCallback;
+import android.hardware.usb.V1_0.PortRole;
+import android.hardware.usb.V1_2.PortStatus;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.hidl.manager.V1_0.IServiceNotification;
+import android.os.IHwBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.usb.UsbPortManager;
+import com.android.server.usb.hal.port.RawPortInfo;
+
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+/**
+ *
+ */
+public final class UsbPortHidl implements UsbPortHal {
+ private static final String TAG = UsbPortHidl.class.getSimpleName();
+ // Cookie sent for usb hal death notification.
+ private static final int USB_HAL_DEATH_COOKIE = 1000;
+ // Proxy object for the usb hal daemon.
+ @GuardedBy("mLock")
+ private IUsb mProxy;
+ private UsbPortManager mPortManager;
+ public IndentingPrintWriter mPw;
+ // Mutex for all mutable shared state.
+ private final Object mLock = new Object();
+ // Callback when the UsbPort status is changed by the kernel.
+ private HALCallback mHALCallback;
+ private boolean mSystemReady;
+ // Workaround since HIDL HAL versions report UsbDataEnabled status in UsbPortStatus;
+ private static boolean sUsbDataEnabled = true;
+
+ public @UsbHalVersion int getUsbHalVersion() throws RemoteException {
+ int version;
+ synchronized(mLock) {
+ if (mProxy == null) {
+ throw new RemoteException("IUsb not initialized yet");
+ }
+ if (android.hardware.usb.V1_3.IUsb.castFrom(mProxy) != null) {
+ version = USB_HAL_V1_3;
+ } else if (android.hardware.usb.V1_2.IUsb.castFrom(mProxy) != null) {
+ version = USB_HAL_V1_2;
+ } else if (android.hardware.usb.V1_1.IUsb.castFrom(mProxy) != null) {
+ version = USB_HAL_V1_1;
+ } else {
+ version = USB_HAL_V1_0;
+ }
+ logAndPrint(Log.INFO, null, "USB HAL HIDL version: " + version);
+ return version;
+ }
+ }
+
+ final class DeathRecipient implements IHwBinder.DeathRecipient {
+ public IndentingPrintWriter pw;
+
+ DeathRecipient(IndentingPrintWriter pw) {
+ this.pw = pw;
+ }
+
+ @Override
+ public void serviceDied(long cookie) {
+ if (cookie == USB_HAL_DEATH_COOKIE) {
+ logAndPrint(Log.ERROR, pw, "Usb hal service died cookie: " + cookie);
+ synchronized (mLock) {
+ mProxy = null;
+ }
+ }
+ }
+ }
+
+ final class ServiceNotification extends IServiceNotification.Stub {
+ @Override
+ public void onRegistration(String fqName, String name, boolean preexisting) {
+ logAndPrint(Log.INFO, null, "Usb hal service started " + fqName + " " + name);
+ connectToProxy(null);
+ }
+ }
+
+ private void connectToProxy(IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ if (mProxy != null) {
+ return;
+ }
+
+ try {
+ mProxy = IUsb.getService();
+ mProxy.linkToDeath(new DeathRecipient(pw), USB_HAL_DEATH_COOKIE);
+ mProxy.setCallback(mHALCallback);
+ mProxy.queryPortStatus();
+ //updateUsbHalVersion();
+ } catch (NoSuchElementException e) {
+ logAndPrintException(pw, "connectToProxy: usb hal service not found."
+ + " Did the service fail to start?", e);
+ } catch (RemoteException e) {
+ logAndPrintException(pw, "connectToProxy: usb hal service not responding", e);
+ }
+ }
+ }
+
+ @Override
+ public void systemReady() {
+ mSystemReady = true;
+ }
+
+ static boolean isServicePresent(IndentingPrintWriter pw) {
+ try {
+ IUsb.getService(true);
+ } catch (NoSuchElementException e) {
+ logAndPrintException(pw, "connectToProxy: usb hidl hal service not found.", e);
+ return false;
+ } catch (RemoteException e) {
+ logAndPrintException(pw, "IUSB hal service present but failed to get service", e);
+ }
+
+ return true;
+ }
+
+ public UsbPortHidl(UsbPortManager portManager, IndentingPrintWriter pw) {
+ mPortManager = Objects.requireNonNull(portManager);
+ mPw = pw;
+ mHALCallback = new HALCallback(null, mPortManager, this);
+ try {
+ ServiceNotification serviceNotification = new ServiceNotification();
+
+ boolean ret = IServiceManager.getService()
+ .registerForNotifications("android.hardware.usb@1.0::IUsb",
+ "", serviceNotification);
+ if (!ret) {
+ logAndPrint(Log.ERROR, null,
+ "Failed to register service start notification");
+ }
+ } catch (RemoteException e) {
+ logAndPrintException(null,
+ "Failed to register service start notification", e);
+ return;
+ }
+ connectToProxy(mPw);
+ }
+
+ @Override
+ public void enableContaminantPresenceDetection(String portName, boolean enable,
+ long transactionId) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+ return;
+ }
+
+ try {
+ // Oneway call into the hal. Use the castFrom method from HIDL.
+ android.hardware.usb.V1_2.IUsb proxy =
+ android.hardware.usb.V1_2.IUsb.castFrom(mProxy);
+ proxy.enableContaminantPresenceDetection(portName, enable);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set contaminant detection", e);
+ } catch (ClassCastException e) {
+ logAndPrintException(mPw, "Method only applicable to V1.2 or above implementation",
+ e);
+ }
+ }
+ }
+
+ @Override
+ public void queryPortStatus(long transactionId) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+ return;
+ }
+
+ try {
+ mProxy.queryPortStatus();
+ } catch (RemoteException e) {
+ logAndPrintException(null, "ServiceStart: Failed to query port status", e);
+ }
+ }
+ }
+
+ @Override
+ public void switchMode(String portId, @HalUsbPortMode int newMode, long transactionId) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+ return;
+ }
+
+ PortRole newRole = new PortRole();
+ newRole.type = PortRoleType.MODE;
+ newRole.role = newMode;
+ try {
+ mProxy.switchRole(portId, newRole);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set the USB port mode: "
+ + "portId=" + portId
+ + ", newMode=" + UsbPort.modeToString(newRole.role), e);
+ }
+ }
+ }
+
+ @Override
+ public void switchPowerRole(String portId, @HalUsbPowerRole int newPowerRole,
+ long transactionId) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+ return;
+ }
+
+ PortRole newRole = new PortRole();
+ newRole.type = PortRoleType.POWER_ROLE;
+ newRole.role = newPowerRole;
+ try {
+ mProxy.switchRole(portId, newRole);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set the USB power role: portId=" + portId
+ + ", newPowerRole=" + UsbPort.powerRoleToString(newRole.role), e);
+ }
+ }
+ }
+
+ @Override
+ public void switchDataRole(String portId, @HalUsbDataRole int newDataRole, long transactionId) {
+ synchronized (mLock) {
+ if (mProxy == null) {
+ logAndPrint(Log.ERROR, mPw, "Proxy is null. Retry !");
+ return;
+ }
+
+ PortRole newRole = new PortRole();
+ newRole.type = PortRoleType.DATA_ROLE;
+ newRole.role = newDataRole;
+ try {
+ mProxy.switchRole(portId, newRole);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to set the USB data role: portId=" + portId
+ + ", newDataRole=" + UsbPort.dataRoleToString(newRole.role), e);
+ }
+ }
+ }
+
+ @Override
+ public boolean enableUsbData(String portName, boolean enable, long transactionId,
+ IUsbOperationInternal callback) {
+ int halVersion;
+
+ try {
+ halVersion = getUsbHalVersion();
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to query USB HAL version. opID:"
+ + transactionId
+ + " portId:" + portName, e);
+ return false;
+ }
+
+ if (halVersion != USB_HAL_V1_3) {
+ try {
+ callback.onOperationComplete(USB_OPERATION_ERROR_NOT_SUPPORTED);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed to call onOperationComplete. opID:"
+ + transactionId
+ + " portId:" + portName, e);
+ }
+ return false;
+ }
+
+ boolean success;
+ synchronized(mLock) {
+ try {
+ android.hardware.usb.V1_3.IUsb proxy
+ = android.hardware.usb.V1_3.IUsb.castFrom(mProxy);
+ success = proxy.enableUsbDataSignal(enable);
+ } catch (RemoteException e) {
+ logAndPrintException(mPw, "Failed enableUsbData: opId:" + transactionId
+ + " portId=" + portName , e);
+ try {
+ callback.onOperationComplete(USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException r) {
+ logAndPrintException(mPw, "Failed to call onOperationComplete. opID:"
+ + transactionId
+ + " portId:" + portName, r);
+ }
+ return false;
+ }
+ }
+ if (success) {
+ sUsbDataEnabled = enable;
+ }
+
+ try {
+ callback.onOperationComplete(success
+ ? USB_OPERATION_SUCCESS
+ : USB_OPERATION_ERROR_INTERNAL);
+ } catch (RemoteException r) {
+ logAndPrintException(mPw, "Failed to call onOperationComplete. opID:"
+ + transactionId
+ + " portId:" + portName, r);
+ }
+ return false;
+ }
+
+ private static class HALCallback extends IUsbCallback.Stub {
+ public IndentingPrintWriter mPw;
+ public UsbPortManager mPortManager;
+ public UsbPortHidl mUsbPortHidl;
+
+ HALCallback(IndentingPrintWriter pw, UsbPortManager portManager, UsbPortHidl usbPortHidl) {
+ this.mPw = pw;
+ this.mPortManager = portManager;
+ this.mUsbPortHidl = usbPortHidl;
+ }
+
+ public void notifyPortStatusChange(
+ ArrayList<android.hardware.usb.V1_0.PortStatus> currentPortStatus, int retval) {
+ if (!mUsbPortHidl.mSystemReady) {
+ return;
+ }
+
+ if (retval != Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed");
+ return;
+ }
+
+ ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
+
+ for (android.hardware.usb.V1_0.PortStatus current : currentPortStatus) {
+ RawPortInfo temp = new RawPortInfo(current.portName,
+ current.supportedModes, CONTAMINANT_PROTECTION_NONE,
+ current.currentMode,
+ current.canChangeMode, current.currentPowerRole,
+ current.canChangePowerRole,
+ current.currentDataRole, current.canChangeDataRole,
+ false, CONTAMINANT_PROTECTION_NONE,
+ false, CONTAMINANT_DETECTION_NOT_SUPPORTED, sUsbDataEnabled);
+ newPortInfo.add(temp);
+ UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_0: "
+ + current.portName);
+ }
+
+ mPortManager.updatePorts(newPortInfo);
+ }
+
+
+ public void notifyPortStatusChange_1_1(ArrayList<PortStatus_1_1> currentPortStatus,
+ int retval) {
+ if (!mUsbPortHidl.mSystemReady) {
+ return;
+ }
+
+ if (retval != Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed");
+ return;
+ }
+
+ ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
+
+ int numStatus = currentPortStatus.size();
+ for (int i = 0; i < numStatus; i++) {
+ PortStatus_1_1 current = currentPortStatus.get(i);
+ RawPortInfo temp = new RawPortInfo(current.status.portName,
+ current.supportedModes, CONTAMINANT_PROTECTION_NONE,
+ current.currentMode,
+ current.status.canChangeMode, current.status.currentPowerRole,
+ current.status.canChangePowerRole,
+ current.status.currentDataRole, current.status.canChangeDataRole,
+ false, CONTAMINANT_PROTECTION_NONE,
+ false, CONTAMINANT_DETECTION_NOT_SUPPORTED, sUsbDataEnabled);
+ newPortInfo.add(temp);
+ UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_1: "
+ + current.status.portName);
+ }
+ mPortManager.updatePorts(newPortInfo);
+ }
+
+ public void notifyPortStatusChange_1_2(
+ ArrayList<PortStatus> currentPortStatus, int retval) {
+ if (!mUsbPortHidl.mSystemReady) {
+ return;
+ }
+
+ if (retval != Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, "port status enquiry failed");
+ return;
+ }
+
+ ArrayList<RawPortInfo> newPortInfo = new ArrayList<>();
+
+ int numStatus = currentPortStatus.size();
+ for (int i = 0; i < numStatus; i++) {
+ PortStatus current = currentPortStatus.get(i);
+ RawPortInfo temp = new RawPortInfo(current.status_1_1.status.portName,
+ current.status_1_1.supportedModes,
+ current.supportedContaminantProtectionModes,
+ current.status_1_1.currentMode,
+ current.status_1_1.status.canChangeMode,
+ current.status_1_1.status.currentPowerRole,
+ current.status_1_1.status.canChangePowerRole,
+ current.status_1_1.status.currentDataRole,
+ current.status_1_1.status.canChangeDataRole,
+ current.supportsEnableContaminantPresenceProtection,
+ current.contaminantProtectionStatus,
+ current.supportsEnableContaminantPresenceDetection,
+ current.contaminantDetectionStatus,
+ sUsbDataEnabled);
+ newPortInfo.add(temp);
+ UsbPortManager.logAndPrint(Log.INFO, mPw, "ClientCallback V1_2: "
+ + current.status_1_1.status.portName);
+ }
+ mPortManager.updatePorts(newPortInfo);
+ }
+
+ public void notifyRoleSwitchStatus(String portName, PortRole role, int retval) {
+ if (retval == Status.SUCCESS) {
+ UsbPortManager.logAndPrint(Log.INFO, mPw, portName + " role switch successful");
+ } else {
+ UsbPortManager.logAndPrint(Log.ERROR, mPw, portName + " role switch failed");
+ }
+ }
+ }
+}
diff --git a/telecomm/OWNERS b/telecomm/OWNERS
index 9969ee9..eb0c432 100644
--- a/telecomm/OWNERS
+++ b/telecomm/OWNERS
@@ -1,8 +1,6 @@
set noparent
breadley@google.com
-hallliu@google.com
tgunn@google.com
xiaotonj@google.com
-shuoq@google.com
rgreenwalt@google.com
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 6f286d9..23b5787 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -344,6 +344,11 @@
void setTestDefaultCallRedirectionApp(String packageName);
+ /**
+ * @see TelecomServiceImpl#requestLogMark
+ */
+ void requestLogMark(in String message);
+
void setTestPhoneAcctSuggestionComponent(String flattenedComponentName);
void setTestDefaultCallScreeningApp(String packageName);
diff --git a/telephony/OWNERS b/telephony/OWNERS
index f248fd5..9681ee8 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -1,15 +1,10 @@
set noparent
-amitmahajan@google.com
breadley@google.com
fionaxu@google.com
jackyu@google.com
rgreenwalt@google.com
tgunn@google.com
-jminjie@google.com
-shuoq@google.com
-sarahchin@google.com
-xiaotonj@google.com
huiwang@google.com
jayachandranc@google.com
chinmayd@google.com
diff --git a/telephony/java/android/service/euicc/OWNERS b/telephony/java/android/service/euicc/OWNERS
index 6aa399d..fbeb6da 100644
--- a/telephony/java/android/service/euicc/OWNERS
+++ b/telephony/java/android/service/euicc/OWNERS
@@ -1,5 +1,5 @@
-# Bug component: 20868
+set noparent
+fionaxu@google.com
rgreenwalt@google.com
-tgunn@google.com
-amitmahajan@google.com
+amruthr@google.com
diff --git a/telephony/java/android/service/sms/OWNERS b/telephony/java/android/service/sms/OWNERS
deleted file mode 100644
index 6aa399d..0000000
--- a/telephony/java/android/service/sms/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 20868
-
-rgreenwalt@google.com
-tgunn@google.com
-amitmahajan@google.com
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ee646d9..0d0142c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1900,6 +1900,13 @@
"show_4g_for_lte_data_icon_bool";
/**
+ * Boolean indicating if default data account should show 4G LTE or 4G icon.
+ * @hide
+ */
+ public static final String KEY_SHOW_4GLTE_FOR_LTE_DATA_ICON_BOOL =
+ "show_4glte_for_lte_data_icon_bool";
+
+ /**
* Boolean indicating if default data account should show 4G icon when in 3G.
*/
public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL =
@@ -2846,11 +2853,11 @@
* <p>
* 4 threshold integers must be within the boundaries [-140 dB, -44 dB], and the levels are:
* <UL>
- * <LI>"NONE: [-140, threshold1]"</LI>
- * <LI>"POOR: (threshold1, threshold2]"</LI>
- * <LI>"MODERATE: (threshold2, threshold3]"</LI>
- * <LI>"GOOD: (threshold3, threshold4]"</LI>
- * <LI>"EXCELLENT: (threshold4, -44]"</LI>
+ * <LI>"NONE: [-140, threshold1)"</LI>
+ * <LI>"POOR: [threshold1, threshold2)"</LI>
+ * <LI>"MODERATE: [threshold2, threshold3)"</LI>
+ * <LI>"GOOD: [threshold3, threshold4)"</LI>
+ * <LI>"EXCELLENT: [threshold4, -44]"</LI>
* </UL>
* <p>
* This key is considered invalid if the format is violated. If the key is invalid or
@@ -2866,11 +2873,11 @@
* <p>
* 4 threshold integers must be within the boundaries [-43 dB, 20 dB], and the levels are:
* <UL>
- * <LI>"NONE: [-43, threshold1]"</LI>
- * <LI>"POOR: (threshold1, threshold2]"</LI>
- * <LI>"MODERATE: (threshold2, threshold3]"</LI>
- * <LI>"GOOD: (threshold3, threshold4]"</LI>
- * <LI>"EXCELLENT: (threshold4, 20]"</LI>
+ * <LI>"NONE: [-43, threshold1)"</LI>
+ * <LI>"POOR: [threshold1, threshold2)"</LI>
+ * <LI>"MODERATE: [threshold2, threshold3)"</LI>
+ * <LI>"GOOD: [threshold3, threshold4)"</LI>
+ * <LI>"EXCELLENT: [threshold4, 20]"</LI>
* </UL>
* <p>
* This key is considered invalid if the format is violated. If the key is invalid or
@@ -2887,11 +2894,11 @@
* <p>
* 4 threshold integers must be within the boundaries [-23 dB, 40 dB], and the levels are:
* <UL>
- * <LI>"NONE: [-23, threshold1]"</LI>
- * <LI>"POOR: (threshold1, threshold2]"</LI>
- * <LI>"MODERATE: (threshold2, threshold3]"</LI>
- * <LI>"GOOD: (threshold3, threshold4]"</LI>
- * <LI>"EXCELLENT: (threshold4, 40]"</LI>
+ * <LI>"NONE: [-23, threshold1)"</LI>
+ * <LI>"POOR: [threshold1, threshold2)"</LI>
+ * <LI>"MODERATE: [threshold2, threshold3)"</LI>
+ * <LI>"GOOD: [threshold3, threshold4)"</LI>
+ * <LI>"EXCELLENT: [threshold4, 40]"</LI>
* </UL>
* <p>
* This key is considered invalid if the format is violated. If the key is invalid or
@@ -5842,8 +5849,9 @@
* <item value="source=GERAN|UTRAN, target:IWLAN, type=disallowed"/>
* <!-- Handover from IWLAN to 3G/4G/5G is not allowed if the device is roaming. -->
* <item value="source=IWLAN, target=UTRAN|EUTRAN|NGRAN, roaming=true, type=disallowed"/>
- * <!-- Handover from 4G to IWLAN is not allowed -->
- * <item value="source=EUTRAN, target=IWLAN, type=disallowed"/>
+ * <!-- Handover from 4G to IWLAN is not allowed if the device has capability in either IMS
+ * or EIMS-->
+ * <item value="source=EUTRAN, target=IWLAN, type=disallowed, capabilities=IMS|EIMS"/>
* <!-- Handover is always allowed in any condition. -->
* <item value="source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN,
* target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"/>
@@ -6238,6 +6246,7 @@
sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL, false);
+ sDefaults.putBoolean(KEY_SHOW_4GLTE_FOR_LTE_DATA_ICON_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL, false);
sDefaults.putString(KEY_OPERATOR_NAME_FILTER_PATTERN_STRING, "");
sDefaults.putString(KEY_SHOW_CARRIER_DATA_ICON_PATTERN_STRING, "");
@@ -6836,42 +6845,26 @@
private void addConfig(String key, Object value, PersistableBundle configs) {
if (value instanceof String) {
configs.putString(key, (String) value);
- }
-
- if (value instanceof String[]) {
+ } else if (value instanceof String[]) {
configs.putStringArray(key, (String[]) value);
- }
-
- if (value instanceof Integer) {
+ } else if (value instanceof Integer) {
configs.putInt(key, (Integer) value);
- }
-
- if (value instanceof Long) {
+ } else if (value instanceof Long) {
configs.putLong(key, (Long) value);
- }
-
- if (value instanceof Double) {
+ } else if (value instanceof Double) {
configs.putDouble(key, (Double) value);
- }
-
- if (value instanceof Boolean) {
+ } else if (value instanceof Boolean) {
configs.putBoolean(key, (Boolean) value);
- }
-
- if (value instanceof int[]) {
+ } else if (value instanceof int[]) {
configs.putIntArray(key, (int[]) value);
- }
-
- if (value instanceof double[]) {
+ } else if (value instanceof double[]) {
configs.putDoubleArray(key, (double[]) value);
- }
-
- if (value instanceof boolean[]) {
+ } else if (value instanceof boolean[]) {
configs.putBooleanArray(key, (boolean[]) value);
- }
-
- if (value instanceof long[]) {
+ } else if (value instanceof long[]) {
configs.putLongArray(key, (long[]) value);
+ } else if (value instanceof PersistableBundle) {
+ configs.putPersistableBundle(key, (PersistableBundle) value);
}
}
}
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index cd22abd..417fd49 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -438,13 +438,13 @@
int level;
if (measure == CellInfo.UNAVAILABLE) {
level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- } else if (measure > thresholds[3]) {
+ } else if (measure >= thresholds[3]) {
level = SIGNAL_STRENGTH_GREAT;
- } else if (measure > thresholds[2]) {
+ } else if (measure >= thresholds[2]) {
level = SIGNAL_STRENGTH_GOOD;
- } else if (measure > thresholds[1]) {
+ } else if (measure >= thresholds[1]) {
level = SIGNAL_STRENGTH_MODERATE;
- } else if (measure > thresholds[0]) {
+ } else if (measure >= thresholds[0]) {
level = SIGNAL_STRENGTH_POOR;
} else {
level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
diff --git a/telephony/java/android/telephony/OWNERS b/telephony/java/android/telephony/OWNERS
deleted file mode 100644
index 6aa399d..0000000
--- a/telephony/java/android/telephony/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 20868
-
-rgreenwalt@google.com
-tgunn@google.com
-amitmahajan@google.com
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a5adf52..b482515 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -11487,7 +11487,25 @@
if (SubscriptionManager.isValidPhoneId(phoneId)) {
List<String> newList = updateTelephonyProperty(
TelephonyProperties.operator_alpha(), phoneId, name);
- TelephonyProperties.operator_alpha(newList);
+ try {
+ TelephonyProperties.operator_alpha(newList);
+ } catch (IllegalArgumentException e) { //property value is longer than the byte limit
+ Log.e(TAG, "setNetworkOperatorNameForPhone: ", e);
+
+ int numberOfEntries = newList.size();
+ int maxOperatorLength = //save 1 byte for joiner " , "
+ (SystemProperties.PROP_VALUE_MAX - numberOfEntries) / numberOfEntries;
+
+ //examine and truncate every operator and retry
+ for (int i = 0; i < newList.size(); i++) {
+ if (newList.get(i) != null) {
+ newList.set(i, TextUtils
+ .truncateStringForUtf8Storage(newList.get(i), maxOperatorLength));
+ }
+ }
+ TelephonyProperties.operator_alpha(newList);
+ Log.e(TAG, "successfully truncated operator_alpha: " + newList);
+ }
}
}
diff --git a/telephony/java/android/telephony/cdma/OWNERS b/telephony/java/android/telephony/cdma/OWNERS
deleted file mode 100644
index 6aa399d..0000000
--- a/telephony/java/android/telephony/cdma/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 20868
-
-rgreenwalt@google.com
-tgunn@google.com
-amitmahajan@google.com
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 281b018..b0ddf2c 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -369,6 +369,10 @@
public @interface AuthType {}
// Possible values for protocol which is defined in TS 27.007 section 10.1.1.
+ /** Unknown protocol.
+ * @hide
+ */
+ public static final int PROTOCOL_UNKNOWN = -1;
/** Internet protocol. */
public static final int PROTOCOL_IP = 0;
/** Internet protocol, version 6. */
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index ec73471..77d4837 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -50,12 +50,13 @@
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef({RESULT_SUCCESS, RESULT_ERROR_UNSUPPORTED, RESULT_ERROR_INVALID_ARG, RESULT_ERROR_BUSY,
- RESULT_ERROR_ILLEGAL_STATE})
+ RESULT_ERROR_ILLEGAL_STATE, RESULT_ERROR_TEMPORARILY_UNAVAILABLE,
+ RESULT_ERROR_INVALID_RESPONSE})
public @interface ResultCode {}
/** Request is completed successfully */
public static final int RESULT_SUCCESS = 0;
- /** Request is not support */
+ /** Request is not supported */
public static final int RESULT_ERROR_UNSUPPORTED = 1;
/** Request contains invalid arguments */
public static final int RESULT_ERROR_INVALID_ARG = 2;
@@ -68,6 +69,11 @@
* @hide
*/
public static final int RESULT_ERROR_TEMPORARILY_UNAVAILABLE = 5;
+ /**
+ * Request failed to complete due to an invalid response.
+ * @hide
+ */
+ public static final int RESULT_ERROR_INVALID_RESPONSE = 6;
private final IDataServiceCallback mCallback;
@@ -255,6 +261,8 @@
return "RESULT_ERROR_ILLEGAL_STATE";
case RESULT_ERROR_TEMPORARILY_UNAVAILABLE:
return "RESULT_ERROR_TEMPORARILY_UNAVAILABLE";
+ case RESULT_ERROR_INVALID_RESPONSE:
+ return "RESULT_ERROR_INVALID_RESPONSE";
default:
return "Unknown(" + resultCode + ")";
}
diff --git a/telephony/java/android/telephony/data/OWNERS b/telephony/java/android/telephony/data/OWNERS
index 932b35c..9dce366 100644
--- a/telephony/java/android/telephony/data/OWNERS
+++ b/telephony/java/android/telephony/data/OWNERS
@@ -1,5 +1,6 @@
-# Bug component: 20868
+set noparent
-rgreenwalt@google.com
-tgunn@google.com
jackyu@google.com
+amruthr@google.com
+rgreenwalt@google.com
+
diff --git a/telephony/java/android/telephony/emergency/OWNERS b/telephony/java/android/telephony/emergency/OWNERS
index fa07dce..1d8de5d 100644
--- a/telephony/java/android/telephony/emergency/OWNERS
+++ b/telephony/java/android/telephony/emergency/OWNERS
@@ -1,5 +1,3 @@
-# Bug component: 20868
+set noparent
-rgreenwalt@google.com
-tgunn@google.com
-shuoq@google.com
+file:platform/frameworks/base:/telecomm/OWNERS
diff --git a/telephony/java/android/telephony/euicc/OWNERS b/telephony/java/android/telephony/euicc/OWNERS
index 9e51a4b..781550c 100644
--- a/telephony/java/android/telephony/euicc/OWNERS
+++ b/telephony/java/android/telephony/euicc/OWNERS
@@ -1,6 +1,4 @@
-# Bug component: 20868
+set noparent
-rgreenwalt@google.com
-tgunn@google.com
-refuhoo@google.com
-amitmahajan@google.com
+file:platform/frameworks/base:/telephony/java/android/service/euicc/OWNERS
+
diff --git a/telephony/java/android/telephony/gsm/OWNERS b/telephony/java/android/telephony/gsm/OWNERS
deleted file mode 100644
index 6aa399d..0000000
--- a/telephony/java/android/telephony/gsm/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 20868
-
-rgreenwalt@google.com
-tgunn@google.com
-amitmahajan@google.com
diff --git a/telephony/java/android/telephony/ims/OWNERS b/telephony/java/android/telephony/ims/OWNERS
index 0854c5d..3c72680 100644
--- a/telephony/java/android/telephony/ims/OWNERS
+++ b/telephony/java/android/telephony/ims/OWNERS
@@ -1,5 +1,6 @@
-# Bug component: 20868
+set noparent
rgreenwalt@google.com
tgunn@google.com
breadley@google.com
+amruthr@google.com
diff --git a/telephony/java/android/telephony/ims/aidl/OWNERS b/telephony/java/android/telephony/ims/aidl/OWNERS
deleted file mode 100644
index 0854c5d..0000000
--- a/telephony/java/android/telephony/ims/aidl/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 20868
-
-rgreenwalt@google.com
-tgunn@google.com
-breadley@google.com
diff --git a/telephony/java/android/telephony/ims/compat/OWNERS b/telephony/java/android/telephony/ims/compat/OWNERS
deleted file mode 100644
index 0854c5d..0000000
--- a/telephony/java/android/telephony/ims/compat/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 20868
-
-rgreenwalt@google.com
-tgunn@google.com
-breadley@google.com
diff --git a/telephony/java/android/telephony/ims/compat/feature/OWNERS b/telephony/java/android/telephony/ims/compat/feature/OWNERS
deleted file mode 100644
index 0854c5d..0000000
--- a/telephony/java/android/telephony/ims/compat/feature/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 20868
-
-rgreenwalt@google.com
-tgunn@google.com
-breadley@google.com
diff --git a/telephony/java/android/telephony/ims/compat/stub/OWNERS b/telephony/java/android/telephony/ims/compat/stub/OWNERS
deleted file mode 100644
index 0854c5d..0000000
--- a/telephony/java/android/telephony/ims/compat/stub/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 20868
-
-rgreenwalt@google.com
-tgunn@google.com
-breadley@google.com
diff --git a/telephony/java/android/telephony/ims/feature/OWNERS b/telephony/java/android/telephony/ims/feature/OWNERS
deleted file mode 100644
index 0854c5d..0000000
--- a/telephony/java/android/telephony/ims/feature/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 20868
-
-rgreenwalt@google.com
-tgunn@google.com
-breadley@google.com
diff --git a/telephony/java/android/telephony/ims/stub/OWNERS b/telephony/java/android/telephony/ims/stub/OWNERS
deleted file mode 100644
index 0854c5d..0000000
--- a/telephony/java/android/telephony/ims/stub/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 20868
-
-rgreenwalt@google.com
-tgunn@google.com
-breadley@google.com
diff --git a/telephony/java/android/telephony/mbms/OWNERS b/telephony/java/android/telephony/mbms/OWNERS
index 718e0a2..1d8de5d 100644
--- a/telephony/java/android/telephony/mbms/OWNERS
+++ b/telephony/java/android/telephony/mbms/OWNERS
@@ -1,5 +1,3 @@
-# Bug component: 20868
+set noparent
-rgreenwalt@google.com
-tgunn@google.com
-hallliu@google.com
+file:platform/frameworks/base:/telecomm/OWNERS
diff --git a/telephony/java/android/telephony/mbms/vendor/OWNERS b/telephony/java/android/telephony/mbms/vendor/OWNERS
deleted file mode 100644
index 718e0a2..0000000
--- a/telephony/java/android/telephony/mbms/vendor/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-# Bug component: 20868
-
-rgreenwalt@google.com
-tgunn@google.com
-hallliu@google.com
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 883e2ad..7799113 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2556,4 +2556,7 @@
* for the slot, or {@code null} if none is resolved
*/
String getCarrierServicePackageNameForLogicalSlot(int logicalSlotIndex);
+
+ /** Check if telephony new data stack is enabled. */
+ boolean isUsingNewDataStack();
}
diff --git a/test-legacy/Android.mk b/test-legacy/Android.mk
index 284008c..da9dc25 100644
--- a/test-legacy/Android.mk
+++ b/test-legacy/Android.mk
@@ -40,6 +40,10 @@
include $(BUILD_STATIC_JAVA_LIBRARY)
+$(call declare-license-metadata,$(full_classes_jar),\
+ SPDX-license-identifier-Apache-2.0 SPDX-license-identifier-MIT SPDX-license-identifier-Unicode-DFS,\
+ notice,$(LOCAL_PATH)/../NOTICE,Android,frameworks/base)
+
# Archive a copy of the classes.jar in SDK build.
$(call dist-for-goals,sdk,$(full_classes_jar):android.test.legacy.jar)
diff --git a/tests/FlickerTests/OWNERS b/tests/FlickerTests/OWNERS
index c1221e3..d40ff56 100644
--- a/tests/FlickerTests/OWNERS
+++ b/tests/FlickerTests/OWNERS
@@ -1,4 +1,4 @@
-# Bug component: 909476
+# Bug component: 1157642
include /services/core/java/com/android/server/wm/OWNERS
natanieljr@google.com
pablogamito@google.com
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index bb98bc0..54b3c40 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -65,6 +65,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.TelephonyNetworkSpecifier;
+import android.net.Uri;
import android.net.vcn.IVcnStatusCallback;
import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
@@ -114,18 +115,24 @@
public class VcnManagementServiceTest {
private static final String TEST_PACKAGE_NAME =
VcnManagementServiceTest.class.getPackage().getName();
+ private static final String TEST_PACKAGE_NAME_2 = "TEST_PKG_2";
private static final String TEST_CB_PACKAGE_NAME =
VcnManagementServiceTest.class.getPackage().getName() + ".callback";
private static final ParcelUuid TEST_UUID_1 = new ParcelUuid(new UUID(0, 0));
private static final ParcelUuid TEST_UUID_2 = new ParcelUuid(new UUID(1, 1));
+ private static final ParcelUuid TEST_UUID_3 = new ParcelUuid(new UUID(2, 2));
private static final VcnConfig TEST_VCN_CONFIG;
+ private static final VcnConfig TEST_VCN_CONFIG_PKG_2;
private static final int TEST_UID = Process.FIRST_APPLICATION_UID;
static {
final Context mockConfigContext = mock(Context.class);
- doReturn(TEST_PACKAGE_NAME).when(mockConfigContext).getOpPackageName();
+ doReturn(TEST_PACKAGE_NAME).when(mockConfigContext).getOpPackageName();
TEST_VCN_CONFIG = VcnConfigTest.buildTestConfig(mockConfigContext);
+
+ doReturn(TEST_PACKAGE_NAME_2).when(mockConfigContext).getOpPackageName();
+ TEST_VCN_CONFIG_PKG_2 = VcnConfigTest.buildTestConfig(mockConfigContext);
}
private static final Map<ParcelUuid, VcnConfig> TEST_VCN_CONFIG_MAP =
@@ -246,18 +253,24 @@
eq(android.Manifest.permission.NETWORK_FACTORY), any());
}
+
private void setupMockedCarrierPrivilege(boolean isPrivileged) {
+ setupMockedCarrierPrivilege(isPrivileged, TEST_PACKAGE_NAME);
+ }
+
+ private void setupMockedCarrierPrivilege(boolean isPrivileged, String pkg) {
doReturn(Collections.singletonList(TEST_SUBSCRIPTION_INFO))
.when(mSubMgr)
.getSubscriptionsInGroup(any());
doReturn(mTelMgr)
.when(mTelMgr)
.createForSubscriptionId(eq(TEST_SUBSCRIPTION_INFO.getSubscriptionId()));
- doReturn(isPrivileged
- ? CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
- : CARRIER_PRIVILEGE_STATUS_NO_ACCESS)
+ doReturn(
+ isPrivileged
+ ? CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
+ : CARRIER_PRIVILEGE_STATUS_NO_ACCESS)
.when(mTelMgr)
- .checkCarrierPrivilegesForPackage(eq(TEST_PACKAGE_NAME));
+ .checkCarrierPrivilegesForPackage(eq(pkg));
}
@Test
@@ -414,7 +427,13 @@
private BroadcastReceiver getPackageChangeReceiver() {
final ArgumentCaptor<BroadcastReceiver> captor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
- verify(mMockContext).registerReceiver(captor.capture(), any(), any(), any());
+ verify(mMockContext).registerReceiver(captor.capture(), argThat(filter -> {
+ return filter.hasAction(Intent.ACTION_PACKAGE_ADDED)
+ && filter.hasAction(Intent.ACTION_PACKAGE_REPLACED)
+ && filter.hasAction(Intent.ACTION_PACKAGE_REMOVED)
+ && filter.hasAction(Intent.ACTION_PACKAGE_DATA_CLEARED)
+ && filter.hasAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
+ }), any(), any());
return captor.getValue();
}
@@ -539,6 +558,44 @@
}
@Test
+ public void testPackageChangeListener_packageDataCleared() throws Exception {
+ triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_1, Collections.singleton(TEST_UUID_1));
+ final Vcn vcn = mVcnMgmtSvc.getAllVcns().get(TEST_UUID_1);
+
+ final BroadcastReceiver receiver = getPackageChangeReceiver();
+ assertEquals(TEST_VCN_CONFIG_MAP, mVcnMgmtSvc.getConfigs());
+
+ final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED);
+ intent.setData(Uri.parse("package:" + TEST_PACKAGE_NAME));
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(TEST_UID));
+
+ receiver.onReceive(mMockContext, intent);
+ mTestLooper.dispatchAll();
+ verify(vcn).teardownAsynchronously();
+ assertTrue(mVcnMgmtSvc.getConfigs().isEmpty());
+ verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
+ }
+
+ @Test
+ public void testPackageChangeListener_packageFullyRemoved() throws Exception {
+ triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_1, Collections.singleton(TEST_UUID_1));
+ final Vcn vcn = mVcnMgmtSvc.getAllVcns().get(TEST_UUID_1);
+
+ final BroadcastReceiver receiver = getPackageChangeReceiver();
+ assertEquals(TEST_VCN_CONFIG_MAP, mVcnMgmtSvc.getConfigs());
+
+ final Intent intent = new Intent(Intent.ACTION_PACKAGE_FULLY_REMOVED);
+ intent.setData(Uri.parse("package:" + TEST_PACKAGE_NAME));
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(TEST_UID));
+
+ receiver.onReceive(mMockContext, intent);
+ mTestLooper.dispatchAll();
+ verify(vcn).teardownAsynchronously();
+ assertTrue(mVcnMgmtSvc.getConfigs().isEmpty());
+ verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
+ }
+
+ @Test
public void testSetVcnConfigRequiresNonSystemServer() throws Exception {
doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid();
@@ -578,7 +635,7 @@
@Test
public void testSetVcnConfigMismatchedPackages() throws Exception {
try {
- mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, "IncorrectPackage");
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, TEST_VCN_CONFIG, TEST_PACKAGE_NAME_2);
fail("Expected exception due to mismatched packages in config and method call");
} catch (IllegalArgumentException expected) {
verify(mMockPolicyListener, never()).onPolicyChanged();
@@ -678,11 +735,12 @@
}
@Test
- public void testClearVcnConfigRequiresCarrierPrivileges() throws Exception {
+ public void testClearVcnConfigRequiresCarrierPrivilegesOrProvisioningPackage()
+ throws Exception {
setupMockedCarrierPrivilege(false);
try {
- mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, TEST_PACKAGE_NAME);
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, TEST_PACKAGE_NAME_2);
fail("Expected security exception for missing carrier privileges");
} catch (SecurityException expected) {
}
@@ -691,20 +749,32 @@
@Test
public void testClearVcnConfigMismatchedPackages() throws Exception {
try {
- mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, "IncorrectPackage");
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, TEST_PACKAGE_NAME_2);
fail("Expected security exception due to mismatched packages");
} catch (SecurityException expected) {
}
}
@Test
- public void testClearVcnConfig() throws Exception {
+ public void testClearVcnConfig_callerIsProvisioningPackage() throws Exception {
+ // Lose carrier privileges to test that provisioning package is sufficient.
+ setupMockedCarrierPrivilege(false);
+
mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, TEST_PACKAGE_NAME);
assertTrue(mVcnMgmtSvc.getConfigs().isEmpty());
verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
}
@Test
+ public void testClearVcnConfig_callerIsCarrierPrivileged() throws Exception {
+ setupMockedCarrierPrivilege(true, TEST_PACKAGE_NAME_2);
+
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1, TEST_PACKAGE_NAME_2);
+ assertTrue(mVcnMgmtSvc.getConfigs().isEmpty());
+ verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class));
+ }
+
+ @Test
public void testClearVcnConfigNotifiesStatusCallback() throws Exception {
setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_2, true /* isActive */);
mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_2, mMockStatusCallback, TEST_PACKAGE_NAME);
@@ -755,11 +825,12 @@
@Test
public void testGetConfiguredSubscriptionGroupsMismatchedPackages() throws Exception {
- final String badPackage = "IncorrectPackage";
- doThrow(new SecurityException()).when(mAppOpsMgr).checkPackage(TEST_UID, badPackage);
+ doThrow(new SecurityException())
+ .when(mAppOpsMgr)
+ .checkPackage(TEST_UID, TEST_PACKAGE_NAME_2);
try {
- mVcnMgmtSvc.getConfiguredSubscriptionGroups(badPackage);
+ mVcnMgmtSvc.getConfiguredSubscriptionGroups(TEST_PACKAGE_NAME_2);
fail("Expected security exception due to mismatched packages");
} catch (SecurityException expected) {
}
@@ -767,14 +838,16 @@
@Test
public void testGetConfiguredSubscriptionGroups() throws Exception {
+ setupMockedCarrierPrivilege(true, TEST_PACKAGE_NAME_2);
mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
+ mVcnMgmtSvc.setVcnConfig(TEST_UUID_3, TEST_VCN_CONFIG_PKG_2, TEST_PACKAGE_NAME_2);
- // Assert that if both UUID 1 and 2 are provisioned, the caller only gets ones that they are
- // privileged for.
+ // Assert that if UUIDs 1, 2 and 3 are provisioned, the caller only gets ones that they are
+ // privileged for, or are the provisioning package of.
triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_1, Collections.singleton(TEST_UUID_1));
final List<ParcelUuid> subGrps =
mVcnMgmtSvc.getConfiguredSubscriptionGroups(TEST_PACKAGE_NAME);
- assertEquals(Collections.singletonList(TEST_UUID_1), subGrps);
+ assertEquals(Arrays.asList(new ParcelUuid[] {TEST_UUID_1, TEST_UUID_2}), subGrps);
}
@Test
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 5f606e1..09080be 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -26,6 +26,7 @@
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -50,9 +51,11 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.vcn.VcnManager;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.ParcelUuid;
+import android.os.PersistableBundle;
import android.os.test.TestLooper;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionInfo;
@@ -104,6 +107,26 @@
TEST_SUBID_TO_INFO_MAP = Collections.unmodifiableMap(subIdToGroupMap);
}
+ private static final String TEST_CARRIER_CONFIG_KEY_1 = "TEST_CARRIER_CONFIG_KEY_1";
+ private static final String TEST_CARRIER_CONFIG_KEY_2 = "TEST_CARRIER_CONFIG_KEY_2";
+ private static final PersistableBundle TEST_CARRIER_CONFIG = new PersistableBundle();
+ private static final PersistableBundleWrapper TEST_CARRIER_CONFIG_WRAPPER;
+ private static final Map<Integer, PersistableBundleWrapper> TEST_SUBID_TO_CARRIER_CONFIG_MAP;
+
+ static {
+ TEST_CARRIER_CONFIG.putString(
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY,
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_ENTRY_RSSI_THRESHOLD_KEY);
+ TEST_CARRIER_CONFIG.putString(
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY,
+ VcnManager.VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY);
+ TEST_CARRIER_CONFIG_WRAPPER = new PersistableBundleWrapper(TEST_CARRIER_CONFIG);
+
+ final Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap = new HashMap<>();
+ subIdToCarrierConfigMap.put(TEST_SUBSCRIPTION_ID_1, TEST_CARRIER_CONFIG_WRAPPER);
+ TEST_SUBID_TO_CARRIER_CONFIG_MAP = Collections.unmodifiableMap(subIdToCarrierConfigMap);
+ }
+
@NonNull private final Context mContext;
@NonNull private final TestLooper mTestLooper;
@NonNull private final Handler mHandler;
@@ -144,6 +167,9 @@
doReturn(mCarrierConfigManager)
.when(mContext)
.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ doReturn(TEST_CARRIER_CONFIG)
+ .when(mCarrierConfigManager)
+ .getConfigForSubId(eq(TEST_SUBSCRIPTION_ID_1));
// subId 1, 2 are in same subGrp, only subId 1 is active
doReturn(TEST_PARCEL_UUID).when(TEST_SUBINFO_1).getGroupUuid();
@@ -227,14 +253,24 @@
private TelephonySubscriptionSnapshot buildExpectedSnapshot(
Map<Integer, SubscriptionInfo> subIdToInfoMap,
Map<ParcelUuid, Set<String>> privilegedPackages) {
- return new TelephonySubscriptionSnapshot(0, subIdToInfoMap, privilegedPackages);
+ return buildExpectedSnapshot(0, subIdToInfoMap, privilegedPackages);
}
private TelephonySubscriptionSnapshot buildExpectedSnapshot(
int activeSubId,
Map<Integer, SubscriptionInfo> subIdToInfoMap,
Map<ParcelUuid, Set<String>> privilegedPackages) {
- return new TelephonySubscriptionSnapshot(activeSubId, subIdToInfoMap, privilegedPackages);
+ return buildExpectedSnapshot(
+ activeSubId, subIdToInfoMap, TEST_SUBID_TO_CARRIER_CONFIG_MAP, privilegedPackages);
+ }
+
+ private TelephonySubscriptionSnapshot buildExpectedSnapshot(
+ int activeSubId,
+ Map<Integer, SubscriptionInfo> subIdToInfoMap,
+ Map<Integer, PersistableBundleWrapper> subIdToCarrierConfigMap,
+ Map<ParcelUuid, Set<String>> privilegedPackages) {
+ return new TelephonySubscriptionSnapshot(
+ activeSubId, subIdToInfoMap, subIdToCarrierConfigMap, privilegedPackages);
}
private void verifyNoActiveSubscriptions() {
@@ -245,6 +281,8 @@
private void setupReadySubIds() {
mTelephonySubscriptionTracker.setReadySubIdsBySlotId(
Collections.singletonMap(TEST_SIM_SLOT_INDEX, TEST_SUBSCRIPTION_ID_1));
+ mTelephonySubscriptionTracker.setSubIdToCarrierConfigMap(
+ Collections.singletonMap(TEST_SUBSCRIPTION_ID_1, TEST_CARRIER_CONFIG_WRAPPER));
}
private void setPrivilegedPackagesForMock(@NonNull List<String> privilegedPackages) {
@@ -300,6 +338,7 @@
readySubIdsBySlotId.put(TEST_SIM_SLOT_INDEX + 1, TEST_SUBSCRIPTION_ID_1);
mTelephonySubscriptionTracker.setReadySubIdsBySlotId(readySubIdsBySlotId);
+ mTelephonySubscriptionTracker.setSubIdToCarrierConfigMap(TEST_SUBID_TO_CARRIER_CONFIG_MAP);
doReturn(1).when(mTelephonyManager).getActiveModemCount();
List<CarrierPrivilegesCallback> carrierPrivilegesCallbacks =
@@ -464,8 +503,16 @@
mTelephonySubscriptionTracker.onReceive(mContext, buildTestBroadcastIntent(false));
mTestLooper.dispatchAll();
- verify(mCallback).onNewSnapshot(eq(buildExpectedSnapshot(emptyMap())));
+ verify(mCallback)
+ .onNewSnapshot(
+ eq(
+ buildExpectedSnapshot(
+ 0, TEST_SUBID_TO_INFO_MAP, emptyMap(), emptyMap())));
assertNull(mTelephonySubscriptionTracker.getReadySubIdsBySlotId().get(TEST_SIM_SLOT_INDEX));
+ assertNull(
+ mTelephonySubscriptionTracker
+ .getSubIdToCarrierConfigMap()
+ .get(TEST_SUBSCRIPTION_ID_1));
}
@Test
@@ -493,7 +540,7 @@
public void testTelephonySubscriptionSnapshotGetGroupForSubId() throws Exception {
final TelephonySubscriptionSnapshot snapshot =
new TelephonySubscriptionSnapshot(
- TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap());
+ TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap(), emptyMap());
assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_1));
assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_2));
@@ -503,7 +550,7 @@
public void testTelephonySubscriptionSnapshotGetAllSubIdsInGroup() throws Exception {
final TelephonySubscriptionSnapshot snapshot =
new TelephonySubscriptionSnapshot(
- TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap());
+ TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap(), emptyMap());
assertEquals(
new ArraySet<>(Arrays.asList(TEST_SUBSCRIPTION_ID_1, TEST_SUBSCRIPTION_ID_2)),
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 4cfa93b..15d4f10 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -56,6 +56,7 @@
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.ipsec.ike.ChildSaProposal;
+import android.net.ipsec.ike.IkeSessionConnectionInfo;
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeInternalException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
@@ -216,14 +217,23 @@
@Test
public void testMigration() throws Exception {
triggerChildOpened();
+ mTestLooper.dispatchAll();
+ assertEquals(mIkeConnectionInfo, mGatewayConnection.getIkeConnectionInfo());
mGatewayConnection
.getUnderlyingNetworkControllerCallback()
.onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
+
+ final IkeSessionConnectionInfo newIkeConnectionInfo =
+ new IkeSessionConnectionInfo(
+ TEST_ADDR_V4, TEST_ADDR_V4_2, TEST_UNDERLYING_NETWORK_RECORD_2.network);
+ getIkeSessionCallback().onIkeSessionConnectionInfoChanged(newIkeConnectionInfo);
getChildSessionCallback()
.onIpSecTransformsMigrated(makeDummyIpSecTransform(), makeDummyIpSecTransform());
mTestLooper.dispatchAll();
+ assertEquals(newIkeConnectionInfo, mGatewayConnection.getIkeConnectionInfo());
+
verify(mIpSecSvc, times(2))
.setNetworkForTunnelInterface(
eq(TEST_IPSEC_TUNNEL_RESOURCE_ID),
@@ -246,12 +256,16 @@
MtuUtils.getMtu(
saProposals,
mConfig.getMaxMtu(),
- TEST_UNDERLYING_NETWORK_RECORD_2.linkProperties.getMtu());
+ TEST_UNDERLYING_NETWORK_RECORD_2.linkProperties.getMtu(),
+ true /* isIpv4 */);
verify(mNetworkAgent).sendLinkProperties(
argThat(lp -> expectedMtu == lp.getMtu()
&& TEST_TCP_BUFFER_SIZES_2.equals(lp.getTcpBufferSizes())));
verify(mNetworkAgent)
.setUnderlyingNetworks(eq(singletonList(TEST_UNDERLYING_NETWORK_RECORD_2.network)));
+
+ // Verify revalidation is triggered on VCN network
+ verify(mConnMgr).reportNetworkConnectivity(eq(mNetworkAgent.getNetwork()), eq(false));
}
private void triggerChildOpened() {
@@ -266,6 +280,7 @@
.when(mMockChildSessionConfig)
.getInternalDnsServers();
+ getIkeSessionCallback().onOpened(mIkeSessionConfiguration);
getChildSessionCallback().onOpened(mMockChildSessionConfig);
}
@@ -295,6 +310,7 @@
mTestLooper.dispatchAll();
assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ assertEquals(mIkeConnectionInfo, mGatewayConnection.getIkeConnectionInfo());
final ArgumentCaptor<LinkProperties> lpCaptor =
ArgumentCaptor.forClass(LinkProperties.class);
@@ -425,6 +441,9 @@
triggerValidation(NetworkAgent.VALIDATION_STATUS_NOT_VALID);
mTestLooper.dispatchAll();
+ verify(mConnMgr)
+ .reportNetworkConnectivity(eq(TEST_UNDERLYING_NETWORK_RECORD_1.network), eq(false));
+
final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
verify(mDeps, times(2))
.newWakeupMessage(
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index b9dfda3..6a9a1e2 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -213,7 +213,8 @@
VcnGatewayConnectionConfigTest.buildTestConfig(),
tunnelIface,
childSessionConfig,
- record);
+ record,
+ mIkeConnectionInfo);
verify(mDeps).getUnderlyingIfaceMtu(LOOPBACK_IFACE);
@@ -226,7 +227,8 @@
VcnGatewayConnectionConfigTest.buildTestConfig(),
tunnelIface,
childSessionConfig,
- record);
+ record,
+ mIkeConnectionInfo);
verify(mDeps, times(2)).getUnderlyingIfaceMtu(LOOPBACK_IFACE);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 5628321..785bff1 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -47,6 +47,8 @@
import android.net.NetworkCapabilities;
import android.net.ipsec.ike.ChildSessionCallback;
import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.IkeSessionConfiguration;
+import android.net.ipsec.ike.IkeSessionConnectionInfo;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
@@ -80,6 +82,13 @@
doReturn(TEST_SUB_GRP).when(TEST_SUB_INFO).getGroupUuid();
}
+ protected static final InetAddress TEST_ADDR = InetAddresses.parseNumericAddress("2001:db8::1");
+ protected static final InetAddress TEST_ADDR_2 =
+ InetAddresses.parseNumericAddress("2001:db8::2");
+ protected static final InetAddress TEST_ADDR_V4 =
+ InetAddresses.parseNumericAddress("192.0.2.1");
+ protected static final InetAddress TEST_ADDR_V4_2 =
+ InetAddresses.parseNumericAddress("192.0.2.2");
protected static final InetAddress TEST_DNS_ADDR =
InetAddresses.parseNumericAddress("2001:DB8:0:1::");
protected static final InetAddress TEST_DNS_ADDR_2 =
@@ -129,6 +138,7 @@
new TelephonySubscriptionSnapshot(
TEST_SUB_ID,
Collections.singletonMap(TEST_SUB_ID, TEST_SUB_INFO),
+ Collections.EMPTY_MAP,
Collections.EMPTY_MAP);
@NonNull protected final Context mContext;
@@ -148,6 +158,9 @@
@NonNull protected final IpSecService mIpSecSvc;
@NonNull protected final ConnectivityManager mConnMgr;
+ @NonNull protected final IkeSessionConnectionInfo mIkeConnectionInfo;
+ @NonNull protected final IkeSessionConfiguration mIkeSessionConfiguration;
+
protected VcnIkeSession mMockIkeSession;
protected VcnGatewayConnection mGatewayConnection;
@@ -173,6 +186,10 @@
VcnTestUtils.setupSystemService(
mContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+ mIkeConnectionInfo =
+ new IkeSessionConnectionInfo(TEST_ADDR, TEST_ADDR_2, mock(Network.class));
+ mIkeSessionConfiguration = new IkeSessionConfiguration.Builder(mIkeConnectionInfo).build();
+
doReturn(mContext).when(mVcnContext).getContext();
doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index 6c849b5..b0d6895 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -30,6 +30,7 @@
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesPriorityRule;
import static com.android.server.vcn.routeselection.NetworkPriorityClassifier.checkMatchesWifiPriorityRule;
import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName;
+import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -309,7 +310,9 @@
wifiNetworkPriority,
mWifiNetworkRecord,
selectedNetworkRecord,
- carrierConfig));
+ carrierConfig == null
+ ? null
+ : new PersistableBundleWrapper(carrierConfig)));
}
@Test
diff --git a/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java b/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
index 29511f7..e9e7078 100644
--- a/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
+++ b/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
@@ -46,34 +46,85 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MtuUtilsTest {
- @Test
- public void testUnderlyingMtuZero() {
+ private void verifyUnderlyingMtuZero(boolean isIpv4) {
assertEquals(
- IPV6_MIN_MTU, getMtu(emptyList(), ETHER_MTU /* maxMtu */, 0 /* underlyingMtu */));
+ IPV6_MIN_MTU,
+ getMtu(emptyList(), ETHER_MTU /* maxMtu */, 0 /* underlyingMtu */, isIpv4));
}
@Test
- public void testClampsToMaxMtu() {
- assertEquals(0, getMtu(emptyList(), 0 /* maxMtu */, IPV6_MIN_MTU /* underlyingMtu */));
+ public void testUnderlyingMtuZeroV4() {
+ verifyUnderlyingMtuZero(true /* isIpv4 */);
}
@Test
- public void testNormalModeAlgorithmLessThanUnderlyingMtu() {
- final List<ChildSaProposal> saProposals =
- Arrays.asList(
- new ChildSaProposal.Builder()
- .addEncryptionAlgorithm(
- ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256)
- .addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128)
- .build());
+ public void testUnderlyingMtuZeroV6() {
+ verifyUnderlyingMtuZero(false /* isIpv4 */);
+ }
+ private void verifyClampsToMaxMtu(boolean isIpv4) {
+ assertEquals(
+ 0, getMtu(emptyList(), 0 /* maxMtu */, IPV6_MIN_MTU /* underlyingMtu */, isIpv4));
+ }
+
+ @Test
+ public void testClampsToMaxMtuV4() {
+ verifyClampsToMaxMtu(true /* isIpv4 */);
+ }
+
+ @Test
+ public void testClampsToMaxMtuV6() {
+ verifyClampsToMaxMtu(false /* isIpv4 */);
+ }
+
+ private List<ChildSaProposal> buildChildSaProposalsWithNormalModeAlgo() {
+ return Arrays.asList(
+ new ChildSaProposal.Builder()
+ .addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256)
+ .addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128)
+ .build());
+ }
+
+ private void verifyNormalModeAlgorithmLessThanUnderlyingMtu(boolean isIpv4) {
final int actualMtu =
- getMtu(saProposals, ETHER_MTU /* maxMtu */, ETHER_MTU /* underlyingMtu */);
+ getMtu(
+ buildChildSaProposalsWithNormalModeAlgo(),
+ ETHER_MTU /* maxMtu */,
+ ETHER_MTU /* underlyingMtu */,
+ isIpv4);
assertTrue(ETHER_MTU > actualMtu);
}
@Test
- public void testCombinedModeAlgorithmLessThanUnderlyingMtu() {
+ public void testNormalModeAlgorithmLessThanUnderlyingMtuV4() {
+ verifyNormalModeAlgorithmLessThanUnderlyingMtu(true /* isIpv4 */);
+ }
+
+ @Test
+ public void testNormalModeAlgorithmLessThanUnderlyingMtuV6() {
+ verifyNormalModeAlgorithmLessThanUnderlyingMtu(false /* isIpv4 */);
+ }
+
+ @Test
+ public void testMtuIpv4LessThanMtuIpv6() {
+ final int actualMtuV4 =
+ getMtu(
+ buildChildSaProposalsWithNormalModeAlgo(),
+ ETHER_MTU /* maxMtu */,
+ ETHER_MTU /* underlyingMtu */,
+ true /* isIpv4 */);
+
+ final int actualMtuV6 =
+ getMtu(
+ buildChildSaProposalsWithNormalModeAlgo(),
+ ETHER_MTU /* maxMtu */,
+ ETHER_MTU /* underlyingMtu */,
+ false /* isIpv4 */);
+
+ assertTrue(actualMtuV4 < actualMtuV6);
+ }
+
+ private void verifyCombinedModeAlgorithmLessThanUnderlyingMtu(boolean isIpv4) {
final List<ChildSaProposal> saProposals =
Arrays.asList(
new ChildSaProposal.Builder()
@@ -86,7 +137,17 @@
.build());
final int actualMtu =
- getMtu(saProposals, ETHER_MTU /* maxMtu */, ETHER_MTU /* underlyingMtu */);
+ getMtu(saProposals, ETHER_MTU /* maxMtu */, ETHER_MTU /* underlyingMtu */, isIpv4);
assertTrue(ETHER_MTU > actualMtu);
}
+
+ @Test
+ public void testCombinedModeAlgorithmLessThanUnderlyingMtuV4() {
+ verifyCombinedModeAlgorithmLessThanUnderlyingMtu(true /* isIpv4 */);
+ }
+
+ @Test
+ public void testCombinedModeAlgorithmLessThanUnderlyingMtuV6() {
+ verifyCombinedModeAlgorithmLessThanUnderlyingMtu(false /* isIpv4 */);
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java b/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java
index a44a734..294f5c1 100644
--- a/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java
+++ b/tests/vcn/java/com/android/server/vcn/util/PersistableBundleUtilsTest.java
@@ -18,6 +18,8 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import android.os.PersistableBundle;
@@ -211,4 +213,84 @@
assertEquals(testInt, result);
}
+
+ private PersistableBundle getTestBundle() {
+ final PersistableBundle bundle = new PersistableBundle();
+
+ bundle.putBoolean(TEST_KEY + "Boolean", true);
+ bundle.putBooleanArray(TEST_KEY + "BooleanArray", new boolean[] {true, false});
+ bundle.putDouble(TEST_KEY + "Double", 0.1);
+ bundle.putDoubleArray(TEST_KEY + "DoubleArray", new double[] {0.1, 0.2, 0.3});
+ bundle.putInt(TEST_KEY + "Int", 1);
+ bundle.putIntArray(TEST_KEY + "IntArray", new int[] {1, 2});
+ bundle.putLong(TEST_KEY + "Long", 5L);
+ bundle.putLongArray(TEST_KEY + "LongArray", new long[] {0L, -1L, -2L});
+ bundle.putString(TEST_KEY + "String", "TEST");
+ bundle.putStringArray(TEST_KEY + "StringArray", new String[] {"foo", "bar", "bas"});
+ bundle.putPersistableBundle(
+ TEST_KEY + "PersistableBundle",
+ new TestClass(1, TEST_INT_ARRAY, TEST_STRING_PREFIX, new PersistableBundle())
+ .toPersistableBundle());
+
+ return bundle;
+ }
+
+ @Test
+ public void testMinimizeBundle() throws Exception {
+ final String[] minimizedKeys =
+ new String[] {
+ TEST_KEY + "Boolean",
+ TEST_KEY + "BooleanArray",
+ TEST_KEY + "Double",
+ TEST_KEY + "DoubleArray",
+ TEST_KEY + "Int",
+ TEST_KEY + "IntArray",
+ TEST_KEY + "Long",
+ TEST_KEY + "LongArray",
+ TEST_KEY + "String",
+ TEST_KEY + "StringArray",
+ TEST_KEY + "PersistableBundle"
+ };
+
+ final PersistableBundle testBundle = getTestBundle();
+ testBundle.putBoolean(TEST_KEY + "Boolean2", true);
+
+ final PersistableBundle minimized =
+ PersistableBundleUtils.minimizeBundle(testBundle, minimizedKeys);
+
+ // Verify that the minimized bundle is NOT the same in size OR values due to the extra
+ // Boolean2 key
+ assertFalse(PersistableBundleUtils.isEqual(testBundle, minimized));
+
+ // Verify that removing the extra key from the source bundle results in equality.
+ testBundle.remove(TEST_KEY + "Boolean2");
+ assertTrue(PersistableBundleUtils.isEqual(testBundle, minimized));
+ }
+
+ @Test
+ public void testEquality_identical() throws Exception {
+ final PersistableBundle left = getTestBundle();
+ final PersistableBundle right = getTestBundle();
+
+ assertTrue(PersistableBundleUtils.isEqual(left, right));
+ }
+
+ @Test
+ public void testEquality_different() throws Exception {
+ final PersistableBundle left = getTestBundle();
+ final PersistableBundle right = getTestBundle();
+
+ left.putBoolean(TEST_KEY + "Boolean2", true);
+ assertFalse(PersistableBundleUtils.isEqual(left, right));
+
+ left.remove(TEST_KEY + "Boolean2");
+ assertTrue(PersistableBundleUtils.isEqual(left, right));
+ }
+
+ @Test
+ public void testEquality_null() throws Exception {
+ assertFalse(PersistableBundleUtils.isEqual(getTestBundle(), null));
+ assertFalse(PersistableBundleUtils.isEqual(null, getTestBundle()));
+ assertTrue(PersistableBundleUtils.isEqual(null, null));
+ }
}
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 740b44e..50cd455 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -165,6 +165,7 @@
],
proto: {
export_proto_headers: true,
+ type: "full",
},
defaults: ["aapt2_defaults"],
}
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index b165c6b..7b94e71 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -15,6 +15,8 @@
$(aapt2_results): $(HOST_OUT_NATIVE_TESTS)/aapt2_tests/aapt2_tests
-$(HOST_OUT_NATIVE_TESTS)/aapt2_tests/aapt2_tests --gtest_output=xml:$@ > /dev/null 2>&1
+$(call declare-0p-target,$(aapt2_results))
+
aapt2_results :=
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp
index 7ea4ab1..0aca293 100644
--- a/tools/aapt2/SdkConstants.cpp
+++ b/tools/aapt2/SdkConstants.cpp
@@ -27,7 +27,7 @@
static ApiVersion sDevelopmentSdkLevel = 10000;
static const auto sDevelopmentSdkCodeNames =
- std::unordered_set<StringPiece>({"Q", "R", "S", "Sv2", "Tiramisu"});
+ std::unordered_set<StringPiece>({"Q", "R", "S", "Sv2", "Tiramisu", "UpsideDownCake"});
static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = {
{0x021c, 1},
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index be09545..383be56 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -349,7 +349,7 @@
const std::string root_dir = path.to_string();
std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()), closedir);
if (!d) {
- diag->Error(DiagMessage() << SystemErrorCodeToString(errno));
+ diag->Error(DiagMessage() << SystemErrorCodeToString(errno) << ": " << root_dir);
return {};
}
diff --git a/tools/apilint/deprecated_at_birth.py b/tools/apilint/deprecated_at_birth.py
old mode 100644
new mode 100755
index 297d9c3b..da9f19f
--- a/tools/apilint/deprecated_at_birth.py
+++ b/tools/apilint/deprecated_at_birth.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (C) 2021 The Android Open Source Project
#
@@ -208,17 +208,17 @@
def _parse_stream_path(path):
api = {}
- print "Parsing", path
+ print("Parsing %s" % path)
for f in os.listdir(path):
f = os.path.join(path, f)
if not os.path.isfile(f): continue
if not f.endswith(".txt"): continue
if f.endswith("removed.txt"): continue
- print "\t", f
+ print("\t%s" % f)
with open(f) as s:
api = _parse_stream(s, api)
- print "Parsed", len(api), "APIs"
- print
+ print("Parsed %d APIs" % len(api))
+ print()
return api
@@ -306,8 +306,8 @@
if "@Deprecated " in i.raw:
error(clazz, i, None, "Found API deprecation at birth " + i.ident)
- print "%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True),
- format(reset=True)))
+ print("%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True),
+ format(reset=True))))
for f in sorted(failures):
- print failures[f]
- print
+ print(failures[f])
+ print()