diff options
6 files changed, 164 insertions, 16 deletions
diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp index 3ff08483c956..2a3c26ed3ea3 100644 --- a/ravenwood/runtime-jni/ravenwood_runtime.cpp +++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp @@ -173,6 +173,24 @@ static void Linux_setenv(JNIEnv* env, jobject, jstring javaName, jstring javaVal throwIfMinusOne(env, "setenv", setenv(name.c_str(), value.c_str(), overwrite ? 1 : 0)); } +static void maybeRedirectLog() { + auto ravenwoodLogOut = getenv("RAVENWOOD_LOG_OUT"); + if (ravenwoodLogOut == NULL) { + return; + } + ALOGI("RAVENWOOD_LOG_OUT set. Redirecting output to %s", ravenwoodLogOut); + + // Redirect stdin / stdout to /dev/tty. + int ttyFd = open(ravenwoodLogOut, O_WRONLY); + if (ttyFd == -1) { + ALOGW("$RAVENWOOD_LOG_OUT is set to %s, but failed to open: %s ", ravenwoodLogOut, + strerror(errno)); + return; + } + dup2(ttyFd, 1); + dup2(ttyFd, 2); +} + // ---- Registration ---- extern void register_android_system_OsConstants(JNIEnv* env); @@ -192,6 +210,8 @@ static const JNINativeMethod sMethods[] = }; extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) { + maybeRedirectLog(); + ALOGI("%s: JNI_OnLoad", __FILE__); JNIEnv* env = GetJNIEnvOrDie(vm); diff --git a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java index 01f770b1e89f..9fa5da47aae7 100644 --- a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java +++ b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java @@ -133,6 +133,9 @@ public class LoudnessCodecHelper { private static final EventLogger sLogger = new EventLogger( AudioService.LOG_NB_EVENTS_LOUDNESS_CODEC, "Loudness updates"); + private final Object mDispatcherLock = new Object(); + + @GuardedBy("mDispatcherLock") private final LoudnessRemoteCallbackList mLoudnessUpdateDispatchers = new LoudnessRemoteCallbackList(this); @@ -339,12 +342,16 @@ public class LoudnessCodecHelper { } void registerLoudnessCodecUpdatesDispatcher(ILoudnessCodecUpdatesDispatcher dispatcher) { - mLoudnessUpdateDispatchers.register(dispatcher, Binder.getCallingPid()); + synchronized (mDispatcherLock) { + mLoudnessUpdateDispatchers.register(dispatcher, Binder.getCallingPid()); + } } void unregisterLoudnessCodecUpdatesDispatcher( ILoudnessCodecUpdatesDispatcher dispatcher) { - mLoudnessUpdateDispatchers.unregister(dispatcher); + synchronized (mDispatcherLock) { + mLoudnessUpdateDispatchers.unregister(dispatcher); + } } void startLoudnessCodecUpdates(int sessionId) { @@ -640,17 +647,20 @@ public class LoudnessCodecHelper { Log.d(TAG, "dispatchNewLoudnessParameters: sessionId " + sessionId + " bundle: " + bundle); } - final int nbDispatchers = mLoudnessUpdateDispatchers.beginBroadcast(); - for (int i = 0; i < nbDispatchers; ++i) { - try { - mLoudnessUpdateDispatchers.getBroadcastItem(i) - .dispatchLoudnessCodecParameterChange(sessionId, bundle); - } catch (RemoteException e) { - Log.e(TAG, "Error dispatching for sessionId " + sessionId + " bundle: " + bundle, - e); + synchronized (mDispatcherLock) { + final int nbDispatchers = mLoudnessUpdateDispatchers.beginBroadcast(); + for (int i = 0; i < nbDispatchers; ++i) { + try { + mLoudnessUpdateDispatchers.getBroadcastItem(i) + .dispatchLoudnessCodecParameterChange(sessionId, bundle); + } catch (RemoteException e) { + Log.e(TAG, + "Error dispatching for sessionId " + sessionId + " bundle: " + bundle, + e); + } } + mLoudnessUpdateDispatchers.finishBroadcast(); } - mLoudnessUpdateDispatchers.finishBroadcast(); } @GuardedBy("mLock") diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 45885f0399c4..c314ab06fbad 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -347,6 +347,8 @@ public class LockSettingsService extends ILockSettings.Stub { private final StorageManagerInternal mStorageManagerInternal; + private final Object mGcWorkToken = new Object(); + // This class manages life cycle events for encrypted users on File Based Encryption (FBE) // devices. The most basic of these is to show/hide notifications about missing features until // the user unlocks the account and credential-encrypted storage is available. @@ -3632,11 +3634,19 @@ public class LockSettingsService extends ILockSettings.Stub { * release references to the argument. */ private void scheduleGc() { + // Cancel any existing GC request first, so that GC requests don't pile up if lockscreen + // credential operations are happening very quickly, e.g. as sometimes happens during tests. + // + // This delays the already-requested GC, but that is fine in practice where lockscreen + // operations don't happen very quickly. And the precise time that the sanitization happens + // isn't very important; doing it within a minute can be fine, for example. + mHandler.removeCallbacksAndMessages(mGcWorkToken); + mHandler.postDelayed(() -> { System.gc(); System.runFinalization(); System.gc(); - }, 2000); + }, mGcWorkToken, 2000); } private class DeviceProvisionedObserver extends ContentObserver { diff --git a/tools/systemfeatures/Android.bp b/tools/systemfeatures/Android.bp index 590f7190881a..e6d0a3d4149f 100644 --- a/tools/systemfeatures/Android.bp +++ b/tools/systemfeatures/Android.bp @@ -58,6 +58,7 @@ java_test_host { "junit", "objenesis", "mockito", + "systemfeatures-gen-lib", "truth", ], } diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt index 196b5e7c02ab..1abe77fd3ceb 100644 --- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt +++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt @@ -71,7 +71,7 @@ object SystemFeaturesGenerator { println("Usage: SystemFeaturesGenerator <outputClassName> [options]") println(" Options:") println(" --readonly=true|false Whether to encode features as build-time constants") - println(" --feature=\$NAME:\$VER A feature+version pair, where \$VER can be:") + println(" --feature=\$NAME:\$VER A feature+version pair, where \$VER can be:") println(" * blank/empty == undefined (variable API)") println(" * valid int == enabled (constant API)") println(" * UNAVAILABLE == disabled (constant API)") @@ -89,6 +89,17 @@ object SystemFeaturesGenerator { /** Main entrypoint for build-time system feature codegen. */ @JvmStatic fun main(args: Array<String>) { + generate(args, System.out) + } + + /** + * Simple API entrypoint for build-time system feature codegen. + * + * Note: Typically this would be implemented in terms of a proper Builder-type input argument, + * but it's primarily used for testing as opposed to direct production usage. + */ + @JvmStatic + fun generate(args: Array<String>, output: Appendable) { if (args.size < 1) { usage() return @@ -155,7 +166,7 @@ object SystemFeaturesGenerator { .addFileComment("This file is auto-generated. DO NOT MODIFY.\n") .addFileComment("Args: ${args.joinToString(" \\\n ")}") .build() - .writeTo(System.out) + .writeTo(output) } /* @@ -171,12 +182,27 @@ object SystemFeaturesGenerator { return when (featureArgs.getOrNull(1)) { null, "" -> FeatureInfo(name, null, readonly = false) "UNAVAILABLE" -> FeatureInfo(name, null, readonly = true) - else -> FeatureInfo(name, featureArgs[1].toIntOrNull(), readonly = true) + else -> { + val featureVersion = + featureArgs[1].toIntOrNull() + ?: throw IllegalArgumentException( + "Invalid feature version input for $name: ${featureArgs[1]}" + ) + FeatureInfo(name, featureArgs[1].toInt(), readonly = true) + } } } private fun parseFeatureName(name: String): String = - if (name.startsWith("FEATURE_")) name else "FEATURE_$name" + when { + name.startsWith("android") -> + throw IllegalArgumentException( + "Invalid feature name input: \"android\"-namespaced features must be " + + "provided as PackageManager.FEATURE_* suffixes, not raw feature strings." + ) + name.startsWith("FEATURE_") -> name + else -> "FEATURE_$name" + } /* * Adds per-feature query methods to the class with the form: diff --git a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java new file mode 100644 index 000000000000..f8c585d60ef7 --- /dev/null +++ b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemfeatures; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.io.IOException; + +// Note: This is a very simple argument test to validate certain behaviors for +// invalid arguments. Correctness and validity is largely exercised by +// SystemFeaturesGeneratorTest. +@RunWith(JUnit4.class) +public class SystemFeaturesGeneratorApiTest { + + @Rule public final MockitoRule mockito = MockitoJUnit.rule(); + + @Mock private Appendable mOut; + + @Test + public void testEmpty() throws IOException { + final String[] args = new String[] {}; + // This should just print the commandline and return. + SystemFeaturesGenerator.generate(args, mOut); + verify(mOut, never()).append(any()); + } + + @Test + public void testBasic() throws IOException { + final String[] args = new String[] { + "com.foo.Features", + "--feature=TELEVISION:0", + }; + SystemFeaturesGenerator.generate(args, mOut); + verify(mOut, atLeastOnce()).append(any()); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidFeatureVersion() throws IOException { + final String[] args = new String[] { + "com.foo.Features", + "--feature=TELEVISION:blarg", + }; + SystemFeaturesGenerator.generate(args, mOut); + verify(mOut, never()).append(any()); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidFeatureNameFromAndroidNamespace() throws IOException { + final String[] args = new String[] { + "com.foo.Features", + "--feature=android.hardware.doesntexist:0", + }; + SystemFeaturesGenerator.generate(args, mOut); + verify(mOut, never()).append(any()); + } +} |