diff options
32 files changed, 653 insertions, 140 deletions
diff --git a/apct-tests/perftests/core/src/com/android/internal/util/FastDataPerfTest.java b/apct-tests/perftests/core/src/com/android/internal/util/FastDataPerfTest.java index 2700fff4cba1..76656bd67afa 100644 --- a/apct-tests/perftests/core/src/com/android/internal/util/FastDataPerfTest.java +++ b/apct-tests/perftests/core/src/com/android/internal/util/FastDataPerfTest.java @@ -52,21 +52,45 @@ public class FastDataPerfTest { while (state.keepRunning()) { os.reset(); final BufferedOutputStream bos = new BufferedOutputStream(os, BUFFER_SIZE); - final DataOutput out = new DataOutputStream(bos); - doWrite(out); - bos.flush(); + final DataOutputStream out = new DataOutputStream(bos); + try { + doWrite(out); + out.flush(); + } finally { + out.close(); + } } } @Test - public void timeWrite_Local() throws IOException { + public void timeWrite_LocalUsing4ByteSequences() throws IOException { final ByteArrayOutputStream os = new ByteArrayOutputStream(OUTPUT_SIZE); final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { os.reset(); - final FastDataOutput out = new FastDataOutput(os, BUFFER_SIZE); - doWrite(out); - out.flush(); + final FastDataOutput out = FastDataOutput.obtainUsing4ByteSequences(os); + try { + doWrite(out); + out.flush(); + } finally { + out.release(); + } + } + } + + @Test + public void timeWrite_LocalUsing3ByteSequences() throws IOException { + final ByteArrayOutputStream os = new ByteArrayOutputStream(OUTPUT_SIZE); + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + os.reset(); + final FastDataOutput out = FastDataOutput.obtainUsing3ByteSequences(os); + try { + doWrite(out); + out.flush(); + } finally { + out.release(); + } } } @@ -77,19 +101,42 @@ public class FastDataPerfTest { while (state.keepRunning()) { is.reset(); final BufferedInputStream bis = new BufferedInputStream(is, BUFFER_SIZE); - final DataInput in = new DataInputStream(bis); - doRead(in); + final DataInputStream in = new DataInputStream(bis); + try { + doRead(in); + } finally { + in.close(); + } + } + } + + @Test + public void timeRead_LocalUsing4ByteSequences() throws Exception { + final ByteArrayInputStream is = new ByteArrayInputStream(doWrite()); + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + is.reset(); + final FastDataInput in = FastDataInput.obtainUsing4ByteSequences(is); + try { + doRead(in); + } finally { + in.release(); + } } } @Test - public void timeRead_Local() throws Exception { + public void timeRead_LocalUsing3ByteSequences() throws Exception { final ByteArrayInputStream is = new ByteArrayInputStream(doWrite()); final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { is.reset(); - final DataInput in = new FastDataInput(is, BUFFER_SIZE); - doRead(in); + final FastDataInput in = FastDataInput.obtainUsing3ByteSequences(is); + try { + doRead(in); + } finally { + in.release(); + } } } diff --git a/core/api/current.txt b/core/api/current.txt index dc1949693802..e513bba57f9f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -29758,7 +29758,7 @@ package android.os { method @Deprecated public static void changeDebugPort(int); method public static void dumpHprofData(String) throws java.io.IOException; method public static boolean dumpService(String, java.io.FileDescriptor, String[]); - method public static void enableEmulatorTraceOutput(); + method @Deprecated public static void enableEmulatorTraceOutput(); method public static int getBinderDeathObjectCount(); method public static int getBinderLocalObjectCount(); method public static int getBinderProxyObjectCount(); diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index b4930fa931eb..399f11b0f2a7 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -1155,6 +1155,8 @@ public final class Debug * consequences. * * To temporarily enable tracing, use {@link #startNativeTracing()}. + * + * @deprecated Please use other tracing method in this class. */ public static void enableEmulatorTraceOutput() { Log.w(TAG, "Unimplemented"); diff --git a/core/java/android/util/CharsetUtils.java b/core/java/android/util/CharsetUtils.java index fa146675b8d1..3b08c3b6d52f 100644 --- a/core/java/android/util/CharsetUtils.java +++ b/core/java/android/util/CharsetUtils.java @@ -26,6 +26,12 @@ import dalvik.annotation.optimization.FastNative; * <p> * These methods purposefully accept only non-movable byte array addresses to * avoid extra JNI overhead. + * <p> + * Callers are cautioned that there is a long-standing ART bug that emits + * non-standard 4-byte sequences, as described by {@code kUtfUse4ByteSequence} + * in {@code art/runtime/jni/jni_internal.cc}. If precise modified UTF-8 + * encoding is required, use {@link com.android.internal.util.ModifiedUtf8} + * instead. * * @hide */ @@ -33,6 +39,12 @@ public class CharsetUtils { /** * Attempt to encode the given string as modified UTF-8 into the destination * byte array without making any new allocations. + * <p> + * Callers are cautioned that there is a long-standing ART bug that emits + * non-standard 4-byte sequences, as described by + * {@code kUtfUse4ByteSequence} in {@code art/runtime/jni/jni_internal.cc}. + * If precise modified UTF-8 encoding is required, use + * {@link com.android.internal.util.ModifiedUtf8} instead. * * @param src string value to be encoded * @param dest destination byte array to encode into @@ -50,6 +62,12 @@ public class CharsetUtils { /** * Attempt to encode the given string as modified UTF-8 into the destination * byte array without making any new allocations. + * <p> + * Callers are cautioned that there is a long-standing ART bug that emits + * non-standard 4-byte sequences, as described by + * {@code kUtfUse4ByteSequence} in {@code art/runtime/jni/jni_internal.cc}. + * If precise modified UTF-8 encoding is required, use + * {@link com.android.internal.util.ModifiedUtf8} instead. * * @param src string value to be encoded * @param srcLen exact length of string to be encoded @@ -66,6 +84,12 @@ public class CharsetUtils { /** * Attempt to decode a modified UTF-8 string from the source byte array. + * <p> + * Callers are cautioned that there is a long-standing ART bug that emits + * non-standard 4-byte sequences, as described by + * {@code kUtfUse4ByteSequence} in {@code art/runtime/jni/jni_internal.cc}. + * If precise modified UTF-8 encoding is required, use + * {@link com.android.internal.util.ModifiedUtf8} instead. * * @param src source byte array to decode from * @param srcOff offset into source where decoding should begin diff --git a/core/java/android/util/TEST_MAPPING b/core/java/android/util/TEST_MAPPING new file mode 100644 index 000000000000..0ae1c1593366 --- /dev/null +++ b/core/java/android/util/TEST_MAPPING @@ -0,0 +1,28 @@ +{ + "presubmit": [ + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.util.CharsetUtilsTest" + }, + { + "include-filter": "com.android.internal.util.FastDataTest" + } + ], + "file_patterns": ["CharsetUtils|FastData"] + }, + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.util.XmlTest" + }, + { + "include-filter": "android.util.BinaryXmlTest" + } + ], + "file_patterns": ["Xml"] + } + ] +} diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ffa577a47c88..271d7ba9cdd3 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -12810,6 +12810,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Set if view is a heading for a section of content for accessibility purposes. + * <p> + * Users of some accessibility services can choose to navigate between headings + * instead of between paragraphs, words, etc. Apps that provide headings on + * sections of text can help the text navigation experience. * * @param isHeading {@code true} if the view is a heading, {@code false} otherwise. * diff --git a/core/java/com/android/internal/util/BinaryXmlPullParser.java b/core/java/com/android/internal/util/BinaryXmlPullParser.java index 57552f301bd6..d3abac9f8877 100644 --- a/core/java/com/android/internal/util/BinaryXmlPullParser.java +++ b/core/java/com/android/internal/util/BinaryXmlPullParser.java @@ -73,12 +73,6 @@ import java.util.Objects; * </ul> */ public final class BinaryXmlPullParser implements TypedXmlPullParser { - /** - * Default buffer size, which matches {@code FastXmlSerializer}. This should - * be kept in sync with {@link BinaryXmlPullParser}. - */ - private static final int BUFFER_SIZE = 32_768; - private FastDataInput mIn; private int mCurrentToken = START_DOCUMENT; @@ -100,7 +94,12 @@ public final class BinaryXmlPullParser implements TypedXmlPullParser { throw new UnsupportedOperationException(); } - mIn = new FastDataInput(is, BUFFER_SIZE); + if (mIn != null) { + mIn.release(); + mIn = null; + } + + mIn = FastDataInput.obtainUsing4ByteSequences(is); mCurrentToken = START_DOCUMENT; mCurrentDepth = 0; diff --git a/core/java/com/android/internal/util/BinaryXmlSerializer.java b/core/java/com/android/internal/util/BinaryXmlSerializer.java index 9df4bdb157c8..485430a43768 100644 --- a/core/java/com/android/internal/util/BinaryXmlSerializer.java +++ b/core/java/com/android/internal/util/BinaryXmlSerializer.java @@ -91,12 +91,6 @@ public final class BinaryXmlSerializer implements TypedXmlSerializer { static final int TYPE_BOOLEAN_TRUE = 12 << 4; static final int TYPE_BOOLEAN_FALSE = 13 << 4; - /** - * Default buffer size, which matches {@code FastXmlSerializer}. This should - * be kept in sync with {@link BinaryXmlPullParser}. - */ - private static final int BUFFER_SIZE = 32_768; - private FastDataOutput mOut; /** @@ -124,7 +118,7 @@ public final class BinaryXmlSerializer implements TypedXmlSerializer { throw new UnsupportedOperationException(); } - mOut = new FastDataOutput(os, BUFFER_SIZE); + mOut = FastDataOutput.obtainUsing4ByteSequences(os); mOut.write(PROTOCOL_MAGIC_VERSION_0); mTagCount = 0; @@ -138,7 +132,9 @@ public final class BinaryXmlSerializer implements TypedXmlSerializer { @Override public void flush() throws IOException { - mOut.flush(); + if (mOut != null) { + mOut.flush(); + } } @Override @@ -157,6 +153,9 @@ public final class BinaryXmlSerializer implements TypedXmlSerializer { public void endDocument() throws IOException { mOut.writeByte(END_DOCUMENT | TYPE_NULL); flush(); + + mOut.release(); + mOut = null; } @Override diff --git a/core/java/com/android/internal/util/FastDataInput.java b/core/java/com/android/internal/util/FastDataInput.java index f8d241b5ede0..5117034815fc 100644 --- a/core/java/com/android/internal/util/FastDataInput.java +++ b/core/java/com/android/internal/util/FastDataInput.java @@ -30,6 +30,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.Arrays; import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; /** * Optimized implementation of {@link DataInput} which buffers data in memory @@ -41,13 +42,18 @@ import java.util.Objects; public class FastDataInput implements DataInput, Closeable { private static final int MAX_UNSIGNED_SHORT = 65_535; + private static final int DEFAULT_BUFFER_SIZE = 32_768; + + private static AtomicReference<FastDataInput> sInCache = new AtomicReference<>(); + private final VMRuntime mRuntime; - private final InputStream mIn; private final byte[] mBuffer; private final long mBufferPtr; private final int mBufferCap; + private final boolean mUse4ByteSequence; + private InputStream mIn; private int mBufferPos; private int mBufferLim; @@ -57,7 +63,18 @@ public class FastDataInput implements DataInput, Closeable { private int mStringRefCount = 0; private String[] mStringRefs = new String[32]; + /** + * @deprecated callers must specify {@code use4ByteSequence} so they make a + * clear choice about working around a long-standing ART bug, as + * described by the {@code kUtfUse4ByteSequence} comments in + * {@code art/runtime/jni/jni_internal.cc}. + */ + @Deprecated public FastDataInput(@NonNull InputStream in, int bufferSize) { + this(in, bufferSize, true /* use4ByteSequence */); + } + + public FastDataInput(@NonNull InputStream in, int bufferSize, boolean use4ByteSequence) { mRuntime = VMRuntime.getRuntime(); mIn = Objects.requireNonNull(in); if (bufferSize < 8) { @@ -67,6 +84,64 @@ public class FastDataInput implements DataInput, Closeable { mBuffer = (byte[]) mRuntime.newNonMovableArray(byte.class, bufferSize); mBufferPtr = mRuntime.addressOf(mBuffer); mBufferCap = mBuffer.length; + mUse4ByteSequence = use4ByteSequence; + } + + /** + * Obtain a {@link FastDataInput} configured with the given + * {@link InputStream} and which encodes large code-points using 3-byte + * sequences. + * <p> + * This <em>is</em> compatible with the {@link DataInput} API contract, + * which specifies that large code-points must be encoded with 3-byte + * sequences. + */ + public static FastDataInput obtainUsing3ByteSequences(@NonNull InputStream in) { + return new FastDataInput(in, DEFAULT_BUFFER_SIZE, false /* use4ByteSequence */); + } + + /** + * Obtain a {@link FastDataInput} configured with the given + * {@link InputStream} and which decodes large code-points using 4-byte + * sequences. + * <p> + * This <em>is not</em> compatible with the {@link DataInput} API contract, + * which specifies that large code-points must be encoded with 3-byte + * sequences. + */ + public static FastDataInput obtainUsing4ByteSequences(@NonNull InputStream in) { + FastDataInput instance = sInCache.getAndSet(null); + if (instance != null) { + instance.setInput(in); + return instance; + } + return new FastDataInput(in, DEFAULT_BUFFER_SIZE, true /* use4ByteSequence */); + } + + /** + * Release a {@link FastDataInput} to potentially be recycled. You must not + * interact with the object after releasing it. + */ + public void release() { + mIn = null; + mBufferPos = 0; + mBufferLim = 0; + mStringRefCount = 0; + + if (mBufferCap == DEFAULT_BUFFER_SIZE && mUse4ByteSequence) { + // Try to return to the cache. + sInCache.compareAndSet(null, this); + } + } + + /** + * Re-initializes the object for the new input. + */ + private void setInput(@NonNull InputStream in) { + mIn = Objects.requireNonNull(in); + mBufferPos = 0; + mBufferLim = 0; + mStringRefCount = 0; } private void fill(int need) throws IOException { @@ -90,6 +165,7 @@ public class FastDataInput implements DataInput, Closeable { @Override public void close() throws IOException { mIn.close(); + release(); } @Override @@ -126,6 +202,14 @@ public class FastDataInput implements DataInput, Closeable { @Override public String readUTF() throws IOException { + if (mUse4ByteSequence) { + return readUTFUsing4ByteSequences(); + } else { + return readUTFUsing3ByteSequences(); + } + } + + private String readUTFUsing4ByteSequences() throws IOException { // Attempt to read directly from buffer space if there's enough room, // otherwise fall back to chunking into place final int len = readUnsignedShort(); @@ -141,6 +225,22 @@ public class FastDataInput implements DataInput, Closeable { } } + private String readUTFUsing3ByteSequences() throws IOException { + // Attempt to read directly from buffer space if there's enough room, + // otherwise fall back to chunking into place + final int len = readUnsignedShort(); + if (mBufferCap > len) { + if (mBufferLim - mBufferPos < len) fill(len); + final String res = ModifiedUtf8.decode(mBuffer, new char[len], mBufferPos, len); + mBufferPos += len; + return res; + } else { + final byte[] tmp = (byte[]) mRuntime.newNonMovableArray(byte.class, len + 1); + readFully(tmp, 0, len); + return ModifiedUtf8.decode(tmp, new char[len], 0, len); + } + } + /** * Read a {@link String} value with the additional signal that the given * value is a candidate for being canonicalized, similar to diff --git a/core/java/com/android/internal/util/FastDataOutput.java b/core/java/com/android/internal/util/FastDataOutput.java index cf5b2966d889..c9e8f8f08229 100644 --- a/core/java/com/android/internal/util/FastDataOutput.java +++ b/core/java/com/android/internal/util/FastDataOutput.java @@ -30,6 +30,7 @@ import java.io.IOException; import java.io.OutputStream; import java.util.HashMap; import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; /** * Optimized implementation of {@link DataOutput} which buffers data in memory @@ -41,23 +42,38 @@ import java.util.Objects; public class FastDataOutput implements DataOutput, Flushable, Closeable { private static final int MAX_UNSIGNED_SHORT = 65_535; + private static final int DEFAULT_BUFFER_SIZE = 32_768; + + private static AtomicReference<FastDataOutput> sOutCache = new AtomicReference<>(); + private final VMRuntime mRuntime; - private final OutputStream mOut; private final byte[] mBuffer; private final long mBufferPtr; private final int mBufferCap; + private final boolean mUse4ByteSequence; + private OutputStream mOut; private int mBufferPos; /** * Values that have been "interned" by {@link #writeInternedUTF(String)}. */ - private HashMap<String, Short> mStringRefs = new HashMap<>(); + private final HashMap<String, Short> mStringRefs = new HashMap<>(); + /** + * @deprecated callers must specify {@code use4ByteSequence} so they make a + * clear choice about working around a long-standing ART bug, as + * described by the {@code kUtfUse4ByteSequence} comments in + * {@code art/runtime/jni/jni_internal.cc}. + */ + @Deprecated public FastDataOutput(@NonNull OutputStream out, int bufferSize) { + this(out, bufferSize, true /* use4ByteSequence */); + } + + public FastDataOutput(@NonNull OutputStream out, int bufferSize, boolean use4ByteSequence) { mRuntime = VMRuntime.getRuntime(); - mOut = Objects.requireNonNull(out); if (bufferSize < 8) { throw new IllegalArgumentException(); } @@ -65,6 +81,68 @@ public class FastDataOutput implements DataOutput, Flushable, Closeable { mBuffer = (byte[]) mRuntime.newNonMovableArray(byte.class, bufferSize); mBufferPtr = mRuntime.addressOf(mBuffer); mBufferCap = mBuffer.length; + mUse4ByteSequence = use4ByteSequence; + + setOutput(out); + } + + /** + * Obtain a {@link FastDataOutput} configured with the given + * {@link OutputStream} and which encodes large code-points using 3-byte + * sequences. + * <p> + * This <em>is</em> compatible with the {@link DataOutput} API contract, + * which specifies that large code-points must be encoded with 3-byte + * sequences. + */ + public static FastDataOutput obtainUsing3ByteSequences(@NonNull OutputStream out) { + return new FastDataOutput(out, DEFAULT_BUFFER_SIZE, false /* use4ByteSequence */); + } + + /** + * Obtain a {@link FastDataOutput} configured with the given + * {@link OutputStream} and which encodes large code-points using 4-byte + * sequences. + * <p> + * This <em>is not</em> compatible with the {@link DataOutput} API contract, + * which specifies that large code-points must be encoded with 3-byte + * sequences. + */ + public static FastDataOutput obtainUsing4ByteSequences(@NonNull OutputStream out) { + FastDataOutput instance = sOutCache.getAndSet(null); + if (instance != null) { + instance.setOutput(out); + return instance; + } + return new FastDataOutput(out, DEFAULT_BUFFER_SIZE, true /* use4ByteSequence */); + } + + /** + * Release a {@link FastDataOutput} to potentially be recycled. You must not + * interact with the object after releasing it. + */ + public void release() { + if (mBufferPos > 0) { + throw new IllegalStateException("Lingering data, call flush() before releasing."); + } + + mOut = null; + mBufferPos = 0; + mStringRefs.clear(); + + if (mBufferCap == DEFAULT_BUFFER_SIZE && mUse4ByteSequence) { + // Try to return to the cache. + sOutCache.compareAndSet(null, this); + } + } + + /** + * Re-initializes the object for the new output. + */ + private void setOutput(@NonNull OutputStream out) { + mOut = Objects.requireNonNull(out); + mBufferPos = 0; + mStringRefs.clear(); } private void drain() throws IOException { @@ -83,6 +161,7 @@ public class FastDataOutput implements DataOutput, Flushable, Closeable { @Override public void close() throws IOException { mOut.close(); + release(); } @Override @@ -109,6 +188,14 @@ public class FastDataOutput implements DataOutput, Flushable, Closeable { @Override public void writeUTF(String s) throws IOException { + if (mUse4ByteSequence) { + writeUTFUsing4ByteSequences(s); + } else { + writeUTFUsing3ByteSequences(s); + } + } + + private void writeUTFUsing4ByteSequences(String s) throws IOException { // Attempt to write directly to buffer space if there's enough room, // otherwise fall back to chunking into place if (mBufferCap - mBufferPos < 2 + s.length()) drain(); @@ -136,6 +223,27 @@ public class FastDataOutput implements DataOutput, Flushable, Closeable { } } + private void writeUTFUsing3ByteSequences(String s) throws IOException { + final int len = (int) ModifiedUtf8.countBytes(s, false); + if (len > MAX_UNSIGNED_SHORT) { + throw new IOException("Modified UTF-8 length too large: " + len); + } + + // Attempt to write directly to buffer space if there's enough room, + // otherwise fall back to chunking into place + if (mBufferCap >= 2 + len) { + if (mBufferCap - mBufferPos < 2 + len) drain(); + writeShort(len); + ModifiedUtf8.encode(mBuffer, mBufferPos, s); + mBufferPos += len; + } else { + final byte[] tmp = (byte[]) mRuntime.newNonMovableArray(byte.class, len + 1); + ModifiedUtf8.encode(tmp, 0, s); + writeShort(len); + write(tmp, 0, len); + } + } + /** * Write a {@link String} value with the additional signal that the given * value is a candidate for being canonicalized, similar to diff --git a/core/java/com/android/internal/util/ModifiedUtf8.java b/core/java/com/android/internal/util/ModifiedUtf8.java new file mode 100644 index 000000000000..a144c0034dd2 --- /dev/null +++ b/core/java/com/android/internal/util/ModifiedUtf8.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.internal.util; + +import java.io.UTFDataFormatException; + +public class ModifiedUtf8 { + /** + * Decodes a byte array containing <i>modified UTF-8</i> bytes into a string. + * + * <p>Note that although this method decodes the (supposedly impossible) zero byte to U+0000, + * that's what the RI does too. + */ + public static String decode(byte[] in, char[] out, int offset, int utfSize) + throws UTFDataFormatException { + int count = 0, s = 0, a; + while (count < utfSize) { + if ((out[s] = (char) in[offset + count++]) < '\u0080') { + s++; + } else if (((a = out[s]) & 0xe0) == 0xc0) { + if (count >= utfSize) { + throw new UTFDataFormatException("bad second byte at " + count); + } + int b = in[offset + count++]; + if ((b & 0xC0) != 0x80) { + throw new UTFDataFormatException("bad second byte at " + (count - 1)); + } + out[s++] = (char) (((a & 0x1F) << 6) | (b & 0x3F)); + } else if ((a & 0xf0) == 0xe0) { + if (count + 1 >= utfSize) { + throw new UTFDataFormatException("bad third byte at " + (count + 1)); + } + int b = in[offset + count++]; + int c = in[offset + count++]; + if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) { + throw new UTFDataFormatException("bad second or third byte at " + (count - 2)); + } + out[s++] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F)); + } else { + throw new UTFDataFormatException("bad byte at " + (count - 1)); + } + } + return new String(out, 0, s); + } + + /** + * Returns the number of bytes the modified UTF-8 representation of 's' would take. Note + * that this is just the space for the bytes representing the characters, not the length + * which precedes those bytes, because different callers represent the length differently, + * as two, four, or even eight bytes. If {@code shortLength} is true, we'll throw an + * exception if the string is too long for its length to be represented by a short. + */ + public static long countBytes(String s, boolean shortLength) throws UTFDataFormatException { + long result = 0; + final int length = s.length(); + for (int i = 0; i < length; ++i) { + char ch = s.charAt(i); + if (ch != 0 && ch <= 127) { // U+0000 uses two bytes. + ++result; + } else if (ch <= 2047) { + result += 2; + } else { + result += 3; + } + if (shortLength && result > 65535) { + throw new UTFDataFormatException("String more than 65535 UTF bytes long"); + } + } + return result; + } + + /** + * Encodes the <i>modified UTF-8</i> bytes corresponding to string {@code s} into the + * byte array {@code dst}, starting at the given {@code offset}. + */ + public static void encode(byte[] dst, int offset, String s) { + final int length = s.length(); + for (int i = 0; i < length; i++) { + char ch = s.charAt(i); + if (ch != 0 && ch <= 127) { // U+0000 uses two bytes. + dst[offset++] = (byte) ch; + } else if (ch <= 2047) { + dst[offset++] = (byte) (0xc0 | (0x1f & (ch >> 6))); + dst[offset++] = (byte) (0x80 | (0x3f & ch)); + } else { + dst[offset++] = (byte) (0xe0 | (0x0f & (ch >> 12))); + dst[offset++] = (byte) (0x80 | (0x3f & (ch >> 6))); + dst[offset++] = (byte) (0x80 | (0x3f & ch)); + } + } + } + + private ModifiedUtf8() { + } +} diff --git a/core/java/com/android/internal/util/TEST_MAPPING b/core/java/com/android/internal/util/TEST_MAPPING index 5881c5198617..41d59bbeb801 100644 --- a/core/java/com/android/internal/util/TEST_MAPPING +++ b/core/java/com/android/internal/util/TEST_MAPPING @@ -1,7 +1,20 @@ { "presubmit": [ { - "name": "ScreenshotHelperTests" + "name": "ScreenshotHelperTests", + "file_patterns": ["ScreenshotHelper"] + }, + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.util.XmlTest" + }, + { + "include-filter": "android.util.BinaryXmlTest" + } + ], + "file_patterns": ["Xml"] } ] -}
\ No newline at end of file +} diff --git a/core/jni/TEST_MAPPING b/core/jni/TEST_MAPPING new file mode 100644 index 000000000000..004c30ee5113 --- /dev/null +++ b/core/jni/TEST_MAPPING @@ -0,0 +1,16 @@ +{ + "presubmit": [ + { + "name": "FrameworksCoreTests", + "options": [ + { + "include-filter": "android.util.CharsetUtilsTest" + }, + { + "include-filter": "com.android.internal.util.FastDataTest" + } + ], + "file_patterns": ["CharsetUtils|FastData"] + } + ] +} diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp index ef6fd7dd6829..be82879c8411 100644 --- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp +++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp @@ -36,7 +36,6 @@ #include <inttypes.h> #include <sys/stat.h> #include <sys/types.h> -#include <linux/fs.h> #include <memory> @@ -254,16 +253,6 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr return INSTALL_FAILED_CONTAINER_ERROR; } - // If a filesystem like f2fs supports per-file compression, set the compression bit before data - // writes - unsigned int flags; - if (ioctl(fd, FS_IOC_GETFLAGS, &flags) == -1) { - ALOGE("Failed to call FS_IOC_GETFLAGS on %s: %s\n", localTmpFileName, strerror(errno)); - } else if ((flags & FS_COMPR_FL) == 0) { - flags |= FS_COMPR_FL; - ioctl(fd, FS_IOC_SETFLAGS, &flags); - } - if (!zipFile->uncompressEntry(zipEntry, fd)) { ALOGE("Failed uncompressing %s to %s\n", fileName, localTmpFileName); close(fd); diff --git a/core/tests/coretests/src/android/util/XmlTest.java b/core/tests/coretests/src/android/util/XmlTest.java index 4e10ea926966..1cd4d139b229 100644 --- a/core/tests/coretests/src/android/util/XmlTest.java +++ b/core/tests/coretests/src/android/util/XmlTest.java @@ -224,7 +224,7 @@ public class XmlTest { doVerifyRead(in); } - private static final String TEST_STRING = "com.example"; + private static final String TEST_STRING = "com☃example😀typical☃package😀name"; private static final String TEST_STRING_EMPTY = ""; private static final byte[] TEST_BYTES = new byte[] { 0, 1, 2, 3, 4, 3, 2, 1, 0 }; private static final byte[] TEST_BYTES_EMPTY = new byte[0]; diff --git a/core/tests/coretests/src/com/android/internal/util/FastDataTest.java b/core/tests/coretests/src/com/android/internal/util/FastDataTest.java index 81fb39fed026..04dfd6ee30e2 100644 --- a/core/tests/coretests/src/com/android/internal/util/FastDataTest.java +++ b/core/tests/coretests/src/com/android/internal/util/FastDataTest.java @@ -23,10 +23,13 @@ import static org.junit.Assert.fail; import android.annotation.NonNull; import android.util.ExceptionUtils; -import androidx.test.ext.junit.runners.AndroidJUnit4; +import libcore.util.HexEncoding; +import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -38,22 +41,34 @@ import java.io.EOFException; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.Collection; import java.util.function.Consumer; -@RunWith(AndroidJUnit4.class) +@RunWith(Parameterized.class) public class FastDataTest { + private final boolean use4ByteSequence; + private static final String TEST_SHORT_STRING = "a"; - private static final String TEST_LONG_STRING = "com☃example☃typical☃package☃name"; + private static final String TEST_LONG_STRING = "com☃example😀typical☃package😀name"; private static final byte[] TEST_BYTES = TEST_LONG_STRING.getBytes(StandardCharsets.UTF_16LE); + @Parameters(name = "use4ByteSequence={0}") + public static Collection<Object[]> data() { + return Arrays.asList(new Object[][] { {true}, {false} }); + } + + public FastDataTest(boolean use4ByteSequence) { + this.use4ByteSequence = use4ByteSequence; + } + @Test public void testEndOfFile_Int() throws Exception { try (FastDataInput in = new FastDataInput(new ByteArrayInputStream( - new byte[] { 1 }), 1000)) { + new byte[] { 1 }), 1000, use4ByteSequence)) { assertThrows(EOFException.class, () -> in.readInt()); } try (FastDataInput in = new FastDataInput(new ByteArrayInputStream( - new byte[] { 1, 1, 1, 1 }), 1000)) { + new byte[] { 1, 1, 1, 1 }), 1000, use4ByteSequence)) { assertEquals(1, in.readByte()); assertThrows(EOFException.class, () -> in.readInt()); } @@ -62,11 +77,11 @@ public class FastDataTest { @Test public void testEndOfFile_String() throws Exception { try (FastDataInput in = new FastDataInput(new ByteArrayInputStream( - new byte[] { 1 }), 1000)) { + new byte[] { 1 }), 1000, use4ByteSequence)) { assertThrows(EOFException.class, () -> in.readUTF()); } try (FastDataInput in = new FastDataInput(new ByteArrayInputStream( - new byte[] { 1, 1, 1, 1 }), 1000)) { + new byte[] { 1, 1, 1, 1 }), 1000, use4ByteSequence)) { assertThrows(EOFException.class, () -> in.readUTF()); } } @@ -74,12 +89,12 @@ public class FastDataTest { @Test public void testEndOfFile_Bytes_Small() throws Exception { try (FastDataInput in = new FastDataInput(new ByteArrayInputStream( - new byte[] { 1, 1, 1, 1 }), 1000)) { + new byte[] { 1, 1, 1, 1 }), 1000, use4ByteSequence)) { final byte[] tmp = new byte[10]; assertThrows(EOFException.class, () -> in.readFully(tmp)); } try (FastDataInput in = new FastDataInput(new ByteArrayInputStream( - new byte[] { 1, 1, 1, 1 }), 1000)) { + new byte[] { 1, 1, 1, 1 }), 1000, use4ByteSequence)) { final byte[] tmp = new byte[10_000]; assertThrows(EOFException.class, () -> in.readFully(tmp)); } @@ -88,7 +103,8 @@ public class FastDataTest { @Test public void testUTF_Bounds() throws Exception { final char[] buf = new char[65_534]; - try (FastDataOutput out = new FastDataOutput(new ByteArrayOutputStream(), BOUNCE_SIZE)) { + try (FastDataOutput out = new FastDataOutput(new ByteArrayOutputStream(), + BOUNCE_SIZE, use4ByteSequence)) { // Writing simple string will fit fine Arrays.fill(buf, '!'); final String simple = new String(buf); @@ -100,11 +116,15 @@ public class FastDataTest { final String complex = new String(buf); assertThrows(IOException.class, () -> out.writeUTF(complex)); assertThrows(IOException.class, () -> out.writeInternedUTF(complex)); + + out.flush(); } } @Test public void testTranscode() throws Exception { + Assume.assumeFalse(use4ByteSequence); + // Verify that upstream data can be read by fast { final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); @@ -113,20 +133,22 @@ public class FastDataTest { out.flush(); final FastDataInput in = new FastDataInput( - new ByteArrayInputStream(outStream.toByteArray()), BOUNCE_SIZE); - doTransodeRead(in); + new ByteArrayInputStream(outStream.toByteArray()), + BOUNCE_SIZE, use4ByteSequence); + doTranscodeRead(in); } // Verify that fast data can be read by upstream { final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); - final FastDataOutput out = new FastDataOutput(outStream, BOUNCE_SIZE); + final FastDataOutput out = new FastDataOutput(outStream, + BOUNCE_SIZE, use4ByteSequence); doTranscodeWrite(out); out.flush(); final DataInputStream in = new DataInputStream( new ByteArrayInputStream(outStream.toByteArray())); - doTransodeRead(in); + doTranscodeRead(in); } } @@ -144,7 +166,7 @@ public class FastDataTest { out.writeDouble(32d); } - private static void doTransodeRead(DataInput in) throws IOException { + private static void doTranscodeRead(DataInput in) throws IOException { assertEquals(true, in.readBoolean()); assertEquals(false, in.readBoolean()); assertEquals(1, in.readByte()); @@ -225,10 +247,12 @@ public class FastDataTest { doBounce((out) -> { out.writeUTF(""); out.writeUTF("☃"); + out.writeUTF("😀"); out.writeUTF("example"); }, (in) -> { assertEquals("", in.readUTF()); assertEquals("☃", in.readUTF()); + assertEquals("😀", in.readUTF()); assertEquals("example", in.readUTF()); }); } @@ -263,6 +287,35 @@ public class FastDataTest { }, 1); } + /** + * Verify that we encode every valid code-point identically to RI when + * running in 3-byte mode. + */ + @Test + public void testBounce_UTF_Exhaustive() throws Exception { + Assume.assumeFalse(use4ByteSequence); + + final ByteArrayOutputStream slowStream = new ByteArrayOutputStream(); + final DataOutput slowData = new DataOutputStream(slowStream); + + final ByteArrayOutputStream fastStream = new ByteArrayOutputStream(); + final FastDataOutput fastData = FastDataOutput.obtainUsing3ByteSequences(fastStream); + + for (int cp = Character.MIN_CODE_POINT; cp < Character.MAX_CODE_POINT; cp++) { + if (Character.isValidCodePoint(cp)) { + final String cpString = new String(Character.toChars(cp)); + slowStream.reset(); + slowData.writeUTF(cpString); + fastStream.reset(); + fastData.writeUTF(cpString); + fastData.flush(); + assertEquals("Bad encoding for code-point " + Integer.toHexString(cp), + HexEncoding.encodeToString(slowStream.toByteArray()), + HexEncoding.encodeToString(fastStream.toByteArray())); + } + } + } + @Test public void testBounce_InternedUTF() throws Exception { doBounce((out) -> { @@ -355,22 +408,24 @@ public class FastDataTest { * Verify that some common data can be written and read back, effectively * "bouncing" it through a serialized representation. */ - private static void doBounce(@NonNull ThrowingConsumer<FastDataOutput> out, + private void doBounce(@NonNull ThrowingConsumer<FastDataOutput> out, @NonNull ThrowingConsumer<FastDataInput> in) throws Exception { doBounce(out, in, BOUNCE_REPEAT); } - private static void doBounce(@NonNull ThrowingConsumer<FastDataOutput> out, + private void doBounce(@NonNull ThrowingConsumer<FastDataOutput> out, @NonNull ThrowingConsumer<FastDataInput> in, int count) throws Exception { final ByteArrayOutputStream outStream = new ByteArrayOutputStream(); - final FastDataOutput outData = new FastDataOutput(outStream, BOUNCE_SIZE); + final FastDataOutput outData = new FastDataOutput(outStream, + BOUNCE_SIZE, use4ByteSequence); for (int i = 0; i < count; i++) { out.accept(outData); } outData.flush(); final ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray()); - final FastDataInput inData = new FastDataInput(inStream, BOUNCE_SIZE); + final FastDataInput inData = new FastDataInput(inStream, + BOUNCE_SIZE, use4ByteSequence); for (int i = 0; i < count; i++) { in.accept(inData); } diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 055e5ad17def..ad089b63a7c1 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -519,7 +519,7 @@ public final class Bitmap implements Parcelable { ARGB_8888 (5), /** - * Each pixels is stored on 8 bytes. Each channel (RGB and alpha + * Each pixel is stored on 8 bytes. Each channel (RGB and alpha * for translucency) is stored as a * {@link android.util.Half half-precision floating point value}. * diff --git a/libs/incident/libincident.map.txt b/libs/incident/libincident.map.txt index f157763f1a03..f75cceaf59fa 100644 --- a/libs/incident/libincident.map.txt +++ b/libs/incident/libincident.map.txt @@ -1,15 +1,15 @@ LIBINCIDENT { global: - AIncidentReportArgs_init; # apex # introduced=30 - AIncidentReportArgs_clone; # apex # introduced=30 - AIncidentReportArgs_delete; # apex # introduced=30 - AIncidentReportArgs_setAll; # apex # introduced=30 - AIncidentReportArgs_setPrivacyPolicy; # apex # introduced=30 - AIncidentReportArgs_addSection; # apex # introduced=30 - AIncidentReportArgs_setReceiverPackage; # apex # introduced=30 - AIncidentReportArgs_setReceiverClass; # apex # introduced=30 - AIncidentReportArgs_addHeader; # apex # introduced=30 - AIncidentReportArgs_takeReport; # apex # introduced=30 + AIncidentReportArgs_init; # systemapi # introduced=30 + AIncidentReportArgs_clone; # systemapi # introduced=30 + AIncidentReportArgs_delete; # systemapi # introduced=30 + AIncidentReportArgs_setAll; # systemapi # introduced=30 + AIncidentReportArgs_setPrivacyPolicy; # systemapi # introduced=30 + AIncidentReportArgs_addSection; # systemapi # introduced=30 + AIncidentReportArgs_setReceiverPackage; # systemapi # introduced=30 + AIncidentReportArgs_setReceiverClass; # systemapi # introduced=30 + AIncidentReportArgs_addHeader; # systemapi # introduced=30 + AIncidentReportArgs_takeReport; # systemapi # introduced=30 local: *; }; diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedBackupTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedBackupTaskTest.java index f6914efd6d83..23d6e34db4f1 100644 --- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedBackupTaskTest.java +++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedBackupTaskTest.java @@ -22,8 +22,8 @@ import static com.android.server.backup.encryption.protos.nano.ChunksMetadataPro import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTaskTest.java index 096b2da10c98..bfc5d0dca3ff 100644 --- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTaskTest.java +++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedFullBackupTaskTest.java @@ -18,9 +18,9 @@ package com.android.server.backup.encryption.tasks; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.any; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertThrows; diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTaskTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTaskTest.java index fa4fef50ac1a..222b88221ba2 100644 --- a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTaskTest.java +++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/EncryptedKvBackupTaskTest.java @@ -19,8 +19,8 @@ package com.android.server.backup.encryption.tasks; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertFalse; @@ -41,13 +41,6 @@ import com.android.server.backup.encryption.protos.nano.KeyValueListingProto; import com.android.server.backup.encryption.protos.nano.WrappedKeyProto; import com.android.server.backup.testing.CryptoTestUtils; -import java.io.IOException; -import java.util.Arrays; -import java.util.Map; -import java.util.Map.Entry; - -import javax.crypto.SecretKey; - import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -59,6 +52,14 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; +import java.io.IOException; +import java.util.Arrays; +import java.util.Map; +import java.util.Map.Entry; + +import javax.crypto.SecretKey; + + @RunWith(RobolectricTestRunner.class) public class EncryptedKvBackupTaskTest { private static final boolean INCREMENTAL = true; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java index 5efce4da970f..30bbc2a3a864 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/ApplicationsStateRoboTest.java @@ -21,9 +21,10 @@ import static android.os.UserHandle.MU_ENABLED; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java index 95f7ef41b10b..508dffc2fa21 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java @@ -18,7 +18,7 @@ package com.android.settingslib.net; import static com.google.common.truth.Truth.assertThat; -import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java index d6c8816ecc58..c190d0731a40 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java @@ -22,7 +22,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import android.app.Activity; @@ -141,7 +141,7 @@ public class EditUserInfoControllerTest { dialog.show(); dialog.cancel(); - verifyZeroInteractions(successCallback); + verifyNoInteractions(successCallback); verify(cancelCallback, times(1)) .run(); } @@ -157,7 +157,7 @@ public class EditUserInfoControllerTest { dialog.show(); dialog.getButton(Dialog.BUTTON_NEGATIVE).performClick(); - verifyZeroInteractions(successCallback); + verifyNoInteractions(successCallback); verify(cancelCallback, times(1)) .run(); } @@ -178,7 +178,7 @@ public class EditUserInfoControllerTest { verify(successCallback, times(1)) .accept("test", oldUserIcon); - verifyZeroInteractions(cancelCallback); + verifyNoInteractions(cancelCallback); } @Test @@ -196,7 +196,7 @@ public class EditUserInfoControllerTest { verify(successCallback, times(1)) .accept("test", null); - verifyZeroInteractions(cancelCallback); + verifyNoInteractions(cancelCallback); } @Test @@ -217,7 +217,7 @@ public class EditUserInfoControllerTest { verify(successCallback, times(1)) .accept(expectedNewName, mCurrentIcon); - verifyZeroInteractions(cancelCallback); + verifyNoInteractions(cancelCallback); } @Test @@ -236,7 +236,7 @@ public class EditUserInfoControllerTest { verify(successCallback, times(1)) .accept("test", newPhoto); - verifyZeroInteractions(cancelCallback); + verifyNoInteractions(cancelCallback); } @Test @@ -255,7 +255,7 @@ public class EditUserInfoControllerTest { verify(successCallback, times(1)) .accept("test", newPhoto); - verifyZeroInteractions(cancelCallback); + verifyNoInteractions(cancelCallback); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/UpdatableListPreferenceDialogFragmentTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/UpdatableListPreferenceDialogFragmentTest.java index 0b3495def21f..ca0aa0d7b900 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/UpdatableListPreferenceDialogFragmentTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/UpdatableListPreferenceDialogFragmentTest.java @@ -56,7 +56,7 @@ public class UpdatableListPreferenceDialogFragmentTest { mUpdatableListPrefDlgFragment = spy(UpdatableListPreferenceDialogFragment .newInstance(KEY, MetricsProto.MetricsEvent.DIALOG_SWITCH_A2DP_DEVICES)); - mEntries = spy(new ArrayList<>()); + mEntries = new ArrayList<>(); mUpdatableListPrefDlgFragment.setEntries(mEntries); mUpdatableListPrefDlgFragment .setMetricsCategory(mUpdatableListPrefDlgFragment.getArguments()); diff --git a/rs/OWNERS b/rs/OWNERS index 61853d3d40cf..fd03660d2d81 100644 --- a/rs/OWNERS +++ b/rs/OWNERS @@ -1,5 +1,2 @@ -butlermichael@google.com -dgross@google.com -jeanluc@google.com -miaowang@google.com -yangni@google.com +# Bug component: 43047 +include platform/frameworks/rs:/RS_OWNERS # RenderScript team diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index c3fc8e0a7535..774fe5bc862c 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -1095,7 +1095,7 @@ public class Vpn { // Except for Settings and VpnDialogs, the caller should be matched one of oldPackage or // newPackage. Otherwise, non VPN owner might get the VPN always-on status of the VPN owner. // See b/191382886. - if (mContext.checkCallingOrSelfPermission(CONTROL_VPN) != PERMISSION_GRANTED) { + if (!hasControlVpnPermission()) { if (oldPackage != null) { verifyCallingUidAndPackage(oldPackage); } @@ -2045,6 +2045,10 @@ public class Vpn { "Unauthorized Caller"); } + private boolean hasControlVpnPermission() { + return mContext.checkCallingOrSelfPermission(CONTROL_VPN) == PERMISSION_GRANTED; + } + private class Connection implements ServiceConnection { private IBinder mService; @@ -3337,7 +3341,7 @@ public class Vpn { // TODO(b/230548427): Remove SDK check once VPN related stuff are // decoupled from ConnectivityServiceTest. - if (SdkLevel.isAtLeastT() && category != null) { + if (SdkLevel.isAtLeastT() && category != null && isVpnApp(mPackage)) { sendEventToVpnManagerApp(category, errorClass, errorCode, getPackage(), mSessionKey, makeVpnProfileStateLocked(), mActiveNetwork, @@ -3846,8 +3850,10 @@ public class Vpn { Binder.restoreCallingIdentity(token); } - // TODO: if package has CONTROL_VPN, grant the ACTIVATE_PLATFORM_VPN appop. - // This mirrors the prepareAndAuthorize that is used by VpnService. + // If package has CONTROL_VPN, grant the ACTIVATE_PLATFORM_VPN appop. + if (hasControlVpnPermission()) { + setPackageAuthorization(packageName, VpnManager.TYPE_VPN_PLATFORM); + } // Return whether the app is already pre-consented return isVpnProfilePreConsented(mContext, packageName); diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java index 7d17109af7d3..edcf357cd233 100644 --- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java +++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java @@ -43,23 +43,23 @@ import static com.android.server.backup.testing.Utils.transferStreamedData; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.intThat; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.argThat; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.intThat; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; import static org.robolectric.shadow.api.Shadow.extract; @@ -2185,7 +2185,7 @@ public class KeyValueBackupTaskTest { task.waitCancel(); reset(transportMock.transport); taskFinished.block(); - verifyZeroInteractions(transportMock.transport); + verifyNoInteractions(transportMock.transport); } @Test diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java index 9dfb0cc289ee..d4b6c91eb7a0 100644 --- a/telephony/common/android/telephony/LocationAccessPolicy.java +++ b/telephony/common/android/telephony/LocationAccessPolicy.java @@ -316,9 +316,11 @@ public final class LocationAccessPolicy { return LocationPermissionResult.ALLOWED; } - // Check the system-wide requirements. If the location main switch is off or - // the app's profile isn't in foreground, return a soft denial. - if (!checkSystemLocationAccess(context, query.callingUid, query.callingPid)) { + // Check the system-wide requirements. If the location main switch is off and the caller is + // not in the allowlist of apps that always have loation access or the app's profile + // isn't in the foreground, return a soft denial. + if (!checkSystemLocationAccess(context, query.callingUid, query.callingPid, + query.callingPackage)) { return LocationPermissionResult.DENIED_SOFT; } @@ -344,15 +346,16 @@ public final class LocationAccessPolicy { return LocationPermissionResult.ALLOWED; } - private static boolean checkManifestPermission(Context context, int pid, int uid, String permissionToCheck) { return context.checkPermission(permissionToCheck, pid, uid) == PackageManager.PERMISSION_GRANTED; } - private static boolean checkSystemLocationAccess(@NonNull Context context, int uid, int pid) { - if (!isLocationModeEnabled(context, UserHandle.getUserHandleForUid(uid).getIdentifier())) { + private static boolean checkSystemLocationAccess(@NonNull Context context, int uid, int pid, + @NonNull String callingPackage) { + if (!isLocationModeEnabled(context, UserHandle.getUserHandleForUid(uid).getIdentifier()) + && !isLocationBypassAllowed(context, callingPackage)) { if (DBG) Log.w(TAG, "Location disabled, failed, (" + uid + ")"); return false; } @@ -373,6 +376,16 @@ public final class LocationAccessPolicy { return locationManager.isLocationEnabledForUser(UserHandle.of(userId)); } + private static boolean isLocationBypassAllowed(@NonNull Context context, + @NonNull String callingPackage) { + for (String bypassPackage : getLocationBypassPackages(context)) { + if (callingPackage.equals(bypassPackage)) { + return true; + } + } + return false; + } + /** * @return An array of packages that are always allowed to access location. */ diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java index 417fd4904599..a22e43e07d34 100644 --- a/telephony/java/android/telephony/CellSignalStrengthNr.java +++ b/telephony/java/android/telephony/CellSignalStrengthNr.java @@ -49,7 +49,7 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa private static final String TAG = "CellSignalStrengthNr"; // Lifted from Default carrier configs and max range of SSRSRP - // Boundaries: [-140 dB, -44 dB] + // Boundaries: [-156 dB, -31 dB] private int[] mSsRsrpThresholds = new int[] { -110, /* SIGNAL_STRENGTH_POOR */ -90, /* SIGNAL_STRENGTH_MODERATE */ @@ -173,14 +173,14 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa */ public CellSignalStrengthNr(int csiRsrp, int csiRsrq, int csiSinr, int csiCqiTableIndex, List<Byte> csiCqiReport, int ssRsrp, int ssRsrq, int ssSinr) { - mCsiRsrp = inRangeOrUnavailable(csiRsrp, -140, -44); + mCsiRsrp = inRangeOrUnavailable(csiRsrp, -156, -31); mCsiRsrq = inRangeOrUnavailable(csiRsrq, -20, -3); mCsiSinr = inRangeOrUnavailable(csiSinr, -23, 23); mCsiCqiTableIndex = inRangeOrUnavailable(csiCqiTableIndex, 1, 3); mCsiCqiReport = csiCqiReport.stream() - .map(cqi -> new Integer(inRangeOrUnavailable(Byte.toUnsignedInt(cqi), 0, 15))) + .map(cqi -> inRangeOrUnavailable(Byte.toUnsignedInt(cqi), 0, 15)) .collect(Collectors.toList()); - mSsRsrp = inRangeOrUnavailable(ssRsrp, -140, -44); + mSsRsrp = inRangeOrUnavailable(ssRsrp, -156, -31); mSsRsrq = inRangeOrUnavailable(ssRsrq, -43, 20); mSsSinr = inRangeOrUnavailable(ssSinr, -23, 40); updateLevel(null, null); @@ -212,8 +212,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa } /** - * Reference: 3GPP TS 38.215. - * Range: -140 dBm to -44 dBm. + * Reference: 3GPP TS 38.133 10.1.6.1. + * Range: -156 dBm to -31 dBm. * @return SS reference signal received power, {@link CellInfo#UNAVAILABLE} means unreported * value. */ @@ -242,8 +242,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa } /** - * Reference: 3GPP TS 38.215. - * Range: -140 dBm to -44 dBm. + * Reference: 3GPP TS 38.133 10.1.6.1. + * Range: -156 dBm to -31 dBm. * @return CSI reference signal received power, {@link CellInfo#UNAVAILABLE} means unreported * value. */ diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index d2858eca8171..f25498022087 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -3040,8 +3040,9 @@ public class SubscriptionManager { * @param callback Callback will be triggered once it succeeds or failed. * Pass null if don't care about the result. * + * @throws IllegalStateException when subscription manager service is not available. + * @throws SecurityException when clients do not have MODIFY_PHONE_STATE permission. * @hide - * */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @@ -3051,7 +3052,9 @@ public class SubscriptionManager { if (VDBG) logd("[setPreferredDataSubscriptionId]+ subId:" + subId); try { ISub iSub = TelephonyManager.getSubscriptionService(); - if (iSub == null) return; + if (iSub == null) { + throw new IllegalStateException("subscription manager service is null."); + } ISetOpportunisticDataCallback callbackStub = new ISetOpportunisticDataCallback.Stub() { @Override @@ -3071,7 +3074,8 @@ public class SubscriptionManager { }; iSub.setPreferredDataSubscriptionId(subId, needValidation, callbackStub); } catch (RemoteException ex) { - // ignore it + loge("setPreferredDataSubscriptionId RemoteException=" + ex); + ex.rethrowFromSystemServer(); } } diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index a710e38538ce..7fc8871e89c8 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -1134,10 +1134,7 @@ public class ApnSetting implements Parcelable { return false; } // DEFAULT can handle HIPRI. - if (hasApnType(type)) { - return true; - } - return false; + return hasApnType(type); } // Check whether the types of two APN same (even only one type of each APN is same). |