diff options
75 files changed, 5483 insertions, 793 deletions
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/AnnotatedElementPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/AnnotatedElementPerfTest.java new file mode 100644 index 000000000000..d38d5197b937 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/regression/AnnotatedElementPerfTest.java @@ -0,0 +1,331 @@ +/* + * 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.regression; + +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.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class AnnotatedElementPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + private Class<?> mType; + private Field mField; + private Method mMethod; + + @Before + public void setUp() throws Exception { + mType = Type.class; + mField = Type.class.getField("field"); + mMethod = Type.class.getMethod("method", String.class); + } + + // get annotations by member type and method + + @Test + public void timeGetTypeAnnotations() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mType.getAnnotations(); + } + } + + @Test + public void timeGetFieldAnnotations() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mField.getAnnotations(); + } + } + + @Test + public void timeGetMethodAnnotations() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mMethod.getAnnotations(); + } + } + + @Test + public void timeGetParameterAnnotations() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mMethod.getParameterAnnotations(); + } + } + + @Test + public void timeGetTypeAnnotation() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mType.getAnnotation(Marker.class); + } + } + + @Test + public void timeGetFieldAnnotation() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mField.getAnnotation(Marker.class); + } + } + + @Test + public void timeGetMethodAnnotation() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mMethod.getAnnotation(Marker.class); + } + } + + @Test + public void timeIsTypeAnnotationPresent() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mType.isAnnotationPresent(Marker.class); + } + } + + @Test + public void timeIsFieldAnnotationPresent() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mField.isAnnotationPresent(Marker.class); + } + } + + @Test + public void timeIsMethodAnnotationPresent() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mMethod.isAnnotationPresent(Marker.class); + } + } + + // get annotations by result size + + @Test + public void timeGetAllReturnsLargeAnnotation() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + HasLargeAnnotation.class.getAnnotations(); + } + } + + @Test + public void timeGetAllReturnsSmallAnnotation() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + HasSmallAnnotation.class.getAnnotations(); + } + } + + @Test + public void timeGetAllReturnsMarkerAnnotation() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + HasMarkerAnnotation.class.getAnnotations(); + } + } + + @Test + public void timeGetAllReturnsNoAnnotation() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + HasNoAnnotations.class.getAnnotations(); + } + } + + @Test + public void timeGetAllReturnsThreeAnnotations() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + HasThreeAnnotations.class.getAnnotations(); + } + } + + // get annotations with inheritance + + @Test + public void timeGetAnnotationsOnSubclass() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + ExtendsHasThreeAnnotations.class.getAnnotations(); + } + } + + @Test + public void timeGetDeclaredAnnotationsOnSubclass() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + ExtendsHasThreeAnnotations.class.getDeclaredAnnotations(); + } + } + + // get annotations with enclosing / inner classes + + @Test + public void timeGetDeclaredClasses() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + AnnotatedElementPerfTest.class.getDeclaredClasses(); + } + } + + @Test + public void timeGetDeclaringClass() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + HasSmallAnnotation.class.getDeclaringClass(); + } + } + + @Test + public void timeGetEnclosingClass() { + Object anonymousClass = new Object() {}; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + anonymousClass.getClass().getEnclosingClass(); + } + } + + @Test + public void timeGetEnclosingConstructor() { + Object anonymousClass = new Object() {}; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + anonymousClass.getClass().getEnclosingConstructor(); + } + } + + @Test + public void timeGetEnclosingMethod() { + Object anonymousClass = new Object() {}; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + anonymousClass.getClass().getEnclosingMethod(); + } + } + + @Test + public void timeGetModifiers() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + HasSmallAnnotation.class.getModifiers(); + } + } + + @Test + public void timeGetSimpleName() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + HasSmallAnnotation.class.getSimpleName(); + } + } + + @Test + public void timeIsAnonymousClass() { + Object anonymousClass = new Object() {}; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + anonymousClass.getClass().isAnonymousClass(); + } + } + + @Test + public void timeIsLocalClass() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + HasSmallAnnotation.class.isLocalClass(); + } + } + + // the annotated elements + + @Marker + public class Type { + @Marker public String field; + + @Marker + public void method(@Marker String parameter) {} + } + + @Large( + a = "on class", + b = {"A", "B", "C"}, + c = @Small(e = "E1", f = 1695938256, g = 7264081114510713000L), + d = {@Small(e = "E2", f = 1695938256, g = 7264081114510713000L)}) + public class HasLargeAnnotation {} + + @Small(e = "E1", f = 1695938256, g = 7264081114510713000L) + public class HasSmallAnnotation {} + + @Marker + public class HasMarkerAnnotation {} + + public class HasNoAnnotations {} + + @Large( + a = "on class", + b = {"A", "B", "C"}, + c = @Small(e = "E1", f = 1695938256, g = 7264081114510713000L), + d = {@Small(e = "E2", f = 1695938256, g = 7264081114510713000L)}) + @Small(e = "E1", f = 1695938256, g = 7264081114510713000L) + @Marker + public class HasThreeAnnotations {} + + public class ExtendsHasThreeAnnotations extends HasThreeAnnotations {} + + // the annotations + + @Retention(RetentionPolicy.RUNTIME) + public @interface Marker {} + + @Retention(RetentionPolicy.RUNTIME) + public @interface Large { + String a() default ""; + + String[] b() default {}; + + Small c() default @Small; + + Small[] d() default {}; + } + + @Retention(RetentionPolicy.RUNTIME) + public @interface Small { + String e() default ""; + + int f() default 0; + + long g() default 0L; + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/regression/BidiPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/BidiPerfTest.java new file mode 100644 index 000000000000..cc56868468e5 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/regression/BidiPerfTest.java @@ -0,0 +1,128 @@ +/* + * 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.regression; + +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.BigDecimal; +import java.text.AttributedCharacterIterator; +import java.text.Bidi; +import java.text.DecimalFormat; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class BidiPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + private static final AttributedCharacterIterator CHAR_ITER = + DecimalFormat.getInstance().formatToCharacterIterator(new BigDecimal(Math.PI)); + + @Test + public void time_createBidiFromIter() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + Bidi bidi = new Bidi(CHAR_ITER); + } + } + + @Test + public void time_createBidiFromCharArray() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + Bidi bd = + new Bidi( + new char[] {'s', 's', 's'}, + 0, + new byte[] {(byte) 1, (byte) 2, (byte) 3}, + 0, + 3, + Bidi.DIRECTION_RIGHT_TO_LEFT); + } + } + + @Test + public void time_createBidiFromString() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + Bidi bidi = new Bidi("Hello", Bidi.DIRECTION_LEFT_TO_RIGHT); + } + } + + @Test + public void time_reorderVisually() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + Bidi.reorderVisually( + new byte[] {2, 1, 3, 0, 4}, 0, new String[] {"H", "e", "l", "l", "o"}, 0, 5); + } + } + + @Test + public void time_hebrewBidi() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + Bidi bd = + new Bidi( + new char[] {'\u05D0', '\u05D0', '\u05D0'}, + 0, + new byte[] {(byte) -1, (byte) -2, (byte) -3}, + 0, + 3, + Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT); + bd = + new Bidi( + new char[] {'\u05D0', '\u05D0', '\u05D0'}, + 0, + new byte[] {(byte) -1, (byte) -2, (byte) -3}, + 0, + 3, + Bidi.DIRECTION_LEFT_TO_RIGHT); + } + } + + @Test + public void time_complicatedOverrideBidi() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + Bidi bd = + new Bidi( + "a\u05D0a\"a\u05D0\"\u05D0a".toCharArray(), + 0, + new byte[] {0, 0, 0, -3, -3, 2, 2, 0, 3}, + 0, + 9, + Bidi.DIRECTION_RIGHT_TO_LEFT); + } + } + + @Test + public void time_requiresBidi() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + Bidi.requiresBidi("\u05D0".toCharArray(), 1, 1); // false. + Bidi.requiresBidi("\u05D0".toCharArray(), 0, 1); // true. + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/regression/BigIntegerPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/BigIntegerPerfTest.java new file mode 100644 index 000000000000..662694b1b5d1 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/regression/BigIntegerPerfTest.java @@ -0,0 +1,69 @@ +/* + * 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.regression; + +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; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class BigIntegerPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Test + public void timeRandomDivision() throws Exception { + Random r = new Random(); + BigInteger x = new BigInteger(1024, r); + BigInteger y = new BigInteger(1024, r); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + x.divide(y); + } + } + + @Test + public void timeRandomGcd() throws Exception { + Random r = new Random(); + BigInteger x = new BigInteger(1024, r); + BigInteger y = new BigInteger(1024, r); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + x.gcd(y); + } + } + + @Test + public void timeRandomMultiplication() throws Exception { + Random r = new Random(); + BigInteger x = new BigInteger(1024, r); + BigInteger y = new BigInteger(1024, r); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + x.multiply(y); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/regression/BitSetPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/BitSetPerfTest.java new file mode 100644 index 000000000000..db5462cd69bf --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/regression/BitSetPerfTest.java @@ -0,0 +1,115 @@ +/* + * 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.regression; + +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.util.Arrays; +import java.util.BitSet; +import java.util.Collection; + +@RunWith(Parameterized.class) +@LargeTest +public class BitSetPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Parameters(name = "mSize={0}") + public static Collection<Object[]> data() { + return Arrays.asList(new Object[][] {{1000}, {10000}}); + } + + @Parameterized.Parameter(0) + public int mSize; + + private BitSet mBitSet; + + @Before + public void setUp() throws Exception { + mBitSet = new BitSet(mSize); + } + + @Test + public void timeIsEmptyTrue() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + if (!mBitSet.isEmpty()) throw new RuntimeException(); + } + } + + @Test + public void timeIsEmptyFalse() { + mBitSet.set(mBitSet.size() - 1); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + if (mBitSet.isEmpty()) throw new RuntimeException(); + } + } + + @Test + public void timeGet() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + int i = 1; + while (state.keepRunning()) { + mBitSet.get(++i % mSize); + } + } + + @Test + public void timeClear() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + int i = 1; + while (state.keepRunning()) { + mBitSet.clear(++i % mSize); + } + } + + @Test + public void timeSet() { + int i = 1; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mBitSet.set(++i % mSize); + } + } + + @Test + public void timeSetOn() { + int i = 1; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mBitSet.set(++i % mSize, true); + } + } + + @Test + public void timeSetOff() { + int i = 1; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mBitSet.set(++i % mSize, false); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/regression/BreakIteratorPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/BreakIteratorPerfTest.java new file mode 100644 index 000000000000..3952c12b3bfe --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/regression/BreakIteratorPerfTest.java @@ -0,0 +1,193 @@ +/* + * 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.regression; + +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.text.BreakIterator; +import java.util.Arrays; +import java.util.Collection; +import java.util.Locale; + +@RunWith(Parameterized.class) +@LargeTest +public final class BreakIteratorPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + public enum Text { + LIPSUM( + Locale.US, + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi mollis consequat" + + " nisl non pharetra. Praesent pretium vehicula odio sed ultrices. Aenean a" + + " felis libero. Vivamus sed commodo nibh. Pellentesque turpis lectus, euismod" + + " vel ante nec, cursus posuere orci. Suspendisse velit neque, fermentum" + + " luctus ultrices in, ultrices vitae arcu. Duis tincidunt cursus lorem. Nam" + + " ultricies accumsan quam vitae imperdiet. Pellentesque habitant morbi" + + " tristique senectus et netus et malesuada fames ac turpis egestas. Quisque" + + " aliquet pretium nisi, eget laoreet enim molestie sit amet. Class aptent" + + " taciti sociosqu ad litora torquent per conubia nostra, per inceptos" + + " himenaeos.\n" + + "Nam dapibus aliquam lacus ac suscipit. Proin in nibh sit amet purus congue" + + " laoreet eget quis nisl. Morbi gravida dignissim justo, a venenatis ante" + + " pulvinar at. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin" + + " ultrices vestibulum dui, vel aliquam lacus aliquam quis. Duis fringilla" + + " sapien ac lacus egestas, vel adipiscing elit euismod. Donec non tellus" + + " odio. Donec gravida eu massa ac feugiat. Aliquam erat volutpat. Praesent id" + + " adipiscing metus, nec laoreet enim. Aliquam vitae posuere turpis. Mauris ac" + + " pharetra sem. In at placerat tortor. Vivamus ac vehicula neque. Cras" + + " volutpat ullamcorper massa et varius. Praesent sagittis neque vitae nulla" + + " euismod pharetra.\n" + + "Sed placerat sapien non molestie sollicitudin. Nullam sit amet dictum quam." + + " Etiam tincidunt tortor vel pretium vehicula. Praesent fringilla ipsum vel" + + " velit luctus dignissim. Nulla massa ligula, mattis in enim et, mattis" + + " lacinia odio. Suspendisse tristique urna a orci commodo tempor. Duis" + + " lacinia egestas arcu a sollicitudin.\n" + + "In ac feugiat lacus. Nunc fermentum eu est at tristique. Pellentesque quis" + + " ligula et orci placerat lacinia. Maecenas quis mauris diam. Etiam mi ipsum," + + " tempus in purus quis, euismod faucibus orci. Nulla facilisi. Praesent sit" + + " amet sapien vel elit porta adipiscing. Phasellus sit amet volutpat diam.\n" + + "Proin bibendum elit non lacus pharetra, quis eleifend tellus placerat. Nulla" + + " facilisi. Maecenas ante diam, pellentesque mattis mattis in, porta ut" + + " lorem. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices" + + " posuere cubilia Curae; Nunc interdum tristique metus, in scelerisque odio" + + " fermentum eget. Cras nec venenatis lacus. Aenean euismod eget metus quis" + + " molestie. Cras tincidunt dolor ut massa ornare, in elementum lacus auctor." + + " Cras sodales nisl lacus, id ultrices ligula varius at. Sed tristique sit" + + " amet tellus vel mollis. Sed sed sollicitudin quam. Sed sed adipiscing" + + " risus, et dictum orci. Cras tempor pellentesque turpis et tempus."), + LONGPARA( + Locale.US, + "During dinner, Mr. Bennet scarcely spoke at all; but when the servants were" + + " withdrawn, he thought it time to have some conversation with his guest, and" + + " therefore started a subject in which he expected him to shine, by observing" + + " that he seemed very fortunate in his patroness. Lady Catherine de Bourgh's" + + " attention to his wishes, and consideration for his comfort, appeared very" + + " remarkable. Mr. Bennet could not have chosen better. Mr. Collins was" + + " eloquent in her praise. The subject elevated him to more than usual" + + " solemnity of manner, and with a most important aspect he protested that" + + " \"he had never in his life witnessed such behaviour in a person of" + + " rank--such affability and condescension, as he had himself experienced from" + + " Lady Catherine. She had been graciously pleased to approve of both of the" + + " discourses which he had already had the honour of preaching before her. She" + + " had also asked him twice to dine at Rosings, and had sent for him only the" + + " Saturday before, to make up her pool of quadrille in the evening. Lady" + + " Catherine was reckoned proud by many people he knew, but _he_ had never" + + " seen anything but affability in her. She had always spoken to him as she" + + " would to any other gentleman; she made not the smallest objection to his" + + " joining in the society of the neighbourhood nor to his leaving the parish" + + " occasionally for a week or two, to visit his relations. She had even" + + " condescended to advise him to marry as soon as he could, provided he chose" + + " with discretion; and had once paid him a visit in his humble parsonage," + + " where she had perfectly approved all the alterations he had been making," + + " and had even vouchsafed to suggest some herself--some shelves in the closet" + + " up stairs.\""), + GERMAN( + Locale.GERMANY, + "Aber dieser Freiheit setzte endlich der Winter ein Ziel. Draußen auf den Feldern" + + " und den hohen Bergen lag der Schnee und Peter wäre in seinem dünnen" + + " Leinwandjäckchen bald erfroren. Es war also seine einzige Freude, hinaus" + + " vor die Hütte zu treten und den Sperlingen Brotkrümchen zu streuen, was er" + + " sich jedesmal an seinem Frühstück absparte. Wenn nun die Vögel so lustig" + + " zwitscherten und um ihn herumflogen, da klopfte ihm das Herz vor Lust, und" + + " oft gab er ihnen sein ganzes Stück Schwarzbrot, ohne daran zu denken, daß" + + " er dafür alsdann selbst hungern müsse."), + THAI( + Locale.forLanguageTag("th-TH"), + "เป็นสำเนียงทางการของภาษาไทย" + + " เดิมทีเป็นการผสมผสานกันระหว่างสำเนียงอยุธยาและชาวไทยเชื้อสายจีนรุ่นหลังที่" + + "พูดไทยแทนกลุ่มภาษาจีน" + + " ลักษณะเด่นคือมีการออกเสียงที่ชัดเจนและแข็งกระด้างซึ่งได้รับอิทธิพลจากภาษาแต" + + "้จิ๋ว" + + " การออกเสียงพยัญชนะ สระ การผันวรรณยุกต์ที่ในภาษาไทยมาตรฐาน" + + " มาจากสำเนียงถิ่นนี้ในขณะที่ภาษาไทยสำเนียงอื่นล้วนเหน่อทั้งสิ้น" + + " คำศัพท์ที่ใช้ในสำเนียงกรุงเทพจำนวนมากได้รับมาจากกลุ่มภาษาจีนเช่นคำว่า โป๊," + + " เฮ็ง, อาหมวย, อาซิ่ม ซึ่งมาจากภาษาแต้จิ๋ว และจากภาษาจีนเช่น ถู(涂), ชิ่ว(去" + + " อ่านว่า\"ชู่\") และคำว่า ทาย(猜 อ่านว่า \"ชาย\") เป็นต้น" + + " เนื่องจากสำเนียงกรุงเทพได้รับอิทธิพลมาจากภาษาจีนดังนั้นตัวอักษร \"ร\"" + + " มักออกเสียงเหมารวมเป็น \"ล\" หรือคำควบกล่ำบางคำถูกละทิ้งไปด้วยเช่น รู้ เป็น" + + " ลู้, เรื่อง เป็น เลื่อง หรือ ประเทศ เป็น ปะเทศ" + + " เป็นต้นสร้างความลำบากให้แก่ต่างชาติที่ต้องการเรียนภาษาไทย" + + " แต่อย่างไรก็ตามผู้ที่พูดสำเนียงถิ่นนี้ก็สามารถออกอักขระภาษาไทยตามมาตรฐานได" + + "้อย่างถูกต้องเพียงแต่มักเผลอไม่ค่อยออกเสียง"), + THAI2(Locale.forLanguageTag("th-TH"), "this is the word browser in Thai: เบราว์เซอร์"), + TABS(Locale.US, "one\t\t\t\t\t\t\t\t\t\t\t\t\t\ttwo\n"), + ACCENT(Locale.US, "e\u0301\u00e9\nwhich is:\n\"e\\u0301\\u00e9\""), + EMOJI(Locale.US, ">>\ud83d\ude01<<\nwhich is:\n\">>\\ud83d\\ude01<<\""), + SPACES(Locale.US, " leading spaces and trailing ones too "), + EMPTY(Locale.US, ""), + NEWLINE(Locale.US, "\\n:\n"), + BIDI( + Locale.forLanguageTag("he-IL"), + "Sarah שרה is spelled sin ש resh ר heh ה from right to left."); + + final Locale mLocale; + final String mText; + + Text(Locale locale, String text) { + this.mText = text; + this.mLocale = locale; + } + } + + @Parameters(name = "mText={0}") + public static Collection<Object[]> data() { + return Arrays.asList( + new Object[][] { + {Text.ACCENT}, {Text.BIDI}, {Text.EMOJI}, {Text.EMPTY}, {Text.GERMAN}, + {Text.LIPSUM}, {Text.LONGPARA}, {Text.NEWLINE}, {Text.SPACES}, {Text.TABS}, + {Text.THAI}, {Text.THAI2} + }); + } + + @Parameterized.Parameter(0) + public Text mText; + + @Test + public void timeBreakIterator() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + BreakIterator it = BreakIterator.getLineInstance(mText.mLocale); + it.setText(mText.mText); + + while (it.next() != BreakIterator.DONE) { + // Keep iterating + } + } + } + + @Test + public void timeIcuBreakIterator() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + android.icu.text.BreakIterator it = + android.icu.text.BreakIterator.getLineInstance(mText.mLocale); + it.setText(mText.mText); + + while (it.next() != android.icu.text.BreakIterator.DONE) { + // Keep iterating + } + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferBulkPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferBulkPerfTest.java new file mode 100644 index 000000000000..8e57b28a0550 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferBulkPerfTest.java @@ -0,0 +1,139 @@ +/* + * 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.regression; + +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.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.util.Arrays; +import java.util.Collection; + +@RunWith(Parameterized.class) +@LargeTest +public class ByteBufferBulkPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Parameters(name = "mAligned({0}), mSrcBufferType({1}), mDataBufferType({2}), mBufferSize({3})") + public static Collection<Object[]> data() { + return Arrays.asList( + new Object[][] { + {true, MyBufferType.DIRECT, MyBufferType.DIRECT, 4096}, + {false, MyBufferType.DIRECT, MyBufferType.DIRECT, 4096}, + {true, MyBufferType.HEAP, MyBufferType.DIRECT, 4096}, + {false, MyBufferType.HEAP, MyBufferType.DIRECT, 4096}, + {true, MyBufferType.MAPPED, MyBufferType.DIRECT, 4096}, + {false, MyBufferType.MAPPED, MyBufferType.DIRECT, 4096}, + {true, MyBufferType.DIRECT, MyBufferType.HEAP, 4096}, + {false, MyBufferType.DIRECT, MyBufferType.HEAP, 4096}, + {true, MyBufferType.HEAP, MyBufferType.HEAP, 4096}, + {false, MyBufferType.HEAP, MyBufferType.HEAP, 4096}, + {true, MyBufferType.MAPPED, MyBufferType.HEAP, 4096}, + {false, MyBufferType.MAPPED, MyBufferType.HEAP, 4096}, + {true, MyBufferType.DIRECT, MyBufferType.MAPPED, 4096}, + {false, MyBufferType.DIRECT, MyBufferType.MAPPED, 4096}, + {true, MyBufferType.HEAP, MyBufferType.MAPPED, 4096}, + {false, MyBufferType.HEAP, MyBufferType.MAPPED, 4096}, + {true, MyBufferType.MAPPED, MyBufferType.MAPPED, 4096}, + {false, MyBufferType.MAPPED, MyBufferType.MAPPED, 4096}, + {true, MyBufferType.DIRECT, MyBufferType.DIRECT, 1232896}, + {false, MyBufferType.DIRECT, MyBufferType.DIRECT, 1232896}, + {true, MyBufferType.HEAP, MyBufferType.DIRECT, 1232896}, + {false, MyBufferType.HEAP, MyBufferType.DIRECT, 1232896}, + {true, MyBufferType.MAPPED, MyBufferType.DIRECT, 1232896}, + {false, MyBufferType.MAPPED, MyBufferType.DIRECT, 1232896}, + {true, MyBufferType.DIRECT, MyBufferType.HEAP, 1232896}, + {false, MyBufferType.DIRECT, MyBufferType.HEAP, 1232896}, + {true, MyBufferType.HEAP, MyBufferType.HEAP, 1232896}, + {false, MyBufferType.HEAP, MyBufferType.HEAP, 1232896}, + {true, MyBufferType.MAPPED, MyBufferType.HEAP, 1232896}, + {false, MyBufferType.MAPPED, MyBufferType.HEAP, 1232896}, + {true, MyBufferType.DIRECT, MyBufferType.MAPPED, 1232896}, + {false, MyBufferType.DIRECT, MyBufferType.MAPPED, 1232896}, + {true, MyBufferType.HEAP, MyBufferType.MAPPED, 1232896}, + {false, MyBufferType.HEAP, MyBufferType.MAPPED, 1232896}, + {true, MyBufferType.MAPPED, MyBufferType.MAPPED, 1232896}, + {false, MyBufferType.MAPPED, MyBufferType.MAPPED, 1232896}, + }); + } + + @Parameterized.Parameter(0) + public boolean mAligned; + + enum MyBufferType { + DIRECT, + HEAP, + MAPPED + } + + @Parameterized.Parameter(1) + public MyBufferType mSrcBufferType; + + @Parameterized.Parameter(2) + public MyBufferType mDataBufferType; + + @Parameterized.Parameter(3) + public int mBufferSize; + + public static ByteBuffer newBuffer(boolean aligned, MyBufferType bufferType, int bsize) + throws IOException { + int size = aligned ? bsize : bsize + 8 + 1; + ByteBuffer result = null; + switch (bufferType) { + case DIRECT: + result = ByteBuffer.allocateDirect(size); + break; + case HEAP: + result = ByteBuffer.allocate(size); + break; + case MAPPED: + File tmpFile = File.createTempFile("MappedByteBufferTest", ".tmp"); + tmpFile.createNewFile(); + tmpFile.deleteOnExit(); + RandomAccessFile raf = new RandomAccessFile(tmpFile, "rw"); + raf.setLength(size); + FileChannel fc = raf.getChannel(); + result = fc.map(FileChannel.MapMode.READ_WRITE, 0, fc.size()); + break; + } + result.position(aligned ? 0 : 1); + return result; + } + + @Test + public void timeByteBuffer_putByteBuffer() throws Exception { + ByteBuffer src = ByteBufferBulkPerfTest.newBuffer(mAligned, mSrcBufferType, mBufferSize); + ByteBuffer data = ByteBufferBulkPerfTest.newBuffer(mAligned, mDataBufferType, mBufferSize); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(mAligned ? 0 : 1); + data.position(mAligned ? 0 : 1); + src.put(data); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java new file mode 100644 index 000000000000..4bd7c4e4fa82 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java @@ -0,0 +1,532 @@ +/* + * 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.regression; + +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.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.CharBuffer; +import java.nio.DoubleBuffer; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.LongBuffer; +import java.nio.ShortBuffer; +import java.nio.channels.FileChannel; +import java.util.Arrays; +import java.util.Collection; + +@RunWith(Parameterized.class) +@LargeTest +public class ByteBufferPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + public enum MyByteOrder { + BIG(ByteOrder.BIG_ENDIAN), + LITTLE(ByteOrder.LITTLE_ENDIAN); + final ByteOrder mByteOrder; + + MyByteOrder(ByteOrder mByteOrder) { + this.mByteOrder = mByteOrder; + } + } + + @Parameters(name = "mByteOrder={0}, mAligned={1}, mBufferType={2}") + public static Collection<Object[]> data() { + return Arrays.asList( + new Object[][] { + {MyByteOrder.BIG, true, MyBufferType.DIRECT}, + {MyByteOrder.LITTLE, true, MyBufferType.DIRECT}, + {MyByteOrder.BIG, false, MyBufferType.DIRECT}, + {MyByteOrder.LITTLE, false, MyBufferType.DIRECT}, + {MyByteOrder.BIG, true, MyBufferType.HEAP}, + {MyByteOrder.LITTLE, true, MyBufferType.HEAP}, + {MyByteOrder.BIG, false, MyBufferType.HEAP}, + {MyByteOrder.LITTLE, false, MyBufferType.HEAP}, + {MyByteOrder.BIG, true, MyBufferType.MAPPED}, + {MyByteOrder.LITTLE, true, MyBufferType.MAPPED}, + {MyByteOrder.BIG, false, MyBufferType.MAPPED}, + {MyByteOrder.LITTLE, false, MyBufferType.MAPPED} + }); + } + + @Parameterized.Parameter(0) + public MyByteOrder mByteOrder; + + @Parameterized.Parameter(1) + public boolean mAligned; + + enum MyBufferType { + DIRECT, + HEAP, + MAPPED; + } + + @Parameterized.Parameter(2) + public MyBufferType mBufferType; + + public static ByteBuffer newBuffer( + MyByteOrder byteOrder, boolean aligned, MyBufferType bufferType) throws IOException { + int size = aligned ? 8192 : 8192 + 8 + 1; + ByteBuffer result = null; + switch (bufferType) { + case DIRECT: + result = ByteBuffer.allocateDirect(size); + break; + case HEAP: + result = ByteBuffer.allocate(size); + break; + case MAPPED: + File tmpFile = new File("/sdcard/bm.tmp"); + if (new File("/tmp").isDirectory()) { + // We're running on the desktop. + tmpFile = File.createTempFile("MappedByteBufferTest", ".tmp"); + } + tmpFile.createNewFile(); + tmpFile.deleteOnExit(); + RandomAccessFile raf = new RandomAccessFile(tmpFile, "rw"); + raf.setLength(8192 * 8); + FileChannel fc = raf.getChannel(); + result = fc.map(FileChannel.MapMode.READ_WRITE, 0, fc.size()); + break; + } + result.order(byteOrder.mByteOrder); + result.position(aligned ? 0 : 1); + return result; + } + + // + // peeking + // + + @Test + public void timeByteBuffer_getByte() throws Exception { + ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(mAligned ? 0 : 1); + for (int i = 0; i < 1024; ++i) { + src.get(); + } + } + } + + @Test + public void timeByteBuffer_getByteArray() throws Exception { + ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + byte[] dst = new byte[1024]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 0; i < 1024; ++i) { + src.position(mAligned ? 0 : 1); + src.get(dst); + } + } + } + + @Test + public void timeByteBuffer_getByte_indexed() throws Exception { + ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(mAligned ? 0 : 1); + for (int i = 0; i < 1024; ++i) { + src.get(i); + } + } + } + + @Test + public void timeByteBuffer_getChar() throws Exception { + ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(mAligned ? 0 : 1); + for (int i = 0; i < 1024; ++i) { + src.getChar(); + } + } + } + + @Test + public void timeCharBuffer_getCharArray() throws Exception { + CharBuffer src = + ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asCharBuffer(); + char[] dst = new char[1024]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 0; i < 1024; ++i) { + src.position(0); + src.get(dst); + } + } + } + + @Test + public void timeByteBuffer_getChar_indexed() throws Exception { + ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(mAligned ? 0 : 1); + for (int i = 0; i < 1024; ++i) { + src.getChar(i * 2); + } + } + } + + @Test + public void timeByteBuffer_getDouble() throws Exception { + ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(mAligned ? 0 : 1); + for (int i = 0; i < 1024; ++i) { + src.getDouble(); + } + } + } + + @Test + public void timeDoubleBuffer_getDoubleArray() throws Exception { + DoubleBuffer src = + ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asDoubleBuffer(); + double[] dst = new double[1024]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 0; i < 1024; ++i) { + src.position(0); + src.get(dst); + } + } + } + + @Test + public void timeByteBuffer_getFloat() throws Exception { + ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(mAligned ? 0 : 1); + for (int i = 0; i < 1024; ++i) { + src.getFloat(); + } + } + } + + @Test + public void timeFloatBuffer_getFloatArray() throws Exception { + FloatBuffer src = + ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asFloatBuffer(); + float[] dst = new float[1024]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 0; i < 1024; ++i) { + src.position(0); + src.get(dst); + } + } + } + + @Test + public void timeByteBuffer_getInt() throws Exception { + ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(mAligned ? 0 : 1); + for (int i = 0; i < 1024; ++i) { + src.getInt(); + } + } + } + + @Test + public void timeIntBuffer_getIntArray() throws Exception { + IntBuffer src = + ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asIntBuffer(); + int[] dst = new int[1024]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 0; i < 1024; ++i) { + src.position(0); + src.get(dst); + } + } + } + + @Test + public void timeByteBuffer_getLong() throws Exception { + ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(mAligned ? 0 : 1); + for (int i = 0; i < 1024; ++i) { + src.getLong(); + } + } + } + + @Test + public void timeLongBuffer_getLongArray() throws Exception { + LongBuffer src = + ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asLongBuffer(); + long[] dst = new long[1024]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 0; i < 1024; ++i) { + src.position(0); + src.get(dst); + } + } + } + + @Test + public void timeByteBuffer_getShort() throws Exception { + ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(mAligned ? 0 : 1); + for (int i = 0; i < 1024; ++i) { + src.getShort(); + } + } + } + + @Test + public void timeShortBuffer_getShortArray() throws Exception { + ShortBuffer src = + ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asShortBuffer(); + short[] dst = new short[1024]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 0; i < 1024; ++i) { + src.position(0); + src.get(dst); + } + } + } + + // + // poking + // + + @Test + public void timeByteBuffer_putByte() throws Exception { + ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(0); + for (int i = 0; i < 1024; ++i) { + src.put((byte) 0); + } + } + } + + @Test + public void timeByteBuffer_putByteArray() throws Exception { + ByteBuffer dst = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + byte[] src = new byte[1024]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 0; i < 1024; ++i) { + dst.position(mAligned ? 0 : 1); + dst.put(src); + } + } + } + + @Test + public void timeByteBuffer_putChar() throws Exception { + ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(mAligned ? 0 : 1); + for (int i = 0; i < 1024; ++i) { + src.putChar(' '); + } + } + } + + @Test + public void timeCharBuffer_putCharArray() throws Exception { + CharBuffer dst = + ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asCharBuffer(); + char[] src = new char[1024]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 0; i < 1024; ++i) { + dst.position(0); + dst.put(src); + } + } + } + + @Test + public void timeByteBuffer_putDouble() throws Exception { + ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(mAligned ? 0 : 1); + for (int i = 0; i < 1024; ++i) { + src.putDouble(0.0); + } + } + } + + @Test + public void timeDoubleBuffer_putDoubleArray() throws Exception { + DoubleBuffer dst = + ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asDoubleBuffer(); + double[] src = new double[1024]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 0; i < 1024; ++i) { + dst.position(0); + dst.put(src); + } + } + } + + @Test + public void timeByteBuffer_putFloat() throws Exception { + ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(mAligned ? 0 : 1); + for (int i = 0; i < 1024; ++i) { + src.putFloat(0.0f); + } + } + } + + @Test + public void timeFloatBuffer_putFloatArray() throws Exception { + FloatBuffer dst = + ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asFloatBuffer(); + float[] src = new float[1024]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 0; i < 1024; ++i) { + dst.position(0); + dst.put(src); + } + } + } + + @Test + public void timeByteBuffer_putInt() throws Exception { + ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(mAligned ? 0 : 1); + for (int i = 0; i < 1024; ++i) { + src.putInt(0); + } + } + } + + @Test + public void timeIntBuffer_putIntArray() throws Exception { + IntBuffer dst = + ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asIntBuffer(); + int[] src = new int[1024]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 0; i < 1024; ++i) { + dst.position(0); + dst.put(src); + } + } + } + + @Test + public void timeByteBuffer_putLong() throws Exception { + ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(mAligned ? 0 : 1); + for (int i = 0; i < 1024; ++i) { + src.putLong(0L); + } + } + } + + @Test + public void timeLongBuffer_putLongArray() throws Exception { + LongBuffer dst = + ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asLongBuffer(); + long[] src = new long[1024]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 0; i < 1024; ++i) { + dst.position(0); + dst.put(src); + } + } + } + + @Test + public void timeByteBuffer_putShort() throws Exception { + ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(mAligned ? 0 : 1); + for (int i = 0; i < 1024; ++i) { + src.putShort((short) 0); + } + } + } + + @Test + public void timeShortBuffer_putShortArray() throws Exception { + ShortBuffer dst = + ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType).asShortBuffer(); + short[] src = new short[1024]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 0; i < 1024; ++i) { + dst.position(0); + dst.put(src); + } + } + } + + @Test + public void time_new_byteArray() throws Exception { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + byte[] bs = new byte[8192]; + } + } + + @Test + public void time_ByteBuffer_allocate() throws Exception { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + ByteBuffer bs = ByteBuffer.allocate(8192); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferScalarVersusVectorPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferScalarVersusVectorPerfTest.java new file mode 100644 index 000000000000..81f9e59f2423 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferScalarVersusVectorPerfTest.java @@ -0,0 +1,149 @@ +/* + * 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.regression; + +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.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Collection; + +@RunWith(Parameterized.class) +@LargeTest +public class ByteBufferScalarVersusVectorPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Parameters(name = "mByteOrder={0}, mAligned={1}, mBufferType={2}") + public static Collection<Object[]> data() { + return Arrays.asList( + new Object[][] { + { + ByteBufferPerfTest.MyByteOrder.BIG, + true, + ByteBufferPerfTest.MyBufferType.DIRECT + }, + { + ByteBufferPerfTest.MyByteOrder.LITTLE, + true, + ByteBufferPerfTest.MyBufferType.DIRECT + }, + { + ByteBufferPerfTest.MyByteOrder.BIG, + false, + ByteBufferPerfTest.MyBufferType.DIRECT + }, + { + ByteBufferPerfTest.MyByteOrder.LITTLE, + false, + ByteBufferPerfTest.MyBufferType.DIRECT + }, + { + ByteBufferPerfTest.MyByteOrder.BIG, + true, + ByteBufferPerfTest.MyBufferType.HEAP + }, + { + ByteBufferPerfTest.MyByteOrder.LITTLE, + true, + ByteBufferPerfTest.MyBufferType.HEAP + }, + { + ByteBufferPerfTest.MyByteOrder.BIG, + false, + ByteBufferPerfTest.MyBufferType.HEAP + }, + { + ByteBufferPerfTest.MyByteOrder.LITTLE, + false, + ByteBufferPerfTest.MyBufferType.HEAP + }, + { + ByteBufferPerfTest.MyByteOrder.BIG, + true, + ByteBufferPerfTest.MyBufferType.MAPPED + }, + { + ByteBufferPerfTest.MyByteOrder.LITTLE, + true, + ByteBufferPerfTest.MyBufferType.MAPPED + }, + { + ByteBufferPerfTest.MyByteOrder.BIG, + false, + ByteBufferPerfTest.MyBufferType.MAPPED + }, + { + ByteBufferPerfTest.MyByteOrder.LITTLE, + false, + ByteBufferPerfTest.MyBufferType.MAPPED + } + }); + } + + @Parameterized.Parameter(0) + public ByteBufferPerfTest.MyByteOrder mByteOrder; + + @Parameterized.Parameter(1) + public boolean mAligned; + + @Parameterized.Parameter(2) + public ByteBufferPerfTest.MyBufferType mBufferType; + + @Test + public void timeManualByteBufferCopy() throws Exception { + ByteBuffer src = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + ByteBuffer dst = ByteBufferPerfTest.newBuffer(mByteOrder, mAligned, mBufferType); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(0); + dst.position(0); + for (int i = 0; i < 8192; ++i) { + dst.put(src.get()); + } + } + } + + @Test + public void timeByteBufferBulkGet() throws Exception { + ByteBuffer src = ByteBuffer.allocate(mAligned ? 8192 : 8192 + 1); + byte[] dst = new byte[8192]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(mAligned ? 0 : 1); + src.get(dst, 0, dst.length); + } + } + + @Test + public void timeDirectByteBufferBulkGet() throws Exception { + ByteBuffer src = ByteBuffer.allocateDirect(mAligned ? 8192 : 8192 + 1); + byte[] dst = new byte[8192]; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + src.position(mAligned ? 0 : 1); + src.get(dst, 0, dst.length); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CharacterPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CharacterPerfTest.java new file mode 100644 index 000000000000..28ec6ded3c86 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/regression/CharacterPerfTest.java @@ -0,0 +1,359 @@ +/* + * 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.regression; + +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.util.Arrays; +import java.util.Collection; + +/** + * Tests various Character methods, intended for testing multiple implementations against each + * other. + */ +@RunWith(Parameterized.class) +@LargeTest +public class CharacterPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Parameters(name = "mCharacterSet({0}), mOverload({1})") + public static Collection<Object[]> data() { + return Arrays.asList( + new Object[][] { + {CharacterSet.ASCII, Overload.CHAR}, + {CharacterSet.ASCII, Overload.INT}, + {CharacterSet.UNICODE, Overload.CHAR}, + {CharacterSet.UNICODE, Overload.INT} + }); + } + + @Parameterized.Parameter(0) + public CharacterSet mCharacterSet; + + @Parameterized.Parameter(1) + public Overload mOverload; + + private char[] mChars; + + @Before + public void setUp() throws Exception { + this.mChars = mCharacterSet.mChars; + } + + public enum Overload { + CHAR, + INT + } + + public double nanosToUnits(double nanos) { + return nanos / 65536; + } + + public enum CharacterSet { + ASCII(128), + UNICODE(65536); + final char[] mChars; + + CharacterSet(int size) { + this.mChars = new char[65536]; + for (int i = 0; i < 65536; ++i) { + mChars[i] = (char) (i % size); + } + } + } + + // A fake benchmark to give us a baseline. + @Test + public void timeIsSpace() { + boolean fake = false; + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + if (mOverload == Overload.CHAR) { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + fake ^= ((char) ch == ' '); + } + } + } else { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + fake ^= (ch == ' '); + } + } + } + } + + @Test + public void timeDigit() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + if (mOverload == Overload.CHAR) { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.digit(mChars[ch], 10); + } + } + } else { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.digit((int) mChars[ch], 10); + } + } + } + } + + @Test + public void timeGetNumericValue() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + if (mOverload == Overload.CHAR) { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.getNumericValue(mChars[ch]); + } + } + } else { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.getNumericValue((int) mChars[ch]); + } + } + } + } + + @Test + public void timeIsDigit() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + if (mOverload == Overload.CHAR) { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isDigit(mChars[ch]); + } + } + } else { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isDigit((int) mChars[ch]); + } + } + } + } + + @Test + public void timeIsIdentifierIgnorable() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + if (mOverload == Overload.CHAR) { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isIdentifierIgnorable(mChars[ch]); + } + } + } else { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isIdentifierIgnorable((int) mChars[ch]); + } + } + } + } + + @Test + public void timeIsJavaIdentifierPart() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + if (mOverload == Overload.CHAR) { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isJavaIdentifierPart(mChars[ch]); + } + } + } else { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isJavaIdentifierPart((int) mChars[ch]); + } + } + } + } + + @Test + public void timeIsJavaIdentifierStart() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + if (mOverload == Overload.CHAR) { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isJavaIdentifierStart(mChars[ch]); + } + } + } else { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isJavaIdentifierStart((int) mChars[ch]); + } + } + } + } + + @Test + public void timeIsLetter() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + if (mOverload == Overload.CHAR) { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isLetter(mChars[ch]); + } + } + } else { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isLetter((int) mChars[ch]); + } + } + } + } + + @Test + public void timeIsLetterOrDigit() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + if (mOverload == Overload.CHAR) { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isLetterOrDigit(mChars[ch]); + } + } + } else { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isLetterOrDigit((int) mChars[ch]); + } + } + } + } + + @Test + public void timeIsLowerCase() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + if (mOverload == Overload.CHAR) { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isLowerCase(mChars[ch]); + } + } + } else { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isLowerCase((int) mChars[ch]); + } + } + } + } + + @Test + public void timeIsSpaceChar() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + if (mOverload == Overload.CHAR) { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isSpaceChar(mChars[ch]); + } + } + } else { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isSpaceChar((int) mChars[ch]); + } + } + } + } + + @Test + public void timeIsUpperCase() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + if (mOverload == Overload.CHAR) { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isUpperCase(mChars[ch]); + } + } + } else { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isUpperCase((int) mChars[ch]); + } + } + } + } + + @Test + public void timeIsWhitespace() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + if (mOverload == Overload.CHAR) { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isWhitespace(mChars[ch]); + } + } + } else { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.isWhitespace((int) mChars[ch]); + } + } + } + } + + @Test + public void timeToLowerCase() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + if (mOverload == Overload.CHAR) { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.toLowerCase(mChars[ch]); + } + } + } else { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.toLowerCase((int) mChars[ch]); + } + } + } + } + + @Test + public void timeToUpperCase() { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + if (mOverload == Overload.CHAR) { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.toUpperCase(mChars[ch]); + } + } + } else { + while (state.keepRunning()) { + for (int ch = 0; ch < 65536; ++ch) { + Character.toUpperCase((int) mChars[ch]); + } + } + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CharsetForNamePerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CharsetForNamePerfTest.java new file mode 100644 index 000000000000..603b182e7c36 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/regression/CharsetForNamePerfTest.java @@ -0,0 +1,63 @@ +/* + * 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.regression; + +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 java.nio.charset.Charset; +import java.util.Arrays; +import java.util.Collection; + +@RunWith(Parameterized.class) +@LargeTest +public class CharsetForNamePerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Parameterized.Parameters(name = "mCharsetName({0})") + public static Collection<Object[]> data() { + return Arrays.asList( + new Object[][] { + {"UTF-16"}, + {"UTF-8"}, + {"UTF8"}, + {"ISO-8859-1"}, + {"8859_1"}, + {"ISO-8859-2"}, + {"8859_2"}, + {"US-ASCII"}, + {"ASCII"}, + }); + } + + @Parameterized.Parameter(0) + public String mCharsetName; + + @Test + public void timeCharsetForName() throws Exception { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + Charset.forName(mCharsetName); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CharsetPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CharsetPerfTest.java new file mode 100644 index 000000000000..437d186834e0 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/regression/CharsetPerfTest.java @@ -0,0 +1,146 @@ +/* + * 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.regression; + +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 CharsetPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Parameters(name = "mLength({0}), mName({1})") + public static Collection<Object[]> data() { + return Arrays.asList( + new Object[][] { + {1, "UTF-16"}, + {1, "UTF-8"}, + {1, "UTF8"}, + {1, "ISO-8859-1"}, + {1, "8859_1"}, + {1, "ISO-8859-2"}, + {1, "8859_2"}, + {1, "US-ASCII"}, + {1, "ASCII"}, + {10, "UTF-16"}, + {10, "UTF-8"}, + {10, "UTF8"}, + {10, "ISO-8859-1"}, + {10, "8859_1"}, + {10, "ISO-8859-2"}, + {10, "8859_2"}, + {10, "US-ASCII"}, + {10, "ASCII"}, + {100, "UTF-16"}, + {100, "UTF-8"}, + {100, "UTF8"}, + {100, "ISO-8859-1"}, + {100, "8859_1"}, + {100, "ISO-8859-2"}, + {100, "8859_2"}, + {100, "US-ASCII"}, + {100, "ASCII"}, + {1000, "UTF-16"}, + {1000, "UTF-8"}, + {1000, "UTF8"}, + {1000, "ISO-8859-1"}, + {1000, "8859_1"}, + {1000, "ISO-8859-2"}, + {1000, "8859_2"}, + {1000, "US-ASCII"}, + {1000, "ASCII"}, + {10000, "UTF-16"}, + {10000, "UTF-8"}, + {10000, "UTF8"}, + {10000, "ISO-8859-1"}, + {10000, "8859_1"}, + {10000, "ISO-8859-2"}, + {10000, "8859_2"}, + {10000, "US-ASCII"}, + {10000, "ASCII"}, + }); + } + + @Parameterized.Parameter(0) + public int mLength; + + @Parameterized.Parameter(1) + public String mName; + + @Test + public void time_new_String_BString() throws Exception { + byte[] bytes = makeBytes(makeString(mLength)); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + new String(bytes, mName); + } + } + + @Test + public void time_new_String_BII() throws Exception { + byte[] bytes = makeBytes(makeString(mLength)); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + new String(bytes, 0, bytes.length); + } + } + + @Test + public void time_new_String_BIIString() throws Exception { + byte[] bytes = makeBytes(makeString(mLength)); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + new String(bytes, 0, bytes.length, mName); + } + } + + @Test + public void time_String_getBytes() throws Exception { + String string = makeString(mLength); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + string.getBytes(mName); + } + } + + private static String makeString(int length) { + StringBuilder result = new StringBuilder(length); + for (int i = 0; i < length; ++i) { + result.append('A' + (i % 26)); + } + return result.toString(); + } + + private static byte[] makeBytes(String s) { + try { + return s.getBytes("US-ASCII"); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CharsetUtf8PerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CharsetUtf8PerfTest.java new file mode 100644 index 000000000000..f31e9c154f15 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/regression/CharsetUtf8PerfTest.java @@ -0,0 +1,82 @@ +/* + * 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.regression; + +import android.icu.lang.UCharacter; +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.nio.charset.Charset; + +/** + * Decode the same size of ASCII, BMP, Supplementary character using fast-path UTF-8 decoder. The + * fast-path code is in {@link StringFactory#newStringFromBytes(byte[], int, int, Charset)} + */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class CharsetUtf8PerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + private final int mNoOfBytes = 0x100; // 4MB + + private void makeUnicodeRange(int startingCodePoint, int endingCodePoint, int repeated) { + StringBuilder builder = new StringBuilder(); + for (int codePoint = startingCodePoint; codePoint <= endingCodePoint; codePoint++) { + if (codePoint < Character.MIN_SURROGATE || codePoint > Character.MAX_SURROGATE) { + builder.append(UCharacter.toString(codePoint)); + } + } + + String str = builder.toString(); + StringBuilder builder2 = new StringBuilder(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + for (int i = 0; i < repeated; i++) { + builder2.append(str); + } + } + } + + @Test + public void time_ascii() { + makeUnicodeRange(0, 0x7f, mNoOfBytes / 0x80); + } + + @Test + public void time_bmp2() { + makeUnicodeRange(0x0080, 0x07ff, mNoOfBytes / 2 / 0x780); + } + + @Test + public void time_bmp3() { + makeUnicodeRange( + 0x0800, + 0xffff, + mNoOfBytes / 3 / 0xf000 /* 0x10000 - 0x0800 - no of surrogate code points */); + } + + @Test + public void time_supplementary() { + makeUnicodeRange(0x10000, 0x10ffff, mNoOfBytes / 4 / 0x100000); + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ChecksumPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ChecksumPerfTest.java new file mode 100644 index 000000000000..1d33fcb250b9 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/regression/ChecksumPerfTest.java @@ -0,0 +1,74 @@ +/* + * 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.regression; + +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.zip.Adler32; +import java.util.zip.CRC32; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class ChecksumPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Test + public void timeAdler_block() throws Exception { + byte[] bytes = new byte[10000]; + Adler32 adler = new Adler32(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + adler.update(bytes); + } + } + + @Test + public void timeAdler_byte() throws Exception { + Adler32 adler = new Adler32(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + adler.update(1); + } + } + + @Test + public void timeCrc_block() throws Exception { + byte[] bytes = new byte[10000]; + CRC32 crc = new CRC32(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + crc.update(bytes); + } + } + + @Test + public void timeCrc_byte() throws Exception { + CRC32 crc = new CRC32(); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + crc.update(1); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CipherInputStreamPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CipherInputStreamPerfTest.java new file mode 100644 index 000000000000..35730ec753f1 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/regression/CipherInputStreamPerfTest.java @@ -0,0 +1,92 @@ +/* + * 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.regression; + +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.ByteArrayInputStream; +import java.io.InputStream; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; + +/** CipherInputStream benchmark. */ +@RunWith(AndroidJUnit4.class) +@LargeTest +public class CipherInputStreamPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + private static final int DATA_SIZE = 1024 * 1024; + private static final byte[] DATA = new byte[DATA_SIZE]; + + private static final int IV_SIZE = 16; + private static final byte[] IV = new byte[IV_SIZE]; + + static { + for (int i = 0; i < DATA_SIZE; i++) { + DATA[i] = (byte) i; + } + for (int i = 0; i < IV_SIZE; i++) { + IV[i] = (byte) i; + } + } + + private SecretKey mKey; + + private byte[] mOutput = new byte[8192]; + + private Cipher mCipherEncrypt; + + private AlgorithmParameterSpec mSpec; + + @Before + public void setUp() throws Exception { + KeyGenerator generator = KeyGenerator.getInstance("AES"); + generator.init(128); + mKey = generator.generateKey(); + + mSpec = new IvParameterSpec(IV); + + mCipherEncrypt = Cipher.getInstance("AES/CBC/PKCS5Padding"); + mCipherEncrypt.init(Cipher.ENCRYPT_MODE, mKey, mSpec); + } + + @Test + public void timeEncrypt() throws Exception { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mCipherEncrypt.init(Cipher.ENCRYPT_MODE, mKey, mSpec); + InputStream is = new CipherInputStream(new ByteArrayInputStream(DATA), mCipherEncrypt); + while (is.read(mOutput) != -1) { + // Keep iterating + } + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java new file mode 100644 index 000000000000..15c27f2366e1 --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/regression/CipherPerfTest.java @@ -0,0 +1,210 @@ +/* + * 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.regression; + +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.security.spec.AlgorithmParameterSpec; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; + +/** + * Cipher benchmarks. Only runs on AES currently because of the combinatorial explosion of the test + * as it stands. + */ +@RunWith(Parameterized.class) +@LargeTest +public class CipherPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Parameterized.Parameters( + name = + "mMode({0}), mPadding({1}), mKeySize({2}), mInputSize({3})," + + " mImplementation({4})") + public static Collection cases() { + int[] mKeySizes = new int[] {128, 192, 256}; + int[] inputSizes = new int[] {16, 32, 64, 128, 1024, 8192}; + final List<Object[]> params = new ArrayList<>(); + for (Mode mode : Mode.values()) { + for (Padding padding : Padding.values()) { + for (Implementation implementation : Implementation.values()) { + if ((mode == Mode.CBC + || mode == Mode.CFB + || mode == Mode.CTR + || mode == Mode.ECB + || mode == Mode.OFB) + && padding == Padding.PKCS1PADDING) { + continue; + } + if ((mode == Mode.CFB || mode == Mode.OFB) + && padding == Padding.NOPADDING + && implementation == Implementation.OpenSSL) { + continue; + } + for (int mKeySize : mKeySizes) { + for (int inputSize : inputSizes) { + params.add( + new Object[] { + mode, padding, mKeySize, inputSize, implementation + }); + } + } + } + } + } + return params; + } + + private static final int DATA_SIZE = 8192; + private static final byte[] DATA = new byte[DATA_SIZE]; + + private static final int IV_SIZE = 16; + + private static final byte[] IV = new byte[IV_SIZE]; + + static { + for (int i = 0; i < DATA_SIZE; i++) { + DATA[i] = (byte) i; + } + for (int i = 0; i < IV_SIZE; i++) { + IV[i] = (byte) i; + } + } + + public Algorithm mAlgorithm = Algorithm.AES; + + public enum Algorithm { + AES, + }; + + @Parameterized.Parameter(0) + public Mode mMode; + + public enum Mode { + CBC, + CFB, + CTR, + ECB, + OFB, + }; + + @Parameterized.Parameter(1) + public Padding mPadding; + + public enum Padding { + NOPADDING, + PKCS1PADDING, + }; + + @Parameterized.Parameter(2) + public int mKeySize; + + @Parameterized.Parameter(3) + public int mInputSize; + + @Parameterized.Parameter(4) + public Implementation mImplementation; + + public enum Implementation { + OpenSSL, + BouncyCastle + }; + + private String mProviderName; + + // Key generation isn't part of the benchmark so cache the results + private static Map<Integer, SecretKey> sKeySizes = new HashMap<Integer, SecretKey>(); + + private String mCipherAlgorithm; + private SecretKey mKey; + + private byte[] mOutput = new byte[DATA.length]; + + private Cipher mCipherEncrypt; + + private Cipher mCipherDecrypt; + + private AlgorithmParameterSpec mSpec; + + @Before + public void setUp() throws Exception { + mCipherAlgorithm = + mAlgorithm.toString() + "/" + mMode.toString() + "/" + mPadding.toString(); + + String mKeyAlgorithm = mAlgorithm.toString(); + mKey = sKeySizes.get(mKeySize); + if (mKey == null) { + KeyGenerator generator = KeyGenerator.getInstance(mKeyAlgorithm); + generator.init(mKeySize); + mKey = generator.generateKey(); + sKeySizes.put(mKeySize, mKey); + } + + switch (mImplementation) { + case OpenSSL: + mProviderName = "AndroidOpenSSL"; + break; + case BouncyCastle: + mProviderName = "BC"; + break; + default: + throw new RuntimeException(mImplementation.toString()); + } + + if (mMode != Mode.ECB) { + mSpec = new IvParameterSpec(IV); + } + + mCipherEncrypt = Cipher.getInstance(mCipherAlgorithm, mProviderName); + mCipherEncrypt.init(Cipher.ENCRYPT_MODE, mKey, mSpec); + + mCipherDecrypt = Cipher.getInstance(mCipherAlgorithm, mProviderName); + mCipherDecrypt.init(Cipher.DECRYPT_MODE, mKey, mSpec); + } + + @Test + public void timeEncrypt() throws Exception { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mCipherEncrypt.doFinal(DATA, 0, mInputSize, mOutput); + } + } + + @Test + public void timeDecrypt() throws Exception { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + mCipherDecrypt.doFinal(DATA, 0, mInputSize, mOutput); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CollatorPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CollatorPerfTest.java new file mode 100644 index 000000000000..6728e73d0c4b --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/regression/CollatorPerfTest.java @@ -0,0 +1,84 @@ +/* + * 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.regression; + +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.text.Collator; +import java.text.RuleBasedCollator; +import java.util.Locale; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class CollatorPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + private static final RuleBasedCollator COLLATOR = + (RuleBasedCollator) Collator.getInstance(Locale.US); + + @Test + public void timeCollatorPrimary() { + COLLATOR.setStrength(Collator.PRIMARY); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + COLLATOR.compare("abcde", "abcdf"); + COLLATOR.compare("abcde", "abcde"); + COLLATOR.compare("abcdf", "abcde"); + } + } + + @Test + public void timeCollatorSecondary() { + COLLATOR.setStrength(Collator.SECONDARY); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + COLLATOR.compare("abcdÂ", "abcdÄ"); + COLLATOR.compare("abcdÂ", "abcdÂ"); + COLLATOR.compare("abcdÄ", "abcdÂ"); + } + } + + @Test + public void timeCollatorTertiary() { + COLLATOR.setStrength(Collator.TERTIARY); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + COLLATOR.compare("abcdE", "abcde"); + COLLATOR.compare("abcde", "abcde"); + COLLATOR.compare("abcde", "abcdE"); + } + } + + @Test + public void timeCollatorIdentical() { + COLLATOR.setStrength(Collator.IDENTICAL); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + COLLATOR.compare("abcdȪ", "abcdȫ"); + COLLATOR.compare("abcdȪ", "abcdȪ"); + COLLATOR.compare("abcdȫ", "abcdȪ"); + } + } +} diff --git a/apct-tests/perftests/core/src/android/libcore/regression/CollectionsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/CollectionsPerfTest.java new file mode 100644 index 000000000000..a89efffcdd1f --- /dev/null +++ b/apct-tests/perftests/core/src/android/libcore/regression/CollectionsPerfTest.java @@ -0,0 +1,107 @@ +/* + * 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.regression; + +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.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Random; +import java.util.Vector; + +@RunWith(Parameterized.class) +@LargeTest +public class CollectionsPerfTest { + @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + @Parameters(name = "mArrayListLength({0})") + public static Collection<Object[]> data() { + return Arrays.asList(new Object[][] {{4}, {16}, {64}, {256}, {1024}}); + } + + @Parameterized.Parameter(0) + public int arrayListLength; + + public static Comparator<Integer> REVERSE = + new Comparator<Integer>() { + @Override + public int compare(Integer lhs, Integer rhs) { + int lhsAsInt = lhs.intValue(); + int rhsAsInt = rhs.intValue(); + return rhsAsInt < lhsAsInt ? -1 : (lhsAsInt == rhsAsInt ? 0 : 1); + } + }; + + @Test + public void timeSort_arrayList() throws Exception { + List<Integer> input = buildList(arrayListLength, ArrayList.class); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + Collections.sort(input); + } + } + + @Test + public void timeSortWithComparator_arrayList() throws Exception { + List<Integer> input = buildList(arrayListLength, ArrayList.class); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + Collections.sort(input, REVERSE); + } + } + + @Test + public void timeSort_vector() throws Exception { + List<Integer> input = buildList(arrayListLength, Vector.class); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + Collections.sort(input); + } + } + + @Test + public void timeSortWithComparator_vector() throws Exception { + List<Integer> input = buildList(arrayListLength, Vector.class); + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + Collections.sort(input, REVERSE); + } + } + + private static <T extends List<Integer>> List<Integer> buildList( + int arrayListLength, Class<T> listClass) throws Exception { + Random random = new Random(); + random.setSeed(0); + List<Integer> list = listClass.newInstance(); + for (int i = 0; i < arrayListLength; ++i) { + list.add(random.nextInt()); + } + return list; + } +} diff --git a/core/api/current.txt b/core/api/current.txt index 641b1a3697e2..e4fc9f3a57d1 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -29612,6 +29612,7 @@ package android.os { 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 { diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 9db3cdc1f03c..fd87a6168d7e 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -100,10 +100,12 @@ package android.hardware.usb { 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 95fc7ec58e95..abc2b74c0645 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -4169,8 +4169,14 @@ package android.hardware.usb { } 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 { diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl index 7f07af79ab69..3e79f18a4d65 100644 --- a/core/java/android/hardware/usb/IUsbManager.aidl +++ b/core/java/android/hardware/usb/IUsbManager.aidl @@ -18,6 +18,7 @@ package android.hardware.usb; 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 @@ interface IUsbManager 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 @@ interface IUsbManager /* 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 000000000000..3f3bbf63ed8b --- /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 964d7c1658dc..df70bfd4c1a0 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; 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.Collections; 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 class UsbManager { 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 class UsbManager { 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 @@ public class UsbManager { USB_HAL_V1_1, USB_HAL_V1_2, USB_HAL_V1_3, + USB_HAL_V2_0, }) public @interface UsbHalVersion {} @@ -1168,8 +1188,9 @@ public class UsbManager { /** * 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 @@ public class UsbManager { */ @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 @@ public class UsbManager { } /** + * 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 000000000000..9bc2b3892a1e --- /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 274e23fff292..f469a3e66d50 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_SINK; 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 @@ import java.util.Objects; */ @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 @@ public final class UsbPort { */ 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 @@ public final class UsbPort { * {@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 @@ public final class UsbPort { } /** + * 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 bb7aff651b3d..bd2f9aa55bda 100644 --- a/core/java/android/hardware/usb/UsbPortStatus.java +++ b/core/java/android/hardware/usb/UsbPortStatus.java @@ -19,7 +19,6 @@ package android.hardware.usb; 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 @@ import java.lang.annotation.RetentionPolicy; @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 @@ public final class UsbPortStatus implements Parcelable { /** * 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,23 +93,23 @@ public final class UsbPortStatus implements Parcelable { /** * There is currently nothing connected to this USB port. */ - public static final int MODE_NONE = Constants.PortMode.NONE; + public static final int MODE_NONE = 0; /** - * This USB port can act as a downstream facing port (host). + * This USB port can act as an upstream facing port (device). * - * <p> Implies that the port supports the {@link #POWER_ROLE_SOURCE} and - * {@link #DATA_ROLE_HOST} combination of roles (and possibly others as well). + * <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_DFP = Constants.PortMode.DFP; + public static final int MODE_UFP = 1 << 0; /** - * This USB port can act as an upstream facing port (device). + * This USB port can act as a downstream facing port (host). * - * <p> Implies that the port supports the {@link #POWER_ROLE_SINK} and - * {@link #DATA_ROLE_DEVICE} combination of roles (and possibly others as well). + * <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_UFP = Constants.PortMode.UFP; + 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 @@ public final class UsbPortStatus implements Parcelable { * * @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 @@ public final class UsbPortStatus implements Parcelable { /** @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 @@ public final class UsbPortStatus implements Parcelable { mSupportedRoleCombinations = supportedRoleCombinations; mContaminantProtectionStatus = contaminantProtectionStatus; mContaminantDetectionStatus = contaminantDetectionStatus; + mUsbDataEnabled = true; } /** @@ -323,6 +327,15 @@ public final class UsbPortStatus implements Parcelable { return mContaminantProtectionStatus; } + /** + * Returns UsbData status. + * + * @hide + */ + public boolean getUsbDataStatus() { + return mUsbDataEnabled; + } + @NonNull @Override public String toString() { @@ -336,6 +349,8 @@ public final class UsbPortStatus implements Parcelable { + getContaminantDetectionStatus() + ", contaminantProtectionStatus=" + getContaminantProtectionStatus() + + ", usbDataEnabled=" + + getUsbDataStatus() + "}"; } @@ -352,6 +367,7 @@ public final class UsbPortStatus implements Parcelable { dest.writeInt(mSupportedRoleCombinations); dest.writeInt(mContaminantProtectionStatus); dest.writeInt(mContaminantDetectionStatus); + dest.writeBoolean(mUsbDataEnabled); } public static final @NonNull Parcelable.Creator<UsbPortStatus> CREATOR = @@ -364,9 +380,10 @@ public final class UsbPortStatus implements Parcelable { 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/VpnProfileState.java b/core/java/android/net/VpnProfileState.java index 0f21a9d7f471..552a2c171f21 100644 --- a/core/java/android/net/VpnProfileState.java +++ b/core/java/android/net/VpnProfileState.java @@ -24,6 +24,7 @@ import android.os.Parcelable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; import java.util.StringJoiner; /** @@ -176,4 +177,19 @@ public final class VpnProfileState implements Parcelable { 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/os/Build.java b/core/java/android/os/Build.java index 394e7c3db17f..141b1412424a 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -1167,6 +1167,11 @@ public class Build { * 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/permission/OWNERS b/core/java/android/permission/OWNERS index b5466b6d3cb5..49f4bf71c172 100644 --- a/core/java/android/permission/OWNERS +++ b/core/java/android/permission/OWNERS @@ -5,10 +5,14 @@ evanxinchen@google.com ewol@google.com guojing@google.com jaysullivan@google.com +kvakil@google.com +mrulhania@google.com +narayan@google.com +ntmyren@google.com olekarg@google.com pyuli@google.com -ntmyren@google.com -svetoslavganov@android.com -svetoslavganov@google.com +raphk@google.com +rmacgregor@google.com +sergeynv@google.com theianchen@google.com zhanghai@google.com diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 4c1cc97e2ae6..71b5354e4d42 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -306,6 +306,13 @@ public final class DeviceConfig { public static final String NAMESPACE_MEDIA_NATIVE = "media_native"; /** + * Namespace for all Kernel Multi-Gen LRU feature. + * + * @hide + */ + public static final String NAMESPACE_MGLRU_NATIVE = "mglru_native"; + + /** * Namespace for all netd related features. * * @hide diff --git a/core/java/android/speech/OWNERS b/core/java/android/speech/OWNERS index 32f482264103..462d8bed743c 100644 --- a/core/java/android/speech/OWNERS +++ b/core/java/android/speech/OWNERS @@ -1,3 +1,4 @@ volnov@google.com eugeniom@google.com schfan@google.com +andreaambu@google.com diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index f2ddf52351de..ffa577a47c88 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -28283,7 +28283,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <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/usb/DumpUtils.java b/core/java/com/android/internal/usb/DumpUtils.java index 32601368f2fb..744fe595d283 100644 --- a/core/java/com/android/internal/usb/DumpUtils.java +++ b/core/java/com/android/internal/usb/DumpUtils.java @@ -244,7 +244,8 @@ public class DumpUtils { 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/jni/OWNERS b/core/jni/OWNERS index 4072687c87cc..361988c5cde8 100644 --- a/core/jni/OWNERS +++ b/core/jni/OWNERS @@ -66,6 +66,7 @@ per-file com_android_internal_net_* = file:/services/core/java/com/android/serve per-file android_graphics_* = file:/graphics/java/android/graphics/OWNERS per-file android_hardware_HardwareBuffer.cpp = file:/graphics/java/android/graphics/OWNERS per-file android_hardware_SyncFence.cpp = file:/graphics/java/android/graphics/OWNERS +per-file android_os_GraphicsEnvironment.cpp = file:platform/frameworks/native:/opengl/OWNERS ### Text ### per-file android_text_* = file:/core/java/android/text/OWNERS diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 51185d4d59c9..f4f9f9437eb0 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -1177,38 +1177,40 @@ static void relabelAllDirs(const char* path, const char* context, fail_fn_t fail } /** - * Make other apps data directory not visible in CE, DE storage. + * Hide the CE and DE data directories of non-related apps. * - * Apps without app data isolation can detect if another app is installed on system, - * by "touching" other apps data directory like /data/data/com.whatsapp, if it returns - * "Permission denied" it means apps installed, otherwise it returns "File not found". - * Traditional file permissions or SELinux can only block accessing those directories but - * can't fix fingerprinting like this. - * We fix it by "overlaying" data directory, and only relevant app data packages exists - * in data directories. + * Without this, apps can detect if any app is installed by trying to "touch" the app's CE + * or DE data directory, e.g. /data/data/com.whatsapp. This fails with EACCES if the app + * is installed, or ENOENT if it's not. Traditional file permissions or SELinux can only + * block accessing those directories but can't fix fingerprinting like this. * - * Steps: - * 1). Collect a list of all related apps (apps with same uid and allowlisted apps) data info - * (package name, data stored volume uuid, and inode number of its CE data directory) - * 2). Mount tmpfs on /data/data, /data/user(_de) and /mnt/expand, so apps no longer - * able to access apps data directly. - * 3). For each related app, create its app data directory and bind mount the actual content - * from apps data mirror directory. This works on both CE and DE storage, as DE storage - * is always available even storage is FBE locked, while we use inode number to find - * the encrypted DE directory in mirror so we can still bind mount it successfully. + * Instead, we hide non-related apps' data directories from the filesystem entirely by + * mounting tmpfs instances over their parent directories and bind-mounting in just the + * needed app data directories. This is done in a private mount namespace. * - * Example: - * 0). Assuming com.android.foo CE data is stored in /data/data and no shared uid - * 1). Mount a tmpfs on /data/data, /data/user, /data/user_de, /mnt/expand - * List = ["com.android.foo", "null" (volume uuid "null"=default), - * 123456 (inode number)] - * 2). On DE storage, we create a directory /data/user_de/0/com.com.android.foo, and bind - * mount (in the app's mount namespace) it from /data_mirror/data_de/0/com.android.foo. - * 3). We do similar for CE storage. But in direct boot mode, as /data_mirror/data_ce/0/ is - * encrypted, we can't find a directory with name com.android.foo on it, so we will - * use the inode number to find the right directory instead, which that directory content will - * be decrypted after storage is decrypted. + * Steps: + * (1) Collect a list of all related apps (apps with same uid and allowlisted apps) data info + * (package name, data stored volume uuid, and inode number of its CE data directory) + * (2) Mount tmpfs on /data/data and /data/user{,_de}, and on /mnt/expand/$volume/user{,_de} + * for all adoptable storage volumes. This hides all app data directories. + * (3) For each related app, create stubs for its data directories in the relevant tmpfs + * instances, then bind mount in the actual directories from /data_mirror. This works + * for both the CE and DE directories. DE storage is always unlocked, whereas the + * app's CE directory can be found via inode number if CE storage is locked. * + * Example assuming user 0, app "com.android.foo", no shared uid, and no adoptable storage: + * (1) Info = ["com.android.foo", "null" (volume uuid "null"=default), "123456" (inode number)] + * (2) Mount tmpfs on /data/data, /data/user, and /data/user_de. + * (3) For DE storage, create a directory /data/user_de/0/com.android.foo and bind mount + * /data_mirror/data_de/0/com.android.foo onto it. + * (4) Do similar for CE storage. But if the device is in direct boot mode, then CE + * storage will be locked, so the app's CE data directory won't exist at the usual + * path /data_mirror/data_ce/0/com.android.foo. It will still exist in + * /data_mirror/data_ce/0, but its filename will be an unpredictable no-key name. In + * this case, we use the inode number to find the right directory instead. Note that + * the bind-mounted app CE data directory will remain locked. It will be unlocked + * automatically if/when the user's CE storage is unlocked, since adding an encryption + * key takes effect on a whole filesystem instance including all its mounts. */ static void isolateAppData(JNIEnv* env, const std::vector<std::string>& merged_data_info_list, uid_t uid, const char* process_name, @@ -1599,10 +1601,11 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, // 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))); } } diff --git a/core/proto/android/os/system_properties.proto b/core/proto/android/os/system_properties.proto index 7e26952a92da..4f3eeb0733f4 100644 --- a/core/proto/android/os/system_properties.proto +++ b/core/proto/android/os/system_properties.proto @@ -434,8 +434,9 @@ message SystemPropertiesProto { optional string vibrator = 37; optional string virtual_device = 38; optional string vulkan = 39; + optional string egl_legacy = 40; - // Next Tag: 40 + // Next Tag: 41 } optional Hardware hardware = 27; @@ -555,4 +556,3 @@ message SystemPropertiesProto { // Next Tag: 32 } - diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto index 45f8c132fbf0..cd002da38e20 100644 --- a/core/proto/android/service/usb.proto +++ b/core/proto/android/service/usb.proto @@ -195,9 +195,19 @@ message UsbIsHeadsetProto { 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 @@ message UsbPortStatusProto { 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/OWNERS b/core/res/OWNERS index a20b89540a04..0608a35c29d0 100644 --- a/core/res/OWNERS +++ b/core/res/OWNERS @@ -34,4 +34,8 @@ per-file res/xml/public-final.xml = file:/tools/aapt2/OWNERS per-file res/xml/config_user_types.xml = file:/MULTIUSER_OWNERS # Car -per-file res/values/dimens_car.xml = file:/platform/packages/services/Car:/OWNERS
\ No newline at end of file +per-file res/values/dimens_car.xml = file:/platform/packages/services/Car:/OWNERS + +# Telephony +per-file res/values/config_telephony.xml = file:/platform/frameworks/opt/telephony:/OWNERS + diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index c5d50fb05fa2..a8ab1a88e4c7 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2572,36 +2572,10 @@ <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. - --> - <string name="config_tcp_buffers" translatable="false"></string> - <!-- Configure ethernet tcp buffersizes in the form: rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max --> <string name="config_ethernet_tcp_buffers" translatable="false">524288,1048576,3145728,524288,1048576,2097152</string> - <!-- What source to use to estimate link upstream and downstream bandwidth capacities. - Default is bandwidth_estimator. - 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: @@ -3148,27 +3122,6 @@ <!-- String array containing numbers that shouldn't be logged. Country-specific. --> <string-array name="unloggable_phone_numbers" /> - <!-- Cellular data service package name to bind to by default. If none is specified in an overlay, an - empty string is passed in --> - <string name="config_wwan_data_service_package" translatable="false">com.android.phone</string> - - <!-- IWLAN data service package name to bind to by default. If none is specified in an overlay, an - empty string is passed in --> - <string name="config_wlan_data_service_package" translatable="false"></string> - - <!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec - tunnels across service restart. If iwlan tunnels are not persisted across restart, - Framework will clean up dangling data connections when service restarts --> - <bool name="config_wlan_data_service_conn_persistence_on_restart">true</bool> - - <!-- Cellular data service class name to bind to by default. If none is specified in an overlay, an - empty string is passed in --> - <string name="config_wwan_data_service_class" translatable="false"></string> - - <!-- IWLAN data service class name to bind to by default. If none is specified in an overlay, an - empty string is passed in --> - <string name="config_wlan_data_service_class" translatable="false"></string> - <bool name="config_networkSamplingWakesDevice">true</bool> <!--From SmsMessage--> @@ -3254,11 +3207,6 @@ and one pSIM) --> <integer name="config_num_physical_slots">1</integer> - <!-- When a radio power off request is received, we will delay completing the request until - either IMS moves to the deregistered state or the timeout defined by this configuration - elapses. If 0, this feature is disabled and we do not delay radio power off requests.--> - <integer name="config_delay_for_ims_dereg_millis">0</integer> - <!--Thresholds for LTE dbm in status bar--> <integer-array translatable="false" name="config_lteDbmThresholds"> <item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN --> @@ -4194,24 +4142,6 @@ <bool name="config_keepRestrictedProfilesInBackground">true</bool> - <!-- Cellular network service package name to bind to by default. --> - <string name="config_wwan_network_service_package" translatable="false">com.android.phone</string> - - <!-- Cellular network service class name to bind to by default.--> - <string name="config_wwan_network_service_class" translatable="false"></string> - - <!-- IWLAN network service package name to bind to by default. If none is specified in an overlay, an - empty string is passed in --> - <string name="config_wlan_network_service_package" translatable="false"></string> - - <!-- IWLAN network service class name to bind to by default. If none is specified in an overlay, an - empty string is passed in --> - <string name="config_wlan_network_service_class" translatable="false"></string> - <!-- Telephony qualified networks service package name to bind to by default. --> - <string name="config_qualified_networks_service_package" translatable="false"></string> - - <!-- Telephony qualified networks service class name to bind to by default. --> - <string name="config_qualified_networks_service_class" translatable="false"></string> <!-- Wear devices: Controls the radios affected by Activity Mode. --> <string-array name="config_wearActivityModeRadios"> <item>"wifi"</item> diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml new file mode 100644 index 000000000000..cd3578c727f0 --- /dev/null +++ b/core/res/res/values/config_telephony.xml @@ -0,0 +1,112 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> +<resources> + <!-- This file defines Android telephony related resources --> + + <!-- Whether force to enable telephony new data stack or not --> + <bool name="config_force_enable_telephony_new_data_stack">true</bool> + <java-symbol type="bool" name="config_force_enable_telephony_new_data_stack" /> + + <!-- 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> + <java-symbol type="array" name="config_network_type_tcp_buffers" /> + + <!-- 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. + --> + <string name="config_tcp_buffers" translatable="false"></string> + <java-symbol type="string" name="config_tcp_buffers" /> + + <!-- What source to use to estimate link upstream and downstream bandwidth capacities. + Default is bandwidth_estimator. + Values are bandwidth_estimator, carrier_config and modem. --> + <string name="config_bandwidthEstimateSource">bandwidth_estimator</string> + <java-symbol type="string" name="config_bandwidthEstimateSource" /> + + <!-- Whether to adopt the predefined handover policies for IWLAN. + {@see CarrierConfigManager#KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY} + --> + <bool name="config_enable_iwlan_handover_policy">true</bool> + <java-symbol type="bool" name="config_enable_iwlan_handover_policy" /> + + <!-- When a radio power off request is received, we will delay completing the request until + either IMS moves to the deregistered state or the timeout defined by this configuration + elapses. If 0, this feature is disabled and we do not delay radio power off requests.--> + <integer name="config_delay_for_ims_dereg_millis">0</integer> + <java-symbol type="integer" name="config_delay_for_ims_dereg_millis" /> + + <!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec + tunnels across service restart. If iwlan tunnels are not persisted across restart, + Framework will clean up dangling data connections when service restarts --> + <bool name="config_wlan_data_service_conn_persistence_on_restart">true</bool> + <java-symbol type="bool" name="config_wlan_data_service_conn_persistence_on_restart" /> + + <!-- Cellular data service package name to bind to by default. If none is specified in an + overlay, an empty string is passed in --> + <string name="config_wwan_data_service_package" translatable="false">com.android.phone</string> + <java-symbol type="string" name="config_wwan_data_service_package" /> + + <!-- IWLAN data service package name to bind to by default. If none is specified in an overlay, + an empty string is passed in --> + <string name="config_wlan_data_service_package" translatable="false"></string> + <java-symbol type="string" name="config_wlan_data_service_package" /> + + <!-- Cellular data service class name to bind to by default. If none is specified in an overlay, + an empty string is passed in --> + <string name="config_wwan_data_service_class" translatable="false"></string> + <java-symbol type="string" name="config_wwan_data_service_class" /> + + <!-- IWLAN data service class name to bind to by default. If none is specified in an overlay, an + empty string is passed in --> + <string name="config_wlan_data_service_class" translatable="false"></string> + <java-symbol type="string" name="config_wlan_data_service_class" /> + + <!-- Cellular network service package name to bind to by default. --> + <string name="config_wwan_network_service_package" translatable="false"> + com.android.phone + </string> + <java-symbol type="string" name="config_wwan_network_service_package" /> + + <!-- Cellular network service class name to bind to by default.--> + <string name="config_wwan_network_service_class" translatable="false"></string> + <java-symbol type="string" name="config_wwan_network_service_class" /> + + <!-- IWLAN network service package name to bind to by default. If none is specified in an + overlay, an empty string is passed in --> + <string name="config_wlan_network_service_package" translatable="false"></string> + <java-symbol type="string" name="config_wlan_network_service_package" /> + + <!-- IWLAN network service class name to bind to by default. If none is specified in an overlay, + an empty string is passed in --> + <string name="config_wlan_network_service_class" translatable="false"></string> + <java-symbol type="string" name="config_wlan_network_service_class" /> + + <!-- Telephony qualified networks service package name to bind to by default. --> + <string name="config_qualified_networks_service_package" translatable="false"></string> + <java-symbol type="string" name="config_qualified_networks_service_package" /> + + <!-- Telephony qualified networks service class name to bind to by default. --> + <string name="config_qualified_networks_service_class" translatable="false"></string> + <java-symbol type="string" name="config_qualified_networks_service_class" /> +</resources> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 6e0fd91cc915..4ad95b6fc877 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -294,17 +294,6 @@ <java-symbol type="bool" name="config_enableBurnInProtection" /> <java-symbol type="bool" name="config_hotswapCapable" /> <java-symbol type="bool" name="config_mms_content_disposition_support" /> - <java-symbol type="string" name="config_wwan_network_service_package" /> - <java-symbol type="string" name="config_wlan_network_service_package" /> - <java-symbol type="string" name="config_wwan_network_service_class" /> - <java-symbol type="string" name="config_wlan_network_service_class" /> - <java-symbol type="bool" name="config_wlan_data_service_conn_persistence_on_restart" /> - <java-symbol type="string" name="config_wwan_data_service_package" /> - <java-symbol type="string" name="config_wlan_data_service_package" /> - <java-symbol type="string" name="config_wwan_data_service_class" /> - <java-symbol type="string" name="config_wlan_data_service_class" /> - <java-symbol type="string" name="config_qualified_networks_service_package" /> - <java-symbol type="string" name="config_qualified_networks_service_class" /> <java-symbol type="bool" name="config_networkSamplingWakesDevice" /> <java-symbol type="bool" name="config_showMenuShortcutsWhenKeyboardPresent" /> <java-symbol type="bool" name="config_sip_wifi_only" /> @@ -466,9 +455,6 @@ <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" /> @@ -480,10 +466,8 @@ <java-symbol type="string" name="config_deviceSpecificDevicePolicyManagerService" /> <java-symbol type="string" name="config_deviceSpecificAudioService" /> <java-symbol type="integer" name="config_num_physical_slots" /> - <java-symbol type="integer" name="config_delay_for_ims_dereg_millis" /> <java-symbol type="array" name="config_integrityRuleProviderPackages" /> <java-symbol type="bool" name="config_useAssistantVolume" /> - <java-symbol type="string" name="config_bandwidthEstimateSource" /> <java-symbol type="integer" name="config_smartSelectionInitializedTimeoutMillis" /> <java-symbol type="integer" name="config_smartSelectionInitializingTimeoutMillis" /> <java-symbol type="bool" name="config_hibernationDeletesOatArtifactsEnabled"/> diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml index d1d86a782016..dab4f1bdda4d 100644 --- a/core/res/res/xml/sms_short_codes.xml +++ b/core/res/res/xml/sms_short_codes.xml @@ -156,7 +156,7 @@ <!-- Italy: 5 digits (premium=41xxx,42xxx), plus EU: https://www.itu.int/dms_pub/itu-t/oth/02/02/T020200006B0001PDFE.pdf --> - <shortcode country="it" pattern="\\d{5}" premium="4\\d{4}" free="116\\d{3}|4112503|40\\d{0,12}" standard="430\\d{2}|431\\d{2}|434\\d{4}|435\\d{4}|439\\d{7}" /> + <shortcode country="it" pattern="\\d{5}" premium="44[0-4]\\d{2}|47[0-4]\\d{2}|48[0-4]\\d{2}|44[5-9]\\d{4}|47[5-9]\\d{4}|48[5-9]\\d{4}|455\\d{2}|499\\d{2}" free="116\\d{3}|4112503|40\\d{0,12}" standard="430\\d{2}|431\\d{2}|434\\d{4}|435\\d{4}|439\\d{7}" /> <!-- Japan: 8083 used by SOFTBANK_DCB_2 --> <shortcode country="jp" pattern="\\d{1,5}" free="8083" /> diff --git a/core/tests/PackageInstallerSessions/Android.bp b/core/tests/PackageInstallerSessions/Android.bp index c112cbb47b55..de2a013df999 100644 --- a/core/tests/PackageInstallerSessions/Android.bp +++ b/core/tests/PackageInstallerSessions/Android.bp @@ -51,6 +51,5 @@ android_test { ], platform_apis: true, - sdk_version: "core_platform", test_suites: ["device-tests"], } diff --git a/core/tests/bugreports/Android.bp b/core/tests/bugreports/Android.bp index f87797a90c90..43a96793447d 100644 --- a/core/tests/bugreports/Android.bp +++ b/core/tests/bugreports/Android.bp @@ -35,7 +35,6 @@ android_test { "truth-prebuilt", ], test_suites: ["general-tests"], - sdk_version: "test_current", platform_apis: true, } diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index 05ec00fe84f0..b52118425a2d 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -73,7 +73,6 @@ android_test { ], platform_apis: true, - sdk_version: "core_platform", test_suites: ["device-tests"], certificate: "platform", diff --git a/packages/CtsShim/build/Android.bp b/packages/CtsShim/build/Android.bp index 0b3f9bb62a9b..54351131a433 100644 --- a/packages/CtsShim/build/Android.bp +++ b/packages/CtsShim/build/Android.bp @@ -45,6 +45,10 @@ android_app { jni_libs: ["libshim_jni"], uses_libs: ["android.test.runner"], + + apex_available: [ + "com.android.apex.cts.shim.v2_apk_in_apex_upgrades", + ], } genrule { @@ -84,6 +88,7 @@ android_app { "//apex_available:platform", "com.android.apex.cts.shim.v1", "com.android.apex.cts.shim.v2", + "com.android.apex.cts.shim.v2_apk_in_apex_upgrades", "com.android.apex.cts.shim.v2_no_hashtree", "com.android.apex.cts.shim.v2_legacy", "com.android.apex.cts.shim.v2_sdk_target_p", @@ -159,6 +164,7 @@ android_app { "//apex_available:platform", "com.android.apex.cts.shim.v1", "com.android.apex.cts.shim.v2", + "com.android.apex.cts.shim.v2_apk_in_apex_upgrades", "com.android.apex.cts.shim.v2_no_hashtree", "com.android.apex.cts.shim.v2_legacy", "com.android.apex.cts.shim.v2_sdk_target_p", diff --git a/packages/CtsShim/build/jni/Android.bp b/packages/CtsShim/build/jni/Android.bp index ba586dbb2d88..2dbf2a212cc3 100644 --- a/packages/CtsShim/build/jni/Android.bp +++ b/packages/CtsShim/build/jni/Android.bp @@ -32,6 +32,7 @@ cc_library_shared { "//apex_available:platform", "com.android.apex.cts.shim.v1", "com.android.apex.cts.shim.v2", + "com.android.apex.cts.shim.v2_apk_in_apex_upgrades", "com.android.apex.cts.shim.v2_no_hashtree", "com.android.apex.cts.shim.v2_legacy", "com.android.apex.cts.shim.v2_sdk_target_p", diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java index e5fd0ba5d9bc..bbb1ec6a5623 100644 --- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java +++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java @@ -16,6 +16,7 @@ package com.android.settingslib.datetime; +import android.annotation.Nullable; import android.content.Context; import android.content.res.XmlResourceParser; import android.icu.text.TimeZoneFormat; @@ -35,6 +36,7 @@ import androidx.core.text.TextDirectionHeuristicsCompat; import com.android.i18n.timezone.CountryTimeZones; import com.android.i18n.timezone.CountryTimeZones.TimeZoneMapping; import com.android.i18n.timezone.TimeZoneFinder; +import com.android.internal.app.LocaleHelper; import com.android.settingslib.R; import org.xmlpull.v1.XmlPullParserException; @@ -99,7 +101,8 @@ public class ZoneGetter { TimeZoneFormat tzFormatter = TimeZoneFormat.getInstance(locale); CharSequence gmtText = getGmtOffsetText(tzFormatter, locale, tz, now); TimeZoneNames timeZoneNames = TimeZoneNames.getInstance(locale); - String zoneNameString = getZoneLongName(timeZoneNames, tz, now); + String zoneNameString = capitalizeForStandaloneDisplay( + locale, getZoneLongName(locale, timeZoneNames, tz, now)); if (zoneNameString == null) { return gmtText; } @@ -108,6 +111,24 @@ public class ZoneGetter { return TextUtils.concat(gmtText, " ", zoneNameString); } + /** + * Capitalizes {@code toCapitalize} for standalone display, i.e. in lists. This is intended for + * use with "display name" strings from sources like ICU/CLDR which typically capitalize strings + * for the inclusion in the middle of sentences. Some locales (such as Polish) do not capitalize + * terms like "Coordinated Universal Time" as in English but do capitalize the first letter for + * standalone locations like lists, and so must be explicitly capitalized. + * + * @return the capitalized string, or {@code null} if the argument is null + */ + @Nullable + public static String capitalizeForStandaloneDisplay( + Locale locale, @Nullable String toCapitalize) { + if (TextUtils.isEmpty(toCapitalize)) { + return toCapitalize; + } + return LocaleHelper.toSentenceCase(toCapitalize, locale); + } + public static List<Map<String, Object>> getZonesList(Context context) { final Locale locale = context.getResources().getConfiguration().locale; final Date now = new Date(); @@ -116,7 +137,7 @@ public class ZoneGetter { // Work out whether the display names we would show by default would be ambiguous. final boolean useExemplarLocationForLocalNames = - shouldUseExemplarLocationForLocalNames(data, timeZoneNames); + shouldUseExemplarLocationForLocalNames(locale, data, timeZoneNames); // Generate the list of zone entries to return. List<Map<String, Object>> zones = new ArrayList<Map<String, Object>>(); @@ -124,7 +145,7 @@ public class ZoneGetter { TimeZone tz = data.timeZones[i]; CharSequence gmtOffsetText = data.gmtOffsetTexts[i]; - CharSequence displayName = getTimeZoneDisplayName(data, timeZoneNames, + CharSequence displayName = getTimeZoneDisplayName(locale, data, timeZoneNames, useExemplarLocationForLocalNames, tz, data.olsonIdsToDisplay[i]); if (TextUtils.isEmpty(displayName)) { displayName = gmtOffsetText; @@ -181,15 +202,15 @@ public class ZoneGetter { return olsonIds; } - private static boolean shouldUseExemplarLocationForLocalNames(ZoneGetterData data, - TimeZoneNames timeZoneNames) { + private static boolean shouldUseExemplarLocationForLocalNames(Locale locale, + ZoneGetterData data, TimeZoneNames timeZoneNames) { final Set<CharSequence> localZoneNames = new HashSet<>(); final Date now = new Date(); for (int i = 0; i < data.zoneCount; i++) { final String olsonId = data.olsonIdsToDisplay[i]; if (data.localZoneIds.contains(olsonId)) { final TimeZone tz = data.timeZones[i]; - CharSequence displayName = getZoneLongName(timeZoneNames, tz, now); + CharSequence displayName = getZoneLongName(locale, timeZoneNames, tz, now); if (displayName == null) { displayName = data.gmtOffsetTexts[i]; } @@ -203,7 +224,7 @@ public class ZoneGetter { return false; } - private static CharSequence getTimeZoneDisplayName(ZoneGetterData data, + private static CharSequence getTimeZoneDisplayName(Locale locale, ZoneGetterData data, TimeZoneNames timeZoneNames, boolean useExemplarLocationForLocalNames, TimeZone tz, String olsonId) { final Date now = new Date(); @@ -212,7 +233,7 @@ public class ZoneGetter { String displayName; if (preferLongName) { - displayName = getZoneLongName(timeZoneNames, tz, now); + displayName = getZoneLongName(locale, timeZoneNames, tz, now); } else { // Canonicalize the zone ID for ICU. It will only return valid strings for zone IDs // that match ICUs zone IDs (which are similar but not guaranteed the same as those @@ -223,10 +244,11 @@ public class ZoneGetter { if (canonicalZoneId == null) { canonicalZoneId = tz.getID(); } - displayName = timeZoneNames.getExemplarLocationName(canonicalZoneId); + displayName = capitalizeForStandaloneDisplay( + locale, timeZoneNames.getExemplarLocationName(canonicalZoneId)); if (displayName == null || displayName.isEmpty()) { // getZoneExemplarLocation can return null. Fall back to the long name. - displayName = getZoneLongName(timeZoneNames, tz, now); + displayName = getZoneLongName(locale, timeZoneNames, tz, now); } } @@ -237,11 +259,13 @@ public class ZoneGetter { * Returns the long name for the timezone for the given locale at the time specified. * Can return {@code null}. */ - private static String getZoneLongName(TimeZoneNames names, TimeZone tz, Date now) { + private static String getZoneLongName( + Locale locale, TimeZoneNames names, TimeZone tz, Date now) { final TimeZoneNames.NameType nameType = tz.inDaylightTime(now) ? TimeZoneNames.NameType.LONG_DAYLIGHT : TimeZoneNames.NameType.LONG_STANDARD; - return names.getDisplayName(getCanonicalZoneId(tz), nameType, now.getTime()); + return capitalizeForStandaloneDisplay(locale, + names.getDisplayName(getCanonicalZoneId(tz), nameType, now.getTime())); } private static String getCanonicalZoneId(TimeZone timeZone) { diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt index 40662536e57e..4a3350e056bf 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt @@ -320,7 +320,7 @@ private class ControlHolderAccessibilityDelegate( info.className = Switch::class.java.name } - override fun performAccessibilityAction(host: View?, action: Int, args: Bundle?): Boolean { + override fun performAccessibilityAction(host: View, action: Int, args: Bundle?): Boolean { if (super.performAccessibilityAction(host, action, args)) { return true } diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java index aac1035059ec..296b7bf24269 100644 --- a/services/core/java/com/android/server/BootReceiver.java +++ b/services/core/java/com/android/server/BootReceiver.java @@ -98,11 +98,13 @@ public class BootReceiver extends BroadcastReceiver { // example: fs_stat,/dev/block/platform/soc/by-name/userdata,0x5 private static final String FS_STAT_PATTERN = "fs_stat,[^,]*/([^/,]+),(0x[0-9a-fA-F]+)"; - private static final int FS_STAT_FS_FIXED = 0x400; // should match with fs_mgr.cpp:FsStatFlags + private static final int FS_STAT_FSCK_FS_FIXED = + 0x400; // should match with fs_mgr.cpp:FsStatFlags private static final String FSCK_PASS_PATTERN = "Pass ([1-9]E?):"; private static final String FSCK_TREE_OPTIMIZATION_PATTERN = "Inode [0-9]+ extent tree.*could be shorter"; - private static final String FSCK_FS_MODIFIED = "FILE SYSTEM WAS MODIFIED"; + private static final String E2FSCK_FS_MODIFIED = "FILE SYSTEM WAS MODIFIED"; + private static final String F2FS_FSCK_FS_MODIFIED = "[FSCK] Unreachable"; // ro.boottime.init.mount_all. + postfix for mount_all duration private static final String[] MOUNT_DURATION_PROPS_POSTFIX = new String[] { "early", "default", "late" }; @@ -460,9 +462,9 @@ public class BootReceiver extends BroadcastReceiver { int lineNumber = 0; int lastFsStatLineNumber = 0; for (String line : lines) { // should check all lines - if (line.contains(FSCK_FS_MODIFIED)) { + if (line.contains(E2FSCK_FS_MODIFIED) || line.contains(F2FS_FSCK_FS_MODIFIED)) { uploadNeeded = true; - } else if (line.contains("fs_stat")){ + } else if (line.contains("fs_stat")) { Matcher matcher = pattern.matcher(line); if (matcher.find()) { handleFsckFsStat(matcher, lines, lastFsStatLineNumber, lineNumber); @@ -474,12 +476,13 @@ public class BootReceiver extends BroadcastReceiver { lineNumber++; } - if (uploadEnabled && uploadNeeded ) { + if (uploadEnabled && uploadNeeded) { addFileToDropBox(db, timestamps, headers, "/dev/fscklogs/log", maxSize, tag); } - // Remove the file so we don't re-upload if the runtime restarts. - file.delete(); + // Rename the file so we don't re-upload if the runtime restarts. + File pfile = new File("/dev/fscklogs/fsck"); + file.renameTo(pfile); } private static void logFsMountTime() { @@ -673,7 +676,7 @@ public class BootReceiver extends BroadcastReceiver { public static int fixFsckFsStat(String partition, int statOrg, String[] lines, int startLineNumber, int endLineNumber) { int stat = statOrg; - if ((stat & FS_STAT_FS_FIXED) != 0) { + if ((stat & FS_STAT_FSCK_FS_FIXED) != 0) { // fs was fixed. should check if quota warning was caused by tree optimization. // This is not a real fix but optimization, so should not be counted as a fs fix. Pattern passPattern = Pattern.compile(FSCK_PASS_PATTERN); @@ -686,7 +689,8 @@ public class BootReceiver extends BroadcastReceiver { String otherFixLine = null; for (int i = startLineNumber; i < endLineNumber; i++) { String line = lines[i]; - if (line.contains(FSCK_FS_MODIFIED)) { // no need to parse above this + if (line.contains(E2FSCK_FS_MODIFIED) + || line.contains(F2FS_FSCK_FS_MODIFIED)) { // no need to parse above this break; } else if (line.startsWith("Pass ")) { Matcher matcher = passPattern.matcher(line); @@ -714,9 +718,9 @@ public class BootReceiver extends BroadcastReceiver { } } else if (line.startsWith("Update quota info") && currentPass.equals("5")) { // follows "[QUOTA WARNING]", ignore - } else if (line.startsWith("Timestamp(s) on inode") && - line.contains("beyond 2310-04-04 are likely pre-1970") && - currentPass.equals("1")) { + } else if (line.startsWith("Timestamp(s) on inode") + && line.contains("beyond 2310-04-04 are likely pre-1970") + && currentPass.equals("1")) { Slog.i(TAG, "fs_stat, partition:" + partition + " found timestamp adjustment:" + line); // followed by next line, "Fix? yes" @@ -744,7 +748,7 @@ public class BootReceiver extends BroadcastReceiver { } else if ((foundTreeOptimization && foundQuotaFix) || foundTimestampAdjustment) { // not a real fix, so clear it. Slog.i(TAG, "fs_stat, partition:" + partition + " fix ignored"); - stat &= ~FS_STAT_FS_FIXED; + stat &= ~FS_STAT_FSCK_FS_FIXED; } } return stat; diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index ab2147dff853..d0503f3f3ddc 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -168,8 +168,8 @@ public class AccountManagerService } @Override - public void onUserStopping(@NonNull TargetUser user) { - Slog.i(TAG, "onStopUser " + user); + public void onUserStopped(@NonNull TargetUser user) { + Slog.i(TAG, "onUserStopped " + user); mService.purgeUserData(user.getUserIdentifier()); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ec9f1fe9212a..fd109ff12ba6 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -91,7 +91,6 @@ import static android.os.Process.killProcessQuiet; import static android.os.Process.myPid; import static android.os.Process.myUid; import static android.os.Process.readProcFile; -import static android.os.Process.removeAllProcessGroups; import static android.os.Process.sendSignal; import static android.os.Process.setThreadPriority; import static android.os.Process.setThreadScheduler; @@ -2388,8 +2387,6 @@ public class ActivityManagerService extends IActivityManager.Stub } private void start() { - removeAllProcessGroups(); - mBatteryStatsService.publish(); mAppOpsService.publish(); Slog.d("AppOps", "AppOpsService published"); diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index dc80c4b33297..1257f1f4bc10 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -2425,8 +2425,8 @@ public final class ProcessList { 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 + ")"); } } @@ -2897,6 +2897,15 @@ public final class ProcessList { } int N = procs.size(); + for (int i = 0; i < N; ++i) { + final ProcessRecord proc = procs.get(i).first; + try { + Process.setProcessFrozen(proc.getPid(), proc.uid, true); + } catch (Exception e) { + Slog.w(TAG, "Unable to freeze " + proc.getPid() + " " + proc.processName); + } + } + for (int i=0; i<N; i++) { final Pair<ProcessRecord, Boolean> proc = procs.get(i); removeProcessLocked(proc.first, callerWillRestart, allowRestart || proc.second, diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index c5ac3907ecfe..d01030bed56d 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -86,6 +86,7 @@ public class SettingsToPropertiesMapper { DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS, DeviceConfig.NAMESPACE_LMKD_NATIVE, DeviceConfig.NAMESPACE_MEDIA_NATIVE, + DeviceConfig.NAMESPACE_MGLRU_NATIVE, DeviceConfig.NAMESPACE_NETD_NATIVE, DeviceConfig.NAMESPACE_NNAPI_NATIVE, DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT, diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index d9db28a9aa78..64ec12f7c131 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -86,7 +86,10 @@ 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.IkeNetworkLostException; +import android.net.ipsec.ike.exceptions.IkeNonProtocolException; import android.net.ipsec.ike.exceptions.IkeProtocolException; +import android.net.ipsec.ike.exceptions.IkeTimeoutException; import android.os.Binder; import android.os.Build.VERSION_CODES; import android.os.Bundle; @@ -122,6 +125,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnProfile; +import com.android.modules.utils.build.SdkLevel; import com.android.net.module.util.NetdUtils; import com.android.net.module.util.NetworkStackConstants; import com.android.server.DeviceIdleInternal; @@ -207,7 +211,9 @@ public class Vpn { private final Context mUserIdContext; @VisibleForTesting final Dependencies mDeps; private final NetworkInfo mNetworkInfo; + @GuardedBy("this") private int mLegacyState; + @GuardedBy("this") @VisibleForTesting protected String mPackage; private int mOwnerUID; private boolean mIsPackageTargetingAtLeastQ; @@ -245,6 +251,7 @@ public class Vpn { * Whether to keep the connection active after rebooting, or upgrading or reinstalling. This * only applies to {@link VpnService} connections. */ + @GuardedBy("this") @VisibleForTesting protected boolean mAlwaysOn = false; /** @@ -252,6 +259,7 @@ public class Vpn { * apps can still bypass by choosing explicit networks. Has no effect if {@link mAlwaysOn} is * not set. Applies to all types of VPNs. */ + @GuardedBy("this") @VisibleForTesting protected boolean mLockdown = false; /** @@ -604,7 +612,7 @@ public class Vpn { } /** Returns the package name that is currently prepared. */ - public String getPackage() { + public synchronized String getPackage() { return mPackage; } @@ -685,6 +693,36 @@ public class Vpn { return true; } + private boolean sendEventToVpnManagerApp(@NonNull String category, int errorClass, + int errorCode, @NonNull final String packageName, @Nullable final String sessionKey, + @NonNull final VpnProfileState profileState, @Nullable final Network underlyingNetwork, + @Nullable final NetworkCapabilities nc, @Nullable final LinkProperties lp) { + final Intent intent = new Intent(VpnManager.ACTION_VPN_MANAGER_EVENT); + intent.setPackage(packageName); + intent.addCategory(category); + intent.putExtra(VpnManager.EXTRA_VPN_PROFILE_STATE, profileState); + intent.putExtra(VpnManager.EXTRA_SESSION_KEY, sessionKey); + intent.putExtra(VpnManager.EXTRA_UNDERLYING_NETWORK, underlyingNetwork); + intent.putExtra(VpnManager.EXTRA_UNDERLYING_NETWORK_CAPABILITIES, nc); + intent.putExtra(VpnManager.EXTRA_UNDERLYING_LINK_PROPERTIES, lp); + intent.putExtra(VpnManager.EXTRA_TIMESTAMP_MILLIS, System.currentTimeMillis()); + if (!VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER.equals(category) + || !VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED.equals(category)) { + intent.putExtra(VpnManager.EXTRA_ERROR_CLASS, errorClass); + intent.putExtra(VpnManager.EXTRA_ERROR_CODE, errorCode); + } + try { + return mUserIdContext.startService(intent) != null; + } catch (RuntimeException e) { + Log.e(TAG, "Service of VpnManager app " + intent + " failed to start", e); + return false; + } + } + + private boolean isVpnApp(String packageName) { + return packageName != null && !VpnConfig.LEGACY_VPN.equals(packageName); + } + /** * Configures an always-on VPN connection through a specific application. This connection is * automatically granted and persisted after a reboot. @@ -707,9 +745,40 @@ public class Vpn { boolean lockdown, @Nullable List<String> lockdownAllowlist) { enforceControlPermissionOrInternalCaller(); + // Store mPackage since it might be reset or might be replaced with the other VPN app. + final String oldPackage = mPackage; + final boolean isPackageChanged = !Objects.equals(packageName, oldPackage); + // TODO: Remove "SdkLevel.isAtLeastT()" check once VpnManagerService is decoupled from + // ConnectivityServiceTest. + // Only notify VPN apps that were already always-on, and only if the always-on provider + // changed, or the lockdown mode changed. + final boolean shouldNotifyOldPkg = isVpnApp(oldPackage) && mAlwaysOn + && (lockdown != mLockdown || isPackageChanged); + // Also notify the new package if there was a provider change. + final boolean shouldNotifyNewPkg = isVpnApp(packageName) && isPackageChanged; if (setAlwaysOnPackageInternal(packageName, lockdown, lockdownAllowlist)) { saveAlwaysOnPackage(); + // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from + // ConnectivityServiceTest. + if (shouldNotifyOldPkg && SdkLevel.isAtLeastT()) { + // If both of shouldNotifyOldPkg & isPackageChanged are true, which means the + // always-on of old package is disabled or the old package is replaced with the new + // package. In this case, VpnProfileState should be disconnected. + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, + -1 /* errorClass */, -1 /* errorCode*/, oldPackage, + null /* sessionKey */, isPackageChanged ? makeDisconnectedVpnProfileState() + : makeVpnProfileStateLocked(), + null /* underlyingNetwork */, null /* nc */, null /* lp */); + } + // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from + // ConnectivityServiceTest. + if (shouldNotifyNewPkg && SdkLevel.isAtLeastT()) { + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, + -1 /* errorClass */, -1 /* errorCode*/, packageName, + getSessionKeyLocked(), makeVpnProfileStateLocked(), + null /* underlyingNetwork */, null /* nc */, null /* lp */); + } return true; } return false; @@ -1006,6 +1075,7 @@ public class Vpn { return true; } + @GuardedBy("this") private boolean isCurrentPreparedPackage(String packageName) { // We can't just check that packageName matches mPackage, because if the app was uninstalled // and reinstalled it will no longer be prepared. Similarly if there is a shared UID, the @@ -1043,6 +1113,17 @@ public class Vpn { if (!VpnConfig.LEGACY_VPN.equals(mPackage)) { mAppOpsManager.finishOp( AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER, mOwnerUID, mPackage, null); + // The underlying network, NetworkCapabilities and LinkProperties are not + // necessary to send to VPN app since the purpose of this event is to notify + // VPN app that VPN is deactivated by the user. + // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from + // ConnectivityServiceTest. + if (SdkLevel.isAtLeastT()) { + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER, + -1 /* errorClass */, -1 /* errorCode*/, mPackage, + getSessionKeyLocked(), makeVpnProfileStateLocked(), + null /* underlyingNetwork */, null /* nc */, null /* lp */); + } } // cleanupVpnStateLocked() is called from mVpnRunner.exit() mVpnRunner.exit(); @@ -1299,6 +1380,7 @@ public class Vpn { return true; } + @GuardedBy("this") private void agentConnect() { LinkProperties lp = makeLinkProperties(); @@ -1993,6 +2075,7 @@ public class Vpn { return isIkev2VpnRunner() ? VpnManager.TYPE_VPN_PLATFORM : VpnManager.TYPE_VPN_LEGACY; } + @GuardedBy("this") private void updateAlwaysOnNotification(DetailedState networkState) { final boolean visible = (mAlwaysOn && networkState != DetailedState.CONNECTED); @@ -2431,6 +2514,21 @@ public class Vpn { } } + @Nullable + protected synchronized NetworkCapabilities getRedactedNetworkCapabilitiesOfUnderlyingNetwork( + NetworkCapabilities nc) { + if (nc == null) return null; + return mConnectivityManager.getRedactedNetworkCapabilitiesForPackage( + nc, mOwnerUID, mPackage); + } + + @Nullable + protected synchronized LinkProperties getRedactedLinkPropertiesOfUnderlyingNetwork( + LinkProperties lp) { + if (lp == null) return null; + return mConnectivityManager.getRedactedLinkPropertiesForPackage(lp, mOwnerUID, mPackage); + } + /** This class represents the common interface for all VPN runners. */ @VisibleForTesting abstract class VpnRunner extends Thread { @@ -2465,6 +2563,10 @@ public class Vpn { interface IkeV2VpnRunnerCallback { void onDefaultNetworkChanged(@NonNull Network network); + void onDefaultNetworkCapabilitiesChanged(@NonNull NetworkCapabilities nc); + + void onDefaultNetworkLinkPropertiesChanged(@NonNull LinkProperties lp); + void onChildOpened( @NonNull Network network, @NonNull ChildSessionConfiguration childConfig); @@ -2521,13 +2623,17 @@ public class Vpn { @Nullable private IpSecTunnelInterface mTunnelIface; @Nullable private IkeSession mSession; @Nullable private Network mActiveNetwork; + @Nullable private NetworkCapabilities mNetworkCapabilities; + @Nullable private LinkProperties mLinkProperties; 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(); } @@ -2682,73 +2788,78 @@ public class Vpn { * <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. - } + 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()); - } + // 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; - // 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); + // 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); + } + } + + /** Called when the NetworkCapabilities of underlying network is changed */ + public void onDefaultNetworkCapabilitiesChanged(@NonNull NetworkCapabilities nc) { + mNetworkCapabilities = nc; + } + + /** Called when the LinkProperties of underlying network is changed */ + public void onDefaultNetworkLinkPropertiesChanged(@NonNull LinkProperties lp) { + mLinkProperties = lp; } /** Marks the state as FAILED, and disconnects. */ @@ -2781,28 +2892,120 @@ public class Vpn { return; } - if (exception instanceof IkeProtocolException) { - final IkeProtocolException ikeException = (IkeProtocolException) exception; - - switch (ikeException.getErrorType()) { - case IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN: // Fallthrough - case IkeProtocolException.ERROR_TYPE_INVALID_KE_PAYLOAD: // Fallthrough - case IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED: // Fallthrough - case IkeProtocolException.ERROR_TYPE_SINGLE_PAIR_REQUIRED: // Fallthrough - case IkeProtocolException.ERROR_TYPE_FAILED_CP_REQUIRED: // Fallthrough - case IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE: - // All the above failures are configuration errors, and are terminal - markFailedAndDisconnect(exception); - return; - // All other cases possibly recoverable. + synchronized (Vpn.this) { + if (exception instanceof IkeProtocolException) { + final IkeProtocolException ikeException = (IkeProtocolException) exception; + + switch (ikeException.getErrorType()) { + case IkeProtocolException.ERROR_TYPE_NO_PROPOSAL_CHOSEN: // Fallthrough + case IkeProtocolException.ERROR_TYPE_INVALID_KE_PAYLOAD: // Fallthrough + case IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED: // Fallthrough + case IkeProtocolException.ERROR_TYPE_SINGLE_PAIR_REQUIRED: // Fallthrough + case IkeProtocolException.ERROR_TYPE_FAILED_CP_REQUIRED: // Fallthrough + case IkeProtocolException.ERROR_TYPE_TS_UNACCEPTABLE: + // All the above failures are configuration errors, and are terminal + // TODO(b/230548427): Remove SDK check once VPN related stuff are + // decoupled from ConnectivityServiceTest. + if (SdkLevel.isAtLeastT()) { + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_IKE_ERROR, + VpnManager.ERROR_CLASS_NOT_RECOVERABLE, + ikeException.getErrorType(), + getPackage(), mSessionKey, makeVpnProfileStateLocked(), + mActiveNetwork, + getRedactedNetworkCapabilitiesOfUnderlyingNetwork( + this.mNetworkCapabilities), + getRedactedLinkPropertiesOfUnderlyingNetwork( + this.mLinkProperties)); + } + markFailedAndDisconnect(exception); + return; + // All other cases possibly recoverable. + default: + // All the above failures are configuration errors, and are terminal + // TODO(b/230548427): Remove SDK check once VPN related stuff are + // decoupled from ConnectivityServiceTest. + if (SdkLevel.isAtLeastT()) { + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_IKE_ERROR, + VpnManager.ERROR_CLASS_RECOVERABLE, + ikeException.getErrorType(), + getPackage(), mSessionKey, makeVpnProfileStateLocked(), + mActiveNetwork, + getRedactedNetworkCapabilitiesOfUnderlyingNetwork( + this.mNetworkCapabilities), + getRedactedLinkPropertiesOfUnderlyingNetwork( + this.mLinkProperties)); + } + } + } else if (exception instanceof IllegalArgumentException) { + // Failed to build IKE/ChildSessionParams; fatal profile configuration error + markFailedAndDisconnect(exception); + return; + } else if (exception instanceof IkeNetworkLostException) { + // TODO(b/230548427): Remove SDK check once VPN related stuff are + // decoupled from ConnectivityServiceTest. + if (SdkLevel.isAtLeastT()) { + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_NETWORK_ERROR, + VpnManager.ERROR_CLASS_RECOVERABLE, + VpnManager.ERROR_CODE_NETWORK_LOST, + getPackage(), mSessionKey, makeVpnProfileStateLocked(), + mActiveNetwork, + getRedactedNetworkCapabilitiesOfUnderlyingNetwork( + this.mNetworkCapabilities), + getRedactedLinkPropertiesOfUnderlyingNetwork( + this.mLinkProperties)); + } + } else if (exception instanceof IkeNonProtocolException) { + if (exception.getCause() instanceof UnknownHostException) { + // TODO(b/230548427): Remove SDK check once VPN related stuff are + // decoupled from ConnectivityServiceTest. + if (SdkLevel.isAtLeastT()) { + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_NETWORK_ERROR, + VpnManager.ERROR_CLASS_RECOVERABLE, + VpnManager.ERROR_CODE_NETWORK_UNKNOWN_HOST, + getPackage(), mSessionKey, makeVpnProfileStateLocked(), + mActiveNetwork, + getRedactedNetworkCapabilitiesOfUnderlyingNetwork( + this.mNetworkCapabilities), + getRedactedLinkPropertiesOfUnderlyingNetwork( + this.mLinkProperties)); + } + } else if (exception.getCause() instanceof IkeTimeoutException) { + // TODO(b/230548427): Remove SDK check once VPN related stuff are + // decoupled from ConnectivityServiceTest. + if (SdkLevel.isAtLeastT()) { + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_NETWORK_ERROR, + VpnManager.ERROR_CLASS_RECOVERABLE, + VpnManager.ERROR_CODE_NETWORK_PROTOCOL_TIMEOUT, + getPackage(), mSessionKey, makeVpnProfileStateLocked(), + mActiveNetwork, + getRedactedNetworkCapabilitiesOfUnderlyingNetwork( + this.mNetworkCapabilities), + getRedactedLinkPropertiesOfUnderlyingNetwork( + this.mLinkProperties)); + } + } else if (exception.getCause() instanceof IOException) { + // TODO(b/230548427): Remove SDK check once VPN related stuff are + // decoupled from ConnectivityServiceTest. + if (SdkLevel.isAtLeastT()) { + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_NETWORK_ERROR, + VpnManager.ERROR_CLASS_RECOVERABLE, + VpnManager.ERROR_CODE_NETWORK_IO, + getPackage(), mSessionKey, makeVpnProfileStateLocked(), + mActiveNetwork, + getRedactedNetworkCapabilitiesOfUnderlyingNetwork( + this.mNetworkCapabilities), + getRedactedLinkPropertiesOfUnderlyingNetwork( + this.mLinkProperties)); + } + } + } else if (exception != null) { + Log.wtf(TAG, "onSessionLost: exception = " + exception); } - } else if (exception instanceof IllegalArgumentException) { - // Failed to build IKE/ChildSessionParams; fatal profile configuration error - markFailedAndDisconnect(exception); - return; } mActiveNetwork = null; + mNetworkCapabilities = null; + mLinkProperties = null; // Close all obsolete state, but keep VPN alive incase a usable network comes up. // (Mirrors VpnService behavior) @@ -2867,6 +3070,8 @@ public class Vpn { */ private void disconnectVpnRunner() { mActiveNetwork = null; + mNetworkCapabilities = null; + mLinkProperties = null; mIsRunning = false; resetIkeState(); @@ -3493,11 +3698,19 @@ public class Vpn { } } - private VpnProfileState makeVpnProfileState() { + @GuardedBy("this") + @NonNull + private VpnProfileState makeVpnProfileStateLocked() { return new VpnProfileState(getStateFromLegacyState(mLegacyState), isIkev2VpnRunner() ? getSessionKeyLocked() : null, mAlwaysOn, mLockdown); } + @NonNull + private VpnProfileState makeDisconnectedVpnProfileState() { + return new VpnProfileState(VpnProfileState.STATE_DISCONNECTED, null /* sessionKey */, + false /* alwaysOn */, false /* lockdown */); + } + /** * Retrieve the VpnProfileState for the profile provisioned by the given package. * @@ -3509,7 +3722,7 @@ public class Vpn { @NonNull String packageName) { requireNonNull(packageName, "No package name provided"); enforceNotRestrictedUser(); - return isCurrentIkev2VpnLocked(packageName) ? makeVpnProfileState() : null; + return isCurrentIkev2VpnLocked(packageName) ? makeVpnProfileStateLocked() : null; } /** diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java index a0a596d998bf..e1e488db679f 100644 --- a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java +++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java @@ -50,7 +50,9 @@ import android.net.InetAddresses; import android.net.IpPrefix; import android.net.IpSecAlgorithm; import android.net.IpSecTransform; +import android.net.LinkProperties; import android.net.Network; +import android.net.NetworkCapabilities; import android.net.RouteInfo; import android.net.eap.EapSessionConfig; import android.net.ipsec.ike.ChildSaProposal; @@ -86,6 +88,7 @@ import java.util.Arrays; 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 +379,41 @@ public class VpnIkev2Utils { 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 onCapabilitiesChanged(@NonNull Network network, + @NonNull NetworkCapabilities networkCapabilities) { + Log.d(mTag, "NC changed for net " + network + " : " + networkCapabilities); + mExecutor.execute( + () -> mCallback.onDefaultNetworkCapabilitiesChanged(networkCapabilities)); + } + + @Override + public void onLinkPropertiesChanged(@NonNull Network network, + @NonNull LinkProperties linkProperties) { + Log.d(mTag, "LP changed for net " + network + " : " + linkProperties); + mExecutor.execute( + () -> mCallback.onDefaultNetworkLinkPropertiesChanged(linkProperties)); } @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/pm/UserDataPreparer.java b/services/core/java/com/android/server/pm/UserDataPreparer.java index 504769064808..5418dc59097c 100644 --- a/services/core/java/com/android/server/pm/UserDataPreparer.java +++ b/services/core/java/com/android/server/pm/UserDataPreparer.java @@ -150,14 +150,18 @@ class UserDataPreparer { if (Objects.equals(volumeUuid, StorageManager.UUID_PRIVATE_INTERNAL)) { if ((flags & StorageManager.FLAG_STORAGE_DE) != 0) { FileUtils.deleteContentsAndDir(getUserSystemDirectory(userId)); - FileUtils.deleteContentsAndDir(getDataSystemDeDirectory(userId)); + // Delete the contents of /data/system_de/$userId, but not the directory itself + // since vold is responsible for that and system_server isn't allowed to do it. + FileUtils.deleteContents(getDataSystemDeDirectory(userId)); } if ((flags & StorageManager.FLAG_STORAGE_CE) != 0) { - FileUtils.deleteContentsAndDir(getDataSystemCeDirectory(userId)); + // Likewise, delete the contents of /data/system_ce/$userId but not the + // directory itself. + FileUtils.deleteContents(getDataSystemCeDirectory(userId)); } } - // Data with special labels is now gone, so finish the job + // All the user's data directories should be empty now, so finish the job. storage.destroyUserStorage(volumeUuid, userId, flags); } catch (Exception e) { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 075275ac533c..be7e2dadc7ac 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -3966,7 +3966,7 @@ public class UserManagerService extends IUserManager.Stub { for (int i = 0; i < userSize; i++) { final UserData user = mUsers.valueAt(i); if (DBG) Slog.d(LOG_TAG, i + ":" + user.info.toFullString()); - if (user.info.preCreated && user.info.userType.equals(userType)) { + if (user.info.preCreated && !user.info.partial && user.info.userType.equals(userType)) { if (!user.info.isInitialized()) { Slog.w(LOG_TAG, "found pre-created user of type " + userType + ", but it's not initialized yet: " + user.info.toFullString()); diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp index 7e4f0e72b62d..0a033380e6c9 100644 --- a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp +++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp @@ -25,7 +25,6 @@ package { android_test_helper_app { name: "PackageManagerServiceDeviceSideTests", - sdk_version: "test_current", srcs: ["src/**/*.kt"], libs: [ "android.test.base", diff --git a/services/tests/servicestests/src/com/android/server/pm/UserDataPreparerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserDataPreparerTest.java index c489cf0a138d..de83e518067e 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserDataPreparerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserDataPreparerTest.java @@ -17,6 +17,8 @@ package com.android.server.pm; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isNull; import static org.mockito.Mockito.verify; @@ -129,22 +131,16 @@ public class UserDataPreparerTest { } @Test - public void testDestroyUserData() throws Exception { - // Add file in CE + public void testDestroyUserData_De_DoesNotDestroyCe() throws Exception { + // Add file in CE storage File systemCeDir = mUserDataPreparer.getDataSystemCeDirectory(TEST_USER_ID); systemCeDir.mkdirs(); File ceFile = new File(systemCeDir, "file"); writeFile(ceFile, "-----" ); - testDestroyUserData_De(); - // CE directory should be preserved + // Destroy DE storage, then verify that CE storage wasn't destroyed too. + mUserDataPreparer.destroyUserData(TEST_USER_ID, StorageManager.FLAG_STORAGE_DE); assertEquals(Collections.singletonList(ceFile), Arrays.asList(FileUtils.listFilesOrEmpty( systemCeDir))); - - testDestroyUserData_Ce(); - - // Verify that testDir is empty - assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty( - mUserDataPreparer.testDir))); } @Test @@ -163,7 +159,13 @@ public class UserDataPreparerTest { verify(mStorageManagerMock).destroyUserStorage(isNull(String.class), eq(TEST_USER_ID), eq(StorageManager.FLAG_STORAGE_DE)); - assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty(systemDir))); + // systemDir (normal path: /data/system/users/$userId) should have been deleted. + assertFalse(systemDir.exists()); + // systemDeDir (normal path: /data/system_de/$userId) should still exist but be empty, since + // UserDataPreparer itself is responsible for deleting the contents of this directory, but + // it delegates to StorageManager.destroyUserStorage() for deleting the directory itself. + // We've mocked out StorageManager, so StorageManager.destroyUserStorage() will be a no-op. + assertTrue(systemDeDir.exists()); assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty( systemDeDir))); } @@ -181,6 +183,11 @@ public class UserDataPreparerTest { verify(mStorageManagerMock).destroyUserStorage(isNull(String.class), eq(TEST_USER_ID), eq(StorageManager.FLAG_STORAGE_CE)); + // systemCeDir (normal path: /data/system_ce/$userId) should still exist but be empty, since + // UserDataPreparer itself is responsible for deleting the contents of this directory, but + // it delegates to StorageManager.destroyUserStorage() for deleting the directory itself. + // We've mocked out StorageManager, so StorageManager.destroyUserStorage() will be a no-op. + assertTrue(systemCeDir.exists()); assertEquals(Collections.emptyList(), Arrays.asList(FileUtils.listFilesOrEmpty( systemCeDir))); } diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index ac1fcce20dc0..f5047bf82905 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -76,7 +76,6 @@ import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; -import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; @@ -140,13 +139,26 @@ public class UsageStatsService extends SystemService implements private static final boolean ENABLE_KERNEL_UPDATES = true; private static final File KERNEL_COUNTER_FILE = new File("/proc/uid_procstat/set"); - private static final File USAGE_STATS_LEGACY_DIR = new File( - Environment.getDataSystemDirectory(), "usagestats"); - // For migration purposes, indicates whether to keep the legacy usage stats directory or not - private static final boolean KEEP_LEGACY_DIR = false; - - private static final File COMMON_USAGE_STATS_DE_DIR = + // /data/system/usagestats. Now only used for globalcomponentusage. Previously per-user stats + // were stored here too, but they've been moved to /data/system_ce/$userId/usagestats. + private static final File COMMON_USAGE_STATS_DIR = + new File(Environment.getDataSystemDirectory(), "usagestats"); + private static final File LEGACY_USER_USAGE_STATS_DIR = COMMON_USAGE_STATS_DIR; + + // /data/system_de/usagestats. When the globalcomponentusage file was added, it was incorrectly + // added here instead of in /data/system/usagestats where it should be. We lazily migrate this + // file by reading it from here if needed, and always writing it to the new path. We don't + // delete the old directory, as system_server no longer has permission to do so. + // + // Note, this migration is *not* related to the migration of the per-user stats from + // /data/system/usagestats/$userId to /data/system_ce/$userId/usagestats mentioned above. Both + // of these just happen to involve /data/system/usagestats. /data/system is the right place for + // system data not tied to a user, but the wrong place for per-user data. So due to two + // separate mistakes, we've unfortunately ended up with one case where we need to move files out + // of /data/system, and one case where we need to move a different file *into* /data/system. + private static final File LEGACY_COMMON_USAGE_STATS_DIR = new File(Environment.getDataSystemDeDirectory(), "usagestats"); + private static final String GLOBAL_COMPONENT_USAGE_FILE_NAME = "globalcomponentusage"; private static final char TOKEN_DELIMITER = '/'; @@ -630,7 +642,7 @@ public class UsageStatsService extends SystemService implements final int previousVersion = Integer.parseInt(reader.readLine()); // UsageStatsDatabase.BACKUP_VERSION was 4 when usage stats were migrated to CE. if (previousVersion >= 4) { - deleteLegacyDir(userId); + deleteLegacyUserDir(userId); return; } // If migration logic needs to be changed in a future version, do it here. @@ -651,7 +663,7 @@ public class UsageStatsService extends SystemService implements } Slog.i(TAG, "Starting migration to system CE for user " + userId); - final File legacyUserDir = new File(USAGE_STATS_LEGACY_DIR, Integer.toString(userId)); + final File legacyUserDir = new File(LEGACY_USER_USAGE_STATS_DIR, Integer.toString(userId)); if (legacyUserDir.exists()) { copyRecursively(usageStatsDir, legacyUserDir); } @@ -666,8 +678,8 @@ public class UsageStatsService extends SystemService implements } Slog.i(TAG, "Finished migration to system CE for user " + userId); - // Migration was successful - delete the legacy directory - deleteLegacyDir(userId); + // Migration was successful - delete the legacy user directory + deleteLegacyUserDir(userId); } private static void copyRecursively(final File parent, File f) { @@ -698,21 +710,14 @@ public class UsageStatsService extends SystemService implements } } - private void deleteLegacyDir(int userId) { - final File legacyUserDir = new File(USAGE_STATS_LEGACY_DIR, Integer.toString(userId)); - if (!KEEP_LEGACY_DIR && legacyUserDir.exists()) { + private void deleteLegacyUserDir(int userId) { + final File legacyUserDir = new File(LEGACY_USER_USAGE_STATS_DIR, Integer.toString(userId)); + if (legacyUserDir.exists()) { deleteRecursively(legacyUserDir); if (legacyUserDir.exists()) { Slog.w(TAG, "Error occurred while attempting to delete legacy usage stats " + "dir for user " + userId); } - // If all users have been migrated, delete the parent legacy usage stats directory - if (USAGE_STATS_LEGACY_DIR.list() != null - && USAGE_STATS_LEGACY_DIR.list().length == 0) { - if (!USAGE_STATS_LEGACY_DIR.delete()) { - Slog.w(TAG, "Error occurred while attempting to delete legacy usage stats dir"); - } - } } } @@ -807,13 +812,16 @@ public class UsageStatsService extends SystemService implements } private void loadGlobalComponentUsageLocked() { - final File[] packageUsageFile = COMMON_USAGE_STATS_DE_DIR.listFiles( - (dir, name) -> TextUtils.equals(name, GLOBAL_COMPONENT_USAGE_FILE_NAME)); - if (packageUsageFile == null || packageUsageFile.length == 0) { - return; + AtomicFile af = new AtomicFile(new File(COMMON_USAGE_STATS_DIR, + GLOBAL_COMPONENT_USAGE_FILE_NAME)); + if (!af.exists()) { + af = new AtomicFile(new File(LEGACY_COMMON_USAGE_STATS_DIR, + GLOBAL_COMPONENT_USAGE_FILE_NAME)); + if (!af.exists()) { + return; + } + Slog.i(TAG, "Reading " + GLOBAL_COMPONENT_USAGE_FILE_NAME + " file from old location"); } - - final AtomicFile af = new AtomicFile(packageUsageFile[0]); final Map<String, Long> tmpUsage = new ArrayMap<>(); try { try (FileInputStream in = af.openRead()) { @@ -831,7 +839,7 @@ public class UsageStatsService extends SystemService implements } } catch (Exception e) { // Most likely trying to read a corrupted file - log the failure - Slog.e(TAG, "Could not read " + packageUsageFile[0]); + Slog.e(TAG, "Could not read " + af.getBaseFile()); } } @@ -840,11 +848,11 @@ public class UsageStatsService extends SystemService implements return; } - if (!COMMON_USAGE_STATS_DE_DIR.mkdirs() && !COMMON_USAGE_STATS_DE_DIR.exists()) { - throw new IllegalStateException("Common usage stats DE directory does not exist: " - + COMMON_USAGE_STATS_DE_DIR.getAbsolutePath()); + if (!COMMON_USAGE_STATS_DIR.mkdirs() && !COMMON_USAGE_STATS_DIR.exists()) { + throw new IllegalStateException("Common usage stats directory does not exist: " + + COMMON_USAGE_STATS_DIR.getAbsolutePath()); } - final File lastTimePackageFile = new File(COMMON_USAGE_STATS_DE_DIR, + final File lastTimePackageFile = new File(COMMON_USAGE_STATS_DIR, GLOBAL_COMPONENT_USAGE_FILE_NAME); final AtomicFile af = new AtomicFile(lastTimePackageFile); FileOutputStream fos = null; diff --git a/services/usb/Android.bp b/services/usb/Android.bp index 01feacd826c5..4dc5423caeef 100644 --- a/services/usb/Android.bp +++ b/services/usb/Android.bp @@ -29,6 +29,7 @@ java_library_static { "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 ec28040f82d8..d4726397b5eb 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_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.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.ComponentName; 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.FrameworkStatsLog; 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 @@ public class UsbPortManager { // 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 @@ public class UsbPortManager { */ 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 @@ public class UsbPortManager { } 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 @@ public class UsbPortManager { * * @param enable enable or disable USB data signaling */ - public boolean enableUsbDataSignal(boolean enable) { + 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 { - 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); + 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(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); - return false; + 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 @@ public class UsbPortManager { 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 @@ public class UsbPortManager { 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 class UsbPortManager { } } + 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 @@ public class UsbPortManager { 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 @@ public class UsbPortManager { portInfo.supportsEnableContaminantPresenceProtection, portInfo.contaminantProtectionStatus, portInfo.supportsEnableContaminantPresenceDetection, - portInfo.contaminantDetectionStatus, pw); + portInfo.contaminantDetectionStatus, + portInfo.usbDataEnabled, pw); } } else { for (RawPortInfo currentPortInfo : newPortInfo) { @@ -881,7 +727,8 @@ public class UsbPortManager { currentPortInfo.supportsEnableContaminantPresenceProtection, currentPortInfo.contaminantProtectionStatus, currentPortInfo.supportsEnableContaminantPresenceDetection, - currentPortInfo.contaminantDetectionStatus, pw); + currentPortInfo.contaminantDetectionStatus, + currentPortInfo.usbDataEnabled, pw); } } @@ -917,6 +764,7 @@ public class UsbPortManager { 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 @@ public class UsbPortManager { 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 @@ public class UsbPortManager { currentPowerRole, canChangePowerRole, currentDataRole, canChangeDataRole, supportedRoleCombinations, contaminantProtectionStatus, - contaminantDetectionStatus)) { + contaminantDetectionStatus, usbDataEnabled)) { portInfo.mDisposition = PortInfo.DISPOSITION_CHANGED; } else { portInfo.mDisposition = PortInfo.DISPOSITION_READY; @@ -1141,14 +989,14 @@ public class UsbPortManager { } } - 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 @@ public class UsbPortManager { /** * 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 @@ public class UsbPortManager { != 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 @@ public class UsbPortManager { 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 @@ public class UsbPortManager { || 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 @@ public class UsbPortManager { UsbPortInfoProto.CONNECTED_AT_MILLIS, mConnectedAtMillis); dump.write("last_connect_duration_millis", UsbPortInfoProto.LAST_CONNECT_DURATION_MILLIS, mLastConnectDurationMillis); - dump.end(token); } @@ -1304,115 +1153,4 @@ public class UsbPortManager { + ", 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 3d3538d7ae49..28227fcf0468 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.Intent; 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.hardware.usb.UsbPortStatus; 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 @@ public class UsbService extends IUsbManager.Stub { } @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 000000000000..9c6cbbd96460 --- /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 000000000000..1efcd9ca5d65 --- /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 000000000000..e7f9bc2fe7c3 --- /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 000000000000..41f9faef99df --- /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 000000000000..00d0d06705fc --- /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/telephony/java/Android.bp b/telephony/java/Android.bp index 3941b300206f..76a420c430d1 100644 --- a/telephony/java/Android.bp +++ b/telephony/java/Android.bp @@ -13,6 +13,15 @@ filegroup { srcs: [ "**/*.java", "**/*.aidl", + ":statslog-telephony-java-gen", ], visibility: ["//frameworks/base"], } + +genrule { + name: "statslog-telephony-java-gen", + tools: ["stats-log-api-gen"], + cmd: "$(location stats-log-api-gen) --java $(out) --module telephony" + + " --javaPackage com.android.internal.telephony --javaClass TelephonyStatsLog", + out: ["com/android/internal/telephony/TelephonyStatsLog.java"], +} diff --git a/telephony/java/android/telephony/AnomalyReporter.java b/telephony/java/android/telephony/AnomalyReporter.java index ffdb23f98fb8..f47cf3384791 100644 --- a/telephony/java/android/telephony/AnomalyReporter.java +++ b/telephony/java/android/telephony/AnomalyReporter.java @@ -16,6 +16,8 @@ package android.telephony; +import static com.android.internal.telephony.TelephonyStatsLog.TELEPHONY_ANOMALY_DETECTED; + import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.content.Context; @@ -24,6 +26,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.ParcelUuid; +import com.android.internal.telephony.TelephonyStatsLog; import com.android.internal.util.IndentingPrintWriter; import com.android.telephony.Rlog; @@ -83,6 +86,12 @@ public final class AnomalyReporter { return; } + TelephonyStatsLog.write( + TELEPHONY_ANOMALY_DETECTED, + 0, // TODO: carrier id needs to be populated + eventId.getLeastSignificantBits(), + eventId.getMostSignificantBits()); + // If this event has already occurred, skip sending intents for it; regardless log its // invocation here. Integer count = sEvents.containsKey(eventId) ? sEvents.get(eventId) + 1 : 1; diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index b4825153bf0a..56988ae0944d 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -9051,6 +9051,7 @@ public class TelephonyManager { try { ITelephony telephony = getITelephony(); if (telephony != null) { + networkTypeBitmask = checkNetworkTypeBitmask(networkTypeBitmask); return telephony.setAllowedNetworkTypesForReason(getSubId(), TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER, networkTypeBitmask); } @@ -9061,6 +9062,20 @@ public class TelephonyManager { } /** + * If {@link #NETWORK_TYPE_BITMASK_LTE_CA} bit is set, convert it to NETWORK_TYPE_BITMASK_LTE. + * + * @param networkTypeBitmask The networkTypeBitmask being checked + * @return The checked/converted networkTypeBitmask + */ + private long checkNetworkTypeBitmask(@NetworkTypeBitMask long networkTypeBitmask) { + if ((networkTypeBitmask & NETWORK_TYPE_BITMASK_LTE_CA) != 0) { + networkTypeBitmask ^= NETWORK_TYPE_BITMASK_LTE_CA; + networkTypeBitmask |= NETWORK_TYPE_BITMASK_LTE; + } + return networkTypeBitmask; + } + + /** * Set the allowed network types of the device. This is for carrier or privileged apps to * enable/disable certain network types on the device. The user preferred network types should * be set through {@link #setPreferredNetworkTypeBitmask}. @@ -9086,6 +9101,7 @@ public class TelephonyManager { try { ITelephony telephony = getITelephony(); if (telephony != null) { + allowedNetworkTypes = checkNetworkTypeBitmask(allowedNetworkTypes); return telephony.setAllowedNetworkTypesForReason(getSubId(), TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_CARRIER, allowedNetworkTypes); } @@ -9171,6 +9187,7 @@ public class TelephonyManager { try { ITelephony telephony = getITelephony(); if (telephony != null) { + allowedNetworkTypes = checkNetworkTypeBitmask(allowedNetworkTypes); telephony.setAllowedNetworkTypesForReason(getSubId(), reason, allowedNetworkTypes); } else { @@ -13429,7 +13446,11 @@ public class TelephonyManager { */ public static final long NETWORK_TYPE_BITMASK_LTE = (1 << (NETWORK_TYPE_LTE -1)); /** + * NOT USED; this bitmask is exposed accidentally, will be deprecated in U. + * If used, will be converted to {@link #NETWORK_TYPE_BITMASK_LTE}. * network type bitmask indicating the support of radio tech LTE CA (carrier aggregation). + * + * @see #NETWORK_TYPE_BITMASK_LTE */ public static final long NETWORK_TYPE_BITMASK_LTE_CA = (1 << (NETWORK_TYPE_LTE_CA -1)); diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp index 7ea4ab13961d..0aca2939cee0 100644 --- a/tools/aapt2/SdkConstants.cpp +++ b/tools/aapt2/SdkConstants.cpp @@ -27,7 +27,7 @@ namespace aapt { 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/bit/adb.cpp b/tools/bit/adb.cpp index f521a63255e1..201028ba900a 100644 --- a/tools/bit/adb.cpp +++ b/tools/bit/adb.cpp @@ -73,7 +73,7 @@ string get_system_property(const string& name, int* err) { Command cmd("adb"); - cmd.AddArg("shell"); + cmd.AddArg("exec-out"); cmd.AddArg("getprop"); cmd.AddArg(name); @@ -278,7 +278,7 @@ run_instrumentation_test(const string& packageName, const string& runner, const InstrumentationCallbacks* callbacks) { Command cmd("adb"); - cmd.AddArg("shell"); + cmd.AddArg("exec-out"); cmd.AddArg("am"); cmd.AddArg("instrument"); cmd.AddArg("-w"); diff --git a/tools/bit/main.cpp b/tools/bit/main.cpp index fd184f50091a..0d48070fd0c6 100644 --- a/tools/bit/main.cpp +++ b/tools/bit/main.cpp @@ -52,24 +52,22 @@ struct Target { int testPassCount; int testFailCount; + int testIgnoreCount; int unknownFailureCount; // unknown failure == "Process crashed", etc. - bool actionsWithNoTests; Target(bool b, bool i, bool t, const string& p); }; Target::Target(bool b, bool i, bool t, const string& p) - :build(b), - install(i), - test(t), - pattern(p), - testActionCount(0), - testPassCount(0), - testFailCount(0), - unknownFailureCount(0), - actionsWithNoTests(false) -{ -} + : build(b), + install(i), + test(t), + pattern(p), + testActionCount(0), + testPassCount(0), + testFailCount(0), + testIgnoreCount(0), + unknownFailureCount(0) {} /** * Command line options. @@ -188,13 +186,12 @@ struct TestAction { // The number of tests that failed int failCount; + + // The number of tests that were ignored (because of @Ignore) + int ignoreCount; }; -TestAction::TestAction() - :passCount(0), - failCount(0) -{ -} +TestAction::TestAction() : passCount(0), failCount(0), ignoreCount(0) {} /** * Record for an activity that is going to be launched. @@ -278,7 +275,7 @@ TestResults::OnTestStatus(TestStatus& status) line << " of " << testCount; } } - line << ": " << m_currentAction->target->name << ':' << className << "\\#" << testName; + line << ": " << m_currentAction->target->name << ':' << className << "#" << testName; print_one_line("%s", line.str().c_str()); } else if ((resultCode == -1) || (resultCode == -2)) { // test failed @@ -286,9 +283,9 @@ TestResults::OnTestStatus(TestStatus& status) // all as "failures". m_currentAction->failCount++; m_currentAction->target->testFailCount++; - printf("%s\n%sFailed: %s:%s\\#%s%s\n", g_escapeClearLine, g_escapeRedBold, - m_currentAction->target->name.c_str(), className.c_str(), - testName.c_str(), g_escapeEndColor); + printf("%s\n%sFailed: %s:%s#%s%s\n", g_escapeClearLine, g_escapeRedBold, + m_currentAction->target->name.c_str(), className.c_str(), testName.c_str(), + g_escapeEndColor); bool stackFound; string stack = get_bundle_string(results, &stackFound, "stack", NULL); @@ -300,6 +297,13 @@ TestResults::OnTestStatus(TestStatus& status) } else if (stackFound) { printf("%s\n", stack.c_str()); } + } else if (resultCode == -3) { + // test ignored + m_currentAction->ignoreCount++; + m_currentAction->target->testIgnoreCount++; + printf("%s\n%sIgnored: %s:%s#%s%s\n", g_escapeClearLine, g_escapeYellowBold, + m_currentAction->target->name.c_str(), className.c_str(), testName.c_str(), + g_escapeEndColor); } } @@ -403,11 +407,14 @@ print_usage(FILE* out) { fprintf(out, " Builds and installs CtsProtoTestCases.apk, and runs all the\n"); fprintf(out, " tests in the ProtoOutputStreamBoolTest class.\n"); fprintf(out, "\n"); - fprintf(out, " bit CtsProtoTestCases:.ProtoOutputStreamBoolTest\\#testWrite\n"); + fprintf(out, " bit CtsProtoTestCases:.ProtoOutputStreamBoolTest#testWrite\n"); fprintf(out, " Builds and installs CtsProtoTestCases.apk, and runs the testWrite\n"); fprintf(out, " test method on that class.\n"); fprintf(out, "\n"); - fprintf(out, " bit CtsProtoTestCases:.ProtoOutputStreamBoolTest\\#testWrite,.ProtoOutputStreamBoolTest\\#testRepeated\n"); + fprintf(out, + " bit " + "CtsProtoTestCases:.ProtoOutputStreamBoolTest#testWrite,.ProtoOutputStreamBoolTest#" + "testRepeated\n"); fprintf(out, " Builds and installs CtsProtoTestCases.apk, and runs the testWrite\n"); fprintf(out, " and testRepeated test methods on that class.\n"); fprintf(out, "\n"); @@ -450,6 +457,35 @@ print_usage(FILE* out) { fprintf(out, "\n"); } +/** + * Prints a possibly color-coded summary of test results. Example output: + * + * "34 passed, 0 failed, 1 ignored\n" + */ +static void print_results(int passed, int failed, int ignored) { + char const* nothing = ""; + char const* cp = nothing; + char const* cf = nothing; + char const* ci = nothing; + + if (failed > 0) { + cf = g_escapeRedBold; + } else if (passed > 0 || ignored > 0) { + cp = passed > 0 ? g_escapeGreenBold : nothing; + ci = ignored > 0 ? g_escapeYellowBold : nothing; + } else { + cp = g_escapeYellowBold; + cf = g_escapeYellowBold; + } + + if (ignored > 0) { + printf("%s%d passed%s, %s%d failed%s, %s%d ignored%s\n", cp, passed, g_escapeEndColor, cf, + failed, g_escapeEndColor, ci, ignored, g_escapeEndColor); + } else { + printf("%s%d passed%s, %s%d failed%s\n", cp, passed, g_escapeEndColor, cf, failed, + g_escapeEndColor); + } +} /** * Sets the appropriate flag* variables. If there is a problem with the @@ -812,7 +848,7 @@ run_phases(vector<Target*> targets, const Options& options) // Stop & Sync if (!options.noRestart) { - err = run_adb("shell", "stop", NULL); + err = run_adb("exec-out", "stop", NULL); check_error(err); } err = run_adb("remount", NULL); @@ -831,9 +867,9 @@ run_phases(vector<Target*> targets, const Options& options) } else { print_status("Restarting the runtime"); - err = run_adb("shell", "setprop", "sys.boot_completed", "0", NULL); + err = run_adb("exec-out", "setprop", "sys.boot_completed", "0", NULL); check_error(err); - err = run_adb("shell", "start", NULL); + err = run_adb("exec-out", "start", NULL); check_error(err); } @@ -846,7 +882,7 @@ run_phases(vector<Target*> targets, const Options& options) sleep(2); } sleep(1); - err = run_adb("shell", "wm", "dismiss-keyguard", NULL); + err = run_adb("exec-out", "wm", "dismiss-keyguard", NULL); check_error(err); } } @@ -863,7 +899,7 @@ run_phases(vector<Target*> targets, const Options& options) continue; } // TODO: if (!apk.file.fileInfo.exists || apk.file.HasChanged()) - err = run_adb("shell", "mkdir", "-p", dir.c_str(), NULL); + err = run_adb("exec-out", "mkdir", "-p", dir.c_str(), NULL); check_error(err); err = run_adb("push", pushed.file.filename.c_str(), pushed.dest.c_str(), NULL); check_error(err); @@ -945,9 +981,9 @@ run_phases(vector<Target*> targets, const Options& options) } } if (runAll) { - err = run_adb("shell", installedPath.c_str(), NULL); + err = run_adb("exec-out", installedPath.c_str(), NULL); } else { - err = run_adb("shell", installedPath.c_str(), filterArg.c_str(), NULL); + err = run_adb("exec-out", installedPath.c_str(), filterArg.c_str(), NULL); } if (err == 0) { target->testPassCount++; @@ -1035,22 +1071,10 @@ run_phases(vector<Target*> targets, const Options& options) err = run_instrumentation_test(action.packageName, action.runner, action.className, &testResults); check_error(err); - if (action.passCount == 0 && action.failCount == 0) { - action.target->actionsWithNoTests = true; - } int total = action.passCount + action.failCount; printf("%sRan %d test%s for %s. ", g_escapeClearLine, total, total > 1 ? "s" : "", action.target->name.c_str()); - if (action.passCount == 0 && action.failCount == 0) { - printf("%s%d passed, %d failed%s\n", g_escapeYellowBold, action.passCount, - action.failCount, g_escapeEndColor); - } else if (action.failCount > 0) { - printf("%d passed, %s%d failed%s\n", action.passCount, g_escapeRedBold, - action.failCount, g_escapeEndColor); - } else { - printf("%s%d passed%s, %d failed\n", g_escapeGreenBold, action.passCount, - g_escapeEndColor, action.failCount); - } + print_results(action.passCount, action.failCount, action.ignoreCount); if (!testResults.IsSuccess()) { printf("\n%sTest didn't finish successfully: %s%s\n", g_escapeRedBold, testResults.GetErrorMessage().c_str(), g_escapeEndColor); @@ -1073,7 +1097,7 @@ run_phases(vector<Target*> targets, const Options& options) const ActivityAction& action = activityActions[0]; string componentName = action.packageName + "/" + action.className; - err = run_adb("shell", "am", "start", componentName.c_str(), NULL); + err = run_adb("exec-out", "am", "start", componentName.c_str(), NULL); check_error(err); } @@ -1147,17 +1171,11 @@ run_phases(vector<Target*> targets, const Options& options) printf(" %sUnknown failure, see above message.%s\n", g_escapeRedBold, g_escapeEndColor); hasErrors = true; - } else if (target->actionsWithNoTests) { - printf(" %s%d passed, %d failed%s\n", g_escapeYellowBold, - target->testPassCount, target->testFailCount, g_escapeEndColor); - hasErrors = true; - } else if (target->testFailCount > 0) { - printf(" %d passed, %s%d failed%s\n", target->testPassCount, - g_escapeRedBold, target->testFailCount, g_escapeEndColor); - hasErrors = true; } else { - printf(" %s%d passed%s, %d failed\n", g_escapeGreenBold, - target->testPassCount, g_escapeEndColor, target->testFailCount); + printf(" %s%s ", target->name.c_str(), + padding.c_str() + target->name.length()); + print_results(target->testPassCount, target->testFailCount, + target->testIgnoreCount); } } } |