Merge "Merge ab/AP4A.240925.013 into aosp-main-future" into aosp-main-future
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 3219ce8..b270062 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -789,11 +789,17 @@
public Builder setMinUdpPort4500NatTimeoutSeconds(
@IntRange(from = MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS)
int minUdpPort4500NatTimeoutSeconds) {
- Preconditions.checkArgument(
- minUdpPort4500NatTimeoutSeconds == MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET
- || minUdpPort4500NatTimeoutSeconds
- >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS,
- "Timeout must be at least 120s or MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET");
+ if (Flags.mainlineVcnModuleApi()) {
+ Preconditions.checkArgument(
+ minUdpPort4500NatTimeoutSeconds == MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET
+ || minUdpPort4500NatTimeoutSeconds
+ >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS,
+ "Timeout must be at least 120s or MIN_UDP_PORT_4500_NAT_TIMEOUT_UNSET");
+ } else {
+ Preconditions.checkArgument(
+ minUdpPort4500NatTimeoutSeconds >= MIN_UDP_PORT_4500_NAT_TIMEOUT_SECONDS,
+ "Timeout must be at least 120s");
+ }
mMinUdpPort4500NatTimeoutSeconds = minUdpPort4500NatTimeoutSeconds;
return this;
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index 8b267bf..b63ad5f 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -167,76 +167,90 @@
public static final String EXTRA_CHARGING_STATUS = "android.os.extra.CHARGING_STATUS";
/**
- * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
- * Int value representing the battery's capacity level. These constants are key indicators of
- * battery status and system capabilities, guiding power management decisions for both the
- * system and apps:
- * {@link #BATTERY_CAPACITY_LEVEL_UNSUPPORTED}: Feature not supported on this device.
- * {@link #BATTERY_CAPACITY_LEVEL_UNKNOWN}: Battery status is unavailable or uninitialized.
- * {@link #BATTERY_CAPACITY_LEVEL_CRITICAL}: Battery is critically low and the Android
- * framework has been notified to schedule a shutdown by this value
- * {@link #BATTERY_CAPACITY_LEVEL_LOW}: Android framework must limit background jobs to
- * avoid impacting charging speed
- * {@link #BATTERY_CAPACITY_LEVEL_NORMAL}: Battery level and charging rates are normal,
- * battery temperature is within normal range and adapter power is enough to charge the
- * battery at an acceptable rate. Android framework can run light background tasks without
- * affecting charging performance severely.
- * {@link #BATTERY_CAPACITY_LEVEL_HIGH}: Battery level is high, battery temperature is
- * within normal range and adapter power is enough to charge the battery at an acceptable
- * rate while running background loads. Android framework can run background tasks without
- * affecting charging or battery performance.
- * {@link #BATTERY_CAPACITY_LEVEL_FULL}: The battery is full, battery temperature is
- * within normal range and adapter power is enough to sustain running background loads.
- * Android framework can run background tasks without affecting the battery level or
- * battery performance.
- */
-
- @FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
- public static final String EXTRA_CAPACITY_LEVEL = "android.os.extra.CAPACITY_LEVEL";
-
- /**
- * Battery capacity level is unsupported. @see EXTRA_CAPACITY_LEVEL
+ * Battery capacity level is unsupported.
+ *
+ * @see #EXTRA_CAPACITY_LEVEL
*/
@FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
public static final int BATTERY_CAPACITY_LEVEL_UNSUPPORTED = -1;
/**
- * Battery capacity level is unknown. @see EXTRA_CAPACITY_LEVEL
+ * Battery capacity level is unknown.
+ *
+ * @see #EXTRA_CAPACITY_LEVEL
*/
@FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
public static final int BATTERY_CAPACITY_LEVEL_UNKNOWN = 0;
/**
- * Battery capacity level is critical. @see EXTRA_CAPACITY_LEVEL
+ * Battery capacity level is critical. The Android framework has been notified to schedule
+ * a shutdown by this value.
+ *
+ * @see #EXTRA_CAPACITY_LEVEL
*/
@FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
public static final int BATTERY_CAPACITY_LEVEL_CRITICAL = 1;
/**
- * Battery capacity level is low. @see EXTRA_CAPACITY_LEVEL
+ * Battery capacity level is low. The Android framework must limit background jobs to avoid
+ * impacting charging speed.
+ *
+ * @see #EXTRA_CAPACITY_LEVEL
*/
@FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
public static final int BATTERY_CAPACITY_LEVEL_LOW = 2;
/**
- * Battery capacity level is normal. @see EXTRA_CAPACITY_LEVEL
+ * Battery capacity level is normal. Battery level and charging rates are normal, battery
+ * temperature is within the normal range, and adapter power is enough to charge the battery
+ * at an acceptable rate. The Android framework can run light background tasks without
+ * affecting charging performance severely.
+ *
+ * @see #EXTRA_CAPACITY_LEVEL
*/
@FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
public static final int BATTERY_CAPACITY_LEVEL_NORMAL = 3;
/**
- * Battery capacity level is high. @see EXTRA_CAPACITY_LEVEL
+ * Battery capacity level is high. Battery level is high, battery temperature is within the
+ * normal range, and adapter power is enough to charge the battery at an acceptable rate
+ * while running background loads. The Android framework can run background tasks without
+ * affecting charging or battery performance.
+ *
+ * @see #EXTRA_CAPACITY_LEVEL
*/
@FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
public static final int BATTERY_CAPACITY_LEVEL_HIGH = 4;
/**
- * Battery capacity level is full. @see EXTRA_CAPACITY_LEVEL
+ * Battery capacity level is full. The battery is full, the battery temperature is within the
+ * normal range, and adapter power is enough to sustain running background loads. The Android
+ * framework can run background tasks without affecting the battery level or battery
+ * performance.
+ *
+ * @see #EXTRA_CAPACITY_LEVEL
*/
@FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
public static final int BATTERY_CAPACITY_LEVEL_FULL = 5;
/**
+ * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+ * Int value representing the battery's capacity level. These constants are key indicators of
+ * battery status and system capabilities, guiding power management decisions for both the
+ * system and apps.
+ *
+ * @see #BATTERY_CAPACITY_LEVEL_UNSUPPORTED
+ * @see #BATTERY_CAPACITY_LEVEL_UNKNOWN
+ * @see #BATTERY_CAPACITY_LEVEL_CRITICAL
+ * @see #BATTERY_CAPACITY_LEVEL_LOW
+ * @see #BATTERY_CAPACITY_LEVEL_NORMAL
+ * @see #BATTERY_CAPACITY_LEVEL_HIGH
+ * @see #BATTERY_CAPACITY_LEVEL_FULL
+ */
+ @FlaggedApi(FLAG_BATTERY_PART_STATUS_API)
+ public static final String EXTRA_CAPACITY_LEVEL = "android.os.extra.CAPACITY_LEVEL";
+
+ /**
* Extra for {@link android.content.Intent#ACTION_BATTERY_LEVEL_CHANGED}:
* Contains list of Bundles representing battery events
* @hide
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 94259d7..f9789c1 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -91,6 +91,8 @@
# PerformanceHintManager
per-file CpuHeadroom*.aidl = file:/ADPF_OWNERS
per-file GpuHeadroom*.aidl = file:/ADPF_OWNERS
+per-file CpuHeadroom*.java = file:/ADPF_OWNERS
+per-file GpuHeadroom*.java = file:/ADPF_OWNERS
per-file PerformanceHintManager.java = file:/ADPF_OWNERS
per-file WorkDuration.java = file:/ADPF_OWNERS
per-file IHintManager.aidl = file:/ADPF_OWNERS
diff --git a/core/tests/coretests/src/android/os/OWNERS b/core/tests/coretests/src/android/os/OWNERS
index 4620cb8..c45080fb 100644
--- a/core/tests/coretests/src/android/os/OWNERS
+++ b/core/tests/coretests/src/android/os/OWNERS
@@ -15,3 +15,6 @@
# RemoteCallbackList
per-file RemoteCallbackListTest.java = [email protected]
+
+# MessageQueue
+per-file MessageQueueTest.java = [email protected], [email protected]
diff --git a/core/tests/coretests/src/android/window/OWNERS b/core/tests/coretests/src/android/window/OWNERS
index 6c80cf9..b3fcea2 100644
--- a/core/tests/coretests/src/android/window/OWNERS
+++ b/core/tests/coretests/src/android/window/OWNERS
@@ -1,2 +1,3 @@
include /services/core/java/com/android/server/wm/OWNERS
[email protected]
+
+# Bug component: 1519745 = per-file WindowContext*,WindowMetrics*,WindowProvider*,WindowTokenClient*
\ No newline at end of file
diff --git a/errorprone/Android.bp b/errorprone/Android.bp
index b559a15..1428b89 100644
--- a/errorprone/Android.bp
+++ b/errorprone/Android.bp
@@ -31,6 +31,14 @@
"//external/auto:auto_service_annotations",
],
+ javacflags: [
+ // These exports are needed because this errorprone plugin access some private classes
+ // of the java compiler.
+ "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+ ],
+
plugins: [
"//external/auto:auto_service_plugin",
],
diff --git a/nfc/tests/src/android/nfc/NdefMessageTest.java b/nfc/tests/src/android/nfc/NdefMessageTest.java
new file mode 100644
index 0000000..9ca295d
--- /dev/null
+++ b/nfc/tests/src/android/nfc/NdefMessageTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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 android.nfc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class NdefMessageTest {
+ private NdefMessage mNdefMessage;
+ private NdefRecord mNdefRecord;
+
+ @Before
+ public void setUp() {
+ mNdefRecord = NdefRecord.createUri("http://www.example.com");
+ mNdefMessage = new NdefMessage(mNdefRecord);
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ @Test
+ public void testGetRecords() {
+ NdefRecord[] records = mNdefMessage.getRecords();
+ assertThat(records).isNotNull();
+ assertThat(records).hasLength(1);
+ assertThat(records[0]).isEqualTo(mNdefRecord);
+ }
+
+ @Test
+ public void testToByteArray() throws FormatException {
+ byte[] bytes = mNdefMessage.toByteArray();
+ assertThat(bytes).isNotNull();
+ assertThat(bytes.length).isGreaterThan(0);
+ NdefMessage ndefMessage = new NdefMessage(bytes);
+ assertThat(ndefMessage).isNotNull();
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
index 0329794..0a4198a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigParameterizedStateTest.kt
@@ -32,7 +32,8 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -125,14 +126,11 @@
)
underTest =
- HomeControlsKeyguardQuickAffordanceConfig(
- context = context,
- component = component,
- )
+ HomeControlsKeyguardQuickAffordanceConfig(context = context, component = component)
}
@Test
- fun state() = runBlockingTest {
+ fun state() = runTest(UnconfinedTestDispatcher()) {
whenever(component.isEnabled()).thenReturn(isFeatureEnabled)
whenever(controlsController.getFavorites())
.thenReturn(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
index 7d68cc0..0003d07 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/HomeControlsKeyguardQuickAffordanceConfigTest.kt
@@ -19,19 +19,20 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Expandable
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult
+import com.android.systemui.res.R
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import java.util.Optional
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -54,14 +55,11 @@
whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(true))
underTest =
- HomeControlsKeyguardQuickAffordanceConfig(
- context = context,
- component = component,
- )
+ HomeControlsKeyguardQuickAffordanceConfig(context = context, component = component)
}
@Test
- fun state_whenCannotShowWhileLocked_returnsHidden() = runBlockingTest {
+ fun state_whenCannotShowWhileLocked_returnsHidden() = runTest(UnconfinedTestDispatcher()) {
whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(false))
whenever(component.isEnabled()).thenReturn(true)
whenever(component.getTileImageId()).thenReturn(R.drawable.controls_icon)
@@ -81,7 +79,7 @@
}
@Test
- fun state_whenListingControllerIsMissing_returnsHidden() = runBlockingTest {
+ fun state_whenListingControllerIsMissing_returnsHidden() = runTest(UnconfinedTestDispatcher()) {
whenever(component.isEnabled()).thenReturn(true)
whenever(component.getTileImageId()).thenReturn(R.drawable.controls_icon)
whenever(component.getTileTitleId()).thenReturn(R.string.quick_controls_title)
@@ -100,23 +98,26 @@
}
@Test
- fun onQuickAffordanceTriggered_canShowWhileLockedSettingIsTrue() = runBlockingTest {
- whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(true))
+ fun onQuickAffordanceTriggered_canShowWhileLockedSettingIsTrue() =
+ runTest(UnconfinedTestDispatcher()) {
+ whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(true))
- val onClickedResult = underTest.onTriggered(expandable)
+ val onClickedResult = underTest.onTriggered(expandable)
- assertThat(onClickedResult).isInstanceOf(OnTriggeredResult.StartActivity::class.java)
- assertThat((onClickedResult as OnTriggeredResult.StartActivity).canShowWhileLocked).isTrue()
- }
+ assertThat(onClickedResult).isInstanceOf(OnTriggeredResult.StartActivity::class.java)
+ assertThat((onClickedResult as OnTriggeredResult.StartActivity).canShowWhileLocked)
+ .isTrue()
+ }
@Test
- fun onQuickAffordanceTriggered_canShowWhileLockedSettingIsFalse() = runBlockingTest {
- whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(false))
+ fun onQuickAffordanceTriggered_canShowWhileLockedSettingIsFalse() =
+ runTest(UnconfinedTestDispatcher()) {
+ whenever(component.canShowWhileLockedSetting).thenReturn(MutableStateFlow(false))
- val onClickedResult = underTest.onTriggered(expandable)
+ val onClickedResult = underTest.onTriggered(expandable)
- assertThat(onClickedResult).isInstanceOf(OnTriggeredResult.StartActivity::class.java)
- assertThat((onClickedResult as OnTriggeredResult.StartActivity).canShowWhileLocked)
- .isFalse()
- }
+ assertThat(onClickedResult).isInstanceOf(OnTriggeredResult.StartActivity::class.java)
+ assertThat((onClickedResult as OnTriggeredResult.StartActivity).canShowWhileLocked)
+ .isFalse()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
index ca64cec..05a74c0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
@@ -29,7 +29,7 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -56,60 +56,63 @@
}
@Test
- fun affordance_setsUpRegistrationAndDeliversInitialModel() = runBlockingTest {
- whenever(controller.isEnabledForLockScreenButton).thenReturn(true)
- var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
+ fun affordance_setsUpRegistrationAndDeliversInitialModel() =
+ runTest(UnconfinedTestDispatcher()) {
+ whenever(controller.isEnabledForLockScreenButton).thenReturn(true)
+ var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
- val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+ val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
- val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>()
- verify(controller).addCallback(callbackCaptor.capture())
- verify(controller)
- .registerQRCodeScannerChangeObservers(
- QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE,
- QRCodeScannerController.QR_CODE_SCANNER_PREFERENCE_CHANGE
- )
- assertVisibleState(latest)
+ val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>()
+ verify(controller).addCallback(callbackCaptor.capture())
+ verify(controller)
+ .registerQRCodeScannerChangeObservers(
+ QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE,
+ QRCodeScannerController.QR_CODE_SCANNER_PREFERENCE_CHANGE,
+ )
+ assertVisibleState(latest)
- job.cancel()
- verify(controller).removeCallback(callbackCaptor.value)
- }
+ job.cancel()
+ verify(controller).removeCallback(callbackCaptor.value)
+ }
@Test
- fun affordance_scannerActivityChanged_deliversModelWithUpdatedIntent() = runBlockingTest {
- whenever(controller.isEnabledForLockScreenButton).thenReturn(true)
- var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
- val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
- val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>()
- verify(controller).addCallback(callbackCaptor.capture())
+ fun affordance_scannerActivityChanged_deliversModelWithUpdatedIntent() =
+ runTest(UnconfinedTestDispatcher()) {
+ whenever(controller.isEnabledForLockScreenButton).thenReturn(true)
+ var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
+ val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+ val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>()
+ verify(controller).addCallback(callbackCaptor.capture())
- whenever(controller.intent).thenReturn(INTENT_2)
- callbackCaptor.value.onQRCodeScannerActivityChanged()
+ whenever(controller.intent).thenReturn(INTENT_2)
+ callbackCaptor.value.onQRCodeScannerActivityChanged()
- assertVisibleState(latest)
+ assertVisibleState(latest)
- job.cancel()
- verify(controller).removeCallback(callbackCaptor.value)
- }
+ job.cancel()
+ verify(controller).removeCallback(callbackCaptor.value)
+ }
@Test
- fun affordance_scannerPreferenceChanged_deliversVisibleModel() = runBlockingTest {
- var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
- val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
- val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>()
- verify(controller).addCallback(callbackCaptor.capture())
+ fun affordance_scannerPreferenceChanged_deliversVisibleModel() =
+ runTest(UnconfinedTestDispatcher()) {
+ var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
+ val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
+ val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>()
+ verify(controller).addCallback(callbackCaptor.capture())
- whenever(controller.isEnabledForLockScreenButton).thenReturn(true)
- callbackCaptor.value.onQRCodeScannerPreferenceChanged()
+ whenever(controller.isEnabledForLockScreenButton).thenReturn(true)
+ callbackCaptor.value.onQRCodeScannerPreferenceChanged()
- assertVisibleState(latest)
+ assertVisibleState(latest)
- job.cancel()
- verify(controller).removeCallback(callbackCaptor.value)
- }
+ job.cancel()
+ verify(controller).removeCallback(callbackCaptor.value)
+ }
@Test
- fun affordance_scannerPreferenceChanged_deliversNone() = runBlockingTest {
+ fun affordance_scannerPreferenceChanged_deliversNone() = runTest(UnconfinedTestDispatcher()) {
var latest: KeyguardQuickAffordanceConfig.LockScreenState? = null
val job = underTest.lockScreenState.onEach { latest = it }.launchIn(this)
val callbackCaptor = argumentCaptor<QRCodeScannerController.Callback>()
@@ -128,30 +131,29 @@
fun onQuickAffordanceTriggered() {
assertThat(underTest.onTriggered(mock()))
.isEqualTo(
- OnTriggeredResult.StartActivity(
- intent = INTENT_1,
- canShowWhileLocked = true,
- )
+ OnTriggeredResult.StartActivity(intent = INTENT_1, canShowWhileLocked = true)
)
}
@Test
- fun getPickerScreenState_enabledIfConfiguredOnDevice_isEnabledForPickerState() = runTest {
- whenever(controller.isAllowedOnLockScreen).thenReturn(true)
- whenever(controller.isAbleToLaunchScannerActivity).thenReturn(true)
+ fun getPickerScreenState_enabledIfConfiguredOnDevice_isEnabledForPickerState() =
+ runTest(UnconfinedTestDispatcher()) {
+ whenever(controller.isAllowedOnLockScreen).thenReturn(true)
+ whenever(controller.isAbleToLaunchScannerActivity).thenReturn(true)
- assertThat(underTest.getPickerScreenState())
- .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default())
- }
+ assertThat(underTest.getPickerScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.Default())
+ }
@Test
- fun getPickerScreenState_disabledIfConfiguredOnDevice_isDisabledForPickerState() = runTest {
- whenever(controller.isAllowedOnLockScreen).thenReturn(true)
- whenever(controller.isAbleToLaunchScannerActivity).thenReturn(false)
+ fun getPickerScreenState_disabledIfConfiguredOnDevice_isDisabledForPickerState() =
+ runTest(UnconfinedTestDispatcher()) {
+ whenever(controller.isAllowedOnLockScreen).thenReturn(true)
+ whenever(controller.isAbleToLaunchScannerActivity).thenReturn(false)
- assertThat(underTest.getPickerScreenState())
- .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
- }
+ assertThat(underTest.getPickerScreenState())
+ .isEqualTo(KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice)
+ }
private fun assertVisibleState(latest: KeyguardQuickAffordanceConfig.LockScreenState?) {
assertThat(latest)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
index 2c17181..bfbdc50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
@@ -24,9 +24,9 @@
import android.os.Looper
import android.os.PatternMatcher
import android.os.UserHandle
-import androidx.test.filters.SmallTest
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
import com.android.systemui.dump.DumpManager
@@ -40,8 +40,9 @@
import junit.framework.Assert.assertSame
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
-import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -68,39 +69,28 @@
val DEFAULT_PERMISSION: String? = null
fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+
const val TEST_ACTION = "TEST_ACTION"
const val TEST_SCHEME = "TEST_SCHEME"
const val TEST_PATH = "TEST_PATH"
const val TEST_TYPE = "test/type"
}
- @Mock
- private lateinit var mockContext: Context
- @Mock
- private lateinit var mockUBRUser0: UserBroadcastDispatcher
- @Mock
- private lateinit var mockUBRUser1: UserBroadcastDispatcher
- @Mock
- private lateinit var broadcastReceiver: BroadcastReceiver
- @Mock
- private lateinit var broadcastReceiverOther: BroadcastReceiver
- @Mock
- private lateinit var intentFilter: IntentFilter
- @Mock
- private lateinit var intentFilterOther: IntentFilter
- @Mock
- private lateinit var mockHandler: Handler
- @Mock
- private lateinit var logger: BroadcastDispatcherLogger
- @Mock
- private lateinit var userTracker: UserTracker
- @Mock
- private lateinit var removalPendingStore: PendingRemovalStore
+ @Mock private lateinit var mockContext: Context
+ @Mock private lateinit var mockUBRUser0: UserBroadcastDispatcher
+ @Mock private lateinit var mockUBRUser1: UserBroadcastDispatcher
+ @Mock private lateinit var broadcastReceiver: BroadcastReceiver
+ @Mock private lateinit var broadcastReceiverOther: BroadcastReceiver
+ @Mock private lateinit var intentFilter: IntentFilter
+ @Mock private lateinit var intentFilterOther: IntentFilter
+ @Mock private lateinit var mockHandler: Handler
+ @Mock private lateinit var logger: BroadcastDispatcherLogger
+ @Mock private lateinit var userTracker: UserTracker
+ @Mock private lateinit var removalPendingStore: PendingRemovalStore
private lateinit var mainExecutor: Executor
- @Captor
- private lateinit var argumentCaptor: ArgumentCaptor<ReceiverData>
+ @Captor private lateinit var argumentCaptor: ArgumentCaptor<ReceiverData>
private lateinit var testableLooper: TestableLooper
private lateinit var broadcastDispatcher: BroadcastDispatcher
@@ -112,7 +102,8 @@
mainExecutor = FakeExecutor(FakeSystemClock())
`when`(mockContext.mainExecutor).thenReturn(mainExecutor)
- broadcastDispatcher = TestBroadcastDispatcher(
+ broadcastDispatcher =
+ TestBroadcastDispatcher(
mockContext,
mainExecutor,
testableLooper.looper,
@@ -121,7 +112,8 @@
logger,
userTracker,
removalPendingStore,
- mapOf(0 to mockUBRUser0, 1 to mockUBRUser1))
+ mapOf(0 to mockUBRUser0, 1 to mockUBRUser1),
+ )
// These should be valid filters
`when`(intentFilter.countActions()).thenReturn(1)
@@ -131,10 +123,18 @@
@Test
fun testAddingReceiverToCorrectUBR() {
- broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
- mockHandler, user0)
broadcastDispatcher.registerReceiverWithHandler(
- broadcastReceiverOther, intentFilterOther, mockHandler, user1)
+ broadcastReceiver,
+ intentFilter,
+ mockHandler,
+ user0,
+ )
+ broadcastDispatcher.registerReceiverWithHandler(
+ broadcastReceiverOther,
+ intentFilterOther,
+ mockHandler,
+ user1,
+ )
testableLooper.processAllMessages()
@@ -152,7 +152,11 @@
fun testAddingReceiverToCorrectUBR_executor() {
broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, mainExecutor, user0)
broadcastDispatcher.registerReceiver(
- broadcastReceiverOther, intentFilterOther, mainExecutor, user1)
+ broadcastReceiverOther,
+ intentFilterOther,
+ mainExecutor,
+ user1,
+ )
testableLooper.processAllMessages()
@@ -169,7 +173,10 @@
@Test
fun testAddReceiverDefaultFlag_handler() {
broadcastDispatcher.registerReceiverWithHandler(
- broadcastReceiver, intentFilter, mockHandler)
+ broadcastReceiver,
+ intentFilter,
+ mockHandler,
+ )
testableLooper.processAllMessages()
verify(mockUBRUser0).registerReceiver(capture(argumentCaptor), eq(DEFAULT_FLAG))
@@ -183,7 +190,11 @@
val flag = 3
broadcastDispatcher.registerReceiverWithHandler(
- broadcastReceiver, intentFilter, mockHandler, flags = flag)
+ broadcastReceiver,
+ intentFilter,
+ mockHandler,
+ flags = flag,
+ )
testableLooper.processAllMessages()
verify(mockUBRUser0).registerReceiver(capture(argumentCaptor), eq(flag))
@@ -212,7 +223,7 @@
broadcastReceiver,
intentFilter,
flags = flag,
- permission = permission
+ permission = permission,
)
testableLooper.processAllMessages()
@@ -250,10 +261,18 @@
@Test
fun testRemovingReceiversRemovesFromAllUBR() {
- broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
- mockHandler, user0)
- broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
- mockHandler, user1)
+ broadcastDispatcher.registerReceiverWithHandler(
+ broadcastReceiver,
+ intentFilter,
+ mockHandler,
+ user0,
+ )
+ broadcastDispatcher.registerReceiverWithHandler(
+ broadcastReceiver,
+ intentFilter,
+ mockHandler,
+ user1,
+ )
broadcastDispatcher.unregisterReceiver(broadcastReceiver)
@@ -265,10 +284,18 @@
@Test
fun testRemoveReceiverFromUser() {
- broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
- mockHandler, user0)
- broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
- mockHandler, user1)
+ broadcastDispatcher.registerReceiverWithHandler(
+ broadcastReceiver,
+ intentFilter,
+ mockHandler,
+ user0,
+ )
+ broadcastDispatcher.registerReceiverWithHandler(
+ broadcastReceiver,
+ intentFilter,
+ mockHandler,
+ user1,
+ )
broadcastDispatcher.unregisterReceiverForUser(broadcastReceiver, user0)
@@ -282,13 +309,17 @@
fun testRegisterCurrentAsActualUser() {
`when`(userTracker.userId).thenReturn(user1.identifier)
- broadcastDispatcher.registerReceiverWithHandler(broadcastReceiver, intentFilter,
- mockHandler, UserHandle.CURRENT)
+ broadcastDispatcher.registerReceiverWithHandler(
+ broadcastReceiver,
+ intentFilter,
+ mockHandler,
+ UserHandle.CURRENT,
+ )
testableLooper.processAllMessages()
- verify(mockUBRUser1).registerReceiver(
- capture(argumentCaptor), eq(Context.RECEIVER_EXPORTED))
+ verify(mockUBRUser1)
+ .registerReceiver(capture(argumentCaptor), eq(Context.RECEIVER_EXPORTED))
assertSame(broadcastReceiver, argumentCaptor.value.receiver)
}
@@ -300,41 +331,38 @@
@Test(expected = IllegalArgumentException::class)
fun testFilterMustNotContainDataScheme() {
- val testFilter = IntentFilter(TEST_ACTION).apply {
- addDataScheme(TEST_SCHEME)
- }
+ val testFilter = IntentFilter(TEST_ACTION).apply { addDataScheme(TEST_SCHEME) }
broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
}
@Test(expected = IllegalArgumentException::class)
fun testFilterMustNotContainDataAuthority() {
- val testFilter = IntentFilter(TEST_ACTION).apply {
- addDataAuthority(mock(IntentFilter.AuthorityEntry::class.java))
- }
+ val testFilter =
+ IntentFilter(TEST_ACTION).apply {
+ addDataAuthority(mock(IntentFilter.AuthorityEntry::class.java))
+ }
broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
}
@Test(expected = IllegalArgumentException::class)
fun testFilterMustNotContainDataPath() {
- val testFilter = IntentFilter(TEST_ACTION).apply {
- addDataPath(TEST_PATH, PatternMatcher.PATTERN_LITERAL)
- }
+ val testFilter =
+ IntentFilter(TEST_ACTION).apply {
+ addDataPath(TEST_PATH, PatternMatcher.PATTERN_LITERAL)
+ }
broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
}
@Test(expected = IllegalArgumentException::class)
fun testFilterMustNotContainDataType() {
- val testFilter = IntentFilter(TEST_ACTION).apply {
- addDataType(TEST_TYPE)
- }
+ val testFilter = IntentFilter(TEST_ACTION).apply { addDataType(TEST_TYPE) }
broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
}
@Test(expected = IllegalArgumentException::class)
fun testFilterMustNotSetPriority() {
- val testFilter = IntentFilter(TEST_ACTION).apply {
- priority = IntentFilter.SYSTEM_HIGH_PRIORITY
- }
+ val testFilter =
+ IntentFilter(TEST_ACTION).apply { priority = IntentFilter.SYSTEM_HIGH_PRIORITY }
broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter)
}
@@ -366,12 +394,14 @@
val inOrderUser0 = inOrder(mockUBRUser0, removalPendingStore)
inOrderUser0.verify(mockUBRUser0).unregisterReceiver(broadcastReceiver)
- inOrderUser0.verify(removalPendingStore)
+ inOrderUser0
+ .verify(removalPendingStore)
.clearPendingRemoval(broadcastReceiver, UserHandle.USER_ALL)
val inOrderUser1 = inOrder(mockUBRUser1, removalPendingStore)
inOrderUser1.verify(mockUBRUser1).unregisterReceiver(broadcastReceiver)
- inOrderUser1.verify(removalPendingStore)
+ inOrderUser1
+ .verify(removalPendingStore)
.clearPendingRemoval(broadcastReceiver, UserHandle.USER_ALL)
}
@@ -385,21 +415,21 @@
val inOrderUser1 = inOrder(mockUBRUser1, removalPendingStore)
inOrderUser1.verify(mockUBRUser1).unregisterReceiver(broadcastReceiver)
- inOrderUser1.verify(removalPendingStore)
+ inOrderUser1
+ .verify(removalPendingStore)
.clearPendingRemoval(broadcastReceiver, user1.identifier)
}
@Test
- fun testBroadcastFlow() = runBlockingTest {
- val flow = broadcastDispatcher.broadcastFlow(intentFilter, user1) { intent, receiver ->
- intent to receiver
- }
+ fun testBroadcastFlow() = runTest(UnconfinedTestDispatcher()) {
+ val flow =
+ broadcastDispatcher.broadcastFlow(intentFilter, user1) { intent, receiver ->
+ intent to receiver
+ }
// Collect the values into collectedValues.
val collectedValues = mutableListOf<Pair<Intent, BroadcastReceiver>>()
- val job = launch {
- flow.collect { collectedValues.add(it) }
- }
+ val job = launch { flow.collect { collectedValues.add(it) } }
testableLooper.processAllMessages()
verify(mockUBRUser1).registerReceiver(capture(argumentCaptor), eq(DEFAULT_FLAG))
@@ -436,17 +466,18 @@
logger: BroadcastDispatcherLogger,
userTracker: UserTracker,
removalPendingStore: PendingRemovalStore,
- var mockUBRMap: Map<Int, UserBroadcastDispatcher>
- ) : BroadcastDispatcher(
- context,
- mainExecutor,
- backgroundRunningLooper,
- backgroundRunningExecutor,
- dumpManager,
- logger,
- userTracker,
- removalPendingStore
- ) {
+ var mockUBRMap: Map<Int, UserBroadcastDispatcher>,
+ ) :
+ BroadcastDispatcher(
+ context,
+ mainExecutor,
+ backgroundRunningLooper,
+ backgroundRunningExecutor,
+ dumpManager,
+ logger,
+ userTracker,
+ removalPendingStore,
+ ) {
override fun createUBRForUser(userId: Int): UserBroadcastDispatcher {
return mockUBRMap.getOrDefault(userId, mock(UserBroadcastDispatcher::class.java))
}
diff --git a/packages/Vcn/service-b/Android.bp b/packages/Vcn/service-b/Android.bp
index a462297..03ef4e6 100644
--- a/packages/Vcn/service-b/Android.bp
+++ b/packages/Vcn/service-b/Android.bp
@@ -19,6 +19,19 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+filegroup {
+ name: "vcn-location-sources",
+ srcs: select(release_flag("RELEASE_MOVE_VCN_TO_MAINLINE"), {
+ true: [
+ "vcn-location-flag/module/com/android/server/vcn/VcnLocation.java",
+ ],
+ default: [
+ "vcn-location-flag/platform/com/android/server/vcn/VcnLocation.java",
+ ],
+ }),
+ visibility: ["//frameworks/base/services/core"],
+}
+
java_library {
name: "service-connectivity-b-pre-jarjar",
sdk_version: "system_server_current",
diff --git a/packages/Vcn/service-b/vcn-location-flag/module/com/android/server/vcn/VcnLocation.java b/packages/Vcn/service-b/vcn-location-flag/module/com/android/server/vcn/VcnLocation.java
new file mode 100644
index 0000000..6c7d24d
--- /dev/null
+++ b/packages/Vcn/service-b/vcn-location-flag/module/com/android/server/vcn/VcnLocation.java
@@ -0,0 +1,31 @@
+/*
+ * 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.server.vcn;
+
+/**
+ * Class to represent that VCN is in a mainline module
+ *
+ * <p>This class is used to check whether VCN is in the non-updatable platform or in a mainline
+ * module.
+ */
+// When VCN is in a mainline module, this class (module/com/android/server/vcn/VcnLocation.java)
+// will be built in to the vcn-location-sources filegroup. When VCN is in the non-updatable
+// platform, platform/com/android/server/vcn/VcnLocation.java will be built in to the filegroup
+public class VcnLocation {
+ /** Indicate that VCN is the platform */
+ public static final boolean IS_VCN_IN_MAINLINE = true;
+}
diff --git a/packages/Vcn/service-b/vcn-location-flag/platform/com/android/server/vcn/VcnLocation.java b/packages/Vcn/service-b/vcn-location-flag/platform/com/android/server/vcn/VcnLocation.java
new file mode 100644
index 0000000..c6c82a5
--- /dev/null
+++ b/packages/Vcn/service-b/vcn-location-flag/platform/com/android/server/vcn/VcnLocation.java
@@ -0,0 +1,32 @@
+/*
+ * 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.server.vcn;
+
+/**
+ * Class to represent that VCN is in the platform
+ *
+ * <p>This class is used to check whether VCN is in the non-updatable platform or in a mainline
+ * module.
+ */
+// When VCN is in a mainline module, module/com/android/server/vcn/VcnLocation.java
+// will be built in to the vcn-location-sources filegroup. When VCN is in the non-updatable
+// platform, this class (platform/com/android/server/vcn/VcnLocation.java) will be built in to the
+// filegroup
+public class VcnLocation {
+ /** Indicate that VCN is the platform */
+ public static final boolean IS_VCN_IN_MAINLINE = false;
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
index 9b71f80..de3c5f2 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -133,9 +133,6 @@
Log.v(TAG, "RavenwoodAwareTestRunner starting for " + testClass.getCanonicalName());
- // This is needed to make AndroidJUnit4ClassRunner happy.
- InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
-
// Hook point to allow more customization.
runAnnotatedMethodsOnRavenwood(RavenwoodTestRunnerInitializing.class, null);
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java
deleted file mode 100644
index 870a10a..0000000
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigState.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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 android.platform.test.ravenwood;
-
-import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_EMPTY_RESOURCES_APK;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import android.annotation.Nullable;
-import android.app.ResourcesManager;
-import android.content.res.Resources;
-import android.view.DisplayAdjustments;
-
-import java.io.File;
-import java.util.HashMap;
-
-/**
- * Used to store various states associated with {@link RavenwoodConfig} that's inly needed
- * in junit-impl.
- *
- * We don't want to put it in junit-src to avoid having to recompile all the downstream
- * dependencies after changing this class.
- *
- * All members must be called from the runner's main thread.
- */
-public class RavenwoodConfigState {
- private static final String TAG = "RavenwoodConfigState";
-
- private final RavenwoodConfig mConfig;
-
- // TODO: Move the other contexts from RavenwoodConfig to here too? They're used by
- // RavenwoodRule too, but RavenwoodRule can probably use InstrumentationRegistry?
- RavenwoodContext mSystemServerContext;
-
- public RavenwoodConfigState(RavenwoodConfig config) {
- mConfig = config;
- }
-
- /** Map from path -> resources. */
- private final HashMap<File, Resources> mCachedResources = new HashMap<>();
-
- /**
- * Load {@link Resources} from an APK, with cache.
- */
- public Resources loadResources(@Nullable File apkPath) {
- var cached = mCachedResources.get(apkPath);
- if (cached != null) {
- return cached;
- }
-
- var fileToLoad = apkPath != null ? apkPath : new File(RAVENWOOD_EMPTY_RESOURCES_APK);
-
- assertTrue("File " + fileToLoad + " doesn't exist.", fileToLoad.isFile());
-
- final String path = fileToLoad.getAbsolutePath();
- final var emptyPaths = new String[0];
-
- ResourcesManager.getInstance().initializeApplicationPaths(path, emptyPaths);
-
- final var ret = ResourcesManager.getInstance().getResources(null, path,
- emptyPaths, emptyPaths, emptyPaths,
- emptyPaths, null, null,
- new DisplayAdjustments().getCompatibilityInfo(),
- RavenwoodRuntimeEnvironmentController.class.getClassLoader(), null);
-
- assertNotNull(ret);
-
- mCachedResources.put(apkPath, ret);
- return ret;
- }
-}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
index ec00e8f..6dfcf4ce 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java
@@ -15,24 +15,23 @@
*/
package android.platform.test.ravenwood;
-import static com.android.ravenwood.common.RavenwoodCommonUtils.ensureIsPublicMember;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import android.annotation.Nullable;
import android.util.Log;
+import android.util.Pair;
-import com.android.ravenwood.common.RavenwoodRuntimeException;
+import com.android.ravenwood.RavenwoodRuntimeNative;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.rules.TestRule;
import org.junit.runner.Description;
-import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
/**
- * Used to store various states associated with the current test runner that's inly needed
+ * Used to store various states associated with the current test runner that's only needed
* in junit-impl.
*
* We don't want to put it in junit-src to avoid having to recompile all the downstream
@@ -42,6 +41,11 @@
*/
public final class RavenwoodRunnerState {
private static final String TAG = "RavenwoodRunnerState";
+ private static final String RAVENWOOD_RULE_ERROR =
+ "RavenwoodRule(s) are not executed in the correct order";
+
+ private static final List<Pair<RavenwoodRule, RavenwoodPropertyState>> sActiveProperties =
+ new ArrayList<>();
private final RavenwoodAwareTestRunner mRunner;
@@ -52,207 +56,95 @@
mRunner = runner;
}
- /**
- * The RavenwoodConfig used to configure the current Ravenwood environment.
- * This can either come from mConfig or mRule.
- */
- private RavenwoodConfig mCurrentConfig;
- /**
- * The RavenwoodConfig declared in the test class
- */
- private RavenwoodConfig mConfig;
- /**
- * The RavenwoodRule currently in effect, declared in the test class
- */
- private RavenwoodRule mRule;
- private boolean mHasRavenwoodRule;
private Description mMethodDescription;
- public RavenwoodConfig getConfig() {
- return mCurrentConfig;
- }
-
public void enterTestRunner() {
Log.i(TAG, "enterTestRunner: " + mRunner);
-
- mHasRavenwoodRule = hasRavenwoodRule(mRunner.mTestJavaClass);
- mConfig = extractConfiguration(mRunner.mTestJavaClass);
-
- if (mConfig != null) {
- if (mHasRavenwoodRule) {
- fail("RavenwoodConfig and RavenwoodRule cannot be used in the same class."
- + " Suggest migrating to RavenwoodConfig.");
- }
- mCurrentConfig = mConfig;
- } else if (!mHasRavenwoodRule) {
- // If no RavenwoodConfig and no RavenwoodRule, use a default config
- mCurrentConfig = new RavenwoodConfig.Builder().build();
- }
-
- if (mCurrentConfig != null) {
- RavenwoodRuntimeEnvironmentController.init(mRunner);
- }
+ RavenwoodRuntimeEnvironmentController.initForRunner();
}
public void enterTestClass() {
Log.i(TAG, "enterTestClass: " + mRunner.mTestJavaClass.getName());
-
- if (mCurrentConfig != null) {
- RavenwoodRuntimeEnvironmentController.init(mRunner);
- }
}
public void exitTestClass() {
Log.i(TAG, "exitTestClass: " + mRunner.mTestJavaClass.getName());
- try {
- if (mCurrentConfig != null) {
- RavenwoodRuntimeEnvironmentController.reset();
- }
- } finally {
- mConfig = null;
- mRule = null;
- }
+ assertTrue(RAVENWOOD_RULE_ERROR, sActiveProperties.isEmpty());
+ RavenwoodRuntimeEnvironmentController.exitTestClass();
}
public void enterTestMethod(Description description) {
mMethodDescription = description;
+ RavenwoodRuntimeEnvironmentController.initForMethod();
}
public void exitTestMethod() {
mMethodDescription = null;
- RavenwoodRuntimeEnvironmentController.reinit();
}
public void enterRavenwoodRule(RavenwoodRule rule) {
- if (!mHasRavenwoodRule) {
- fail("If you have a RavenwoodRule in your test, make sure the field type is"
- + " RavenwoodRule so Ravenwood can detect it.");
- }
- if (mRule != null) {
- fail("Multiple nesting RavenwoodRule's are detected in the same class,"
- + " which is not supported.");
- }
- mRule = rule;
- if (mCurrentConfig == null) {
- mCurrentConfig = rule.getConfiguration();
- }
- RavenwoodRuntimeEnvironmentController.init(mRunner);
+ pushTestProperties(rule);
}
public void exitRavenwoodRule(RavenwoodRule rule) {
- if (mRule != rule) {
- fail("RavenwoodRule did not take effect.");
- }
- mRule = null;
+ popTestProperties(rule);
}
- /**
- * @return a configuration from a test class, if any.
- */
- @Nullable
- private static RavenwoodConfig extractConfiguration(Class<?> testClass) {
- var field = findConfigurationField(testClass);
- if (field == null) {
- return null;
+ static class RavenwoodPropertyState {
+
+ final List<Pair<String, String>> mBackup;
+ final Set<String> mKeyReadable;
+ final Set<String> mKeyWritable;
+
+ RavenwoodPropertyState(RavenwoodTestProperties props) {
+ mBackup = props.mValues.keySet().stream()
+ .map(key -> Pair.create(key, RavenwoodRuntimeNative.getSystemProperty(key)))
+ .toList();
+ mKeyReadable = Set.copyOf(props.mKeyReadable);
+ mKeyWritable = Set.copyOf(props.mKeyWritable);
}
- try {
- return (RavenwoodConfig) field.get(null);
- } catch (IllegalAccessException e) {
- throw new RavenwoodRuntimeException("Failed to fetch from the configuration field", e);
+ boolean isKeyAccessible(String key, boolean write) {
+ return write ? mKeyWritable.contains(key) : mKeyReadable.contains(key);
}
- }
- /**
- * @return true if the current target class (or its super classes) has any @Rule / @ClassRule
- * fields of type RavenwoodRule.
- *
- * Note, this check won't detect cases where a Rule is of type
- * {@link TestRule} and still be a {@link RavenwoodRule}. But that'll be detected at runtime
- * as a failure, in {@link #enterRavenwoodRule}.
- */
- private static boolean hasRavenwoodRule(Class<?> testClass) {
- for (var field : testClass.getDeclaredFields()) {
- if (!field.isAnnotationPresent(Rule.class)
- && !field.isAnnotationPresent(ClassRule.class)) {
- continue;
- }
- if (field.getType().equals(RavenwoodRule.class)) {
- return true;
- }
- }
- // JUnit supports rules as methods, so we need to check them too.
- for (var method : testClass.getDeclaredMethods()) {
- if (!method.isAnnotationPresent(Rule.class)
- && !method.isAnnotationPresent(ClassRule.class)) {
- continue;
- }
- if (method.getReturnType().equals(RavenwoodRule.class)) {
- return true;
- }
- }
- // Look into the super class.
- if (!testClass.getSuperclass().equals(Object.class)) {
- return hasRavenwoodRule(testClass.getSuperclass());
- }
- return false;
- }
-
- /**
- * Find and return a field with @RavenwoodConfig.Config, which must be of type
- * RavenwoodConfig.
- */
- @Nullable
- private static Field findConfigurationField(Class<?> testClass) {
- Field foundField = null;
-
- for (var field : testClass.getDeclaredFields()) {
- final var hasAnot = field.isAnnotationPresent(RavenwoodConfig.Config.class);
- final var isType = field.getType().equals(RavenwoodConfig.class);
-
- if (hasAnot) {
- if (isType) {
- // Good, use this field.
- if (foundField != null) {
- fail(String.format(
- "Class %s has multiple fields with %s",
- testClass.getCanonicalName(),
- "@RavenwoodConfig.Config"));
- }
- // Make sure it's static public
- ensureIsPublicMember(field, true);
-
- foundField = field;
+ void restore() {
+ mBackup.forEach(pair -> {
+ if (pair.second == null) {
+ RavenwoodRuntimeNative.removeSystemProperty(pair.first);
} else {
- fail(String.format(
- "Field %s.%s has %s but type is not %s",
- testClass.getCanonicalName(),
- field.getName(),
- "@RavenwoodConfig.Config",
- "RavenwoodConfig"));
- return null; // unreachable
+ RavenwoodRuntimeNative.setSystemProperty(pair.first, pair.second);
}
- } else {
- if (isType) {
- fail(String.format(
- "Field %s.%s does not have %s but type is %s",
- testClass.getCanonicalName(),
- field.getName(),
- "@RavenwoodConfig.Config",
- "RavenwoodConfig"));
- return null; // unreachable
- } else {
- // Unrelated field, ignore.
- continue;
- }
- }
+ });
}
- if (foundField != null) {
- return foundField;
+ }
+
+ private static void pushTestProperties(RavenwoodRule rule) {
+ sActiveProperties.add(Pair.create(rule, new RavenwoodPropertyState(rule.mProperties)));
+ rule.mProperties.mValues.forEach(RavenwoodRuntimeNative::setSystemProperty);
+ }
+
+ private static void popTestProperties(RavenwoodRule rule) {
+ var pair = sActiveProperties.removeLast();
+ assertNotNull(RAVENWOOD_RULE_ERROR, pair);
+ assertEquals(RAVENWOOD_RULE_ERROR, rule, pair.first);
+ pair.second.restore();
+ }
+
+ @SuppressWarnings("unused") // Called from native code (ravenwood_sysprop.cpp)
+ private static void checkSystemPropertyAccess(String key, boolean write) {
+ if (write && RavenwoodSystemProperties.sDefaultValues.containsKey(key)) {
+ // The default core values should never be modified
+ throw new IllegalArgumentException(
+ "Setting core system property '" + key + "' is not allowed");
}
- if (!testClass.getSuperclass().equals(Object.class)) {
- return findConfigurationField(testClass.getSuperclass());
+
+ final boolean result = RavenwoodSystemProperties.isKeyAccessible(key, write)
+ || sActiveProperties.stream().anyMatch(p -> p.second.isKeyAccessible(key, write));
+
+ if (!result) {
+ throw new IllegalArgumentException((write ? "Write" : "Read")
+ + " access to system property '" + key + "' denied via RavenwoodRule");
}
- return null;
}
}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
index 979076e..e730a29 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -16,8 +16,11 @@
package android.platform.test.ravenwood;
+import static android.os.Process.FIRST_APPLICATION_UID;
+import static android.os.UserHandle.SYSTEM;
import static android.platform.test.ravenwood.RavenwoodSystemServer.ANDROID_PACKAGE_NAME;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_EMPTY_RESOURCES_APK;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_INST_RESOURCE_APK;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
@@ -25,7 +28,9 @@
import static com.android.ravenwood.common.RavenwoodCommonUtils.parseNullableInt;
import static com.android.ravenwood.common.RavenwoodCommonUtils.withDefault;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
@@ -53,6 +58,7 @@
import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
+import android.view.DisplayAdjustments;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -62,7 +68,6 @@
import com.android.ravenwood.RavenwoodRuntimeNative;
import com.android.ravenwood.RavenwoodRuntimeState;
import com.android.ravenwood.common.RavenwoodCommonUtils;
-import com.android.ravenwood.common.RavenwoodRuntimeException;
import com.android.ravenwood.common.SneakyThrow;
import com.android.server.LocalServices;
import com.android.server.compat.PlatformCompat;
@@ -74,8 +79,10 @@
import java.io.IOException;
import java.io.PrintStream;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
+import java.util.Random;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@@ -85,8 +92,7 @@
import java.util.function.Supplier;
/**
- * Responsible for initializing and de-initializing the environment, according to a
- * {@link RavenwoodConfig}.
+ * Responsible for initializing and the environment.
*/
public class RavenwoodRuntimeEnvironmentController {
private static final String TAG = "RavenwoodRuntimeEnvironmentController";
@@ -113,8 +119,6 @@
private static ScheduledFuture<?> sPendingTimeout;
- private static long sOriginalIdentityToken = -1;
-
/**
* When enabled, attempt to detect uncaught exceptions from background threads.
*/
@@ -147,6 +151,10 @@
return res;
}
+ /** Map from path -> resources. */
+ private static final HashMap<File, Resources> sCachedResources = new HashMap<>();
+ private static Set<String> sAdoptedPermissions = Collections.emptySet();
+
private static final Object sInitializationLock = new Object();
@GuardedBy("sInitializationLock")
@@ -155,15 +163,16 @@
@GuardedBy("sInitializationLock")
private static Throwable sExceptionFromGlobalInit;
- private static RavenwoodAwareTestRunner sRunner;
- private static RavenwoodSystemProperties sProps;
-
private static final int DEFAULT_TARGET_SDK_LEVEL = VERSION_CODES.CUR_DEVELOPMENT;
private static final String DEFAULT_PACKAGE_NAME = "com.android.ravenwoodtests.defaultname";
+ private static final int sMyPid = new Random().nextInt(100, 32768);
private static int sTargetSdkLevel;
private static String sTestPackageName;
private static String sTargetPackageName;
+ private static Instrumentation sInstrumentation;
+ private static final long sCallingIdentity =
+ packBinderIdentityToken(false, FIRST_APPLICATION_UID, sMyPid);
/**
* Initialize the global environment.
@@ -182,7 +191,7 @@
Log.e(TAG, "globalInit() failed", th);
sExceptionFromGlobalInit = th;
- throw th;
+ SneakyThrow.sneakyThrow(th);
}
} else {
// Subsequent calls. If the first call threw, just throw the same error, to prevent
@@ -197,10 +206,13 @@
}
}
- private static void globalInitInner() {
+ private static void globalInitInner() throws IOException {
if (RAVENWOOD_VERBOSE_LOGGING) {
Log.v(TAG, "globalInit() called here...", new RuntimeException("NOT A CRASH"));
}
+ if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
+ Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler);
+ }
// Some process-wide initialization. (maybe redirect stdout/stderr)
RavenwoodCommonUtils.loadJniLibrary(LIBRAVENWOOD_INITIALIZER_NAME);
@@ -220,7 +232,6 @@
// Do the basic set up for the android sysprops.
RavenwoodSystemProperties.initialize();
- setSystemProperties(null);
// Do this after loading RAVENWOOD_NATIVE_RUNTIME_NAME (which backs Os.setenv()),
// before loadFrameworkNativeCode() (which uses $ANDROID_LOG_TAGS).
@@ -251,6 +262,74 @@
loadRavenwoodProperties();
assertMockitoVersion();
+
+ Log.i(TAG, "TargetPackageName=" + sTargetPackageName);
+ Log.i(TAG, "TestPackageName=" + sTestPackageName);
+ Log.i(TAG, "TargetSdkLevel=" + sTargetSdkLevel);
+
+ RavenwoodRuntimeState.sUid = FIRST_APPLICATION_UID;
+ RavenwoodRuntimeState.sPid = sMyPid;
+ RavenwoodRuntimeState.sTargetSdkLevel = sTargetSdkLevel;
+
+ ServiceManager.init$ravenwood();
+ LocalServices.removeAllServicesForTest();
+
+ ActivityManager.init$ravenwood(SYSTEM.getIdentifier());
+
+ final var main = new HandlerThread(MAIN_THREAD_NAME);
+ main.start();
+ Looper.setMainLooperForTest(main.getLooper());
+
+ final boolean isSelfInstrumenting =
+ Objects.equals(sTestPackageName, sTargetPackageName);
+
+ // This will load the resources from the apk set to `resource_apk` in the build file.
+ // This is supposed to be the "target app"'s resources.
+ final Supplier<Resources> targetResourcesLoader = () -> {
+ var file = new File(RAVENWOOD_RESOURCE_APK);
+ return loadResources(file.exists() ? file : null);
+ };
+
+ // Set up test context's (== instrumentation context's) resources.
+ // If the target package name == test package name, then we use the main resources.
+ final Supplier<Resources> instResourcesLoader;
+ if (isSelfInstrumenting) {
+ instResourcesLoader = targetResourcesLoader;
+ } else {
+ instResourcesLoader = () -> {
+ var file = new File(RAVENWOOD_INST_RESOURCE_APK);
+ return loadResources(file.exists() ? file : null);
+ };
+ }
+
+ var instContext = new RavenwoodContext(
+ sTestPackageName, main, instResourcesLoader);
+ var targetContext = new RavenwoodContext(
+ sTargetPackageName, main, targetResourcesLoader);
+
+ // Set up app context.
+ var appContext = new RavenwoodContext(sTargetPackageName, main, targetResourcesLoader);
+ appContext.setApplicationContext(appContext);
+ if (isSelfInstrumenting) {
+ instContext.setApplicationContext(appContext);
+ targetContext.setApplicationContext(appContext);
+ } else {
+ // When instrumenting into another APK, the test context doesn't have an app context.
+ targetContext.setApplicationContext(appContext);
+ }
+
+ final Supplier<Resources> systemResourcesLoader = () -> loadResources(null);
+
+ var systemServerContext =
+ new RavenwoodContext(ANDROID_PACKAGE_NAME, main, systemResourcesLoader);
+
+ sInstrumentation = new Instrumentation();
+ sInstrumentation.basicInit(instContext, targetContext, null);
+ InstrumentationRegistry.registerInstance(sInstrumentation, Bundle.EMPTY);
+
+ RavenwoodSystemServer.init(systemServerContext);
+
+ initializeCompatIds();
}
private static void loadRavenwoodProperties() {
@@ -265,134 +344,41 @@
}
/**
- * Initialize the environment.
+ * Partially reset and initialize before each test class invocation
*/
- public static void init(RavenwoodAwareTestRunner runner) {
- if (RAVENWOOD_VERBOSE_LOGGING) {
- Log.v(TAG, "init() called here: " + runner, new RuntimeException("STACKTRACE"));
- }
- if (sRunner == runner) {
- return;
- }
- if (sRunner != null) {
- reset();
- }
- sRunner = runner;
- try {
- initInner(runner.mState.getConfig());
- } catch (Exception th) {
- Log.e(TAG, "init() failed", th);
+ public static void initForRunner() {
+ var targetContext = sInstrumentation.getTargetContext();
+ var instContext = sInstrumentation.getContext();
+ // We need to recreate the mock UiAutomation for each test class, because sometimes tests
+ // will call Mockito.framework().clearInlineMocks() after execution.
+ sInstrumentation.basicInit(instContext, targetContext, createMockUiAutomation());
- RavenwoodCommonUtils.runIgnoringException(()-> reset());
+ // Reset some global state
+ Process_ravenwood.reset();
+ DeviceConfig_host.reset();
+ Binder.restoreCallingIdentity(sCallingIdentity);
- SneakyThrow.sneakyThrow(th);
- }
- }
-
- private static void initInner(RavenwoodConfig config) throws IOException {
- if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
- maybeThrowPendingUncaughtException(false);
- Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler);
- }
-
- config.mTargetPackageName = sTargetPackageName;
- config.mTestPackageName = sTestPackageName;
- config.mTargetSdkLevel = sTargetSdkLevel;
-
- Log.i(TAG, "TargetPackageName=" + sTargetPackageName);
- Log.i(TAG, "TestPackageName=" + sTestPackageName);
- Log.i(TAG, "TargetSdkLevel=" + sTargetSdkLevel);
-
- RavenwoodRuntimeState.sUid = config.mUid;
- RavenwoodRuntimeState.sPid = config.mPid;
- RavenwoodRuntimeState.sTargetSdkLevel = config.mTargetSdkLevel;
- sOriginalIdentityToken = Binder.clearCallingIdentity();
- reinit();
- setSystemProperties(config.mSystemProperties);
-
- ServiceManager.init$ravenwood();
- LocalServices.removeAllServicesForTest();
-
- ActivityManager.init$ravenwood(config.mCurrentUser);
-
- final var main = new HandlerThread(MAIN_THREAD_NAME);
- main.start();
- Looper.setMainLooperForTest(main.getLooper());
-
- final boolean isSelfInstrumenting =
- Objects.equals(config.mTestPackageName, config.mTargetPackageName);
-
- // This will load the resources from the apk set to `resource_apk` in the build file.
- // This is supposed to be the "target app"'s resources.
- final Supplier<Resources> targetResourcesLoader = () -> {
- var file = new File(RAVENWOOD_RESOURCE_APK);
- return config.mState.loadResources(file.exists() ? file : null);
- };
-
- // Set up test context's (== instrumentation context's) resources.
- // If the target package name == test package name, then we use the main resources.
- final Supplier<Resources> instResourcesLoader;
- if (isSelfInstrumenting) {
- instResourcesLoader = targetResourcesLoader;
- } else {
- instResourcesLoader = () -> {
- var file = new File(RAVENWOOD_INST_RESOURCE_APK);
- return config.mState.loadResources(file.exists() ? file : null);
- };
- }
-
- var instContext = new RavenwoodContext(
- config.mTestPackageName, main, instResourcesLoader);
- var targetContext = new RavenwoodContext(
- config.mTargetPackageName, main, targetResourcesLoader);
-
- // Set up app context.
- var appContext = new RavenwoodContext(
- config.mTargetPackageName, main, targetResourcesLoader);
- appContext.setApplicationContext(appContext);
- if (isSelfInstrumenting) {
- instContext.setApplicationContext(appContext);
- targetContext.setApplicationContext(appContext);
- } else {
- // When instrumenting into another APK, the test context doesn't have an app context.
- targetContext.setApplicationContext(appContext);
- }
- config.mInstContext = instContext;
- config.mTargetContext = targetContext;
-
- final Supplier<Resources> systemResourcesLoader = () -> config.mState.loadResources(null);
-
- config.mState.mSystemServerContext =
- new RavenwoodContext(ANDROID_PACKAGE_NAME, main, systemResourcesLoader);
-
- // Prepare other fields.
- config.mInstrumentation = new Instrumentation();
- config.mInstrumentation.basicInit(instContext, targetContext, createMockUiAutomation());
- InstrumentationRegistry.registerInstance(config.mInstrumentation, Bundle.EMPTY);
-
- RavenwoodSystemServer.init(config);
-
- initializeCompatIds(config);
+ SystemProperties.clearChangeCallbacksForTest();
if (ENABLE_TIMEOUT_STACKS) {
sPendingTimeout = sTimeoutExecutor.schedule(
RavenwoodRuntimeEnvironmentController::dumpStacks,
TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
}
- }
-
- /**
- * Partially re-initialize after each test method invocation
- */
- public static void reinit() {
- // sRunner could be null, if there was a failure in the initialization.
- if (sRunner != null) {
- var config = sRunner.mState.getConfig();
- Binder.restoreCallingIdentity(packBinderIdentityToken(false, config.mUid, config.mPid));
+ if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
+ maybeThrowPendingUncaughtException(false);
}
}
- private static void initializeCompatIds(RavenwoodConfig config) {
+ /**
+ * Partially reset and initialize before each test method invocation
+ */
+ public static void initForMethod() {
+ // TODO(b/375272444): this is a hacky workaround to ensure binder identity
+ Binder.restoreCallingIdentity(sCallingIdentity);
+ }
+
+ private static void initializeCompatIds() {
// Set up compat-IDs for the app side.
// TODO: Inside the system server, all the compat-IDs should be enabled,
// Due to the `AppCompatCallbacks.install(new long[0], new long[0])` call in
@@ -400,8 +386,8 @@
// Compat framework only uses the package name and the target SDK level.
ApplicationInfo appInfo = new ApplicationInfo();
- appInfo.packageName = config.mTargetPackageName;
- appInfo.targetSdkVersion = config.mTargetSdkLevel;
+ appInfo.packageName = sTargetPackageName;
+ appInfo.targetSdkVersion = sTargetSdkLevel;
PlatformCompat platformCompat = null;
try {
@@ -418,65 +404,42 @@
}
/**
- * De-initialize.
- *
- * Note, we call this method when init() fails too, so this method should deal with
- * any partially-initialized states.
+ * Load {@link Resources} from an APK, with cache.
*/
- public static void reset() {
- if (RAVENWOOD_VERBOSE_LOGGING) {
- Log.v(TAG, "reset() called here", new RuntimeException("STACKTRACE"));
+ private static Resources loadResources(@Nullable File apkPath) {
+ var cached = sCachedResources.get(apkPath);
+ if (cached != null) {
+ return cached;
}
- if (sRunner == null) {
- throw new RavenwoodRuntimeException("Internal error: reset() already called");
- }
- var config = sRunner.mState.getConfig();
- sRunner = null;
+ var fileToLoad = apkPath != null ? apkPath : new File(RAVENWOOD_EMPTY_RESOURCES_APK);
+
+ assertTrue("File " + fileToLoad + " doesn't exist.", fileToLoad.isFile());
+
+ final String path = fileToLoad.getAbsolutePath();
+ final var emptyPaths = new String[0];
+
+ ResourcesManager.getInstance().initializeApplicationPaths(path, emptyPaths);
+
+ final var ret = ResourcesManager.getInstance().getResources(null, path,
+ emptyPaths, emptyPaths, emptyPaths,
+ emptyPaths, null, null,
+ new DisplayAdjustments().getCompatibilityInfo(),
+ RavenwoodRuntimeEnvironmentController.class.getClassLoader(), null);
+
+ assertNotNull(ret);
+
+ sCachedResources.put(apkPath, ret);
+ return ret;
+ }
+
+ /**
+ * A callback when a test class finishes its execution, mostly only for debugging.
+ */
+ public static void exitTestClass() {
if (ENABLE_TIMEOUT_STACKS) {
sPendingTimeout.cancel(false);
}
-
- RavenwoodSystemServer.reset(config);
-
- InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
- config.mInstrumentation = null;
- if (config.mInstContext != null) {
- ((RavenwoodContext) config.mInstContext).cleanUp();
- config.mInstContext = null;
- }
- if (config.mTargetContext != null) {
- ((RavenwoodContext) config.mTargetContext).cleanUp();
- config.mTargetContext = null;
- }
- if (config.mState.mSystemServerContext != null) {
- config.mState.mSystemServerContext.cleanUp();
- }
-
- if (Looper.getMainLooper() != null) {
- Looper.getMainLooper().quit();
- }
- Looper.clearMainLooperForTest();
-
- ActivityManager.reset$ravenwood();
-
- LocalServices.removeAllServicesForTest();
- ServiceManager.reset$ravenwood();
-
- setSystemProperties(null);
- if (sOriginalIdentityToken != -1) {
- Binder.restoreCallingIdentity(sOriginalIdentityToken);
- }
- RavenwoodRuntimeState.reset();
- Process_ravenwood.reset();
- DeviceConfig_host.reset();
-
- try {
- ResourcesManager.setInstance(null); // Better structure needed.
- } catch (Exception e) {
- // AOSP-CHANGE: AOSP doesn't support resources yet.
- }
-
if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
maybeThrowPendingUncaughtException(true);
}
@@ -521,19 +484,6 @@
}
}
- /**
- * Set the current configuration to the actual SystemProperties.
- */
- private static void setSystemProperties(@Nullable RavenwoodSystemProperties systemProperties) {
- SystemProperties.clearChangeCallbacksForTest();
- RavenwoodRuntimeNative.clearSystemProperties();
- if (systemProperties == null) systemProperties = new RavenwoodSystemProperties();
- sProps = new RavenwoodSystemProperties(systemProperties, true);
- for (var entry : systemProperties.getValues().entrySet()) {
- RavenwoodRuntimeNative.setSystemProperty(entry.getKey(), entry.getValue());
- }
- }
-
private static final String MOCKITO_ERROR = "FATAL: Unsupported Mockito detected!"
+ " Your test or its dependencies use one of the \"mockito-target-*\""
+ " modules as static library, which is unusable on host side."
@@ -558,40 +508,31 @@
// TODO: use the real UiAutomation class instead of a mock
private static UiAutomation createMockUiAutomation() {
- final Set[] adoptedPermission = { Collections.emptySet() };
+ sAdoptedPermissions = Collections.emptySet();
var mock = mock(UiAutomation.class, inv -> {
HostTestUtils.onThrowMethodCalled();
return null;
});
doAnswer(inv -> {
- adoptedPermission[0] = UiAutomation.ALL_PERMISSIONS;
+ sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS;
return null;
}).when(mock).adoptShellPermissionIdentity();
doAnswer(inv -> {
if (inv.getArgument(0) == null) {
- adoptedPermission[0] = UiAutomation.ALL_PERMISSIONS;
+ sAdoptedPermissions = UiAutomation.ALL_PERMISSIONS;
} else {
- adoptedPermission[0] = Set.of(inv.getArguments());
+ sAdoptedPermissions = (Set) Set.of(inv.getArguments());
}
return null;
}).when(mock).adoptShellPermissionIdentity(any());
doAnswer(inv -> {
- adoptedPermission[0] = Collections.emptySet();
+ sAdoptedPermissions = Collections.emptySet();
return null;
}).when(mock).dropShellPermissionIdentity();
- doAnswer(inv -> adoptedPermission[0]).when(mock).getAdoptedShellPermissions();
+ doAnswer(inv -> sAdoptedPermissions).when(mock).getAdoptedShellPermissions();
return mock;
}
- @SuppressWarnings("unused") // Called from native code (ravenwood_sysprop.cpp)
- private static void checkSystemPropertyAccess(String key, boolean write) {
- boolean result = write ? sProps.isKeyWritable(key) : sProps.isKeyReadable(key);
- if (!result) {
- throw new IllegalArgumentException((write ? "Write" : "Read")
- + " access to system property '" + key + "' denied via RavenwoodConfig");
- }
- }
-
private static void dumpCommandLineArgs() {
Log.i(TAG, "JVM arguments:");
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
similarity index 74%
rename from ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
rename to ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
index 9bd376a..c545baa 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
@@ -13,7 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package android.platform.test.ravenwood;
import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
@@ -21,26 +20,30 @@
import android.util.Log;
+import com.android.ravenwood.RavenwoodRuntimeNative;
+
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
+/**
+ * A class to manage the core default system properties of the Ravenwood environment.
+ */
public class RavenwoodSystemProperties {
private static final String TAG = "RavenwoodSystemProperties";
- /** We pull in propeties from this file. */
+ /** We pull in properties from this file. */
private static final String RAVENWOOD_BUILD_PROP = "ravenwood-data/ravenwood-build.prop";
/** This is the actual build.prop we use to build the device (contents depends on lunch). */
private static final String DEVICE_BUILD_PROP = "ravenwood-data/build.prop";
/** The default values. */
- private static final Map<String, String> sDefaultValues = new HashMap<>();
+ static final Map<String, String> sDefaultValues = new HashMap<>();
private static final String[] PARTITIONS = {
"bootimage",
@@ -91,7 +94,7 @@
var deviceValue = deviceProps.get(deviceKey);
if (deviceValue == null) {
throw new RuntimeException("Failed to initialize system properties. Key '"
- + deviceKey + "' doesn't exist in the device side build.prop");
+ + deviceKey + "' doesn't exist in the device side build.prop");
}
value = deviceValue;
} else {
@@ -115,6 +118,7 @@
}
}
}
+
if (RAVENWOOD_VERBOSE_LOGGING) {
// Dump all properties for local debugging.
Log.v(TAG, "All system properties:");
@@ -122,35 +126,12 @@
Log.v(TAG, "" + key + "=" + sDefaultValues.get(key));
}
}
+
+ // Actually set the system properties
+ sDefaultValues.forEach(RavenwoodRuntimeNative::setSystemProperty);
}
- private volatile boolean mIsImmutable;
-
- private final Map<String, String> mValues = new HashMap<>();
-
- /** Set of additional keys that should be considered readable */
- private final Set<String> mKeyReadable = new HashSet<>();
-
- /** Set of additional keys that should be considered writable */
- private final Set<String> mKeyWritable = new HashSet<>();
-
- public RavenwoodSystemProperties() {
- mValues.putAll(sDefaultValues);
- }
-
- /** Copy constructor */
- public RavenwoodSystemProperties(RavenwoodSystemProperties source, boolean immutable) {
- mKeyReadable.addAll(source.mKeyReadable);
- mKeyWritable.addAll(source.mKeyWritable);
- mValues.putAll(source.mValues);
- mIsImmutable = immutable;
- }
-
- public Map<String, String> getValues() {
- return new HashMap<>(mValues);
- }
-
- public boolean isKeyReadable(String key) {
+ private static boolean isKeyReadable(String key) {
final String root = getKeyRoot(key);
if (root.startsWith("debug.")) return true;
@@ -183,10 +164,10 @@
return true;
}
- return mKeyReadable.contains(key);
+ return false;
}
- public boolean isKeyWritable(String key) {
+ private static boolean isKeyWritable(String key) {
final String root = getKeyRoot(key);
if (root.startsWith("debug.")) return true;
@@ -194,42 +175,11 @@
// For PropertyInvalidatedCache
if (root.startsWith("cache_key.")) return true;
- return mKeyWritable.contains(key);
+ return false;
}
- private void ensureNotImmutable() {
- if (mIsImmutable) {
- throw new RuntimeException("Unable to update immutable instance");
- }
- }
-
- public void setValue(String key, Object value) {
- ensureNotImmutable();
-
- final String valueString = (value == null) ? null : String.valueOf(value);
- if ((valueString == null) || valueString.isEmpty()) {
- mValues.remove(key);
- } else {
- mValues.put(key, valueString);
- }
- }
-
- public void setAccessNone(String key) {
- ensureNotImmutable();
- mKeyReadable.remove(key);
- mKeyWritable.remove(key);
- }
-
- public void setAccessReadOnly(String key) {
- ensureNotImmutable();
- mKeyReadable.add(key);
- mKeyWritable.remove(key);
- }
-
- public void setAccessReadWrite(String key) {
- ensureNotImmutable();
- mKeyReadable.add(key);
- mKeyWritable.add(key);
+ static boolean isKeyAccessible(String key, boolean write) {
+ return write ? isKeyWritable(key) : isKeyReadable(key);
}
/**
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
index 438a2bf..3346635 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodSystemServer.java
@@ -33,7 +33,7 @@
import com.android.server.compat.PlatformCompatNative;
import com.android.server.utils.TimingsTraceAndSlog;
-import java.util.List;
+import java.util.Collection;
import java.util.Set;
public class RavenwoodSystemServer {
@@ -68,27 +68,24 @@
private static TimingsTraceAndSlog sTimings;
private static SystemServiceManager sServiceManager;
- public static void init(RavenwoodConfig config) {
+ public static void init(Context systemServerContext) {
// Always start PlatformCompat, regardless of the requested services.
// PlatformCompat is not really a SystemService, so it won't receive boot phases / etc.
// This initialization code is copied from SystemServer.java.
- PlatformCompat platformCompat = new PlatformCompat(config.mState.mSystemServerContext);
+ PlatformCompat platformCompat = new PlatformCompat(systemServerContext);
ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE, platformCompat);
ServiceManager.addService(Context.PLATFORM_COMPAT_NATIVE_SERVICE,
new PlatformCompatNative(platformCompat));
- // Avoid overhead if no services required
- if (config.mServicesRequired.isEmpty()) return;
-
sStartedServices = new ArraySet<>();
sTimings = new TimingsTraceAndSlog();
- sServiceManager = new SystemServiceManager(config.mState.mSystemServerContext);
+ sServiceManager = new SystemServiceManager(systemServerContext);
sServiceManager.setStartInfo(false,
SystemClock.elapsedRealtime(),
SystemClock.uptimeMillis());
LocalServices.addService(SystemServiceManager.class, sServiceManager);
- startServices(config.mServicesRequired);
+ startServices(sKnownServices.keySet());
sServiceManager.sealStartedServices();
// TODO: expand to include additional boot phases when relevant
@@ -96,7 +93,7 @@
sServiceManager.startBootPhase(sTimings, SystemService.PHASE_BOOT_COMPLETED);
}
- public static void reset(RavenwoodConfig config) {
+ public static void reset() {
// TODO: consider introducing shutdown boot phases
LocalServices.removeServiceForTest(SystemServiceManager.class);
@@ -105,7 +102,7 @@
sStartedServices = null;
}
- private static void startServices(List<Class<?>> serviceClasses) {
+ private static void startServices(Collection<Class<?>> serviceClasses) {
for (Class<?> serviceClass : serviceClasses) {
// Quietly ignore duplicate requests if service already started
if (sStartedServices.contains(serviceClass)) continue;
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
index 7ca9239..3ed0f50 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java
@@ -15,21 +15,13 @@
*/
package android.platform.test.ravenwood;
-import static android.os.Process.FIRST_APPLICATION_UID;
-import static android.os.UserHandle.SYSTEM;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.Instrumentation;
-import android.content.Context;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* @deprecated This class will be removed. Reach out to g/ravenwood if you need any features in it.
@@ -45,37 +37,10 @@
public @interface Config {
}
- private static final int NOBODY_UID = 9999;
-
- private static final AtomicInteger sNextPid = new AtomicInteger(100);
-
- int mCurrentUser = SYSTEM.getIdentifier();
-
- /**
- * Unless the test author requests differently, run as "nobody", and give each collection of
- * tests its own unique PID.
- */
- int mUid = FIRST_APPLICATION_UID;
- int mPid = sNextPid.getAndIncrement();
-
- String mTestPackageName;
- String mTargetPackageName;
-
- int mTargetSdkLevel;
-
- final RavenwoodSystemProperties mSystemProperties = new RavenwoodSystemProperties();
-
- final List<Class<?>> mServicesRequired = new ArrayList<>();
-
- volatile Context mInstContext;
- volatile Context mTargetContext;
- volatile Instrumentation mInstrumentation;
-
/**
* Stores internal states / methods associated with this config that's only needed in
* junit-impl.
*/
- final RavenwoodConfigState mState = new RavenwoodConfigState(this);
private RavenwoodConfig() {
}
@@ -159,34 +124,11 @@
return this;
}
- Builder setSystemPropertyImmutableReal(@NonNull String key,
- @Nullable Object value) {
- mConfig.mSystemProperties.setValue(key, value);
- mConfig.mSystemProperties.setAccessReadOnly(key);
- return this;
- }
-
- Builder setSystemPropertyMutableReal(@NonNull String key,
- @Nullable Object value) {
- mConfig.mSystemProperties.setValue(key, value);
- mConfig.mSystemProperties.setAccessReadWrite(key);
- return this;
- }
-
/**
- * Configure the set of system services that are required for this test to operate.
- *
- * For example, passing {@code android.hardware.SerialManager.class} as an argument will
- * ensure that the underlying service is created, initialized, and ready to use for the
- * duration of the test. The {@code SerialManager} instance can be obtained via
- * {@code RavenwoodRule.getContext()} and {@code Context.getSystemService()}, and
- * {@code SerialManagerInternal} can be obtained via {@code LocalServices.getService()}.
+ * @deprecated no longer used. All supported services are available.
*/
+ @Deprecated
public Builder setServicesRequired(@NonNull Class<?>... services) {
- mConfig.mServicesRequired.clear();
- for (Class<?> service : services) {
- mConfig.mServicesRequired.add(service);
- }
return this;
}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index 5681a90..e49d3d9 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -92,19 +92,11 @@
}
}
- private final RavenwoodConfig mConfiguration;
-
- public RavenwoodRule() {
- mConfiguration = new RavenwoodConfig.Builder().build();
- }
-
- private RavenwoodRule(RavenwoodConfig config) {
- mConfiguration = config;
- }
+ final RavenwoodTestProperties mProperties = new RavenwoodTestProperties();
public static class Builder {
- private final RavenwoodConfig.Builder mBuilder =
- new RavenwoodConfig.Builder();
+
+ private final RavenwoodRule mRule = new RavenwoodRule();
public Builder() {
}
@@ -152,7 +144,8 @@
* Has no effect on non-Ravenwood environments.
*/
public Builder setSystemPropertyImmutable(@NonNull String key, @Nullable Object value) {
- mBuilder.setSystemPropertyImmutableReal(key, value);
+ mRule.mProperties.setValue(key, value);
+ mRule.mProperties.setAccessReadOnly(key);
return this;
}
@@ -167,26 +160,21 @@
* Has no effect on non-Ravenwood environments.
*/
public Builder setSystemPropertyMutable(@NonNull String key, @Nullable Object value) {
- mBuilder.setSystemPropertyMutableReal(key, value);
+ mRule.mProperties.setValue(key, value);
+ mRule.mProperties.setAccessReadWrite(key);
return this;
}
/**
- * Configure the set of system services that are required for this test to operate.
- *
- * For example, passing {@code android.hardware.SerialManager.class} as an argument will
- * ensure that the underlying service is created, initialized, and ready to use for the
- * duration of the test. The {@code SerialManager} instance can be obtained via
- * {@code RavenwoodRule.getContext()} and {@code Context.getSystemService()}, and
- * {@code SerialManagerInternal} can be obtained via {@code LocalServices.getService()}.
+ * @deprecated no longer used. All supported services are available.
*/
+ @Deprecated
public Builder setServicesRequired(@NonNull Class<?>... services) {
- mBuilder.setServicesRequired(services);
return this;
}
public RavenwoodRule build() {
- return new RavenwoodRule(mBuilder.build());
+ return mRule;
}
}
@@ -227,7 +215,7 @@
@Override
public Statement apply(Statement base, Description description) {
- if (!RavenwoodConfig.isOnRavenwood()) {
+ if (!IS_ON_RAVENWOOD) {
return base;
}
return new Statement() {
@@ -296,8 +284,4 @@
public static RavenwoodPrivate private$ravenwood() {
return sRavenwoodPrivate;
}
-
- RavenwoodConfig getConfiguration() {
- return mConfiguration;
- }
}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodTestProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodTestProperties.java
new file mode 100644
index 0000000..66a26b5
--- /dev/null
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodTestProperties.java
@@ -0,0 +1,59 @@
+/*
+ * 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 android.platform.test.ravenwood;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A class to store system properties defined by tests.
+ */
+public class RavenwoodTestProperties {
+ final Map<String, String> mValues = new HashMap<>();
+
+ /** Set of additional keys that should be considered readable */
+ final Set<String> mKeyReadable = new HashSet<>();
+
+ /** Set of additional keys that should be considered writable */
+ final Set<String> mKeyWritable = new HashSet<>();
+
+ public void setValue(String key, Object value) {
+ final String valueString = (value == null) ? null : String.valueOf(value);
+ if ((valueString == null) || valueString.isEmpty()) {
+ mValues.remove(key);
+ } else {
+ mValues.put(key, valueString);
+ }
+ }
+
+ public void setAccessNone(String key) {
+ mKeyReadable.remove(key);
+ mKeyWritable.remove(key);
+ }
+
+ public void setAccessReadOnly(String key) {
+ mKeyReadable.add(key);
+ mKeyWritable.remove(key);
+ }
+
+ public void setAccessReadWrite(String key) {
+ mKeyReadable.add(key);
+ mKeyWritable.add(key);
+ }
+}
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java
deleted file mode 100644
index 7d3d8b9..0000000
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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 android.platform.test.ravenwood;
-
-/** Stub class. The actual implementation is in junit-impl-src. */
-public class RavenwoodConfigState {
- public RavenwoodConfigState(RavenwoodConfig config) {
- }
-}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
index 7b940b4..9a78989 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
@@ -56,7 +56,11 @@
public static native boolean setSystemProperty(String key, String value);
- public static native void clearSystemProperties();
+ public static native boolean removeSystemProperty(String key);
+
+ public static void clearSystemProperties() {
+ removeSystemProperty(null);
+ }
public static native int gettid();
diff --git a/ravenwood/runtime-jni/jni_helper.h b/ravenwood/runtime-jni/jni_helper.h
index 561fb3b..25d7519 100644
--- a/ravenwood/runtime-jni/jni_helper.h
+++ b/ravenwood/runtime-jni/jni_helper.h
@@ -26,6 +26,7 @@
constexpr const char* kCommonUtils = "com/android/ravenwood/common/RavenwoodCommonUtils";
constexpr const char* kRuntimeEnvController =
"android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController";
+constexpr const char* kRunnerState = "android/platform/test/ravenwood/RavenwoodRunnerState";
constexpr const char* kRuntimeNative = "com/android/ravenwood/RavenwoodRuntimeNative";
// We have to explicitly decode the string to real UTF-8, because when using GetStringUTFChars
diff --git a/ravenwood/runtime-jni/ravenwood_sysprop.cpp b/ravenwood/runtime-jni/ravenwood_sysprop.cpp
index aafc426..a78aa8d 100644
--- a/ravenwood/runtime-jni/ravenwood_sysprop.cpp
+++ b/ravenwood/runtime-jni/ravenwood_sysprop.cpp
@@ -117,7 +117,7 @@
// ---- JNI ----
static JavaVM* gVM = nullptr;
-static jclass gEnvController = nullptr;
+static jclass gRunnerState = nullptr;
static jmethodID gCheckSystemPropertyAccess;
static void reloadNativeLibrary(JNIEnv* env, jclass, jstring javaPath) {
@@ -128,11 +128,11 @@
// Call back into Java code to check property access
static void check_system_property_access(const char* key, bool write) {
- if (gVM != nullptr && gEnvController != nullptr) {
+ if (gVM != nullptr && gRunnerState != nullptr) {
JNIEnv* env;
if (gVM->GetEnv((void**)&env, JNI_VERSION_1_4) >= 0) {
ALOGI("%s access to system property '%s'", write ? "Write" : "Read", key);
- env->CallStaticVoidMethod(gEnvController, gCheckSystemPropertyAccess,
+ env->CallStaticVoidMethod(gRunnerState, gCheckSystemPropertyAccess,
env->NewStringUTF(key), write ? JNI_TRUE : JNI_FALSE);
return;
}
@@ -155,16 +155,29 @@
return property_set(key.c_str(), value.c_str()) ? JNI_TRUE : JNI_FALSE;
}
-static void clearSystemProperties(JNIEnv*, jclass) {
+static jboolean removeSystemProperty(JNIEnv* env, jclass, jstring javaKey) {
std::lock_guard lock(g_properties_lock);
- g_properties.clear();
+
+ if (javaKey == nullptr) {
+ g_properties.clear();
+ return JNI_TRUE;
+ } else {
+ ScopedUtfChars key(env, javaKey);
+ auto it = g_properties.find(key);
+ if (it != g_properties.end()) {
+ g_properties.erase(it);
+ return JNI_TRUE;
+ } else {
+ return JNI_FALSE;
+ }
+ }
}
static const JNINativeMethod sMethods[] = {
{"reloadNativeLibrary", "(Ljava/lang/String;)V", (void*)reloadNativeLibrary},
{"getSystemProperty", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getSystemProperty},
{"setSystemProperty", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)setSystemProperty},
- {"clearSystemProperties", "()V", (void*)clearSystemProperties},
+ {"removeSystemProperty", "(Ljava/lang/String;)Z", (void*)removeSystemProperty},
};
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
@@ -174,9 +187,9 @@
gVM = vm;
// Fetch several references for future use
- gEnvController = FindGlobalClassOrDie(env, kRuntimeEnvController);
+ gRunnerState = FindGlobalClassOrDie(env, kRunnerState);
gCheckSystemPropertyAccess =
- GetStaticMethodIDOrDie(env, gEnvController, "checkSystemPropertyAccess",
+ GetStaticMethodIDOrDie(env, gRunnerState, "checkSystemPropertyAccess",
"(Ljava/lang/String;Z)V");
// Expose raw property methods as JNI methods
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodMultipleRuleTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodMultipleRuleTest.java
deleted file mode 100644
index c25d2b4..0000000
--- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/RavenwoodMultipleRuleTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * 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.ravenwoodtest.bivalenttest;
-
-import android.platform.test.ravenwood.RavenwoodConfig;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Assume;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.runner.RunWith;
-
-/**
- * Make sure having multiple RavenwoodRule's is detected.
- * (But only when running on ravenwod. Otherwise it'll be ignored.)
- */
-@RunWith(AndroidJUnit4.class)
-public class RavenwoodMultipleRuleTest {
-
- @Rule(order = Integer.MIN_VALUE)
- public final ExpectedException mExpectedException = ExpectedException.none();
-
- @Rule
- public final RavenwoodRule mRavenwood1 = new RavenwoodRule();
-
- @Rule
- public final RavenwoodRule mRavenwood2 = new RavenwoodRule();
-
- public RavenwoodMultipleRuleTest() {
- // We can't call it within the test method because the exception happens before
- // calling the method, so set it up here.
- if (RavenwoodConfig.isOnRavenwood()) {
- mExpectedException.expectMessage("Multiple nesting RavenwoodRule");
- }
- }
-
- @Test
- public void testMultipleRulesNotAllowed() {
- Assume.assumeTrue(RavenwoodConfig.isOnRavenwood());
- }
-}
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRuleValidationTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRuleValidationTest.java
new file mode 100644
index 0000000..f9e73db
--- /dev/null
+++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRuleValidationTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.ravenwoodtest.runnercallbacktests;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.SystemProperties;
+import android.platform.test.annotations.NoRavenizer;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for RavenwoodRule.
+ */
+@NoRavenizer // This class shouldn't be executed with RavenwoodAwareTestRunner.
+public class RavenwoodRuleValidationTest extends RavenwoodRunnerTestBase {
+
+ public static class RuleInBaseClass {
+ static String PROPERTY_KEY = "debug.ravenwood.prop.in.base";
+ static String PROPERTY_VAL = "ravenwood";
+ @Rule
+ public final RavenwoodRule mRavenwood1 = new RavenwoodRule.Builder()
+ .setSystemPropertyImmutable(PROPERTY_KEY, PROPERTY_VAL).build();
+ }
+
+ /**
+ * Make sure that RavenwoodRule in a base class takes effect.
+ */
+ @RunWith(AndroidJUnit4.class)
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleInBaseClassSuccessTest
+ testStarted: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleInBaseClassSuccessTest)
+ testFinished: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleInBaseClassSuccessTest)
+ testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleInBaseClassSuccessTest
+ testSuiteFinished: classes
+ testRunFinished: 1,0,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class RuleInBaseClassSuccessTest extends RuleInBaseClass {
+
+ @Test
+ public void testRuleInBaseClass() {
+ assertThat(SystemProperties.get(PROPERTY_KEY)).isEqualTo(PROPERTY_VAL);
+ }
+ }
+
+ /**
+ * Same as {@link RuleInBaseClass}, but the type of the rule field is not {@link RavenwoodRule}.
+ */
+ public abstract static class RuleWithDifferentTypeInBaseClass {
+ static String PROPERTY_KEY = "debug.ravenwood.prop.in.base.different.type";
+ static String PROPERTY_VAL = "ravenwood";
+ @Rule
+ public final TestRule mRavenwood1 = new RavenwoodRule.Builder()
+ .setSystemPropertyImmutable(PROPERTY_KEY, PROPERTY_VAL).build();
+ }
+
+ /**
+ * Make sure that RavenwoodRule in a base class takes effect, even if the field type is not
+ */
+ @RunWith(AndroidJUnit4.class)
+ // CHECKSTYLE:OFF
+ @Expected("""
+ testRunStarted: classes
+ testSuiteStarted: classes
+ testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest
+ testStarted: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest)
+ testFinished: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest)
+ testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRuleValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest
+ testSuiteFinished: classes
+ testRunFinished: 1,0,0,0
+ """)
+ // CHECKSTYLE:ON
+ public static class RuleWithDifferentTypeInBaseClassSuccessTest extends RuleWithDifferentTypeInBaseClass {
+
+ @Test
+ public void testRuleInBaseClass() {
+ assertThat(SystemProperties.get(PROPERTY_KEY)).isEqualTo(PROPERTY_VAL);
+ }
+ }
+}
diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java
deleted file mode 100644
index f94b98b..0000000
--- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java
+++ /dev/null
@@ -1,484 +0,0 @@
-/*
- * 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.ravenwoodtest.runnercallbacktests;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.platform.test.annotations.NoRavenizer;
-import android.platform.test.ravenwood.RavenwoodConfig;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runner.RunWith;
-
-
-/**
- * Test for @Config field extraction and validation.
- *
- * TODO(b/377765941) Most of the tests here will be obsolete and deleted with b/377765941, but
- * some of the tests may need to be re-implemented one way or another. (e.g. the package name
- * test.) Until that happens, we'll keep all tests here but add an {@code @Ignore} instead.
- */
-@NoRavenizer // This class shouldn't be executed with RavenwoodAwareTestRunner.
-public class RavenwoodRunnerConfigValidationTest extends RavenwoodRunnerTestBase {
- public abstract static class ConfigInBaseClass {
- static String PACKAGE_NAME = "com.ConfigInBaseClass";
-
- @RavenwoodConfig.Config
- public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder()
- .setPackageName(PACKAGE_NAME).build();
- }
-
- /**
- * Make sure a config in the base class is detected.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest
- testStarted: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest)
- testFinished: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest)
- testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigInBaseClassTest
- testSuiteFinished: classes
- testRunFinished: 1,0,0,0
- """)
- // CHECKSTYLE:ON
- @Ignore // Package name is no longer set via config.
- public static class ConfigInBaseClassTest extends ConfigInBaseClass {
- @Test
- public void test() {
- assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName())
- .isEqualTo(PACKAGE_NAME);
- }
- }
-
- /**
- * Make sure a config in the base class is detected.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest
- testStarted: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest)
- testFinished: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest)
- testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigOverridingTest
- testSuiteFinished: classes
- testRunFinished: 1,0,0,0
- """)
- // CHECKSTYLE:ON
- @Ignore // Package name is no longer set via config.
- public static class ConfigOverridingTest extends ConfigInBaseClass {
- static String PACKAGE_NAME_OVERRIDE = "com.ConfigOverridingTest";
-
- @RavenwoodConfig.Config
- public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder()
- .setPackageName(PACKAGE_NAME_OVERRIDE).build();
-
- @Test
- public void test() {
- assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName())
- .isEqualTo(PACKAGE_NAME_OVERRIDE);
- }
- }
-
- /**
- * Test to make sure that if a test has a config error, the failure would be reported from
- * each test method.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
- testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest.sConfig expected to be public static
- testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest)
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class ErrorMustBeReportedFromEachTest {
- @RavenwoodConfig.Config
- private static RavenwoodConfig sConfig = // Invalid because it's private.
- new RavenwoodConfig.Builder().build();
-
- @Test
- public void testMethod1() {
- }
-
- @Test
- public void testMethod2() {
- }
-
- @Test
- public void testMethod3() {
- }
- }
-
- /**
- * Invalid because there are two @Config's.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest)
- testFailure: Class com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest.DuplicateConfigTest has multiple fields with @RavenwoodConfig.Config
- testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest)
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class DuplicateConfigTest {
-
- @RavenwoodConfig.Config
- public static RavenwoodConfig sConfig1 =
- new RavenwoodConfig.Builder().build();
-
- @RavenwoodConfig.Config
- public static RavenwoodConfig sConfig2 =
- new RavenwoodConfig.Builder().build();
-
- @Test
- public void testConfig() {
- }
- }
-
- /**
- * @Config's must be static.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest)
- testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest.sConfig expected to be public static
- testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest)
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class NonStaticConfigTest {
-
- @RavenwoodConfig.Config
- public RavenwoodConfig sConfig =
- new RavenwoodConfig.Builder().build();
-
- @Test
- public void testConfig() {
- }
- }
-
- /**
- * @Config's must be public.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest)
- testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest.sConfig expected to be public static
- testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest)
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class NonPublicConfigTest {
-
- @RavenwoodConfig.Config
- RavenwoodConfig sConfig =
- new RavenwoodConfig.Builder().build();
-
- @Test
- public void testConfig() {
- }
- }
-
- /**
- * @Config's must be of type RavenwoodConfig.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest)
- testFailure: Field com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest.WrongTypeConfigTest.sConfig has @RavenwoodConfig.Config but type is not RavenwoodConfig
- testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest)
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class WrongTypeConfigTest {
-
- @RavenwoodConfig.Config
- public static Object sConfig =
- new RavenwoodConfig.Builder().build();
-
- @Test
- public void testConfig() {
- }
-
- }
-
- /**
- * @Rule must be of type RavenwoodRule.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeRuleTest
- testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeRuleTest)
- testFailure: If you have a RavenwoodRule in your test, make sure the field type is RavenwoodRule so Ravenwood can detect it.
- testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeRuleTest)
- testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeRuleTest
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class WrongTypeRuleTest {
-
- @Rule
- public TestRule mRule = new RavenwoodRule.Builder().build();
-
- @Test
- public void testConfig() {
- }
-
- }
-
- /**
- * Config can't be used with a (instance) Rule.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest)
- testFailure: RavenwoodConfig and RavenwoodRule cannot be used in the same class. Suggest migrating to RavenwoodConfig.
- testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest)
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class WithInstanceRuleTest {
-
- @RavenwoodConfig.Config
- public static RavenwoodConfig sConfig =
- new RavenwoodConfig.Builder().build();
-
- @Rule
- public RavenwoodRule mRule = new RavenwoodRule.Builder().build();
-
- @Test
- public void testConfig() {
- }
- }
-
- /**
- * Config can't be used with a (static) Rule.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest)
- testFailure: Failed to instantiate class androidx.test.ext.junit.runners.AndroidJUnit4
- testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest)
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class WithStaticRuleTest {
-
- @RavenwoodConfig.Config
- public static RavenwoodConfig sConfig =
- new RavenwoodConfig.Builder().build();
-
- @Rule
- public static RavenwoodRule sRule = new RavenwoodRule.Builder().build();
-
- @Test
- public void testConfig() {
- }
- }
-
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest
- testStarted: testMultipleRulesNotAllowed(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest)
- testFailure: Multiple nesting RavenwoodRule's are detected in the same class, which is not supported.
- testFinished: testMultipleRulesNotAllowed(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest)
- testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateRulesTest
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class DuplicateRulesTest {
-
- @Rule
- public final RavenwoodRule mRavenwood1 = new RavenwoodRule();
-
- @Rule
- public final RavenwoodRule mRavenwood2 = new RavenwoodRule();
-
- @Test
- public void testMultipleRulesNotAllowed() {
- }
- }
-
- public static class RuleInBaseClass {
- static String PACKAGE_NAME = "com.RuleInBaseClass";
- @Rule
- public final RavenwoodRule mRavenwood1 = new RavenwoodRule.Builder()
- .setPackageName(PACKAGE_NAME).build();
- }
-
- /**
- * Make sure that RavenwoodRule in a base class takes effect.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest
- testStarted: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest)
- testFinished: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest)
- testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleInBaseClassSuccessTest
- testSuiteFinished: classes
- testRunFinished: 1,0,0,0
- """)
- // CHECKSTYLE:ON
- @Ignore // Package name is no longer set via config.
- public static class RuleInBaseClassSuccessTest extends RuleInBaseClass {
-
- @Test
- public void testRuleInBaseClass() {
- assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName())
- .isEqualTo(PACKAGE_NAME);
- }
- }
-
- /**
- * Make sure that having a config and a rule in a base class should fail.
- * RavenwoodRule.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest)
- testFailure: RavenwoodConfig and RavenwoodRule cannot be used in the same class. Suggest migrating to RavenwoodConfig.
- testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest)
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class ConfigWithRuleInBaseClassTest extends RuleInBaseClass {
- @RavenwoodConfig.Config
- public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder().build();
-
- @Test
- public void test() {
- }
- }
-
- /**
- * Same as {@link RuleInBaseClass}, but the type of the rule field is not {@link RavenwoodRule}.
- */
- public abstract static class RuleWithDifferentTypeInBaseClass {
- static String PACKAGE_NAME = "com.RuleWithDifferentTypeInBaseClass";
- @Rule
- public final TestRule mRavenwood1 = new RavenwoodRule.Builder()
- .setPackageName(PACKAGE_NAME).build();
- }
-
- /**
- * Make sure that RavenwoodRule in a base class takes effect, even if the field type is not
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest
- testStarted: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest)
- testFailure: If you have a RavenwoodRule in your test, make sure the field type is RavenwoodRule so Ravenwood can detect it.
- testFinished: testRuleInBaseClass(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest)
- testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$RuleWithDifferentTypeInBaseClassSuccessTest
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- @Ignore // Package name is no longer set via config.
- public static class RuleWithDifferentTypeInBaseClassSuccessTest extends RuleWithDifferentTypeInBaseClass {
-
- @Test
- public void testRuleInBaseClass() {
- assertThat(InstrumentationRegistry.getInstrumentation().getContext().getPackageName())
- .isEqualTo(PACKAGE_NAME);
- }
- }
-
- /**
- * Make sure that having a config and a rule in a base class should fail, even if the field type is not
- * RavenwoodRule.
- */
- @RunWith(AndroidJUnit4.class)
- // CHECKSTYLE:OFF
- @Expected("""
- testRunStarted: classes
- testSuiteStarted: classes
- testSuiteStarted: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest
- testStarted: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest)
- testFailure: If you have a RavenwoodRule in your test, make sure the field type is RavenwoodRule so Ravenwood can detect it.
- testFinished: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest)
- testSuiteFinished: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleWithDifferentTypeInBaseClassTest
- testSuiteFinished: classes
- testRunFinished: 1,1,0,0
- """)
- // CHECKSTYLE:ON
- public static class ConfigWithRuleWithDifferentTypeInBaseClassTest extends RuleWithDifferentTypeInBaseClass {
- @RavenwoodConfig.Config
- public static RavenwoodConfig sConfig = new RavenwoodConfig.Builder().build();
-
- @Test
- public void test() {
- }
- }
-}
diff --git a/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java
index 8e04b69..271c27f 100644
--- a/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java
+++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java
@@ -15,7 +15,6 @@
*/
package com.android.ravenwoodtest.runtimetest;
-import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
import static android.os.Process.FIRST_APPLICATION_UID;
import static org.junit.Assert.assertEquals;
@@ -23,7 +22,6 @@
import android.os.Binder;
import android.os.Build;
import android.os.Process;
-import android.platform.test.ravenwood.RavenwoodConfig;
import android.system.Os;
import com.android.ravenwood.RavenwoodRuntimeState;
@@ -34,13 +32,6 @@
public class IdentityTest {
- @RavenwoodConfig.Config
- public static final RavenwoodConfig sConfig =
- new RavenwoodConfig.Builder()
- .setTargetSdkLevel(UPSIDE_DOWN_CAKE)
- .setProcessApp()
- .build();
-
@Test
public void testUid() {
assertEquals(FIRST_APPLICATION_UID, RavenwoodRuntimeState.sUid);
@@ -60,7 +51,7 @@
@Test
public void testTargetSdkLevel() {
assertEquals(Build.VERSION_CODES.CUR_DEVELOPMENT, RavenwoodRuntimeState.CUR_DEVELOPMENT);
- assertEquals(UPSIDE_DOWN_CAKE, RavenwoodRuntimeState.sTargetSdkLevel);
- assertEquals(UPSIDE_DOWN_CAKE, VMRuntime.getRuntime().getTargetSdkVersion());
+ assertEquals(RavenwoodRuntimeState.sTargetSdkLevel,
+ VMRuntime.getRuntime().getTargetSdkVersion());
}
}
diff --git a/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/SystemPropertyTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/SystemPropertyTest.java
new file mode 100644
index 0000000..70bf204
--- /dev/null
+++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/SystemPropertyTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.ravenwoodtest.runtimetest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.SystemProperties;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runners.model.Statement;
+
+public class SystemPropertyTest {
+
+ private static final String PROP_KEY_1 = "debug.ravenwood.prop1";
+ private static final String PROP_VAL_1 = "ravenwood.1";
+ private static final String PROP_KEY_2 = "debug.ravenwood.prop2";
+ private static final String PROP_VAL_2 = "ravenwood.2";
+ private static final String PROP_KEY_3 = "debug.ravenwood.prop3";
+ private static final String PROP_VAL_3 = "ravenwood.3";
+ private static final String PROP_VAL_4 = "ravenwood.4";
+
+ @ClassRule(order = 0)
+ public static TestRule mCheckClassRule = (base, description) -> new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ assertTrue(SystemProperties.get(PROP_KEY_1).isEmpty());
+ assertTrue(SystemProperties.get(PROP_KEY_3).isEmpty());
+ try {
+ base.evaluate();
+ } finally {
+ assertTrue(SystemProperties.get(PROP_KEY_1).isEmpty());
+ assertTrue(SystemProperties.get(PROP_KEY_3).isEmpty());
+ }
+ }
+ };
+
+ @ClassRule(order = 1)
+ public static RavenwoodRule mClassRule = new RavenwoodRule.Builder()
+ .setSystemPropertyImmutable(PROP_KEY_1, PROP_VAL_1)
+ .setSystemPropertyImmutable(PROP_KEY_3, PROP_VAL_4)
+ .build();
+
+ @Rule(order = 0)
+ public TestRule mCheckRule = (base, description) -> new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ assertTrue(SystemProperties.get(PROP_KEY_2).isEmpty());
+ assertEquals(SystemProperties.get(PROP_KEY_3), PROP_VAL_4);
+ try {
+ base.evaluate();
+ } finally {
+ assertTrue(SystemProperties.get(PROP_KEY_2).isEmpty());
+ assertEquals(SystemProperties.get(PROP_KEY_3), PROP_VAL_4);
+ }
+ }
+ };
+
+ @Rule(order = 1)
+ public RavenwoodRule mRule = new RavenwoodRule.Builder()
+ .setSystemPropertyImmutable(PROP_KEY_2, PROP_VAL_2)
+ .setSystemPropertyImmutable(PROP_KEY_3, PROP_VAL_3)
+ .build();
+
+ @Test
+ public void testRavenwoodRuleSetProperty() {
+ assertEquals(SystemProperties.get(PROP_KEY_1), PROP_VAL_1);
+ assertEquals(SystemProperties.get(PROP_KEY_2), PROP_VAL_2);
+ assertEquals(SystemProperties.get(PROP_KEY_3), PROP_VAL_3);
+ }
+}
diff --git a/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
index 4aae1e1..e83a247 100644
--- a/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
+++ b/ravenwood/tests/services-test/test/com/android/ravenwoodtest/servicestest/RavenwoodServicesTest.java
@@ -24,8 +24,6 @@
import android.content.Context;
import android.hardware.SerialManager;
import android.hardware.SerialManagerInternal;
-import android.platform.test.ravenwood.RavenwoodConfig;
-import android.platform.test.ravenwood.RavenwoodConfig.Config;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -42,12 +40,6 @@
public class RavenwoodServicesTest {
private static final String TEST_VIRTUAL_PORT = "virtual:example";
- @Config
- public static final RavenwoodConfig sRavenwood = new RavenwoodConfig.Builder()
- .setProcessSystem()
- .setServicesRequired(SerialManager.class)
- .build();
-
private Context mContext;
@Before
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 02e2c39..9201f696 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -1353,6 +1353,12 @@
heapFile = "/data/local/tmp/heapdump-" + logNameTimeString + ".prof";
}
+ String argAfterHeapFile = getNextArg();
+ if (argAfterHeapFile != null) {
+ err.println("Error: Arguments cannot be placed after the heap file");
+ return -1;
+ }
+
// Writes an error message to stderr on failure
ParcelFileDescriptor fd = openFileForSystem(heapFile, "w");
if (fd == null) {
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 6f99673..d731912 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -64,6 +64,9 @@
# Activity Security
per-file ActivityManager* = file:/ACTIVITY_SECURITY_OWNERS
+# Aconfig Flags
+per-file flags.aconfig = [email protected], [email protected], [email protected]
+
# Londoners
[email protected] #{LAST_RESORT_SUGGESTION}
[email protected] #{LAST_RESORT_SUGGESTION}
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 8c5152f..6f8dc10 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -100,6 +100,7 @@
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.SystemService.TargetUser;
+import com.android.server.utils.LazyJniRegistrar;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.CompatScaleProvider;
@@ -158,6 +159,10 @@
private static final String GAME_MODE_INTERVENTION_LIST_FILE_NAME =
"game_mode_intervention.list";
+ static {
+ LazyJniRegistrar.registerGameManagerService();
+ }
+
private final Context mContext;
private final Object mLock = new Object();
private final Object mDeviceConfigLock = new Object();
diff --git a/services/core/java/com/android/server/utils/LazyJniRegistrar.java b/services/core/java/com/android/server/utils/LazyJniRegistrar.java
index ac4a92e..6d29e9e 100644
--- a/services/core/java/com/android/server/utils/LazyJniRegistrar.java
+++ b/services/core/java/com/android/server/utils/LazyJniRegistrar.java
@@ -42,6 +42,9 @@
/** Registers native methods for ConsumerIrService. */
public static native void registerConsumerIrService();
+ /** Registers native methods for GameManagerService. */
+ public static native void registerGameManagerService();
+
/** Registers native methods for VrManagerService. */
public static native void registerVrManagerService();
}
diff --git a/services/core/java/com/android/server/utils/WatchableImpl.java b/services/core/java/com/android/server/utils/WatchableImpl.java
index 8a04ccf..fec4351 100644
--- a/services/core/java/com/android/server/utils/WatchableImpl.java
+++ b/services/core/java/com/android/server/utils/WatchableImpl.java
@@ -33,6 +33,7 @@
/**
* The list of observers.
*/
+ @GuardedBy("mObservers")
protected final ArrayList<Watcher> mObservers = new ArrayList<>();
/**
@@ -83,7 +84,9 @@
* @return The number of registered observers.
*/
public int registeredObserverCount() {
- return mObservers.size();
+ synchronized (mObservers) {
+ return mObservers.size();
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index 2401f90..c7667b4 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -19,6 +19,7 @@
[email protected]
[email protected]
[email protected]
[email protected]
# Files related to background activity launches
per-file Background*Start* = set noparent
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index c4bdcf4..9ae4611 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -38,7 +38,6 @@
"com_android_server_adb_AdbDebuggingManager.cpp",
"com_android_server_am_BatteryStatsService.cpp",
"com_android_server_biometrics_SurfaceToNativeHandleConverter.cpp",
- "com_android_server_ConsumerIrService.cpp",
"com_android_server_companion_virtual_InputController.cpp",
"com_android_server_companion_virtual_VirtualDeviceImpl.cpp",
"com_android_server_devicepolicy_CryptoTestHelper.cpp",
@@ -63,7 +62,6 @@
"com_android_server_SystemServer.cpp",
"com_android_server_tv_TvUinputBridge.cpp",
"com_android_server_tv_TvInputHal.cpp",
- "com_android_server_vr_VrManagerService.cpp",
"com_android_server_UsbAlsaJackDetector.cpp",
"com_android_server_UsbAlsaMidiDevice.cpp",
"com_android_server_UsbDeviceManager.cpp",
@@ -75,14 +73,13 @@
"com_android_server_am_LowMemDetector.cpp",
"com_android_server_pm_PackageManagerShellCommandDataLoader.cpp",
"com_android_server_sensor_SensorService.cpp",
- "com_android_server_utils_LazyJniRegistrar.cpp",
"com_android_server_wm_TaskFpsCallbackController.cpp",
"onload.cpp",
":lib_cachedAppOptimizer_native",
":lib_freezer_native",
- ":lib_gameManagerService_native",
":lib_oomConnection_native",
":lib_anrTimer_native",
+ ":lib_lazilyRegisteredServices_native",
],
include_dirs: [
@@ -248,13 +245,6 @@
}
filegroup {
- name: "lib_gameManagerService_native",
- srcs: [
- "com_android_server_app_GameManagerService.cpp",
- ],
-}
-
-filegroup {
name: "lib_oomConnection_native",
srcs: ["com_android_server_am_OomConnection.cpp"],
}
@@ -265,3 +255,13 @@
"com_android_server_utils_AnrTimer.cpp",
],
}
+
+filegroup {
+ name: "lib_lazilyRegisteredServices_native",
+ srcs: [
+ "com_android_server_ConsumerIrService.cpp",
+ "com_android_server_app_GameManagerService.cpp",
+ "com_android_server_utils_LazyJniRegistrar.cpp",
+ "com_android_server_vr_VrManagerService.cpp",
+ ],
+}
diff --git a/services/core/jni/com_android_server_utils_LazyJniRegistrar.cpp b/services/core/jni/com_android_server_utils_LazyJniRegistrar.cpp
index ad7781e..0c0f8b02 100644
--- a/services/core/jni/com_android_server_utils_LazyJniRegistrar.cpp
+++ b/services/core/jni/com_android_server_utils_LazyJniRegistrar.cpp
@@ -22,6 +22,7 @@
// Forward declared per-class registration methods.
int register_android_server_ConsumerIrService(JNIEnv* env);
+int register_android_server_app_GameManagerService(JNIEnv* env);
int register_android_server_vr_VrManagerService(JNIEnv* env);
namespace {
@@ -33,12 +34,17 @@
register_android_server_ConsumerIrService(env);
}
+void registerGameManagerService(JNIEnv* env, jclass) {
+ register_android_server_app_GameManagerService(env);
+}
+
void registerVrManagerService(JNIEnv* env, jclass) {
register_android_server_vr_VrManagerService(env);
}
static const JNINativeMethod sJniRegistrarMethods[] = {
{"registerConsumerIrService", "()V", (void*)registerConsumerIrService},
+ {"registerGameManagerService", "()V", (void*)registerGameManagerService},
{"registerVrManagerService", "()V", (void*)registerVrManagerService},
};
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index c170ae9..df37ec3 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -65,7 +65,6 @@
int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env);
int register_android_server_companion_virtual_InputController(JNIEnv* env);
int register_android_server_companion_virtual_VirtualDeviceImpl(JNIEnv* env);
-int register_android_server_app_GameManagerService(JNIEnv* env);
int register_com_android_server_wm_TaskFpsCallbackController(JNIEnv* env);
int register_com_android_server_display_DisplayControl(JNIEnv* env);
int register_com_android_server_SystemClockTime(JNIEnv* env);
@@ -131,7 +130,6 @@
register_android_server_sensor_SensorService(vm, env);
register_android_server_companion_virtual_InputController(env);
register_android_server_companion_virtual_VirtualDeviceImpl(env);
- register_android_server_app_GameManagerService(env);
register_com_android_server_wm_TaskFpsCallbackController(env);
register_com_android_server_display_DisplayControl(env);
register_com_android_server_SystemClockTime(env);
diff --git a/services/tests/mockingservicestests/jni/Android.bp b/services/tests/mockingservicestests/jni/Android.bp
index 00543a8..94d4b95 100644
--- a/services/tests/mockingservicestests/jni/Android.bp
+++ b/services/tests/mockingservicestests/jni/Android.bp
@@ -22,8 +22,8 @@
srcs: [
":lib_cachedAppOptimizer_native",
":lib_freezer_native",
- ":lib_gameManagerService_native",
":lib_oomConnection_native",
+ ":lib_lazilyRegisteredServices_native",
"onload.cpp",
],
@@ -54,6 +54,8 @@
"[email protected]",
"[email protected]",
"[email protected]",
+ "[email protected]",
+ "[email protected]",
"[email protected]",
],
}
diff --git a/services/tests/mockingservicestests/jni/onload.cpp b/services/tests/mockingservicestests/jni/onload.cpp
index cb246d1..9b4c817 100644
--- a/services/tests/mockingservicestests/jni/onload.cpp
+++ b/services/tests/mockingservicestests/jni/onload.cpp
@@ -26,8 +26,8 @@
namespace android {
int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
int register_android_server_am_Freezer(JNIEnv* env);
-int register_android_server_app_GameManagerService(JNIEnv* env);
int register_android_server_am_OomConnection(JNIEnv* env);
+int register_android_server_utils_LazyJniRegistrar(JNIEnv* env);
};
using namespace android;
@@ -44,7 +44,7 @@
ALOG_ASSERT(env, "Could not retrieve the env!");
register_android_server_am_CachedAppOptimizer(env);
register_android_server_am_Freezer(env);
- register_android_server_app_GameManagerService(env);
register_android_server_am_OomConnection(env);
+ register_android_server_utils_LazyJniRegistrar(env);
return JNI_VERSION_1_4;
}
diff --git a/services/tests/servicestests/jni/Android.bp b/services/tests/servicestests/jni/Android.bp
index 0a31037..e738c19 100644
--- a/services/tests/servicestests/jni/Android.bp
+++ b/services/tests/servicestests/jni/Android.bp
@@ -22,9 +22,9 @@
srcs: [
":lib_cachedAppOptimizer_native",
":lib_freezer_native",
- ":lib_gameManagerService_native",
":lib_oomConnection_native",
":lib_anrTimer_native",
+ ":lib_lazilyRegisteredServices_native",
"onload.cpp",
],
@@ -55,6 +55,8 @@
"[email protected]",
"[email protected]",
"[email protected]",
+ "[email protected]",
+ "[email protected]",
"[email protected]",
],
}
diff --git a/services/tests/servicestests/jni/onload.cpp b/services/tests/servicestests/jni/onload.cpp
index 25487c5..ad979c6 100644
--- a/services/tests/servicestests/jni/onload.cpp
+++ b/services/tests/servicestests/jni/onload.cpp
@@ -25,9 +25,9 @@
namespace android {
int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
-int register_android_server_app_GameManagerService(JNIEnv* env);
int register_android_server_am_OomConnection(JNIEnv* env);
int register_android_server_utils_AnrTimer(JNIEnv *env);
+int register_android_server_utils_LazyJniRegistrar(JNIEnv* env);
};
using namespace android;
@@ -43,8 +43,8 @@
}
ALOG_ASSERT(env, "Could not retrieve the env!");
register_android_server_am_CachedAppOptimizer(env);
- register_android_server_app_GameManagerService(env);
register_android_server_am_OomConnection(env);
register_android_server_utils_AnrTimer(env);
+ register_android_server_utils_LazyJniRegistrar(env);
return JNI_VERSION_1_4;
}
diff --git a/tools/systemfeatures/Android.bp b/tools/systemfeatures/Android.bp
index e6d0a3d..2ebede3 100644
--- a/tools/systemfeatures/Android.bp
+++ b/tools/systemfeatures/Android.bp
@@ -13,6 +13,7 @@
srcs: [
"src/**/*.java",
"src/**/*.kt",
+ ":framework-metalava-annotations",
],
static_libs: [
"guava",
@@ -26,6 +27,12 @@
static_libs: ["systemfeatures-gen-lib"],
}
+java_plugin {
+ name: "systemfeatures-metadata-processor",
+ processor_class: "com.android.systemfeatures.SystemFeaturesMetadataProcessor",
+ static_libs: ["systemfeatures-gen-lib"],
+}
+
genrule {
name: "systemfeatures-gen-tests-srcs",
cmd: "$(location systemfeatures-gen-tool) com.android.systemfeatures.RwNoFeatures --readonly=false > $(location RwNoFeatures.java) && " +
@@ -61,6 +68,7 @@
"systemfeatures-gen-lib",
"truth",
],
+ plugins: ["systemfeatures-metadata-processor"],
}
// Rename the goldens as they may be copied into the source tree, and we don't
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt
new file mode 100644
index 0000000..100d869
--- /dev/null
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesMetadataProcessor.kt
@@ -0,0 +1,110 @@
+/*
+ * 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 android.annotation.SdkConstant
+import com.squareup.javapoet.FieldSpec
+import com.squareup.javapoet.JavaFile
+import com.squareup.javapoet.TypeSpec
+import java.io.IOException
+import javax.annotation.processing.AbstractProcessor
+import javax.annotation.processing.ProcessingEnvironment
+import javax.annotation.processing.RoundEnvironment
+import javax.lang.model.SourceVersion
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.TypeElement
+import javax.tools.Diagnostic
+
+/*
+ * Simple Java code generator for computing metadata for system features.
+ *
+ * <p>The output is a single class file, `com.android.internal.pm.SystemFeaturesMetadata`, with
+ * properties computed from feature constant definitions in the PackageManager class. This
+ * class is only produced if the processed environment includes PackageManager; all other
+ * invocations are ignored.
+ */
+class SystemFeaturesMetadataProcessor : AbstractProcessor() {
+
+ private lateinit var packageManagerType: TypeElement
+
+ override fun getSupportedSourceVersion(): SourceVersion = SourceVersion.latestSupported()
+
+ override fun getSupportedAnnotationTypes() = setOf(SDK_CONSTANT_ANNOTATION_NAME)
+
+ override fun init(processingEnv: ProcessingEnvironment) {
+ super.init(processingEnv)
+ packageManagerType =
+ processingEnv.elementUtils.getTypeElement("android.content.pm.PackageManager")!!
+ }
+
+ override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
+ if (roundEnv.processingOver()) {
+ return false
+ }
+
+ // We're only interested in feature constants defined in PackageManager.
+ var featureCount = 0
+ roundEnv.getElementsAnnotatedWith(SdkConstant::class.java).forEach {
+ if (
+ it.enclosingElement == packageManagerType &&
+ it.getAnnotation(SdkConstant::class.java).value ==
+ SdkConstant.SdkConstantType.FEATURE
+ ) {
+ featureCount++
+ }
+ }
+
+ if (featureCount == 0) {
+ // This is fine, and happens for any environment that doesn't include PackageManager.
+ return false
+ }
+
+ val systemFeatureMetadata =
+ TypeSpec.classBuilder("SystemFeaturesMetadata")
+ .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
+ .addJavadoc("@hide")
+ .addField(
+ FieldSpec.builder(Int::class.java, "SDK_FEATURE_COUNT")
+ .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
+ .addJavadoc(
+ "The number of `@SdkConstant` features defined in PackageManager."
+ )
+ .addJavadoc("@hide")
+ .initializer("\$L", featureCount)
+ .build()
+ )
+ .build()
+
+ try {
+ JavaFile.builder("com.android.internal.pm", systemFeatureMetadata)
+ .skipJavaLangImports(true)
+ .build()
+ .writeTo(processingEnv.filer)
+ } catch (e: IOException) {
+ processingEnv.messager.printMessage(
+ Diagnostic.Kind.ERROR,
+ "Failed to write file: ${e.message}",
+ )
+ }
+
+ return true
+ }
+
+ companion object {
+ private val SDK_CONSTANT_ANNOTATION_NAME = SdkConstant::class.qualifiedName
+ }
+}
diff --git a/tools/systemfeatures/tests/src/PackageManager.java b/tools/systemfeatures/tests/src/PackageManager.java
index db67048..839a937 100644
--- a/tools/systemfeatures/tests/src/PackageManager.java
+++ b/tools/systemfeatures/tests/src/PackageManager.java
@@ -16,14 +16,33 @@
package android.content.pm;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+
/** Stub for testing */
public class PackageManager {
+ @SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_AUTO = "automotive";
+
+ @SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_PC = "pc";
+
+ @SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_VULKAN = "vulkan";
+
+ @SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_WATCH = "watch";
+
+ @SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_WIFI = "wifi";
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String FEATURE_INTENT_CATEGORY = "intent_category_with_feature_name_prefix";
+
+ public static final String FEATURE_NOT_ANNOTATED = "not_annotated";
+
+ public static final String NOT_FEATURE = "not_feature";
+
/** @hide */
public boolean hasSystemFeature(String featureName, int version) {
return false;
diff --git a/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java b/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java
new file mode 100644
index 0000000..4ffb5b9
--- /dev/null
+++ b/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java
@@ -0,0 +1,36 @@
+/*
+ * 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 com.google.common.truth.Truth.assertThat;
+
+import com.android.internal.pm.SystemFeaturesMetadata;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class SystemFeaturesMetadataProcessorTest {
+
+ @Test
+ public void testSdkFeatureCount() {
+ // See the fake PackageManager definition in this directory.
+ // It defines 5 annotated features, and any/all other constants should be ignored.
+ assertThat(SystemFeaturesMetadata.SDK_FEATURE_COUNT).isEqualTo(5);
+ }
+}