diff options
14 files changed, 458 insertions, 67 deletions
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp index 4417b681efc3..f0ef22fc6618 100644 --- a/apex/media/framework/Android.bp +++ b/apex/media/framework/Android.bp @@ -35,7 +35,6 @@ java_library { libs: [ "framework_media_annotation", ], - static_libs: [ "exoplayer2-extractor" ], @@ -110,10 +109,32 @@ java_sdk_library { ], } - java_library { name: "framework_media_annotation", srcs: [":framework-media-annotation-srcs"], installable: false, sdk_version: "core_current", } + +cc_library_shared { + name: "libmediaparser-jni", + srcs: [ + "jni/android_media_MediaParserJNI.cpp", + ], + shared_libs: [ + "libandroid", + "liblog", + "libmediametrics", + ], + cflags: [ + "-Wall", + "-Werror", + "-Wno-unused-parameter", + "-Wunreachable-code", + "-Wunused", + ], + apex_available: [ + "com.android.media", + ], + min_sdk_version: "29", +} diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java index e4b5d19e67c9..045b4136a710 100644 --- a/apex/media/framework/java/android/media/MediaParser.java +++ b/apex/media/framework/java/android/media/MediaParser.java @@ -75,6 +75,8 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Function; /** * Parses media container formats and extracts contained media samples and metadata. @@ -882,6 +884,7 @@ public final class MediaParser { // Private constants. private static final String TAG = "MediaParser"; + private static final String JNI_LIBRARY_NAME = "mediaparser-jni"; private static final Map<String, ExtractorFactory> EXTRACTOR_FACTORIES_BY_NAME; private static final Map<String, Class> EXPECTED_TYPE_BY_PARAMETER_NAME; private static final String TS_MODE_SINGLE_PMT = "single_pmt"; @@ -889,6 +892,14 @@ public final class MediaParser { private static final String TS_MODE_HLS = "hls"; private static final int BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY = 6; private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + private static final String MEDIAMETRICS_ELEMENT_SEPARATOR = "|"; + private static final int MEDIAMETRICS_MAX_STRING_SIZE = 200; + private static final int MEDIAMETRICS_PARAMETER_LIST_MAX_LENGTH; + /** + * Intentional error introduced to reported metrics to prevent identification of the parsed + * media. Note: Increasing this value may cause older hostside CTS tests to fail. + */ + private static final float MEDIAMETRICS_DITHER = .02f; @IntDef( value = { @@ -920,7 +931,7 @@ public final class MediaParser { @NonNull @ParserName String name, @NonNull OutputConsumer outputConsumer) { String[] nameAsArray = new String[] {name}; assertValidNames(nameAsArray); - return new MediaParser(outputConsumer, /* sniff= */ false, name); + return new MediaParser(outputConsumer, /* createdByName= */ true, name); } /** @@ -940,7 +951,7 @@ public final class MediaParser { if (parserNames.length == 0) { parserNames = EXTRACTOR_FACTORIES_BY_NAME.keySet().toArray(new String[0]); } - return new MediaParser(outputConsumer, /* sniff= */ true, parserNames); + return new MediaParser(outputConsumer, /* createdByName= */ false, parserNames); } // Misc static methods. @@ -1052,6 +1063,14 @@ public final class MediaParser { private long mPendingSeekPosition; private long mPendingSeekTimeMicros; private boolean mLoggedSchemeInitDataCreationException; + private boolean mReleased; + + // MediaMetrics fields. + private final boolean mCreatedByName; + private final SparseArray<Format> mTrackFormats; + private String mLastObservedExceptionName; + private long mDurationMillis; + private long mResourceByteCount; // Public methods. @@ -1166,11 +1185,16 @@ public final class MediaParser { if (mExtractorInput == null) { // TODO: For efficiency, the same implementation should be used, by providing a // clearBuffers() method, or similar. + long resourceLength = seekableInputReader.getLength(); + if (resourceLength == -1) { + mResourceByteCount = -1; + } + if (mResourceByteCount != -1) { + mResourceByteCount += resourceLength; + } mExtractorInput = new DefaultExtractorInput( - mExoDataReader, - seekableInputReader.getPosition(), - seekableInputReader.getLength()); + mExoDataReader, seekableInputReader.getPosition(), resourceLength); } mExoDataReader.mInputReader = seekableInputReader; @@ -1195,7 +1219,10 @@ public final class MediaParser { } } if (mExtractor == null) { - throw UnrecognizedInputFormatException.createForExtractors(mParserNamesPool); + UnrecognizedInputFormatException exception = + UnrecognizedInputFormatException.createForExtractors(mParserNamesPool); + mLastObservedExceptionName = exception.getClass().getName(); + throw exception; } return true; } @@ -1223,8 +1250,13 @@ public final class MediaParser { int result; try { result = mExtractor.read(mExtractorInput, mPositionHolder); - } catch (ParserException e) { - throw new ParsingException(e); + } catch (Exception e) { + mLastObservedExceptionName = e.getClass().getName(); + if (e instanceof ParserException) { + throw new ParsingException((ParserException) e); + } else { + throw e; + } } if (result == Extractor.RESULT_END_OF_INPUT) { mExtractorInput = null; @@ -1264,21 +1296,64 @@ public final class MediaParser { * invoked. */ public void release() { - // TODO: Dump media metrics here. mExtractorInput = null; mExtractor = null; + if (mReleased) { + // Nothing to do. + return; + } + mReleased = true; + + String trackMimeTypes = buildMediaMetricsString(format -> format.sampleMimeType); + String trackCodecs = buildMediaMetricsString(format -> format.codecs); + int videoWidth = -1; + int videoHeight = -1; + for (int i = 0; i < mTrackFormats.size(); i++) { + Format format = mTrackFormats.valueAt(i); + if (format.width != Format.NO_VALUE && format.height != Format.NO_VALUE) { + videoWidth = format.width; + videoHeight = format.height; + break; + } + } + + String alteredParameters = + String.join( + MEDIAMETRICS_ELEMENT_SEPARATOR, + mParserParameters.keySet().toArray(new String[0])); + alteredParameters = + alteredParameters.substring( + 0, + Math.min( + alteredParameters.length(), + MEDIAMETRICS_PARAMETER_LIST_MAX_LENGTH)); + + nativeSubmitMetrics( + mParserName, + mCreatedByName, + String.join(MEDIAMETRICS_ELEMENT_SEPARATOR, mParserNamesPool), + mLastObservedExceptionName, + addDither(mResourceByteCount), + addDither(mDurationMillis), + trackMimeTypes, + trackCodecs, + alteredParameters, + videoWidth, + videoHeight); } // Private methods. - private MediaParser(OutputConsumer outputConsumer, boolean sniff, String... parserNamesPool) { + private MediaParser( + OutputConsumer outputConsumer, boolean createdByName, String... parserNamesPool) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { throw new UnsupportedOperationException("Android version must be R or greater."); } mParserParameters = new HashMap<>(); mOutputConsumer = outputConsumer; mParserNamesPool = parserNamesPool; - mParserName = sniff ? PARSER_NAME_UNKNOWN : parserNamesPool[0]; + mCreatedByName = createdByName; + mParserName = createdByName ? parserNamesPool[0] : PARSER_NAME_UNKNOWN; mPositionHolder = new PositionHolder(); mExoDataReader = new InputReadingDataReader(); removePendingSeek(); @@ -1286,6 +1361,24 @@ public final class MediaParser { mScratchParsableByteArrayAdapter = new ParsableByteArrayAdapter(); mSchemeInitDataConstructor = getSchemeInitDataConstructor(); mMuxedCaptionFormats = new ArrayList<>(); + + // MediaMetrics. + mTrackFormats = new SparseArray<>(); + mLastObservedExceptionName = ""; + mDurationMillis = -1; + } + + private String buildMediaMetricsString(Function<Format, String> formatFieldGetter) { + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < mTrackFormats.size(); i++) { + if (i > 0) { + stringBuilder.append(MEDIAMETRICS_ELEMENT_SEPARATOR); + } + String fieldValue = formatFieldGetter.apply(mTrackFormats.valueAt(i)); + stringBuilder.append(fieldValue != null ? fieldValue : ""); + } + return stringBuilder.substring( + 0, Math.min(stringBuilder.length(), MEDIAMETRICS_MAX_STRING_SIZE)); } private void setMuxedCaptionFormats(List<MediaFormat> mediaFormats) { @@ -1528,6 +1621,10 @@ public final class MediaParser { @Override public void seekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) { + long durationUs = exoplayerSeekMap.getDurationUs(); + if (durationUs != C.TIME_UNSET) { + mDurationMillis = C.usToMs(durationUs); + } if (mExposeChunkIndexAsMediaFormat && exoplayerSeekMap instanceof ChunkIndex) { ChunkIndex chunkIndex = (ChunkIndex) exoplayerSeekMap; MediaFormat mediaFormat = new MediaFormat(); @@ -1575,6 +1672,7 @@ public final class MediaParser { @Override public void format(Format format) { + mTrackFormats.put(mTrackIndex, format); mOutputConsumer.onTrackDataFound( mTrackIndex, new TrackData( @@ -2031,6 +2129,20 @@ public final class MediaParser { return new SeekPoint(exoPlayerSeekPoint.timeUs, exoPlayerSeekPoint.position); } + /** + * Introduces random error to the given metric value in order to prevent the identification of + * the parsed media. + */ + private static long addDither(long value) { + // Generate a random in [0, 1]. + double randomDither = ThreadLocalRandom.current().nextFloat(); + // Clamp the random number to [0, 2 * MEDIAMETRICS_DITHER]. + randomDither *= 2 * MEDIAMETRICS_DITHER; + // Translate the random number to [1 - MEDIAMETRICS_DITHER, 1 + MEDIAMETRICS_DITHER]. + randomDither += 1 - MEDIAMETRICS_DITHER; + return value != -1 ? (long) (value * randomDither) : -1; + } + private static void assertValidNames(@NonNull String[] names) { for (String name : names) { if (!EXTRACTOR_FACTORIES_BY_NAME.containsKey(name)) { @@ -2070,9 +2182,26 @@ public final class MediaParser { } } + // Native methods. + + private native void nativeSubmitMetrics( + String parserName, + boolean createdByName, + String parserPool, + String lastObservedExceptionName, + long resourceByteCount, + long durationMillis, + String trackMimeTypes, + String trackCodecs, + String alteredParameters, + int videoWidth, + int videoHeight); + // Static initialization. static { + System.loadLibrary(JNI_LIBRARY_NAME); + // Using a LinkedHashMap to keep the insertion order when iterating over the keys. LinkedHashMap<String, ExtractorFactory> extractorFactoriesByName = new LinkedHashMap<>(); // Parsers are ordered to match ExoPlayer's DefaultExtractorsFactory extractor ordering, @@ -2125,6 +2254,15 @@ public final class MediaParser { // We do not check PARAMETER_EXPOSE_CAPTION_FORMATS here, and we do it in setParameters // instead. Checking that the value is a List is insufficient to catch wrong parameter // value types. + int sumOfParameterNameLengths = + expectedTypeByParameterName.keySet().stream() + .map(String::length) + .reduce(0, Integer::sum); + sumOfParameterNameLengths += PARAMETER_EXPOSE_CAPTION_FORMATS.length(); + // Add space for any required separators. + MEDIAMETRICS_PARAMETER_LIST_MAX_LENGTH = + sumOfParameterNameLengths + expectedTypeByParameterName.size(); + EXPECTED_TYPE_BY_PARAMETER_NAME = Collections.unmodifiableMap(expectedTypeByParameterName); } } diff --git a/apex/media/framework/jni/android_media_MediaParserJNI.cpp b/apex/media/framework/jni/android_media_MediaParserJNI.cpp new file mode 100644 index 000000000000..7fc4628984f5 --- /dev/null +++ b/apex/media/framework/jni/android_media_MediaParserJNI.cpp @@ -0,0 +1,92 @@ +/* + * Copyright 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <jni.h> +#include <media/MediaMetrics.h> + +#define JNI_FUNCTION(RETURN_TYPE, NAME, ...) \ + extern "C" { \ + JNIEXPORT RETURN_TYPE Java_android_media_MediaParser_##NAME(JNIEnv* env, jobject thiz, \ + ##__VA_ARGS__); \ + } \ + JNIEXPORT RETURN_TYPE Java_android_media_MediaParser_##NAME(JNIEnv* env, jobject thiz, \ + ##__VA_ARGS__) + +namespace { + +constexpr char kMediaMetricsKey[] = "mediaparser"; + +constexpr char kAttributeParserName[] = "android.media.mediaparser.parserName"; +constexpr char kAttributeCreatedByName[] = "android.media.mediaparser.createdByName"; +constexpr char kAttributeParserPool[] = "android.media.mediaparser.parserPool"; +constexpr char kAttributeLastException[] = "android.media.mediaparser.lastException"; +constexpr char kAttributeResourceByteCount[] = "android.media.mediaparser.resourceByteCount"; +constexpr char kAttributeDurationMillis[] = "android.media.mediaparser.durationMillis"; +constexpr char kAttributeTrackMimeTypes[] = "android.media.mediaparser.trackMimeTypes"; +constexpr char kAttributeTrackCodecs[] = "android.media.mediaparser.trackCodecs"; +constexpr char kAttributeAlteredParameters[] = "android.media.mediaparser.alteredParameters"; +constexpr char kAttributeVideoWidth[] = "android.media.mediaparser.videoWidth"; +constexpr char kAttributeVideoHeight[] = "android.media.mediaparser.videoHeight"; + +// Util class to handle string resource management. +class JstringHandle { +public: + JstringHandle(JNIEnv* env, jstring value) : mEnv(env), mJstringValue(value) { + mCstringValue = env->GetStringUTFChars(value, /* isCopy= */ nullptr); + } + + ~JstringHandle() { + if (mCstringValue != nullptr) { + mEnv->ReleaseStringUTFChars(mJstringValue, mCstringValue); + } + } + + [[nodiscard]] const char* value() const { + return mCstringValue != nullptr ? mCstringValue : ""; + } + + JNIEnv* mEnv; + jstring mJstringValue; + const char* mCstringValue; +}; + +} // namespace + +JNI_FUNCTION(void, nativeSubmitMetrics, jstring parserNameJstring, jboolean createdByName, + jstring parserPoolJstring, jstring lastExceptionJstring, jlong resourceByteCount, + jlong durationMillis, jstring trackMimeTypesJstring, jstring trackCodecsJstring, + jstring alteredParameters, jint videoWidth, jint videoHeight) { + mediametrics_handle_t item(mediametrics_create(kMediaMetricsKey)); + mediametrics_setCString(item, kAttributeParserName, + JstringHandle(env, parserNameJstring).value()); + mediametrics_setInt32(item, kAttributeCreatedByName, createdByName ? 1 : 0); + mediametrics_setCString(item, kAttributeParserPool, + JstringHandle(env, parserPoolJstring).value()); + mediametrics_setCString(item, kAttributeLastException, + JstringHandle(env, lastExceptionJstring).value()); + mediametrics_setInt64(item, kAttributeResourceByteCount, resourceByteCount); + mediametrics_setInt64(item, kAttributeDurationMillis, durationMillis); + mediametrics_setCString(item, kAttributeTrackMimeTypes, + JstringHandle(env, trackMimeTypesJstring).value()); + mediametrics_setCString(item, kAttributeTrackCodecs, + JstringHandle(env, trackCodecsJstring).value()); + mediametrics_setCString(item, kAttributeAlteredParameters, + JstringHandle(env, alteredParameters).value()); + mediametrics_setInt32(item, kAttributeVideoWidth, videoWidth); + mediametrics_setInt32(item, kAttributeVideoHeight, videoHeight); + mediametrics_selfRecord(item); + mediametrics_delete(item); +} diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 285eb4ac150e..22222670c1a2 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -486,6 +486,8 @@ message Atom { 303 [(module) = "network_tethering"]; ImeTouchReported ime_touch_reported = 304 [(module) = "sysui"]; + MediametricsMediaParserReported mediametrics_mediaparser_reported = 316; + // StatsdStats tracks platform atoms with ids upto 500. // Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value. } @@ -4440,7 +4442,7 @@ message PrivacyIndicatorsInteracted { UNKNOWN = 0; CHIP_VIEWED = 1; CHIP_CLICKED = 2; - reserved 3; // Used only in beta builds, never shipped + reserved 3; // Used only in beta builds, never shipped DIALOG_DISMISS = 4; DIALOG_LINE_ITEM = 5; } @@ -7919,6 +7921,72 @@ message MediametricsExtractorReported { } /** + * Track MediaParser (parsing video/audio streams from containers) usage + * Logged from: + * + * frameworks/av/services/mediametrics/statsd_mediaparser.cpp + * frameworks/base/apex/media/framework/jni/android_media_MediaParserJNI.cpp + */ +message MediametricsMediaParserReported { + optional int64 timestamp_nanos = 1; + optional string package_name = 2; + optional int64 package_version_code = 3; + + // MediaParser specific data. + /** + * The name of the parser selected for parsing the media, or an empty string + * if no parser was selected. + */ + optional string parser_name = 4; + /** + * Whether the parser was created by name. 1 represents true, and 0 + * represents false. + */ + optional int32 created_by_name = 5; + /** + * The parser names in the sniffing pool separated by "|". + */ + optional string parser_pool = 6; + /** + * The fully qualified name of the last encountered exception, or an empty + * string if no exception was encountered. + */ + optional string last_exception = 7; + /** + * The size of the parsed media in bytes, or -1 if unknown. Note this value + * contains intentional random error to prevent media content + * identification. + */ + optional int64 resource_byte_count = 8; + /** + * The duration of the media in milliseconds, or -1 if unknown. Note this + * value contains intentional random error to prevent media content + * identification. + */ + optional int64 duration_millis = 9; + /** + * The MIME types of the tracks separated by "|". + */ + optional string track_mime_types = 10; + /** + * The tracks' RFC 6381 codec strings separated by "|". + */ + optional string track_codecs = 11; + /** + * Concatenation of the parameters altered by the client, separated by "|". + */ + optional string altered_parameters = 12; + /** + * The video width in pixels, or -1 if unknown or not applicable. + */ + optional int32 video_width = 13; + /** + * The video height in pixels, or -1 if unknown or not applicable. + */ + optional int32 video_height = 14; +} + +/** * Track how we arbitrate between microphone/input requests. * Logged from * frameworks/av/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp diff --git a/core/java/android/accounts/GrantCredentialsPermissionActivity.java b/core/java/android/accounts/GrantCredentialsPermissionActivity.java index af74b036a796..42bd80b070d1 100644 --- a/core/java/android/accounts/GrantCredentialsPermissionActivity.java +++ b/core/java/android/accounts/GrantCredentialsPermissionActivity.java @@ -16,16 +16,23 @@ package android.accounts; import android.app.Activity; -import android.content.res.Resources; -import android.os.Bundle; -import android.widget.TextView; -import android.widget.LinearLayout; -import android.view.View; -import android.view.LayoutInflater; +import android.app.ActivityTaskManager; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Process; +import android.os.RemoteException; +import android.os.UserHandle; import android.text.TextUtils; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + import com.android.internal.R; import java.io.IOException; @@ -42,6 +49,7 @@ public class GrantCredentialsPermissionActivity extends Activity implements View private Account mAccount; private String mAuthTokenType; private int mUid; + private int mCallingUid; private Bundle mResultBundle = null; protected LayoutInflater mInflater; @@ -74,6 +82,20 @@ public class GrantCredentialsPermissionActivity extends Activity implements View return; } + try { + IBinder activityToken = getActivityToken(); + mCallingUid = ActivityTaskManager.getService().getLaunchedFromUid(activityToken); + } catch (RemoteException re) { + // Couldn't figure out caller details + Log.w(getClass().getSimpleName(), "Unable to get caller identity \n" + re); + } + + if (!UserHandle.isSameApp(mCallingUid, Process.SYSTEM_UID) && mCallingUid != mUid) { + setResult(Activity.RESULT_CANCELED); + finish(); + return; + } + String accountTypeLabel; try { accountTypeLabel = getAccountLabel(mAccount); diff --git a/core/java/android/os/FileBridge.java b/core/java/android/os/FileBridge.java index 21fd819f3d94..ab5637cbb878 100644 --- a/core/java/android/os/FileBridge.java +++ b/core/java/android/os/FileBridge.java @@ -16,7 +16,6 @@ package android.os; -import static android.system.OsConstants.AF_UNIX; import static android.system.OsConstants.SOCK_STREAM; import android.system.ErrnoException; @@ -58,17 +57,19 @@ public class FileBridge extends Thread { /** CMD_CLOSE */ private static final int CMD_CLOSE = 3; - private FileDescriptor mTarget; + private ParcelFileDescriptor mTarget; - private final FileDescriptor mServer = new FileDescriptor(); - private final FileDescriptor mClient = new FileDescriptor(); + private ParcelFileDescriptor mServer; + private ParcelFileDescriptor mClient; private volatile boolean mClosed; public FileBridge() { try { - Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient); - } catch (ErrnoException e) { + ParcelFileDescriptor[] fds = ParcelFileDescriptor.createSocketPair(SOCK_STREAM); + mServer = fds[0]; + mClient = fds[1]; + } catch (IOException e) { throw new RuntimeException("Failed to create bridge"); } } @@ -80,15 +81,14 @@ public class FileBridge extends Thread { public void forceClose() { IoUtils.closeQuietly(mTarget); IoUtils.closeQuietly(mServer); - IoUtils.closeQuietly(mClient); mClosed = true; } - public void setTargetFile(FileDescriptor target) { + public void setTargetFile(ParcelFileDescriptor target) { mTarget = target; } - public FileDescriptor getClientSocket() { + public ParcelFileDescriptor getClientSocket() { return mClient; } @@ -96,32 +96,33 @@ public class FileBridge extends Thread { public void run() { final byte[] temp = new byte[8192]; try { - while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) { + while (IoBridge.read(mServer.getFileDescriptor(), temp, 0, MSG_LENGTH) == MSG_LENGTH) { final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN); if (cmd == CMD_WRITE) { // Shuttle data into local file int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN); while (len > 0) { - int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len)); + int n = IoBridge.read(mServer.getFileDescriptor(), temp, 0, + Math.min(temp.length, len)); if (n == -1) { throw new IOException( "Unexpected EOF; still expected " + len + " bytes"); } - IoBridge.write(mTarget, temp, 0, n); + IoBridge.write(mTarget.getFileDescriptor(), temp, 0, n); len -= n; } } else if (cmd == CMD_FSYNC) { // Sync and echo back to confirm - Os.fsync(mTarget); - IoBridge.write(mServer, temp, 0, MSG_LENGTH); + Os.fsync(mTarget.getFileDescriptor()); + IoBridge.write(mServer.getFileDescriptor(), temp, 0, MSG_LENGTH); } else if (cmd == CMD_CLOSE) { // Close and echo back to confirm - Os.fsync(mTarget); - Os.close(mTarget); + Os.fsync(mTarget.getFileDescriptor()); + mTarget.close(); mClosed = true; - IoBridge.write(mServer, temp, 0, MSG_LENGTH); + IoBridge.write(mServer.getFileDescriptor(), temp, 0, MSG_LENGTH); break; } } @@ -143,17 +144,11 @@ public class FileBridge extends Thread { mClient = clientPfd.getFileDescriptor(); } - public FileBridgeOutputStream(FileDescriptor client) { - mClientPfd = null; - mClient = client; - } - @Override public void close() throws IOException { try { writeCommandAndBlock(CMD_CLOSE, "close()"); } finally { - IoBridge.closeAndSignalBlockedThreads(mClient); IoUtils.closeQuietly(mClientPfd); } } diff --git a/core/tests/coretests/src/android/os/FileBridgeTest.java b/core/tests/coretests/src/android/os/FileBridgeTest.java index d4f6b1fcec4e..708bfa6ece2e 100644 --- a/core/tests/coretests/src/android/os/FileBridgeTest.java +++ b/core/tests/coretests/src/android/os/FileBridgeTest.java @@ -16,6 +16,9 @@ package android.os; +import static android.os.ParcelFileDescriptor.MODE_CREATE; +import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; + import android.os.FileBridge.FileBridgeOutputStream; import android.test.AndroidTestCase; import android.test.MoreAsserts; @@ -25,7 +28,6 @@ import libcore.io.Streams; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Random; @@ -33,7 +35,7 @@ import java.util.Random; public class FileBridgeTest extends AndroidTestCase { private File file; - private FileOutputStream fileOs; + private ParcelFileDescriptor outputFile; private FileBridge bridge; private FileBridgeOutputStream client; @@ -44,17 +46,17 @@ public class FileBridgeTest extends AndroidTestCase { file = getContext().getFileStreamPath("meow.dat"); file.delete(); - fileOs = new FileOutputStream(file); + outputFile = ParcelFileDescriptor.open(file, MODE_CREATE | MODE_READ_WRITE); bridge = new FileBridge(); - bridge.setTargetFile(fileOs.getFD()); + bridge.setTargetFile(outputFile); bridge.start(); client = new FileBridgeOutputStream(bridge.getClientSocket()); } @Override protected void tearDown() throws Exception { - fileOs.close(); + outputFile.close(); file.delete(); } diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java index 6fbee16e3dae..861a8ef0a910 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java @@ -105,7 +105,8 @@ public class InstallStart extends Activity { } Intent nextActivity = new Intent(intent); - nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); + nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT + | Intent.FLAG_GRANT_READ_URI_PERMISSION); // The the installation source as the nextActivity thinks this activity is the source, hence // set the originating UID and sourceInfo explicitly diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index b581e0b07f42..bd8ce7b009ae 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -309,7 +309,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind AndroidFuture<Association> future = new AndroidFuture<>(); service.startDiscovery(request, callingPackage, callback, future); return future; - }).whenComplete(uncheckExceptions((association, err) -> { + }).cancelTimeout().whenComplete(uncheckExceptions((association, err) -> { if (err == null) { addAssociation(association); } else { diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java index 5c9350a8452c..3bb55a5af5c6 100644 --- a/services/core/java/com/android/server/location/LocationManagerService.java +++ b/services/core/java/com/android/server/location/LocationManagerService.java @@ -906,9 +906,26 @@ public class LocationManagerService extends ILocationManager.Stub { if (enabled == null) { // this generally shouldn't occur, but might be possible due to race conditions // on when we are notified of new users + + // hack to fix b/171910679. mutating the user enabled state within this method + // may cause unexpected changes to other state (for instance, this could cause + // provider enable/disable notifications to be sent to clients, which could + // result in a dead client being detected, which could result in the client + // being removed, which means that if this function is called while clients are + // being iterated over we have now unexpectedly mutated the iterated + // collection). instead, we return a correct value immediately here, and + // schedule the actual update for later. this has been completely rewritten and + // is no longer a problem in the next version of android. + enabled = mProvider.getState().allowed + && mUserInfoHelper.isCurrentUserId(userId) + && mSettingsHelper.isLocationEnabled(userId); + Log.w(TAG, mName + " provider saw user " + userId + " unexpectedly"); - onEnabledChangedLocked(userId); - enabled = Objects.requireNonNull(mEnabled.get(userId)); + mHandler.post(() -> { + synchronized (mLock) { + onEnabledChangedLocked(userId); + } + }); } return enabled; diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index ea53132ae409..33193dfda69e 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -947,6 +947,23 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + private ParcelFileDescriptor openTargetInternal(String path, int flags, int mode) + throws IOException, ErrnoException { + // TODO: this should delegate to DCS so the system process avoids + // holding open FDs into containers. + final FileDescriptor fd = Os.open(path, flags, mode); + return new ParcelFileDescriptor(fd); + } + + private ParcelFileDescriptor createRevocableFdInternal(RevocableFileDescriptor fd, + ParcelFileDescriptor pfd) throws IOException { + int releasedFdInt = pfd.detachFd(); + FileDescriptor releasedFd = new FileDescriptor(); + releasedFd.setInt$(releasedFdInt); + fd.init(mContext, releasedFd); + return fd.getRevocableFileDescriptor(); + } + private ParcelFileDescriptor doWriteInternal(String name, long offsetBytes, long lengthBytes, ParcelFileDescriptor incomingFd) throws IOException { // Quick sanity check of state, and allocate a pipe for ourselves. We @@ -979,21 +996,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { Binder.restoreCallingIdentity(identity); } - // TODO: this should delegate to DCS so the system process avoids - // holding open FDs into containers. - final FileDescriptor targetFd = Os.open(target.getAbsolutePath(), + ParcelFileDescriptor targetPfd = openTargetInternal(target.getAbsolutePath(), O_CREAT | O_WRONLY, 0644); Os.chmod(target.getAbsolutePath(), 0644); // If caller specified a total length, allocate it for them. Free up // cache space to grow, if needed. if (stageDir != null && lengthBytes > 0) { - mContext.getSystemService(StorageManager.class).allocateBytes(targetFd, lengthBytes, + mContext.getSystemService(StorageManager.class).allocateBytes( + targetPfd.getFileDescriptor(), lengthBytes, PackageHelper.translateAllocateFlags(params.installFlags)); } if (offsetBytes > 0) { - Os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET); + Os.lseek(targetPfd.getFileDescriptor(), offsetBytes, OsConstants.SEEK_SET); } if (incomingFd != null) { @@ -1003,8 +1019,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // inserted above to hold the session active. try { final Int64Ref last = new Int64Ref(0); - FileUtils.copy(incomingFd.getFileDescriptor(), targetFd, lengthBytes, null, - Runnable::run, (long progress) -> { + FileUtils.copy(incomingFd.getFileDescriptor(), targetPfd.getFileDescriptor(), + lengthBytes, null, Runnable::run, + (long progress) -> { if (params.sizeBytes > 0) { final long delta = progress - last.value; last.value = progress; @@ -1015,7 +1032,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } }); } finally { - IoUtils.closeQuietly(targetFd); + IoUtils.closeQuietly(targetPfd); IoUtils.closeQuietly(incomingFd); // We're done here, so remove the "bridge" that was holding @@ -1031,12 +1048,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } return null; } else if (PackageInstaller.ENABLE_REVOCABLE_FD) { - fd.init(mContext, targetFd); - return fd.getRevocableFileDescriptor(); + return createRevocableFdInternal(fd, targetPfd); } else { - bridge.setTargetFile(targetFd); + bridge.setTargetFile(targetPfd); bridge.start(); - return new ParcelFileDescriptor(bridge.getClientSocket()); + return bridge.getClientSocket(); } } catch (ErrnoException e) { diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index 8000c639139f..3a74f3022f54 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -727,10 +727,14 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { "compiled_trace.pb"); try { boolean exists = Files.exists(tracePath); - Log.d(TAG, tracePath.toString() + (exists? " exists" : " doesn't exist")); + if (DEBUG) { + Log.d(TAG, tracePath.toString() + (exists? " exists" : " doesn't exist")); + } if (exists) { long bytes = Files.size(tracePath); - Log.d(TAG, tracePath.toString() + " size is " + Long.toString(bytes)); + if (DEBUG) { + Log.d(TAG, tracePath.toString() + " size is " + Long.toString(bytes)); + } return bytes > 0L; } return exists; diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 8f3ed7411f17..3b73c1e92cc2 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -249,6 +249,13 @@ public class StatsPullAtomService extends SystemService { // 20% as a conservative estimate. private static final int MAX_PROCSTATS_RAW_SHARD_SIZE = (int) (MAX_PROCSTATS_SHARD_SIZE * 1.20); + /** + * Threshold to filter out small CPU times at frequency per UID. Those small values appear + * because of more precise accounting in a BPF program. Discarding them reduces the data by at + * least 20% with negligible error. + */ + private static final int MIN_CPU_TIME_PER_UID_FREQ = 10; + private final Object mThermalLock = new Object(); @GuardedBy("mThermalLock") private IThermalService mThermalService; @@ -1556,7 +1563,7 @@ public class StatsPullAtomService extends SystemService { int pullCpuTimePerUidFreqLocked(int atomTag, List<StatsEvent> pulledData) { mCpuUidFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> { for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) { - if (cpuFreqTimeMs[freqIndex] != 0) { + if (cpuFreqTimeMs[freqIndex] >= MIN_CPU_TIME_PER_UID_FREQ) { StatsEvent e = StatsEvent.newBuilder() .setAtomId(atomTag) .writeInt(uid) diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index f2f14125ef6b..7bd5bdfaebc6 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -372,6 +372,14 @@ public class TelecomManager { "android.telecom.extra.CALL_DISCONNECT_MESSAGE"; /** + * A string value for {@link #EXTRA_CALL_DISCONNECT_MESSAGE}, indicates the call was dropped by + * lower layers + * @hide + */ + public static final String CALL_AUTO_DISCONNECT_MESSAGE_STRING = + "Call dropped by lower layers"; + + /** * Optional extra for {@link android.telephony.TelephonyManager#ACTION_PHONE_STATE_CHANGED} * containing the component name of the associated connection service. * @hide |