Loading apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +46 −25 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import android.annotation.Nullable; import android.app.job.JobInfo; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; Loading Loading @@ -85,9 +86,12 @@ public final class ConnectivityController extends RestrictingController implemen @GuardedBy("mLock") private final SparseArray<ArraySet<JobStatus>> mRequestedWhitelistJobs = new SparseArray<>(); /** List of currently available networks. */ /** * Set of currently available networks mapped to their latest network capabilities. Cache the * latest capabilities to avoid unnecessary calls into ConnectivityManager. */ @GuardedBy("mLock") private final ArraySet<Network> mAvailableNetworks = new ArraySet<>(); private final ArrayMap<Network, NetworkCapabilities> mAvailableNetworks = new ArrayMap<>(); private static final int MSG_DATA_SAVER_TOGGLED = 0; private static final int MSG_UID_RULES_CHANGES = 1; Loading Loading @@ -164,9 +168,8 @@ public final class ConnectivityController extends RestrictingController implemen public boolean isNetworkAvailable(JobStatus job) { synchronized (mLock) { for (int i = 0; i < mAvailableNetworks.size(); ++i) { final Network network = mAvailableNetworks.valueAt(i); final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities( network); final Network network = mAvailableNetworks.keyAt(i); final NetworkCapabilities capabilities = mAvailableNetworks.valueAt(i); final boolean satisfied = isSatisfied(job, network, capabilities, mConstants); if (DEBUG) { Slog.v(TAG, "isNetworkAvailable(" + job + ") with network " + network Loading Loading @@ -424,9 +427,33 @@ public final class ConnectivityController extends RestrictingController implemen return false; } @Nullable private NetworkCapabilities getNetworkCapabilities(@Nullable Network network) { if (network == null) { return null; } synchronized (mLock) { // There is technically a race here if the Network object is reused. This can happen // only if that Network disconnects and the auto-incrementing network ID in // ConnectivityService wraps. This should no longer be a concern if/when we only make // use of asynchronous calls. if (mAvailableNetworks.get(network) != null) { return mAvailableNetworks.get(network); } // This should almost never happen because any time a new network connects, the // NetworkCallback would populate mAvailableNetworks. However, it's currently necessary // because we also call synchronous methods such as getActiveNetworkForUid. // TODO(134978280): remove after switching to callback-based APIs final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network); mAvailableNetworks.put(network, capabilities); return capabilities; } } private boolean updateConstraintsSatisfied(JobStatus jobStatus) { final Network network = mConnManager.getActiveNetworkForUid(jobStatus.getSourceUid()); final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network); final NetworkCapabilities capabilities = getNetworkCapabilities(network); return updateConstraintsSatisfied(jobStatus, network, capabilities); } Loading Loading @@ -467,19 +494,13 @@ public final class ConnectivityController extends RestrictingController implemen */ private void updateTrackedJobs(int filterUid, Network filterNetwork) { synchronized (mLock) { // Since this is a really hot codepath, temporarily cache any // answers that we get from ConnectivityManager. final ArrayMap<Network, NetworkCapabilities> networkToCapabilities = new ArrayMap<>(); boolean changed = false; if (filterUid == -1) { for (int i = mTrackedJobs.size() - 1; i >= 0; i--) { changed |= updateTrackedJobsLocked(mTrackedJobs.valueAt(i), filterNetwork, networkToCapabilities); changed |= updateTrackedJobsLocked(mTrackedJobs.valueAt(i), filterNetwork); } } else { changed = updateTrackedJobsLocked(mTrackedJobs.get(filterUid), filterNetwork, networkToCapabilities); changed = updateTrackedJobsLocked(mTrackedJobs.get(filterUid), filterNetwork); } if (changed) { mStateChangedListener.onControllerStateChanged(); Loading @@ -487,18 +508,13 @@ public final class ConnectivityController extends RestrictingController implemen } } private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, Network filterNetwork, ArrayMap<Network, NetworkCapabilities> networkToCapabilities) { private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, Network filterNetwork) { if (jobs == null || jobs.size() == 0) { return false; } final Network network = mConnManager.getActiveNetworkForUid(jobs.valueAt(0).getSourceUid()); NetworkCapabilities capabilities = networkToCapabilities.get(network); if (capabilities == null) { capabilities = mConnManager.getNetworkCapabilities(network); networkToCapabilities.put(network, capabilities); } final NetworkCapabilities capabilities = getNetworkCapabilities(network); final boolean networkMatch = (filterNetwork == null || Objects.equals(filterNetwork, network)); Loading Loading @@ -541,9 +557,9 @@ public final class ConnectivityController extends RestrictingController implemen @Override public void onAvailable(Network network) { if (DEBUG) Slog.v(TAG, "onAvailable: " + network); synchronized (mLock) { mAvailableNetworks.add(network); } // Documentation says not to call getNetworkCapabilities here but wait for // onCapabilitiesChanged instead. onCapabilitiesChanged should be called immediately // after this, so no need to update mAvailableNetworks here. } @Override Loading @@ -551,6 +567,9 @@ public final class ConnectivityController extends RestrictingController implemen if (DEBUG) { Slog.v(TAG, "onCapabilitiesChanged: " + network); } synchronized (mLock) { mAvailableNetworks.put(network, capabilities); } updateTrackedJobs(-1, network); } Loading Loading @@ -627,6 +646,8 @@ public final class ConnectivityController extends RestrictingController implemen pw.println("Available networks:"); pw.increaseIndent(); for (int i = 0; i < mAvailableNetworks.size(); i++) { pw.print(mAvailableNetworks.keyAt(i)); pw.print(": "); pw.println(mAvailableNetworks.valueAt(i)); } pw.decreaseIndent(); Loading Loading @@ -664,7 +685,7 @@ public final class ConnectivityController extends RestrictingController implemen mRequestedWhitelistJobs.keyAt(i)); } for (int i = 0; i < mAvailableNetworks.size(); i++) { Network network = mAvailableNetworks.valueAt(i); Network network = mAvailableNetworks.keyAt(i); if (network != null) { network.dumpDebug(proto, StateControllerProto.ConnectivityController.AVAILABLE_NETWORKS); Loading apex/media/framework/Android.bp +24 −2 Original line number Diff line number Diff line Loading @@ -44,7 +44,6 @@ java_library { libs: [ "framework_media_annotation", ], static_libs: [ "exoplayer2-extractor" ], Loading Loading @@ -115,10 +114,33 @@ java_sdk_library { impl_library_visibility: ["//frameworks/av/apex:__subpackages__"], } 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", ], header_libs: ["jni_headers"], shared_libs: [ "libandroid", "liblog", "libmediametrics", ], cflags: [ "-Wall", "-Werror", "-Wno-unused-parameter", "-Wunreachable-code", "-Wunused", ], apex_available: [ "com.android.media", ], min_sdk_version: "29", } apex/media/framework/java/android/media/MediaParser.java +148 −11 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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"; Loading @@ -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 = { Loading Loading @@ -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); } /** Loading @@ -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. Loading Loading @@ -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. Loading Loading @@ -1166,11 +1185,15 @@ 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 (mResourceByteCount == 0) { // For resource byte count metric collection, we only take into account the length // of the first provided input reader. mResourceByteCount = resourceLength; } mExtractorInput = new DefaultExtractorInput( mExoDataReader, seekableInputReader.getPosition(), seekableInputReader.getLength()); mExoDataReader, seekableInputReader.getPosition(), resourceLength); } mExoDataReader.mInputReader = seekableInputReader; Loading @@ -1195,7 +1218,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; } Loading Loading @@ -1223,8 +1249,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; Loading Loading @@ -1264,21 +1295,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(); Loading @@ -1286,6 +1360,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) { Loading Loading @@ -1528,6 +1620,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(); Loading Loading @@ -1575,6 +1671,7 @@ public final class MediaParser { @Override public void format(Format format) { mTrackFormats.put(mTrackIndex, format); mOutputConsumer.onTrackDataFound( mTrackIndex, new TrackData( Loading Loading @@ -2031,6 +2128,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)) { Loading Loading @@ -2070,9 +2181,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, Loading Loading @@ -2125,6 +2253,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); } } apex/media/framework/jni/android_media_MediaParserJNI.cpp 0 → 100644 +92 −0 Original line number Diff line number Diff line /* * 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); } core/java/android/accessibilityservice/AccessibilityServiceInfo.java +14 −0 Original line number Diff line number Diff line Loading @@ -364,6 +364,18 @@ public class AccessibilityServiceInfo implements Parcelable { */ public static final int FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x0001000; /** * This flag requests that when when {@link #FLAG_REQUEST_MULTI_FINGER_GESTURES} is enabled, * two-finger passthrough gestures are re-enabled. Two-finger swipe gestures are not detected, * but instead passed through as one-finger gestures. In addition, three-finger swipes from the * bottom of the screen are not detected, and instead are passed through unchanged. If {@link * #FLAG_REQUEST_MULTI_FINGER_GESTURES} is disabled this flag has no effect. * * @see #FLAG_REQUEST_TOUCH_EXPLORATION_MODE * @hide */ public static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x0002000; /** {@hide} */ public static final int FLAG_FORCE_DIRECT_BOOT_AWARE = 0x00010000; Loading Loading @@ -1261,6 +1273,8 @@ public class AccessibilityServiceInfo implements Parcelable { return "FLAG_SERVICE_HANDLES_DOUBLE_TAP"; case FLAG_REQUEST_MULTI_FINGER_GESTURES: return "FLAG_REQUEST_MULTI_FINGER_GESTURES"; case FLAG_REQUEST_2_FINGER_PASSTHROUGH: return "FLAG_REQUEST_2_FINGER_PASSTHROUGH"; case FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY: return "FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY"; case FLAG_REPORT_VIEW_IDS: Loading Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +46 −25 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import android.annotation.Nullable; import android.app.job.JobInfo; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; Loading Loading @@ -85,9 +86,12 @@ public final class ConnectivityController extends RestrictingController implemen @GuardedBy("mLock") private final SparseArray<ArraySet<JobStatus>> mRequestedWhitelistJobs = new SparseArray<>(); /** List of currently available networks. */ /** * Set of currently available networks mapped to their latest network capabilities. Cache the * latest capabilities to avoid unnecessary calls into ConnectivityManager. */ @GuardedBy("mLock") private final ArraySet<Network> mAvailableNetworks = new ArraySet<>(); private final ArrayMap<Network, NetworkCapabilities> mAvailableNetworks = new ArrayMap<>(); private static final int MSG_DATA_SAVER_TOGGLED = 0; private static final int MSG_UID_RULES_CHANGES = 1; Loading Loading @@ -164,9 +168,8 @@ public final class ConnectivityController extends RestrictingController implemen public boolean isNetworkAvailable(JobStatus job) { synchronized (mLock) { for (int i = 0; i < mAvailableNetworks.size(); ++i) { final Network network = mAvailableNetworks.valueAt(i); final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities( network); final Network network = mAvailableNetworks.keyAt(i); final NetworkCapabilities capabilities = mAvailableNetworks.valueAt(i); final boolean satisfied = isSatisfied(job, network, capabilities, mConstants); if (DEBUG) { Slog.v(TAG, "isNetworkAvailable(" + job + ") with network " + network Loading Loading @@ -424,9 +427,33 @@ public final class ConnectivityController extends RestrictingController implemen return false; } @Nullable private NetworkCapabilities getNetworkCapabilities(@Nullable Network network) { if (network == null) { return null; } synchronized (mLock) { // There is technically a race here if the Network object is reused. This can happen // only if that Network disconnects and the auto-incrementing network ID in // ConnectivityService wraps. This should no longer be a concern if/when we only make // use of asynchronous calls. if (mAvailableNetworks.get(network) != null) { return mAvailableNetworks.get(network); } // This should almost never happen because any time a new network connects, the // NetworkCallback would populate mAvailableNetworks. However, it's currently necessary // because we also call synchronous methods such as getActiveNetworkForUid. // TODO(134978280): remove after switching to callback-based APIs final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network); mAvailableNetworks.put(network, capabilities); return capabilities; } } private boolean updateConstraintsSatisfied(JobStatus jobStatus) { final Network network = mConnManager.getActiveNetworkForUid(jobStatus.getSourceUid()); final NetworkCapabilities capabilities = mConnManager.getNetworkCapabilities(network); final NetworkCapabilities capabilities = getNetworkCapabilities(network); return updateConstraintsSatisfied(jobStatus, network, capabilities); } Loading Loading @@ -467,19 +494,13 @@ public final class ConnectivityController extends RestrictingController implemen */ private void updateTrackedJobs(int filterUid, Network filterNetwork) { synchronized (mLock) { // Since this is a really hot codepath, temporarily cache any // answers that we get from ConnectivityManager. final ArrayMap<Network, NetworkCapabilities> networkToCapabilities = new ArrayMap<>(); boolean changed = false; if (filterUid == -1) { for (int i = mTrackedJobs.size() - 1; i >= 0; i--) { changed |= updateTrackedJobsLocked(mTrackedJobs.valueAt(i), filterNetwork, networkToCapabilities); changed |= updateTrackedJobsLocked(mTrackedJobs.valueAt(i), filterNetwork); } } else { changed = updateTrackedJobsLocked(mTrackedJobs.get(filterUid), filterNetwork, networkToCapabilities); changed = updateTrackedJobsLocked(mTrackedJobs.get(filterUid), filterNetwork); } if (changed) { mStateChangedListener.onControllerStateChanged(); Loading @@ -487,18 +508,13 @@ public final class ConnectivityController extends RestrictingController implemen } } private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, Network filterNetwork, ArrayMap<Network, NetworkCapabilities> networkToCapabilities) { private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, Network filterNetwork) { if (jobs == null || jobs.size() == 0) { return false; } final Network network = mConnManager.getActiveNetworkForUid(jobs.valueAt(0).getSourceUid()); NetworkCapabilities capabilities = networkToCapabilities.get(network); if (capabilities == null) { capabilities = mConnManager.getNetworkCapabilities(network); networkToCapabilities.put(network, capabilities); } final NetworkCapabilities capabilities = getNetworkCapabilities(network); final boolean networkMatch = (filterNetwork == null || Objects.equals(filterNetwork, network)); Loading Loading @@ -541,9 +557,9 @@ public final class ConnectivityController extends RestrictingController implemen @Override public void onAvailable(Network network) { if (DEBUG) Slog.v(TAG, "onAvailable: " + network); synchronized (mLock) { mAvailableNetworks.add(network); } // Documentation says not to call getNetworkCapabilities here but wait for // onCapabilitiesChanged instead. onCapabilitiesChanged should be called immediately // after this, so no need to update mAvailableNetworks here. } @Override Loading @@ -551,6 +567,9 @@ public final class ConnectivityController extends RestrictingController implemen if (DEBUG) { Slog.v(TAG, "onCapabilitiesChanged: " + network); } synchronized (mLock) { mAvailableNetworks.put(network, capabilities); } updateTrackedJobs(-1, network); } Loading Loading @@ -627,6 +646,8 @@ public final class ConnectivityController extends RestrictingController implemen pw.println("Available networks:"); pw.increaseIndent(); for (int i = 0; i < mAvailableNetworks.size(); i++) { pw.print(mAvailableNetworks.keyAt(i)); pw.print(": "); pw.println(mAvailableNetworks.valueAt(i)); } pw.decreaseIndent(); Loading Loading @@ -664,7 +685,7 @@ public final class ConnectivityController extends RestrictingController implemen mRequestedWhitelistJobs.keyAt(i)); } for (int i = 0; i < mAvailableNetworks.size(); i++) { Network network = mAvailableNetworks.valueAt(i); Network network = mAvailableNetworks.keyAt(i); if (network != null) { network.dumpDebug(proto, StateControllerProto.ConnectivityController.AVAILABLE_NETWORKS); Loading
apex/media/framework/Android.bp +24 −2 Original line number Diff line number Diff line Loading @@ -44,7 +44,6 @@ java_library { libs: [ "framework_media_annotation", ], static_libs: [ "exoplayer2-extractor" ], Loading Loading @@ -115,10 +114,33 @@ java_sdk_library { impl_library_visibility: ["//frameworks/av/apex:__subpackages__"], } 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", ], header_libs: ["jni_headers"], shared_libs: [ "libandroid", "liblog", "libmediametrics", ], cflags: [ "-Wall", "-Werror", "-Wno-unused-parameter", "-Wunreachable-code", "-Wunused", ], apex_available: [ "com.android.media", ], min_sdk_version: "29", }
apex/media/framework/java/android/media/MediaParser.java +148 −11 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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"; Loading @@ -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 = { Loading Loading @@ -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); } /** Loading @@ -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. Loading Loading @@ -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. Loading Loading @@ -1166,11 +1185,15 @@ 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 (mResourceByteCount == 0) { // For resource byte count metric collection, we only take into account the length // of the first provided input reader. mResourceByteCount = resourceLength; } mExtractorInput = new DefaultExtractorInput( mExoDataReader, seekableInputReader.getPosition(), seekableInputReader.getLength()); mExoDataReader, seekableInputReader.getPosition(), resourceLength); } mExoDataReader.mInputReader = seekableInputReader; Loading @@ -1195,7 +1218,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; } Loading Loading @@ -1223,8 +1249,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; Loading Loading @@ -1264,21 +1295,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(); Loading @@ -1286,6 +1360,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) { Loading Loading @@ -1528,6 +1620,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(); Loading Loading @@ -1575,6 +1671,7 @@ public final class MediaParser { @Override public void format(Format format) { mTrackFormats.put(mTrackIndex, format); mOutputConsumer.onTrackDataFound( mTrackIndex, new TrackData( Loading Loading @@ -2031,6 +2128,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)) { Loading Loading @@ -2070,9 +2181,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, Loading Loading @@ -2125,6 +2253,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); } }
apex/media/framework/jni/android_media_MediaParserJNI.cpp 0 → 100644 +92 −0 Original line number Diff line number Diff line /* * 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); }
core/java/android/accessibilityservice/AccessibilityServiceInfo.java +14 −0 Original line number Diff line number Diff line Loading @@ -364,6 +364,18 @@ public class AccessibilityServiceInfo implements Parcelable { */ public static final int FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x0001000; /** * This flag requests that when when {@link #FLAG_REQUEST_MULTI_FINGER_GESTURES} is enabled, * two-finger passthrough gestures are re-enabled. Two-finger swipe gestures are not detected, * but instead passed through as one-finger gestures. In addition, three-finger swipes from the * bottom of the screen are not detected, and instead are passed through unchanged. If {@link * #FLAG_REQUEST_MULTI_FINGER_GESTURES} is disabled this flag has no effect. * * @see #FLAG_REQUEST_TOUCH_EXPLORATION_MODE * @hide */ public static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x0002000; /** {@hide} */ public static final int FLAG_FORCE_DIRECT_BOOT_AWARE = 0x00010000; Loading Loading @@ -1261,6 +1273,8 @@ public class AccessibilityServiceInfo implements Parcelable { return "FLAG_SERVICE_HANDLES_DOUBLE_TAP"; case FLAG_REQUEST_MULTI_FINGER_GESTURES: return "FLAG_REQUEST_MULTI_FINGER_GESTURES"; case FLAG_REQUEST_2_FINGER_PASSTHROUGH: return "FLAG_REQUEST_2_FINGER_PASSTHROUGH"; case FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY: return "FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY"; case FLAG_REPORT_VIEW_IDS: Loading