diff options
187 files changed, 3634 insertions, 5741 deletions
diff --git a/ACTIVITY_SECURITY_OWNERS b/ACTIVITY_SECURITY_OWNERS new file mode 100644 index 000000000000..c39842e1b7a3 --- /dev/null +++ b/ACTIVITY_SECURITY_OWNERS @@ -0,0 +1,2 @@ +haok@google.com +wnan@google.com
\ No newline at end of file diff --git a/INTENT_OWNERS b/INTENT_OWNERS index 58b5f2a61be0..c828215ef225 100644 --- a/INTENT_OWNERS +++ b/INTENT_OWNERS @@ -1,3 +1,4 @@ include /PACKAGE_MANAGER_OWNERS include /services/core/java/com/android/server/wm/OWNERS include /services/core/java/com/android/server/am/OWNERS +include /ACTIVITY_SECURITY_OWNERS
\ No newline at end of file @@ -47,3 +47,4 @@ per-file MULTIUSER_OWNERS = file:/MULTIUSER_OWNERS per-file BROADCASTS_OWNERS = file:/BROADCASTS_OWNERS per-file ADPF_OWNERS = file:/ADPF_OWNERS per-file GAME_MANAGER_OWNERS = file:/GAME_MANAGER_OWNERS +per-file SDK_OWNERS = file:/SDK_OWNERS diff --git a/SDK_OWNERS b/SDK_OWNERS new file mode 100644 index 000000000000..c9ca47aa4703 --- /dev/null +++ b/SDK_OWNERS @@ -0,0 +1,6 @@ +amhk@google.com +kimalexander@google.com +lus@google.com +michaelwr@google.com +nanaasiedu@google.com +paulduffin@google.com diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java index 3577fcdf04d6..f20b1706129b 100644 --- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java +++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java @@ -194,7 +194,7 @@ public final class ClientSocketPerfTest { /** * Simple benchmark for the amount of time to send a given number of messages */ - // @Test Temporarily disabled + @Test @Parameters(method = "getParams") public void time(Config config) throws Exception { reset(); diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java index ac5710047db9..af3c405eab82 100644 --- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java +++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java @@ -198,7 +198,7 @@ public final class ServerSocketPerfTest { executor.awaitTermination(5, TimeUnit.SECONDS); } - // @Test Temporarily disabled + @Test @Parameters(method = "getParams") public void throughput(Config config) throws Exception { setup(config); diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java index 33f6899239c6..ecb9a738aa32 100644 --- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java +++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java @@ -444,8 +444,13 @@ public class AppStateTrackerImpl implements AppStateTracker { private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (action == null) { + return; + } + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); - switch (intent.getAction()) { + switch (action) { case Intent.ACTION_USER_REMOVED: if (userId > 0) { mHandler.doUserRemoved(userId); diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index a37779e681fb..3e650da2e66f 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -666,7 +666,12 @@ public class DeviceIdleController extends SystemService private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - switch (intent.getAction()) { + final String action = intent.getAction(); + if (action == null) { + return; + } + + switch (action) { case ConnectivityManager.CONNECTIVITY_ACTION: { updateConnectivityState(intent); } break; diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java index e3ac780abf09..7a21697331af 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java @@ -84,9 +84,13 @@ public final class BackgroundJobsController extends StateController { private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (action == null) { + return; + } + final String pkgName = getPackageName(intent); final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1); - final String action = intent.getAction(); if (pkgUid == -1) { Slog.e(TAG, "Didn't get package UID in intent (" + action + ")"); return; diff --git a/config/preloaded-classes-denylist b/config/preloaded-classes-denylist index a413bbd68f60..16f069394639 100644 --- a/config/preloaded-classes-denylist +++ b/config/preloaded-classes-denylist @@ -1,13 +1,16 @@ android.content.AsyncTaskLoader$LoadTask +android.media.MediaCodecInfo$CodecCapabilities$FeatureList android.net.ConnectivityThread$Singleton android.os.FileObserver android.os.NullVibrator +android.permission.PermissionManager +android.provider.MediaStore android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask +android.view.HdrRenderState android.widget.Magnifier +com.android.internal.jank.InteractionJankMonitor$InstanceHolder +com.android.internal.util.LatencyTracker$SLatencyTrackerHolder gov.nist.core.net.DefaultNetworkLayer -android.net.rtp.AudioGroup -android.net.rtp.AudioStream -android.net.rtp.RtpStream java.util.concurrent.ThreadLocalRandom java.util.ImmutableCollections -com.android.internal.jank.InteractionJankMonitor$InstanceHolder +sun.nio.fs.UnixChannelFactory diff --git a/core/api/current.txt b/core/api/current.txt index 2c4c1463ba62..6d9fe4494220 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -16232,6 +16232,7 @@ package android.graphics { field public static final int UNKNOWN = 0; // 0x0 field public static final int Y8 = 538982489; // 0x20203859 field public static final int YCBCR_P010 = 54; // 0x36 + field @FlaggedApi("android.media.codec.p210_format_support") public static final int YCBCR_P210 = 60; // 0x3c field public static final int YUV_420_888 = 35; // 0x23 field public static final int YUV_422_888 = 39; // 0x27 field public static final int YUV_444_888 = 40; // 0x28 @@ -18580,6 +18581,7 @@ package android.hardware { field public static final long USAGE_VIDEO_ENCODE = 65536L; // 0x10000L field public static final int YCBCR_420_888 = 35; // 0x23 field public static final int YCBCR_P010 = 54; // 0x36 + field @FlaggedApi("android.media.codec.p210_format_support") public static final int YCBCR_P210 = 60; // 0x3c } @FlaggedApi("android.hardware.flags.overlayproperties_class_api") public final class OverlayProperties implements android.os.Parcelable { @@ -22871,6 +22873,7 @@ package android.media { field public static final int COLOR_FormatYUV444Flexible = 2135181448; // 0x7f444888 field @Deprecated public static final int COLOR_FormatYUV444Interleaved = 29; // 0x1d field public static final int COLOR_FormatYUVP010 = 54; // 0x36 + field @FlaggedApi("android.media.codec.p210_format_support") public static final int COLOR_FormatYUVP210 = 60; // 0x3c field @Deprecated public static final int COLOR_QCOM_FormatYUV420SemiPlanar = 2141391872; // 0x7fa30c00 field @Deprecated public static final int COLOR_TI_FormatYUV420PackedSemiPlanar = 2130706688; // 0x7f000100 field public static final String FEATURE_AdaptivePlayback = "adaptive-playback"; @@ -22917,6 +22920,65 @@ package android.media { field public static final int AC4Profile11 = 514; // 0x202 field public static final int AC4Profile21 = 1026; // 0x402 field public static final int AC4Profile22 = 1028; // 0x404 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel11Band0 = 513; // 0x201 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel11Band1 = 514; // 0x202 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel11Band2 = 516; // 0x204 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel11Band3 = 520; // 0x208 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel1Band0 = 257; // 0x101 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel1Band1 = 258; // 0x102 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel1Band2 = 260; // 0x104 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel1Band3 = 264; // 0x108 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel21Band0 = 2049; // 0x801 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel21Band1 = 2050; // 0x802 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel21Band2 = 2052; // 0x804 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel21Band3 = 2056; // 0x808 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel2Band0 = 1025; // 0x401 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel2Band1 = 1026; // 0x402 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel2Band2 = 1028; // 0x404 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel2Band3 = 1032; // 0x408 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel31Band0 = 8193; // 0x2001 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel31Band1 = 8194; // 0x2002 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel31Band2 = 8196; // 0x2004 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel31Band3 = 8200; // 0x2008 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel3Band0 = 4097; // 0x1001 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel3Band1 = 4098; // 0x1002 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel3Band2 = 4100; // 0x1004 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel3Band3 = 4104; // 0x1008 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel41Band0 = 32769; // 0x8001 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel41Band1 = 32770; // 0x8002 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel41Band2 = 32772; // 0x8004 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel41Band3 = 32776; // 0x8008 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel4Band0 = 16385; // 0x4001 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel4Band1 = 16386; // 0x4002 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel4Band2 = 16388; // 0x4004 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel4Band3 = 16392; // 0x4008 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel51Band0 = 131073; // 0x20001 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel51Band1 = 131074; // 0x20002 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel51Band2 = 131076; // 0x20004 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel51Band3 = 131080; // 0x20008 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel5Band0 = 65537; // 0x10001 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel5Band1 = 65538; // 0x10002 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel5Band2 = 65540; // 0x10004 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel5Band3 = 65544; // 0x10008 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel61Band0 = 524289; // 0x80001 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel61Band1 = 524290; // 0x80002 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel61Band2 = 524292; // 0x80004 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel61Band3 = 524296; // 0x80008 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel6Band0 = 262145; // 0x40001 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel6Band1 = 262146; // 0x40002 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel6Band2 = 262148; // 0x40004 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel6Band3 = 262152; // 0x40008 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel71Band0 = 2097153; // 0x200001 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel71Band1 = 2097154; // 0x200002 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel71Band2 = 2097156; // 0x200004 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel71Band3 = 2097160; // 0x200008 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel7Band0 = 1048577; // 0x100001 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel7Band1 = 1048578; // 0x100002 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel7Band2 = 1048580; // 0x100004 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVLevel7Band3 = 1048584; // 0x100008 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVProfile422_10 = 1; // 0x1 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVProfile422_10HDR10 = 4096; // 0x1000 + field @FlaggedApi("android.media.codec.apv_support") public static final int APVProfile422_10HDR10Plus = 8192; // 0x2000 field public static final int AV1Level2 = 1; // 0x1 field public static final int AV1Level21 = 2; // 0x2 field public static final int AV1Level22 = 4; // 0x4 @@ -23767,6 +23829,7 @@ package android.media { field public static final String MIMETYPE_TEXT_CEA_708 = "text/cea-708"; field public static final String MIMETYPE_TEXT_SUBRIP = "application/x-subrip"; field public static final String MIMETYPE_TEXT_VTT = "text/vtt"; + field @FlaggedApi("android.media.codec.apv_support") public static final String MIMETYPE_VIDEO_APV = "video/apv"; field public static final String MIMETYPE_VIDEO_AV1 = "video/av01"; field public static final String MIMETYPE_VIDEO_AVC = "video/avc"; field public static final String MIMETYPE_VIDEO_DOLBY_VISION = "video/dolby-vision"; diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 9a294fdd6fb1..0b891f6e0a71 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -102,6 +102,7 @@ package android.content { method @NonNull public android.os.UserHandle getUser(); field public static final String PAC_PROXY_SERVICE = "pac_proxy"; field public static final String TEST_NETWORK_SERVICE = "test_network"; + field @FlaggedApi("android.os.mainline_vcn_platform_api") public static final String VCN_MANAGEMENT_SERVICE = "vcn_management"; field @FlaggedApi("android.webkit.update_service_ipc_wrapper") public static final String WEBVIEW_UPDATE_SERVICE = "webviewupdate"; } diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl index 69c3bd3976bc..0264f73f126d 100644..100755 --- a/core/java/android/app/IUiAutomationConnection.aidl +++ b/core/java/android/app/IUiAutomationConnection.aidl @@ -61,6 +61,8 @@ interface IUiAutomationConnection { oneway void shutdown(); void executeShellCommandWithStderr(String command, in ParcelFileDescriptor sink, in ParcelFileDescriptor source, in ParcelFileDescriptor stderrSink); + void executeShellCommandArrayWithStderr(in String[] command, in ParcelFileDescriptor sink, + in ParcelFileDescriptor source, in ParcelFileDescriptor stderrSink); List<String> getAdoptedShellPermissions(); void addOverridePermissionState(int uid, String permission, int result); void removeOverridePermissionState(int uid, String permission); diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index 27f9f54d9522..cb31bc73b9ba 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -250,6 +250,7 @@ import java.util.concurrent.atomic.AtomicLong; * @hide */ @TestApi +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class PropertyInvalidatedCache<Query, Result> { /** * This is a configuration class that customizes a cache instance. diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java index 3c4bd9eb0747..72db465dda53 100644..100755 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -553,7 +553,12 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { } catch (IOException exc) { throw new RuntimeException("Error running shell command '" + command + "'", exc); } + handleExecuteShellCommandProcess(process, sink, source, stderrSink); + } + private void handleExecuteShellCommandProcess(final java.lang.Process process, + final ParcelFileDescriptor sink, final ParcelFileDescriptor source, + final ParcelFileDescriptor stderrSink) { // Read from process and write to pipe final Thread readFromProcess; if (sink != null) { @@ -616,6 +621,26 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { } @Override + public void executeShellCommandArrayWithStderr(final String[] command, + final ParcelFileDescriptor sink, final ParcelFileDescriptor source, + final ParcelFileDescriptor stderrSink) throws RemoteException { + synchronized (mLock) { + throwIfCalledByNotTrustedUidLocked(); + throwIfShutdownLocked(); + throwIfNotConnectedLocked(); + } + final java.lang.Process process; + + try { + process = Runtime.getRuntime().exec(command); + } catch (IOException exc) { + throw new RuntimeException( + "Error running shell command '" + String.join(" ", command) + "'", exc); + } + handleExecuteShellCommandProcess(process, sink, source, stderrSink); + } + + @Override public void shutdown() { synchronized (mLock) { if (isConnectedLocked()) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 36bdf738e402..744f019a4988 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4855,6 +4855,8 @@ public abstract class Context { * @see android.net.vcn.VcnManager * @hide */ + @FlaggedApi(android.os.Flags.FLAG_MAINLINE_VCN_PLATFORM_API) + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static final String VCN_MANAGEMENT_SERVICE = "vcn_management"; /** diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index bca30b4fec5a..8de86d5b60a0 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -146,6 +146,7 @@ public abstract class PackageManager { * This exception is thrown when a given package, application, or component * name cannot be found. */ + @android.ravenwood.annotation.RavenwoodKeepWholeClass public static class NameNotFoundException extends AndroidException { public NameNotFoundException() { } diff --git a/core/java/android/database/BulkCursorNative.java b/core/java/android/database/BulkCursorNative.java index 8ea450c6ca44..7d6e7ad857d1 100644 --- a/core/java/android/database/BulkCursorNative.java +++ b/core/java/android/database/BulkCursorNative.java @@ -53,7 +53,7 @@ public abstract class BulkCursorNative extends Binder implements IBulkCursor return new BulkCursorProxy(obj); } - + @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { @@ -79,7 +79,7 @@ public abstract class BulkCursorNative extends Binder implements IBulkCursor reply.writeNoException(); return true; } - + case CLOSE_TRANSACTION: { data.enforceInterface(IBulkCursor.descriptor); close(); @@ -212,15 +212,22 @@ final class BulkCursorProxy implements IBulkCursor { Parcel reply = Parcel.obtain(); try { data.writeInterfaceToken(IBulkCursor.descriptor); - - mRemote.transact(CLOSE_TRANSACTION, data, reply, 0); - DatabaseUtils.readExceptionFromParcel(reply); + // If close() is being called from the finalizer thread, do not wait for a reply from + // the remote side. + final boolean fromFinalizer = + android.database.sqlite.Flags.onewayFinalizerCloseFixed() + && "FinalizerDaemon".equals(Thread.currentThread().getName()); + mRemote.transact(CLOSE_TRANSACTION, data, reply, + fromFinalizer ? IBinder.FLAG_ONEWAY: 0); + if (!fromFinalizer) { + DatabaseUtils.readExceptionFromParcel(reply); + } } finally { data.recycle(); reply.recycle(); } } - + public int requery(IContentObserver observer) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); @@ -282,4 +289,3 @@ final class BulkCursorProxy implements IBulkCursor { } } } - diff --git a/core/java/android/database/sqlite/flags.aconfig b/core/java/android/database/sqlite/flags.aconfig index 285f984faab7..c2cc9d18e457 100644 --- a/core/java/android/database/sqlite/flags.aconfig +++ b/core/java/android/database/sqlite/flags.aconfig @@ -2,6 +2,14 @@ package: "android.database.sqlite" container: "system" flag { + name: "oneway_finalizer_close_fixed" + namespace: "system_performance" + is_fixed_read_only: true + description: "Make BuildCursorNative.close oneway if in the the finalizer" + bug: "368221351" +} + +flag { name: "sqlite_apis_35" is_exported: true namespace: "system_performance" diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java index ce0f9f598897..0e73978127c9 100644 --- a/core/java/android/hardware/HardwareBuffer.java +++ b/core/java/android/hardware/HardwareBuffer.java @@ -66,6 +66,7 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { DS_FP32UI8, S_UI8, YCBCR_P010, + YCBCR_P210, R_8, R_16, RG_1616, @@ -111,6 +112,16 @@ public final class HardwareBuffer implements Parcelable, AutoCloseable { * little-endian value, with the lower 6 bits set to zero. */ public static final int YCBCR_P010 = 0x36; + /** + * <p>Android YUV P210 format.</p> + * + * P210 is a 4:2:2 YCbCr semiplanar format comprised of a WxH Y plane + * followed by a WxH CbCr plane. Each sample is represented by a 16-bit + * little-endian value, with the lower 6 bits set to zero. + */ + @FlaggedApi(android.media.codec.Flags.FLAG_P210_FORMAT_SUPPORT) + public static final int YCBCR_P210 = 0x3c; + /** Format: 8 bits red */ @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_REQUESTED_FORMATS_V) public static final int R_8 = 0x38; diff --git a/core/java/android/hardware/usb/OWNERS b/core/java/android/hardware/usb/OWNERS index a753f9634d0d..37604bc2eb65 100644 --- a/core/java/android/hardware/usb/OWNERS +++ b/core/java/android/hardware/usb/OWNERS @@ -1,7 +1,7 @@ # Bug component: 175220 -aprasath@google.com -kumarashishg@google.com -sarup@google.com anothermark@google.com +febinthattil@google.com +aprasath@google.com badhri@google.com +kumarashishg@google.com
\ No newline at end of file diff --git a/core/java/android/net/flags.aconfig b/core/java/android/net/flags.aconfig index 48eb9680e647..f7dc7906d50d 100644 --- a/core/java/android/net/flags.aconfig +++ b/core/java/android/net/flags.aconfig @@ -5,13 +5,6 @@ container: "system" # Flags used for module APIs must be in aconfig files under each modules flag { - name: "ipsec_transform_state" - namespace: "core_networking_ipsec" - description: "The flag controls the access for getIpSecTransformState and IpSecTransformState" - bug: "308011229" -} - -flag { name: "powered_off_finding_platform" namespace: "nearby" description: "Controls whether the Powered Off Finding feature is enabled" diff --git a/core/java/android/net/vcn/OWNERS b/core/java/android/net/vcn/OWNERS index 2441e772468c..937699a92968 100644 --- a/core/java/android/net/vcn/OWNERS +++ b/core/java/android/net/vcn/OWNERS @@ -1,7 +1,6 @@ set noparent -benedictwong@google.com -ckesting@google.com evitayan@google.com -junyin@google.com nharold@google.com +benedictwong@google.com #{LAST_RESORT_SUGGESTION} +yangji@google.com #{LAST_RESORT_SUGGESTION} diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig index 5b306243fc36..1adefe5a0b86 100644 --- a/core/java/android/net/vcn/flags.aconfig +++ b/core/java/android/net/vcn/flags.aconfig @@ -10,8 +10,26 @@ flag { } flag { + name: "mainline_vcn_module_api" + namespace: "vcn" + description: "Expose APIs from VCN for mainline migration" + is_exported: true + bug: "376339506" +} + +flag { name: "safe_mode_timeout_config" namespace: "vcn" description: "Feature flag for adjustable safe mode timeout" bug: "317406085" +} + +flag { + name: "fix_config_garbage_collection" + namespace: "vcn" + description: "Handle race condition in subscription change" + bug: "370862489" + metadata { + purpose: PURPOSE_BUGFIX + } }
\ No newline at end of file diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 30d2dec8b4c4..3c3a4fd68b15 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -36,8 +36,6 @@ import android.util.ArraySet; import android.util.Slog; import android.view.View; -import com.android.internal.ravenwood.RavenwoodEnvironment; - import dalvik.system.VMRuntime; import java.util.ArrayList; @@ -51,10 +49,6 @@ import java.util.stream.Collectors; */ @RavenwoodKeepWholeClass public class Build { - static { - // Set up the default system properties. - RavenwoodEnvironment.ensureRavenwoodInitialized(); - } private static final String TAG = "Build"; /** Value used for when a build property is unknown. */ diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java index bf44d65c4002..d7a308df4e6e 100644 --- a/core/java/android/os/IpcDataCache.java +++ b/core/java/android/os/IpcDataCache.java @@ -242,6 +242,7 @@ import java.util.concurrent.atomic.AtomicLong; */ @TestApi @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES) +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query, Result> { /** * {@inheritDoc} diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index e80efd2a9380..60eeb2b8b0d5 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -41,7 +41,6 @@ import android.content.ContentResolver; import android.net.Uri; import android.os.MessageQueue.OnFileDescriptorEventListener; import android.ravenwood.annotation.RavenwoodKeepWholeClass; -import android.ravenwood.annotation.RavenwoodReplace; import android.ravenwood.annotation.RavenwoodThrow; import android.system.ErrnoException; import android.system.Os; @@ -51,8 +50,6 @@ import android.util.CloseGuard; import android.util.Log; import android.util.Slog; -import com.android.internal.ravenwood.RavenwoodEnvironment; - import dalvik.system.VMRuntime; import libcore.io.IoUtils; @@ -1254,15 +1251,10 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { } } - @RavenwoodReplace private static boolean isAtLeastQ() { return (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q); } - private static boolean isAtLeastQ$ravenwood() { - return RavenwoodEnvironment.workaround().isTargetSdkAtLeastQ(); - } - private static int ifAtLeastQ(int value) { return isAtLeastQ() ? value : 0; } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index cd8788db4e70..851953af1fb9 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -29,6 +29,11 @@ import android.annotation.TestApi; import android.annotation.UptimeMillisLong; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build.VERSION_CODES; +import android.ravenwood.annotation.RavenwoodKeep; +import android.ravenwood.annotation.RavenwoodKeepPartialClass; +import android.ravenwood.annotation.RavenwoodRedirect; +import android.ravenwood.annotation.RavenwoodRedirectionClass; +import android.ravenwood.annotation.RavenwoodReplace; import android.sysprop.MemoryProperties; import android.system.ErrnoException; import android.system.Os; @@ -37,8 +42,6 @@ import android.system.StructPollfd; import android.util.Pair; import android.webkit.WebViewZygote; -import com.android.internal.os.SomeArgs; -import com.android.internal.util.Preconditions; import com.android.sdksandbox.flags.Flags; import dalvik.system.VMDebug; @@ -55,6 +58,8 @@ import java.util.concurrent.TimeoutException; /** * Tools for managing OS processes. */ +@RavenwoodKeepPartialClass +@RavenwoodRedirectionClass("Process_ravenwood") public class Process { private static final String LOG_TAG = "Process"; @@ -672,7 +677,6 @@ public class Process { */ public static final ZygoteProcess ZYGOTE_PROCESS = new ZygoteProcess(); - /** * The process name set via {@link #setArgV0(String)}. */ @@ -846,47 +850,20 @@ public class Process { /** * Returns true if the current process is a 64-bit runtime. */ - @android.ravenwood.annotation.RavenwoodKeep + @RavenwoodKeep public static final boolean is64Bit() { return VMRuntime.getRuntime().is64Bit(); } - private static volatile ThreadLocal<SomeArgs> sIdentity$ravenwood; - - /** @hide */ - @android.ravenwood.annotation.RavenwoodKeep - public static void init$ravenwood(final int uid, final int pid) { - sIdentity$ravenwood = ThreadLocal.withInitial(() -> { - final SomeArgs args = SomeArgs.obtain(); - args.argi1 = uid; - args.argi2 = pid; - args.argi3 = Long.hashCode(Thread.currentThread().getId()); - args.argi4 = THREAD_PRIORITY_DEFAULT; - args.arg1 = Boolean.TRUE; // backgroundOk - return args; - }); - } - - /** @hide */ - @android.ravenwood.annotation.RavenwoodKeep - public static void reset$ravenwood() { - sIdentity$ravenwood = null; - } - /** * Returns the identifier of this process, which can be used with * {@link #killProcess} and {@link #sendSignal}. */ - @android.ravenwood.annotation.RavenwoodReplace + @RavenwoodKeep public static final int myPid() { return Os.getpid(); } - /** @hide */ - public static final int myPid$ravenwood() { - return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi2; - } - /** * Returns the identifier of this process' parent. * @hide @@ -900,39 +877,29 @@ public class Process { * Returns the identifier of the calling thread, which be used with * {@link #setThreadPriority(int, int)}. */ - @android.ravenwood.annotation.RavenwoodReplace + @RavenwoodKeep public static final int myTid() { return Os.gettid(); } - /** @hide */ - public static final int myTid$ravenwood() { - return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi3; - } - /** * Returns the identifier of this process's uid. This is the kernel uid * that the process is running under, which is the identity of its * app-specific sandbox. It is different from {@link #myUserHandle} in that * a uid identifies a specific app sandbox in a specific user. */ - @android.ravenwood.annotation.RavenwoodReplace + @RavenwoodKeep public static final int myUid() { return Os.getuid(); } - /** @hide */ - public static final int myUid$ravenwood() { - return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi1; - } - /** * Returns this process's user handle. This is the * user the process is running under. It is distinct from * {@link #myUid()} in that a particular user will have multiple * distinct apps running under it each with their own uid. */ - @android.ravenwood.annotation.RavenwoodKeep + @RavenwoodKeep public static UserHandle myUserHandle() { return UserHandle.of(UserHandle.getUserId(myUid())); } @@ -941,7 +908,7 @@ public class Process { * Returns whether the given uid belongs to a system core component or not. * @hide */ - @android.ravenwood.annotation.RavenwoodKeep + @RavenwoodKeep public static boolean isCoreUid(int uid) { return UserHandle.isCore(uid); } @@ -952,7 +919,7 @@ public class Process { * @return Whether the uid corresponds to an application sandbox running in * a specific user. */ - @android.ravenwood.annotation.RavenwoodKeep + @RavenwoodKeep public static boolean isApplicationUid(int uid) { return UserHandle.isApp(uid); } @@ -960,7 +927,7 @@ public class Process { /** * Returns whether the current process is in an isolated sandbox. */ - @android.ravenwood.annotation.RavenwoodKeep + @RavenwoodKeep public static final boolean isIsolated() { return isIsolated(myUid()); } @@ -972,7 +939,7 @@ public class Process { @Deprecated @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU, publicAlternatives = "Use {@link #isIsolatedUid(int)} instead.") - @android.ravenwood.annotation.RavenwoodKeep + @RavenwoodKeep public static final boolean isIsolated(int uid) { return isIsolatedUid(uid); } @@ -980,7 +947,7 @@ public class Process { /** * Returns whether the process with the given {@code uid} is an isolated sandbox. */ - @android.ravenwood.annotation.RavenwoodKeep + @RavenwoodKeep public static final boolean isIsolatedUid(int uid) { uid = UserHandle.getAppId(uid); return (uid >= FIRST_ISOLATED_UID && uid <= LAST_ISOLATED_UID) @@ -992,7 +959,7 @@ public class Process { * @see android.app.sdksandbox.SdkSandboxManager */ @SuppressLint("UnflaggedApi") // promoting from @SystemApi. - @android.ravenwood.annotation.RavenwoodKeep + @RavenwoodKeep public static final boolean isSdkSandboxUid(int uid) { uid = UserHandle.getAppId(uid); return (uid >= FIRST_SDK_SANDBOX_UID && uid <= LAST_SDK_SANDBOX_UID); @@ -1008,7 +975,7 @@ public class Process { * @throws IllegalArgumentException if input is not an sdk sandbox uid */ @SuppressLint("UnflaggedApi") // promoting from @SystemApi. - @android.ravenwood.annotation.RavenwoodKeep + @RavenwoodKeep public static final int getAppUidForSdkSandboxUid(int uid) { if (!isSdkSandboxUid(uid)) { throw new IllegalArgumentException("Input UID is not an SDK sandbox UID"); @@ -1024,7 +991,7 @@ public class Process { */ @SystemApi(client = MODULE_LIBRARIES) @TestApi - @android.ravenwood.annotation.RavenwoodKeep + @RavenwoodKeep // TODO(b/318651609): Deprecate once Process#getSdkSandboxUidForAppUid is rolled out to 100% public static final int toSdkSandboxUid(int uid) { return uid + (FIRST_SDK_SANDBOX_UID - FIRST_APPLICATION_UID); @@ -1040,7 +1007,7 @@ public class Process { * @throws IllegalArgumentException if input is not an app uid */ @FlaggedApi(Flags.FLAG_SDK_SANDBOX_UID_TO_APP_UID_API) - @android.ravenwood.annotation.RavenwoodKeep + @RavenwoodKeep public static final int getSdkSandboxUidForAppUid(int uid) { if (!isApplicationUid(uid)) { throw new IllegalArgumentException("Input UID is not an app UID"); @@ -1051,7 +1018,7 @@ public class Process { /** * Returns whether the current process is a sdk sandbox process. */ - @android.ravenwood.annotation.RavenwoodKeep + @RavenwoodKeep public static final boolean isSdkSandbox() { return isSdkSandboxUid(myUid()); } @@ -1128,28 +1095,11 @@ public class Process { * not have permission to modify the given thread, or to use the given * priority. */ - @android.ravenwood.annotation.RavenwoodReplace + @RavenwoodRedirect public static final native void setThreadPriority(int tid, @IntRange(from = -20, to = THREAD_PRIORITY_LOWEST) int priority) throws IllegalArgumentException, SecurityException; - /** @hide */ - public static final void setThreadPriority$ravenwood(int tid, int priority) { - final SomeArgs args = - Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get(); - if (args.argi3 == tid) { - boolean backgroundOk = (args.arg1 == Boolean.TRUE); - if (priority >= THREAD_PRIORITY_BACKGROUND && !backgroundOk) { - throw new IllegalArgumentException( - "Priority " + priority + " blocked by setCanSelfBackground()"); - } - args.argi4 = priority; - } else { - throw new UnsupportedOperationException( - "Cross-thread priority management not yet available in Ravenwood"); - } - } - /** * Call with 'false' to cause future calls to {@link #setThreadPriority(int)} to * throw an exception if passed a background-level thread priority. This is only @@ -1157,16 +1107,9 @@ public class Process { * * @hide */ - @android.ravenwood.annotation.RavenwoodReplace + @RavenwoodRedirect public static final native void setCanSelfBackground(boolean backgroundOk); - /** @hide */ - public static final void setCanSelfBackground$ravenwood(boolean backgroundOk) { - final SomeArgs args = - Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get(); - args.arg1 = Boolean.valueOf(backgroundOk); - } - /** * Sets the scheduling group for a thread. * @hide @@ -1295,13 +1238,12 @@ public class Process { * * @see #setThreadPriority(int, int) */ - @android.ravenwood.annotation.RavenwoodReplace + @RavenwoodReplace public static final native void setThreadPriority( @IntRange(from = -20, to = THREAD_PRIORITY_LOWEST) int priority) throws IllegalArgumentException, SecurityException; - /** @hide */ - public static final void setThreadPriority$ravenwood(int priority) { + private static void setThreadPriority$ravenwood(int priority) { setThreadPriority(myTid(), priority); } @@ -1318,23 +1260,11 @@ public class Process { * @throws IllegalArgumentException Throws IllegalArgumentException if * <var>tid</var> does not exist. */ - @android.ravenwood.annotation.RavenwoodReplace + @RavenwoodRedirect @IntRange(from = -20, to = THREAD_PRIORITY_LOWEST) public static final native int getThreadPriority(int tid) throws IllegalArgumentException; - /** @hide */ - public static final int getThreadPriority$ravenwood(int tid) { - final SomeArgs args = - Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get(); - if (args.argi3 == tid) { - return args.argi4; - } else { - throw new UnsupportedOperationException( - "Cross-thread priority management not yet available in Ravenwood"); - } - } - /** * Return the current scheduling policy of a thread, based on Linux. * diff --git a/core/java/android/print/OWNERS b/core/java/android/print/OWNERS index 0809de25b45c..ce79f5d0c669 100644 --- a/core/java/android/print/OWNERS +++ b/core/java/android/print/OWNERS @@ -2,3 +2,4 @@ anothermark@google.com kumarashishg@google.com +bmgordon@google.com diff --git a/core/java/android/printservice/OWNERS b/core/java/android/printservice/OWNERS index 0809de25b45c..ce79f5d0c669 100644 --- a/core/java/android/printservice/OWNERS +++ b/core/java/android/printservice/OWNERS @@ -2,3 +2,4 @@ anothermark@google.com kumarashishg@google.com +bmgordon@google.com diff --git a/core/java/android/service/contextualsearch/OWNERS b/core/java/android/service/contextualsearch/OWNERS index b7238721bc60..c435bd87be21 100644 --- a/core/java/android/service/contextualsearch/OWNERS +++ b/core/java/android/service/contextualsearch/OWNERS @@ -1,2 +1,3 @@ srazdan@google.com -hackz@google.com +hyunyoungs@google.com +awickham@google.com diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java index 8358b9a51adb..1dd9d46fdfb7 100644 --- a/core/java/android/util/Log.java +++ b/core/java/android/util/Log.java @@ -75,8 +75,7 @@ import java.net.UnknownHostException; @android.ravenwood.annotation.RavenwoodClassLoadHook( "com.android.platform.test.ravenwood.runtimehelper.ClassLoadHook.onClassLoaded") // Uncomment the following annotation to switch to the Java substitution version. -//@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass( -// "com.android.platform.test.ravenwood.nativesubstitution.Log_host") +@android.ravenwood.annotation.RavenwoodRedirectionClass("Log_host") public final class Log { /** @hide */ @IntDef({ASSERT, ERROR, WARN, INFO, DEBUG, VERBOSE}) @@ -250,6 +249,7 @@ public final class Log { * tag limit of concern after this API level. */ @FastNative + @android.ravenwood.annotation.RavenwoodRedirect public static native boolean isLoggable(@Nullable String tag, @Level int level); /** @@ -425,6 +425,7 @@ public final class Log { * @hide */ @UnsupportedAppUsage + @android.ravenwood.annotation.RavenwoodRedirect public static native int println_native(int bufID, int priority, String tag, String msg); /** @@ -452,6 +453,7 @@ public final class Log { * Return the maximum payload the log daemon accepts without truncation. * @return LOGGER_ENTRY_MAX_PAYLOAD. */ + @android.ravenwood.annotation.RavenwoodRedirect private static native int logger_entry_max_payload_native(); /** diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java index 69228cafa34b..cd02130fb2f0 100644 --- a/core/java/android/view/HapticFeedbackConstants.java +++ b/core/java/android/view/HapticFeedbackConstants.java @@ -157,7 +157,7 @@ public class HapticFeedbackConstants { /** * The user is executing a swipe/drag-style gesture, such as pull-to-refresh, where the - * gesture action is “eligible” at a certain threshold of movement, and can be cancelled by + * gesture action is "eligible" at a certain threshold of movement, and can be cancelled by * moving back past the threshold. This constant indicates that the user's motion has just * passed the threshold for the action to be activated on release. * @@ -167,7 +167,7 @@ public class HapticFeedbackConstants { /** * The user is executing a swipe/drag-style gesture, such as pull-to-refresh, where the - * gesture action is “eligible” at a certain threshold of movement, and can be cancelled by + * gesture action is "eligible" at a certain threshold of movement, and can be cancelled by * moving back past the threshold. This constant indicates that the user's motion has just * re-crossed back "under" the threshold for the action to be activated, meaning the gesture is * currently in a cancelled state. diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 2acda8ad71c1..e402ddfc637a 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -249,6 +249,10 @@ public class ZygoteInit { return isExperimentEnabled("profilesystemserver"); } + private static boolean shouldProfileBootClasspath() { + return isExperimentEnabled("profilebootclasspath"); + } + /** * Performs Zygote process initialization. Loads and initializes commonly used classes. * @@ -352,7 +356,7 @@ public class ZygoteInit { // If we are profiling the boot image, reset the Jit counters after preloading the // classes. We want to preload for performance, and we can use method counters to // infer what clases are used after calling resetJitCounters, for profile purposes. - if (isExperimentEnabled("profilebootclasspath")) { + if (shouldProfileBootClasspath()) { Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ResetJitCounters"); VMRuntime.resetJitCounters(); Trace.traceEnd(Trace.TRACE_TAG_DALVIK); @@ -460,12 +464,28 @@ public class ZygoteInit { ? String.join(":", systemServerClasspath, standaloneSystemServerJars) : systemServerClasspath; prepareSystemServerProfile(systemServerPaths); + try { + SystemProperties.set("debug.tracing.profile_system_server", "1"); + } catch (RuntimeException e) { + Slog.e(TAG, "Failed to set debug.tracing.profile_system_server", e); + } } catch (Exception e) { Log.wtf(TAG, "Failed to set up system server profile", e); } } } + // Zygote can't set system properties due to permission denied. We need to be in System + // Server to set system properties, so we do it here instead of the more natural place in + // preloadClasses. + if (shouldProfileBootClasspath()) { + try { + SystemProperties.set("debug.tracing.profile_boot_classpath", "1"); + } catch (RuntimeException e) { + Slog.e(TAG, "Failed to set debug.tracing.profile_boot_classpath", e); + } + } + if (parsedArgs.mInvokeWith != null) { String[] args = parsedArgs.mRemainingArgs; // If we have a non-null system server class path, we'll have to duplicate the diff --git a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java index 30b160ab161b..a69d2e4f4dca 100644 --- a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java +++ b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java @@ -28,19 +28,9 @@ import android.ravenwood.annotation.RavenwoodReplace; public final class RavenwoodEnvironment { public static final String TAG = "RavenwoodEnvironment"; - private static final RavenwoodEnvironment sInstance; - private static final Workaround sWorkaround; + private static RavenwoodEnvironment sInstance = new RavenwoodEnvironment(); - private RavenwoodEnvironment() { - } - - static { - sInstance = new RavenwoodEnvironment(); - sWorkaround = new Workaround(); - ensureRavenwoodInitialized(); - } - - public static RuntimeException notSupportedOnDevice() { + private static RuntimeException notSupportedOnDevice() { return new UnsupportedOperationException("This method can only be used on Ravenwood"); } @@ -52,15 +42,6 @@ public final class RavenwoodEnvironment { } /** - * Initialize the ravenwood environment if it hasn't happened already, if running on Ravenwood. - * - * No-op if called on the device side. - */ - @RavenwoodRedirect - public static void ensureRavenwoodInitialized() { - } - - /** * USE IT SPARINGLY! Returns true if it's running on Ravenwood, hostside test environment. * * <p>Using this allows code to behave differently on a real device and on Ravenwood, but @@ -91,38 +72,10 @@ public final class RavenwoodEnvironment { } /** - * See {@link Workaround}. It's only usable on Ravenwood. - */ - @RavenwoodReplace - public static Workaround workaround() { - throw notSupportedOnDevice(); - } - - private static Workaround workaround$ravenwood() { - return sWorkaround; - } - - /** * @return the "ravenwood-runtime" directory. */ @RavenwoodRedirect public String getRavenwoodRuntimePath() { throw notSupportedOnDevice(); } - - /** - * A set of APIs used to work around missing features on Ravenwood. Ideally, this class should - * be empty, and all its APIs should be able to be implemented properly. - */ - public static class Workaround { - Workaround() { - } - - /** - * @return whether the app's target SDK level is at least Q. - */ - public boolean isTargetSdkAtLeastQ() { - return true; - } - } } diff --git a/core/jni/android_text_Hyphenator.cpp b/core/jni/android_text_Hyphenator.cpp index 89fdeeb078d0..933781c3e924 100644 --- a/core/jni/android_text_Hyphenator.cpp +++ b/core/jni/android_text_Hyphenator.cpp @@ -14,17 +14,19 @@ * limitations under the License. */ +#include <core_jni_helpers.h> +#include <cutils/trace.h> #include <fcntl.h> +#include <minikin/Hyphenator.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> +#include <tracing_perfetto.h> +#include <unicode/uloc.h> #include <unistd.h> #include <algorithm> -#include <core_jni_helpers.h> -#include <minikin/Hyphenator.h> - namespace android { static std::string buildFileName(const std::string& locale) { @@ -79,6 +81,23 @@ static void addHyphenatorAlias(const std::string& from, const std::string& to) { minikin::addHyphenatorAlias(from, to); } +/* + * Cache the subtag key map by calling uloc_forLanguageTag with a subtag. + * minikin calls uloc_forLanguageTag with an Unicode extension specifying + * the line breaking strictness. Parsing the extension requires loading the key map + * from keyTypeData.res in the ICU. + * "lb" is the key commonly used by minikin. "ca" is a common legacy key mapping to + * the "calendar" key. It ensures that the key map is loaded and cached in icu4c. + * "en-Latn-US" is a common locale used in the Android system regardless what default locale + * is selected in the Settings app. + */ +inline static void cacheUnicodeExtensionSubtagsKeyMap() { + UErrorCode status = U_ZERO_ERROR; + char localeID[ULOC_FULLNAME_CAPACITY] = {}; + uloc_forLanguageTag("en-Latn-US-u-lb-loose-ca-gregory", localeID, ULOC_FULLNAME_CAPACITY, + nullptr, &status); +} + static void init() { // TODO: Confirm that these are the best values. Various sources suggest (1, 1), but that // appears too small. @@ -190,6 +209,10 @@ static void init() { addHyphenatorAlias("und-Orya", "or"); // Oriya addHyphenatorAlias("und-Taml", "ta"); // Tamil addHyphenatorAlias("und-Telu", "te"); // Telugu + + tracing_perfetto::traceBegin(ATRACE_TAG_VIEW, "CacheUnicodeExtensionSubtagsKeyMap"); + cacheUnicodeExtensionSubtagsKeyMap(); + tracing_perfetto::traceEnd(ATRACE_TAG_VIEW); // CacheUnicodeExtensionSubtagsKeyMap } static const JNINativeMethod gMethods[] = { diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index 7af307317fc5..b1c48ab39396 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -256,6 +256,7 @@ android_ravenwood_test { "src/android/content/ContextTest.java", "src/android/content/pm/PackageManagerTest.java", "src/android/content/pm/UserInfoTest.java", + "src/android/app/PropertyInvalidatedCacheTests.java", "src/android/database/CursorWindowTest.java", "src/android/os/**/*.java", "src/android/telephony/PinResultTest.java", diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java index cd6abddc20a1..95a4fe4811f0 100644 --- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java +++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java @@ -39,11 +39,7 @@ import org.junit.Test; * atest FrameworksCoreTests:PropertyInvalidatedCacheTests */ @SmallTest -@IgnoreUnderRavenwood(blockedBy = PropertyInvalidatedCache.class) public class PropertyInvalidatedCacheTests { - @Rule - public final RavenwoodRule mRavenwood = new RavenwoodRule(); - // Configuration for creating caches private static final String MODULE = PropertyInvalidatedCache.MODULE_TEST; private static final String API = "testApi"; diff --git a/core/tests/coretests/src/android/os/IpcDataCacheTest.java b/core/tests/coretests/src/android/os/IpcDataCacheTest.java index b03fd6485786..5edf0cad1965 100644 --- a/core/tests/coretests/src/android/os/IpcDataCacheTest.java +++ b/core/tests/coretests/src/android/os/IpcDataCacheTest.java @@ -37,10 +37,7 @@ import org.junit.Test; * atest FrameworksCoreTests:IpcDataCacheTest */ @SmallTest -@IgnoreUnderRavenwood(blockedBy = IpcDataCache.class) public class IpcDataCacheTest { - @Rule - public final RavenwoodRule mRavenwood = new RavenwoodRule(); // Configuration for creating caches private static final String MODULE = IpcDataCache.MODULE_TEST; diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java index cb3b64c3e6cd..93d94c9cd7eb 100644 --- a/graphics/java/android/graphics/ImageFormat.java +++ b/graphics/java/android/graphics/ImageFormat.java @@ -16,6 +16,7 @@ package android.graphics; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import java.lang.annotation.Retention; @@ -41,6 +42,7 @@ public class ImageFormat { Y8, Y16, YCBCR_P010, + YCBCR_P210, NV16, NV21, YUY2, @@ -206,6 +208,26 @@ public class ImageFormat { public static final int YCBCR_P010 = 0x36; /** + * <p>Android YUV P210 format.</p> + * + * P210 is a 4:2:2 YCbCr semiplanar format comprised of a WxH Y plane + * followed by a WxH CbCr plane. Each sample is represented by a 16-bit + * little-endian value, with the lower 6 bits set to zero. + * + * <p>For example, the {@link android.media.Image} object can provide data + * in this format from a {@link android.hardware.camera2.CameraDevice} + * through a {@link android.media.ImageReader} object if this format is + * supported by {@link android.hardware.camera2.CameraDevice}.</p> + * + * @see android.media.Image + * @see android.media.ImageReader + * @see android.hardware.camera2.CameraDevice + * + */ + @FlaggedApi(android.media.codec.Flags.FLAG_P210_FORMAT_SUPPORT) + public static final int YCBCR_P210 = 0x3c; + + /** * YCbCr format, used for video. * * <p>For the {@link android.hardware.camera2} API, the {@link #YUV_420_888} format is @@ -849,6 +871,8 @@ public class ImageFormat { return 16; case YCBCR_P010: return 24; + case YCBCR_P210: + return 32; case RAW_DEPTH10: case RAW10: return 10; @@ -899,7 +923,9 @@ public class ImageFormat { case JPEG_R: return true; } - + if (android.media.codec.Flags.p210FormatSupport() && format == YCBCR_P210) { + return true; + } return false; } } diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java index 7f936f28ac4f..344d19af9173 100644 --- a/keystore/java/android/security/keystore/KeyProperties.java +++ b/keystore/java/android/security/keystore/KeyProperties.java @@ -23,9 +23,6 @@ import android.annotation.StringDef; import android.annotation.SystemApi; import android.os.Process; import android.security.keymaster.KeymasterDefs; - -import libcore.util.EmptyArray; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.security.spec.AlgorithmParameterSpec; @@ -33,6 +30,7 @@ import java.security.spec.ECParameterSpec; import java.security.spec.MGF1ParameterSpec; import java.util.Collection; import java.util.Locale; +import libcore.util.EmptyArray; /** * Properties of <a href="{@docRoot}training/articles/keystore.html">Android Keystore</a> keys. @@ -116,7 +114,7 @@ public abstract class KeyProperties { public static final int PURPOSE_AGREE_KEY = 1 << 6; /** - * Purpose of key: Signing attestaions. This purpose is incompatible with all others, meaning + * Purpose of key: Signing attestations. This purpose is incompatible with all others, meaning * that when generating a key with PURPOSE_ATTEST_KEY, no other purposes may be specified. In * addition, PURPOSE_ATTEST_KEY may not be specified for imported keys. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java index 4988a9481d21..851472f7d4c1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java @@ -25,6 +25,7 @@ import android.util.Log; import android.view.IRemoteAnimationFinishedCallback; import android.view.IRemoteAnimationRunner; import android.view.RemoteAnimationTarget; +import android.view.SurfaceControl; import android.window.IBackAnimationRunner; import android.window.IOnBackInvokedCallback; @@ -32,6 +33,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.Cuj.CujType; import com.android.wm.shell.common.InteractionJankMonitorUtils; +import java.lang.ref.WeakReference; + /** * Used to register the animation callback and runner, it will trigger result if gesture was finish * before it received IBackAnimationRunner#onAnimationStart, so the controller could continue @@ -80,22 +83,49 @@ public class BackAnimationRunner { return mCallback; } + private Runnable mFinishedCallback; + private RemoteAnimationTarget[] mApps; + private IRemoteAnimationFinishedCallback mRemoteCallback; + + private static class RemoteAnimationFinishedStub extends IRemoteAnimationFinishedCallback.Stub { + //the binder callback should not hold strong reference to it to avoid memory leak. + private WeakReference<BackAnimationRunner> mRunnerRef; + + private RemoteAnimationFinishedStub(BackAnimationRunner runner) { + mRunnerRef = new WeakReference<>(runner); + } + + @Override + public void onAnimationFinished() { + BackAnimationRunner runner = mRunnerRef.get(); + if (runner == null) { + return; + } + if (runner.shouldMonitorCUJ(runner.mApps)) { + InteractionJankMonitorUtils.endTracing(runner.mCujType); + } + + runner.mFinishedCallback.run(); + for (int i = runner.mApps.length - 1; i >= 0; --i) { + SurfaceControl sc = runner.mApps[i].leash; + if (sc != null && sc.isValid()) { + sc.release(); + } + } + runner.mApps = null; + runner.mFinishedCallback = null; + } + } + /** * Called from {@link IBackAnimationRunner}, it will deliver these * {@link RemoteAnimationTarget}s to the corresponding runner. */ void startAnimation(RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, Runnable finishedCallback) { - final IRemoteAnimationFinishedCallback callback = - new IRemoteAnimationFinishedCallback.Stub() { - @Override - public void onAnimationFinished() { - if (shouldMonitorCUJ(apps)) { - InteractionJankMonitorUtils.endTracing(mCujType); - } - finishedCallback.run(); - } - }; + mFinishedCallback = finishedCallback; + mApps = apps; + if (mRemoteCallback == null) mRemoteCallback = new RemoteAnimationFinishedStub(this); mWaitingAnimation = false; if (shouldMonitorCUJ(apps)) { InteractionJankMonitorUtils.beginTracing( @@ -103,7 +133,7 @@ public class BackAnimationRunner { } try { getRunner().onAnimationStart(TRANSIT_OLD_UNSET, apps, wallpapers, - nonApps, callback); + nonApps, mRemoteCallback); } catch (RemoteException e) { Log.w(TAG, "Failed call onAnimationStart", e); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/OWNERS new file mode 100644 index 000000000000..752d2fd721a5 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/OWNERS @@ -0,0 +1,2 @@ +# WM Shell sub-module dagger owners +jorgegil@google.com
\ No newline at end of file diff --git a/libs/hwui/platform/host/android/api-level.h b/libs/hwui/platform/host/android/api-level.h deleted file mode 120000 index 4fb4784f9f60..000000000000 --- a/libs/hwui/platform/host/android/api-level.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../bionic/libc/include/android/api-level.h
\ No newline at end of file diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 8ff4305a9817..96edd63a9b12 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -23,6 +23,7 @@ import static android.media.codec.Flags.FLAG_HLG_EDITING; import static android.media.codec.Flags.FLAG_IN_PROCESS_SW_AUDIO_CODEC; import static android.media.codec.Flags.FLAG_NULL_OUTPUT_SURFACE; import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST; +import static android.media.codec.Flags.FLAG_APV_SUPPORT; import static android.media.MediaCodec.GetFlag; import android.annotation.FlaggedApi; @@ -462,6 +463,33 @@ public final class MediaCodecInfo { @SuppressLint("AllUpper") public static final int COLOR_FormatYUVP010 = 54; + /** + * P210 is 10-bit-per component 4:2:2 YCbCr semiplanar format. + * <p> + * This format uses 32 allocated bits per pixel with 20 bits of + * data per pixel. Chroma planes are subsampled by 2 both + * horizontally. Each chroma and luma component + * has 16 allocated bits in little-endian configuration with 10 + * MSB of actual data. + * + * <pre> + * byte byte + * <--------- i --------> | <------ i + 1 ------> + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * | UNUSED | Y/Cb/Cr | + * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + * 0 5 6 7 0 7 + * bit + * </pre> + * + * Use this format with {@link Image}. This format corresponds + * to {@link android.graphics.ImageFormat#YCBCR_P210}. + * <p> + */ + @SuppressLint("AllUpper") + @FlaggedApi(android.media.codec.Flags.FLAG_P210_FORMAT_SUPPORT) + public static final int COLOR_FormatYUVP210 = 60; + /** @deprecated Use {@link #COLOR_FormatYUV420Flexible}. */ public static final int COLOR_TI_FormatYUV420PackedSemiPlanar = 0x7f000100; // COLOR_FormatSurface indicates that the data will be a GraphicBuffer metadata reference. @@ -4469,6 +4497,265 @@ public final class MediaCodecInfo { @SuppressLint("AllUpper") public static final int AC4Level4 = 0x10; + // Profiles and levels/bands for APV Codec, corresponding to the definitions in + // "Advanced Professional Video", 10.1.3 Profiles, 10.1.4 Levels and Bands + // found at https://www.ietf.org/archive/id/draft-lim-apv-02.html + + /** + * APV codec profile 422-10 as per IETF lim-apv-02, 10.1.3.1.1 + */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVProfile422_10 = 0x01; + + /** + * APV codec profile 422-10 as per IETF lim-apv-02, 10.1.3.1.1 + * with HDR10. + */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVProfile422_10HDR10 = 0x1000; + + /** + * APV codec profile 422-10 as per IETF lim-apv-02, 10.1.3.1.1 + * with HDR10Plus. + */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVProfile422_10HDR10Plus = 0x2000; + + // For APV Levels, the numerical values are constructed as follows: + // ((0x100 << (level_num - 1)) | (1 << band)) + // where: + // - "level_num" is the APV Level numbered consecutively + // (i.e., Level 1 == 1, Level 1.1 == 2, etc.) + // - "band" is the APV Band + + /** APV Codec Level 1, Band 0 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel1Band0 = 0x101; + /** APV Codec Level 1, Band 1 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel1Band1 = 0x102; + /** APV Codec Level 1, Band 2 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel1Band2 = 0x104; + /** APV Codec Level 1, Band 3 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel1Band3 = 0x108; + /** APV Codec Level 1.1, Band 0 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel11Band0 = 0x201; + /** APV Codec Level 1.1, Band 1 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel11Band1 = 0x202; + /** APV Codec Level 1.1, Band 2 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel11Band2 = 0x204; + /** APV Codec Level 1.1, Band 3 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel11Band3 = 0x208; + /** APV Codec Level 2, Band 0 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel2Band0 = 0x401; + /** APV Codec Level 2, Band 1 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel2Band1 = 0x402; + /** APV Codec Level 2, Band 2 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel2Band2 = 0x404; + /** APV Codec Level 2, Band 3 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel2Band3 = 0x408; + /** APV Codec Level 2.1, Band 0 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel21Band0 = 0x801; + /** APV Codec Level 2.1, Band 1 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel21Band1 = 0x802; + /** APV Codec Level 2.1, Band 2 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel21Band2 = 0x804; + /** APV Codec Level 2.1, Band 3 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel21Band3 = 0x808; + /** APV Codec Level 3, Band 0 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel3Band0 = 0x1001; + /** APV Codec Level 3, Band 1 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel3Band1 = 0x1002; + /** APV Codec Level 3, Band 2 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel3Band2 = 0x1004; + /** APV Codec Level 3, Band 3 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel3Band3 = 0x1008; + /** APV Codec Level 3.1, Band 0 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel31Band0 = 0x2001; + /** APV Codec Level 3.1, Band 1 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel31Band1 = 0x2002; + /** APV Codec Level 3.1, Band 2 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel31Band2 = 0x2004; + /** APV Codec Level 3.1, Band 3 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel31Band3 = 0x2008; + /** APV Codec Level 4, Band 0 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel4Band0 = 0x4001; + /** APV Codec Level 4, Band 1 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel4Band1 = 0x4002; + /** APV Codec Level 4, Band 2 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel4Band2 = 0x4004; + /** APV Codec Level 4, Band 3 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel4Band3 = 0x4008; + /** APV Codec Level 4.1, Band 0 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel41Band0 = 0x8001; + /** APV Codec Level 4.1, Band 1 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel41Band1 = 0x8002; + /** APV Codec Level 4.1, Band 2 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel41Band2 = 0x8004; + /** APV Codec Level 4.1, Band 3 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel41Band3 = 0x8008; + /** APV Codec Level 5, Band 0 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel5Band0 = 0x10001; + /** APV Codec Level 5, Band 1 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel5Band1 = 0x10002; + /** APV Codec Level 5, Band 2 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel5Band2 = 0x10004; + /** APV Codec Level 5, Band 3 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel5Band3 = 0x10008; + /** APV Codec Level 5.1, Band 0 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel51Band0 = 0x20001; + /** APV Codec Level 5.1, Band 1 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel51Band1 = 0x20002; + /** APV Codec Level 5.1, Band 2 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel51Band2 = 0x20004; + /** APV Codec Level 5.1, Band 3 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel51Band3 = 0x20008; + /** APV Codec Level 6, Band 0 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel6Band0 = 0x40001; + /** APV Codec Level 6, Band 1 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel6Band1 = 0x40002; + /** APV Codec Level 6, Band 2 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel6Band2 = 0x40004; + /** APV Codec Level 6, Band 3 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel6Band3 = 0x40008; + /** APV Codec Level 6.1, Band 0 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel61Band0 = 0x80001; + /** APV Codec Level 6.1, Band 1 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel61Band1 = 0x80002; + /** APV Codec Level 6.1, Band 2 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel61Band2 = 0x80004; + /** APV Codec Level 6.1, Band 3 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel61Band3 = 0x80008; + /** APV Codec Level 7, Band 0 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel7Band0 = 0x100001; + /** APV Codec Level 7, Band 1 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel7Band1 = 0x100002; + /** APV Codec Level 7, Band 2 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel7Band2 = 0x100004; + /** APV Codec Level 7, Band 3 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel7Band3 = 0x100008; + /** APV Codec Level 7.1, Band 0 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel71Band0 = 0x200001; + /** APV Codec Level 7.1, Band 1 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel71Band1 = 0x200002; + /** APV Codec Level 7.1, Band 2 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel71Band2 = 0x200004; + /** APV Codec Level 7.1, Band 3 as per IETF lim-apv-02, 10.1.4 */ + @SuppressLint("AllUpper") + @FlaggedApi(FLAG_APV_SUPPORT) + public static final int APVLevel71Band3 = 0x200008; + /** * The profile of the media content. Depending on the type of media this can be * one of the profile values defined in this class. diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index cd0654ceb348..b08a86ee8f46 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -18,6 +18,7 @@ package android.media; import static android.media.codec.Flags.FLAG_IN_PROCESS_SW_AUDIO_CODEC; import static android.media.codec.Flags.FLAG_REGION_OF_INTEREST; +import static android.media.codec.Flags.FLAG_APV_SUPPORT; import static com.android.media.codec.flags.Flags.FLAG_CODEC_IMPORTANCE; import static com.android.media.codec.flags.Flags.FLAG_LARGE_AUDIO_FRAME; @@ -157,6 +158,8 @@ import java.util.stream.Collectors; public final class MediaFormat { public static final String MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8"; public static final String MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9"; + @FlaggedApi(FLAG_APV_SUPPORT) + public static final String MIMETYPE_VIDEO_APV = "video/apv"; public static final String MIMETYPE_VIDEO_AV1 = "video/av01"; public static final String MIMETYPE_VIDEO_AVC = "video/avc"; public static final String MIMETYPE_VIDEO_HEVC = "video/hevc"; diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java index 5e55f64da985..678150b9f3a1 100644 --- a/media/java/android/media/MediaMuxer.java +++ b/media/java/android/media/MediaMuxer.java @@ -584,45 +584,108 @@ final public class MediaMuxer { * The following table summarizes codec support for containers across android releases: * * <table> - * <thead> - * <tr> - * <th rowspan=2>OS Version(s)</th> - * <td colspan=3>Codec support</th> - * </tr><tr> - * <th>{@linkplain OutputFormat#MUXER_OUTPUT_MPEG_4 MP4}</th> - * <th>{@linkplain OutputFormat#MUXER_OUTPUT_WEBM WEBM}</th> - * </tr> - * </thead> - * <tbody> - * <tr> - * <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}</td> - * <td rowspan=6>{@link MediaFormat#MIMETYPE_AUDIO_AAC AAC},<br> - * {@link MediaFormat#MIMETYPE_AUDIO_AMR_NB NB-AMR},<br> - * {@link MediaFormat#MIMETYPE_AUDIO_AMR_WB WB-AMR},<br> - * {@link MediaFormat#MIMETYPE_VIDEO_H263 H.263},<br> - * {@link MediaFormat#MIMETYPE_VIDEO_MPEG4 MPEG-4},<br> - * {@link MediaFormat#MIMETYPE_VIDEO_AVC AVC} (H.264)</td> - * <td rowspan=3>Not supported</td> - * </tr><tr> - * <td>{@link android.os.Build.VERSION_CODES#KITKAT}</td> - * </tr><tr> - * <td>{@link android.os.Build.VERSION_CODES#KITKAT_WATCH}</td> - * </tr><tr> - * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP}</td> - * <td rowspan=3>{@link MediaFormat#MIMETYPE_AUDIO_VORBIS Vorbis},<br> - * {@link MediaFormat#MIMETYPE_VIDEO_VP8 VP8}</td> - * </tr><tr> - * <td>{@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1}</td> - * </tr><tr> - * <td>{@link android.os.Build.VERSION_CODES#M}</td> - * </tr><tr> - * <td>{@link android.os.Build.VERSION_CODES#N}</td> - * <td>as above, plus<br> - * {@link MediaFormat#MIMETYPE_VIDEO_HEVC HEVC} (H.265)</td> - * <td>as above, plus<br> - * {@link MediaFormat#MIMETYPE_VIDEO_VP9 VP9}</td> - * </tr> - * </tbody> + * <thead> + * <tr> + * <th>Codec</th> + * <th>{@linkplain OutputFormat#MUXER_OUTPUT_MPEG_4 MP4}</th> + * <th>{@linkplain OutputFormat#MUXER_OUTPUT_WEBM WEBM}</th> + * <th>{@linkplain OutputFormat#MUXER_OUTPUT_OGG OGG}</th> + * <th>Supported From SDK version</th> + * </tr> + * </thead> + * <tbody> + * <tr> + * <td>{@link MediaFormat#MIMETYPE_AUDIO_AAC AAC}</td> + * <td>✓</td> + * <td></td> + * <td></td> + * <td>17</td> + * </tr> + * <tr> + * <td>{@link MediaFormat#MIMETYPE_AUDIO_AMR_NB NB-AMR}</td> + * <td>✓</td> + * <td></td> + * <td></td> + * <td>17</td> + * </tr> + * <tr> + * <td>{@link MediaFormat#MIMETYPE_AUDIO_AMR_WB WB-AMR}</td> + * <td>✓</td> + * <td></td> + * <td></td> + * <td>17</td> + * </tr> + * <tr> + * <td>{@link MediaFormat#MIMETYPE_VIDEO_H263 H.263}</td> + * <td>✓</td> + * <td></td> + * <td></td> + * <td>17</td> + * </tr> + * <tr> + * <td>{@link MediaFormat#MIMETYPE_VIDEO_MPEG4 MPEG-4}</td> + * <td>✓</td> + * <td></td> + * <td></td> + * <td>17</td> + * </tr> + * <tr> + * <td>{@link MediaFormat#MIMETYPE_VIDEO_AVC AVC} (H.264)</td> + * <td>✓</td> + * <td></td> + * <td></td> + * <td>17</td> + * </tr> + * <tr> + * <td>{@link MediaFormat#MIMETYPE_AUDIO_VORBIS Vorbis}</td> + * <td></td> + * <td>✓</td> + * <td></td> + * <td>21</td> + * </tr> + * <tr> + * <td>{@link MediaFormat#MIMETYPE_VIDEO_VP8 VP8}</td> + * <td></td> + * <td>✓</td> + * <td></td> + * <td>21</td> + * </tr> + * <tr> + * <td>{@link MediaFormat#MIMETYPE_VIDEO_VP9 VP9}</td> + * <td></td> + * <td>✓</td> + * <td></td> + * <td>24</td> + * </tr> + * <tr> + * <td>{@link MediaFormat#MIMETYPE_VIDEO_HEVC HEVC} (H.265)</td> + * <td>✓</td> + * <td></td> + * <td></td> + * <td>24</td> + * </tr> + * <tr> + * <td>{@link MediaFormat#MIMETYPE_AUDIO_OPUS OPUS}</td> + * <td></td> + * <td>✓</td> + * <td>✓</td> + * <td>26</td> + * </tr> + * <tr> + * <td>{@link MediaFormat#MIMETYPE_VIDEO_AV1 AV1}</td> + * <td>✓</td> + * <td></td> + * <td></td> + * <td>31</td> + * </tr> + * <tr> + * <td>{@link MediaFormat#MIMETYPE_VIDEO_DOLBY_VISION Dolby Vision}</td> + * <td>✓</td> + * <td></td> + * <td></td> + * <td>32</td> + * </tr> + * </tbody> * </table> * * @param format The media format for the track. This must not be an empty diff --git a/media/java/android/media/audiofx/Equalizer.java b/media/java/android/media/audiofx/Equalizer.java index 7abada07181d..09863450a717 100644 --- a/media/java/android/media/audiofx/Equalizer.java +++ b/media/java/android/media/audiofx/Equalizer.java @@ -153,9 +153,8 @@ public class Equalizer extends AudioEffect { param[0] = PARAM_GET_PRESET_NAME; for (int i = 0; i < mNumPresets; i++) { param[1] = i; - checkStatus(getParameter(param, value)); - int length = 0; - while (value[length] != 0) length++; + final int length = getParameter(param, value); + checkStatus(length); try { mPresetNames[i] = new String(value, 0, length, "ISO-8859-1"); } catch (java.io.UnsupportedEncodingException e) { diff --git a/media/java/android/media/flags/editing.aconfig b/media/java/android/media/flags/editing.aconfig index 185f579df4b9..0adc4783445a 100644 --- a/media/java/android/media/flags/editing.aconfig +++ b/media/java/android/media/flags/editing.aconfig @@ -15,3 +15,10 @@ flag { description: "Enable B frames for Stagefright recorder." bug: "341121900" } + +flag { + name: "muxer_mp4_enable_apv" + namespace: "media_solutions" + description: "Enable APV support in mp4 writer." + bug: "370061501" +} diff --git a/media/java/android/mtp/OWNERS b/media/java/android/mtp/OWNERS index 6b5336e83bdc..77ed08b1f9a5 100644 --- a/media/java/android/mtp/OWNERS +++ b/media/java/android/mtp/OWNERS @@ -1,10 +1,9 @@ set noparent -aprasath@google.com anothermark@google.com -kumarashishg@google.com -sarup@google.com +febinthattil@google.com +aprasath@google.com jsharkey@android.com jameswei@google.com rmojumder@google.com - +kumarashishg@google.com diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp index 371e3d2deda5..b0c280b426bf 100644 --- a/media/jni/android_media_ImageReader.cpp +++ b/media/jni/android_media_ImageReader.cpp @@ -935,6 +935,11 @@ static jint Image_getFormat(JNIEnv* env, jobject thiz, jint readerFormat) return static_cast<jint>(PublicFormat::PRIVATE); } else { BufferItem* buffer = Image_getBufferItem(env, thiz); + if (buffer == nullptr) { + jniThrowException(env, "java/lang/IllegalStateException", + "Image is not initialized"); + return -1; + } int readerHalFormat = mapPublicFormatToHalFormat(static_cast<PublicFormat>(readerFormat)); int32_t fmt = applyFormatOverrides( buffer->mGraphicBuffer->getPixelFormat(), readerHalFormat); @@ -953,6 +958,11 @@ static jint Image_getFormat(JNIEnv* env, jobject thiz, jint readerFormat) static jobject Image_getHardwareBuffer(JNIEnv* env, jobject thiz) { BufferItem* buffer = Image_getBufferItem(env, thiz); + if (buffer == nullptr) { + jniThrowException(env, "java/lang/IllegalStateException", + "Image is not initialized"); + return NULL; + } AHardwareBuffer* b = AHardwareBuffer_from_GraphicBuffer(buffer->mGraphicBuffer.get()); // don't user the public AHardwareBuffer_toHardwareBuffer() because this would force us // to link against libandroid.so diff --git a/media/tests/MtpTests/OWNERS b/media/tests/MtpTests/OWNERS index 6b5336e83bdc..bdb6cdbea332 100644 --- a/media/tests/MtpTests/OWNERS +++ b/media/tests/MtpTests/OWNERS @@ -1,10 +1,9 @@ set noparent -aprasath@google.com anothermark@google.com -kumarashishg@google.com -sarup@google.com +febinthattil@google.com +aprasath@google.com jsharkey@android.com jameswei@google.com rmojumder@google.com - +kumarashishg@google.com
\ No newline at end of file diff --git a/nfc/api/current.txt b/nfc/api/current.txt index 96b7c1339190..008120429c40 100644 --- a/nfc/api/current.txt +++ b/nfc/api/current.txt @@ -207,6 +207,7 @@ package android.nfc.cardemulation { method public boolean isDefaultServiceForCategory(android.content.ComponentName, String); method @FlaggedApi("android.nfc.enable_card_emulation_euicc") public boolean isEuiccSupported(); method public boolean registerAidsForService(android.content.ComponentName, String, java.util.List<java.lang.String>); + method @FlaggedApi("android.nfc.nfc_event_listener") public void registerNfcEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.cardemulation.CardEmulation.NfcEventListener); method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean registerPollingLoopFilterForService(@NonNull android.content.ComponentName, @NonNull String, boolean); method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean registerPollingLoopPatternFilterForService(@NonNull android.content.ComponentName, @NonNull String, boolean); method public boolean removeAidsForService(android.content.ComponentName, String); @@ -216,6 +217,7 @@ package android.nfc.cardemulation { method public boolean setPreferredService(android.app.Activity, android.content.ComponentName); method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setShouldDefaultToObserveModeForService(@NonNull android.content.ComponentName, boolean); method public boolean supportsAidPrefixRegistration(); + method @FlaggedApi("android.nfc.nfc_event_listener") public void unregisterNfcEventListener(@NonNull android.nfc.cardemulation.CardEmulation.NfcEventListener); method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean unsetOffHostForService(@NonNull android.content.ComponentName); method public boolean unsetPreferredService(android.app.Activity); field @Deprecated public static final String ACTION_CHANGE_DEFAULT = "android.nfc.cardemulation.action.ACTION_CHANGE_DEFAULT"; @@ -233,13 +235,16 @@ package android.nfc.cardemulation { field public static final int SELECTION_MODE_PREFER_DEFAULT = 0; // 0x0 } + @FlaggedApi("android.nfc.nfc_event_listener") public static interface CardEmulation.NfcEventListener { + method @FlaggedApi("android.nfc.nfc_event_listener") public default void onObserveModeStateChanged(boolean); + method @FlaggedApi("android.nfc.nfc_event_listener") public default void onPreferredServiceChanged(boolean); + } + public abstract class HostApduService extends android.app.Service { ctor public HostApduService(); method public final void notifyUnhandled(); method public final android.os.IBinder onBind(android.content.Intent); method public abstract void onDeactivated(int); - method @FlaggedApi("android.nfc.nfc_event_listener") public void onObserveModeStateChanged(boolean); - method @FlaggedApi("android.nfc.nfc_event_listener") public void onPreferredServiceChanged(boolean); method public abstract byte[] processCommandApdu(byte[], android.os.Bundle); method @FlaggedApi("android.nfc.nfc_read_polling_loop") public void processPollingFrames(@NonNull java.util.List<android.nfc.cardemulation.PollingFrame>); method public final void sendResponseApdu(byte[]); diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt index 24e14e69637b..6aa8a2b73f2c 100644 --- a/nfc/api/system-current.txt +++ b/nfc/api/system-current.txt @@ -91,6 +91,7 @@ package android.nfc { method public void onDisable(@NonNull java.util.function.Consumer<java.lang.Boolean>); method public void onDisableFinished(int); method public void onDisableStarted(); + method public void onEeListenActivated(boolean); method public void onEnable(@NonNull java.util.function.Consumer<java.lang.Boolean>); method public void onEnableFinished(int); method public void onEnableStarted(); @@ -105,7 +106,7 @@ package android.nfc { method public void onRfFieldActivated(boolean); method public void onRoutingChanged(); method public void onStateUpdated(int); - method public void onTagConnected(boolean, @NonNull android.nfc.Tag); + method public void onTagConnected(boolean); method public void onTagDispatch(@NonNull java.util.function.Consumer<java.lang.Boolean>); } diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java b/nfc/java/android/nfc/ComponentNameAndUser.aidl index 83cbc5265d8f..e677998a7970 100644 --- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRunnerState.java +++ b/nfc/java/android/nfc/ComponentNameAndUser.aidl @@ -13,10 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package android.platform.test.ravenwood; -/** Stub class. The actual implementaetion is in junit-impl-src. */ -public class RavenwoodRunnerState { - public RavenwoodRunnerState(RavenwoodAwareTestRunner runner) { - } -} +package android.nfc; + +parcelable ComponentNameAndUser;
\ No newline at end of file diff --git a/nfc/java/android/nfc/ComponentNameAndUser.java b/nfc/java/android/nfc/ComponentNameAndUser.java new file mode 100644 index 000000000000..59e6c62926c9 --- /dev/null +++ b/nfc/java/android/nfc/ComponentNameAndUser.java @@ -0,0 +1,100 @@ +/* + * 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 android.annotation.UserIdInt; +import android.content.ComponentName; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Objects; + +/** + * @hide + */ +public class ComponentNameAndUser implements Parcelable { + @UserIdInt private final int mUserId; + private ComponentName mComponentName; + + public ComponentNameAndUser(@UserIdInt int userId, ComponentName componentName) { + mUserId = userId; + mComponentName = componentName; + } + + /** + * @hide + */ + public int describeContents() { + return 0; + } + + /** + * @hide + */ + public void writeToParcel(Parcel out, int flags) { + out.writeInt(mUserId); + out.writeParcelable(mComponentName, flags); + } + + public static final Parcelable.Creator<ComponentNameAndUser> CREATOR = + new Parcelable.Creator<ComponentNameAndUser>() { + public ComponentNameAndUser createFromParcel(Parcel in) { + return new ComponentNameAndUser(in); + } + + public ComponentNameAndUser[] newArray(int size) { + return new ComponentNameAndUser[size]; + } + }; + + private ComponentNameAndUser(Parcel in) { + mUserId = in.readInt(); + mComponentName = in.readParcelable(null, ComponentName.class); + } + + @UserIdInt + public int getUserId() { + return mUserId; + } + + public ComponentName getComponentName() { + return mComponentName; + } + + @Override + public String toString() { + return mComponentName + " for user id: " + mUserId; + } + + @Override + public boolean equals(Object obj) { + if (obj != null && obj instanceof ComponentNameAndUser) { + ComponentNameAndUser other = (ComponentNameAndUser) obj; + return other.getUserId() == mUserId + && Objects.equals(other.getComponentName(), mComponentName); + } + return false; + } + + @Override + public int hashCode() { + if (mComponentName == null) { + return mUserId; + } + return mComponentName.hashCode() + mUserId; + } +} diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl index 8535e4a9cfd2..5e2e92d958a4 100644 --- a/nfc/java/android/nfc/INfcCardEmulation.aidl +++ b/nfc/java/android/nfc/INfcCardEmulation.aidl @@ -17,6 +17,8 @@ package android.nfc; import android.content.ComponentName; +import android.nfc.INfcEventListener; + import android.nfc.cardemulation.AidGroup; import android.nfc.cardemulation.ApduServiceInfo; import android.os.RemoteCallback; @@ -55,4 +57,7 @@ interface INfcCardEmulation boolean isAutoChangeEnabled(); List<String> getRoutingStatus(); void overwriteRoutingTable(int userHandle, String emptyAid, String protocol, String tech, String sc); + + void registerNfcEventListener(in INfcEventListener listener); + void unregisterNfcEventListener(in INfcEventListener listener); } diff --git a/nfc/java/android/nfc/INfcEventListener.aidl b/nfc/java/android/nfc/INfcEventListener.aidl new file mode 100644 index 000000000000..5162c26ac536 --- /dev/null +++ b/nfc/java/android/nfc/INfcEventListener.aidl @@ -0,0 +1,11 @@ +package android.nfc; + +import android.nfc.ComponentNameAndUser; + +/** + * @hide + */ +oneway interface INfcEventListener { + void onPreferredServiceChanged(in ComponentNameAndUser ComponentNameAndUser); + void onObserveModeStateChanged(boolean isEnabled); +}
\ No newline at end of file diff --git a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl index 48c7ee659266..7f1fd15fe68a 100644 --- a/nfc/java/android/nfc/INfcOemExtensionCallback.aidl +++ b/nfc/java/android/nfc/INfcOemExtensionCallback.aidl @@ -27,7 +27,7 @@ import java.util.List; * @hide */ interface INfcOemExtensionCallback { - void onTagConnected(boolean connected, in Tag tag); + void onTagConnected(boolean connected); void onStateUpdated(int state); void onApplyRouting(in ResultReceiver isSkipped); void onNdefRead(in ResultReceiver isSkipped); @@ -46,6 +46,7 @@ interface INfcOemExtensionCallback { void onCardEmulationActivated(boolean isActivated); void onRfFieldActivated(boolean isActivated); void onRfDiscoveryStarted(boolean isDiscoveryStarted); + void onEeListenActivated(boolean isActivated); void onGetOemAppSearchIntent(in List<String> firstPackage, in ResultReceiver intentConsumer); void onNdefMessage(in Tag tag, in NdefMessage message, in ResultReceiver hasOemExecutableContent); void onLaunchHceAppChooserActivity(in String selectedAid, in List<ApduServiceInfo> services, in ComponentName failedComponent, in String category); diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java index 520ba896f01f..1d2085c88213 100644 --- a/nfc/java/android/nfc/NfcOemExtension.java +++ b/nfc/java/android/nfc/NfcOemExtension.java @@ -80,6 +80,14 @@ public final class NfcOemExtension { private boolean mCardEmulationActivated = false; private boolean mRfFieldActivated = false; private boolean mRfDiscoveryStarted = false; + private boolean mEeListenActivated = false; + + /** + * Broadcast Action: Sent on NFC stack initialization when NFC OEM extensions are enabled. + * <p> OEM extension modules should use this intent to start their extension service </p> + * @hide + */ + public static final String ACTION_OEM_EXTENSION_INIT = "android.nfc.action.OEM_EXTENSION_INIT"; /** * Mode Type for {@link #setControllerAlwaysOnMode(int)}. @@ -188,9 +196,8 @@ public final class NfcOemExtension { * ex - if tag is connected notify cover and Nfctest app if app is in testing mode * * @param connected status of the tag true if tag is connected otherwise false - * @param tag Tag details */ - void onTagConnected(boolean connected, @NonNull Tag tag); + void onTagConnected(boolean connected); /** * Update the Nfc Adapter State @@ -320,6 +327,13 @@ public final class NfcOemExtension { void onRfDiscoveryStarted(boolean isDiscoveryStarted); /** + * Notifies the NFCEE (NFC Execution Environment) Listen has been activated. + * + * @param isActivated true, if EE Listen is ON, else EE Listen is OFF. + */ + void onEeListenActivated(boolean isActivated); + + /** * Gets the intent to find the OEM package in the OEM App market. If the consumer returns * {@code null} or a timeout occurs, the intent from the first available package will be * used instead. @@ -430,6 +444,7 @@ public final class NfcOemExtension { callback.onCardEmulationActivated(mCardEmulationActivated); callback.onRfFieldActivated(mRfFieldActivated); callback.onRfDiscoveryStarted(mRfDiscoveryStarted); + callback.onEeListenActivated(mEeListenActivated); }); } } @@ -677,9 +692,9 @@ public final class NfcOemExtension { private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub { @Override - public void onTagConnected(boolean connected, Tag tag) throws RemoteException { + public void onTagConnected(boolean connected) throws RemoteException { mCallbackMap.forEach((cb, ex) -> - handleVoid2ArgCallback(connected, tag, cb::onTagConnected, ex)); + handleVoidCallback(connected, cb::onTagConnected, ex)); } @Override @@ -704,6 +719,13 @@ public final class NfcOemExtension { } @Override + public void onEeListenActivated(boolean isActivated) throws RemoteException { + mEeListenActivated = isActivated; + mCallbackMap.forEach((cb, ex) -> + handleVoidCallback(isActivated, cb::onEeListenActivated, ex)); + } + + @Override public void onStateUpdated(int state) throws RemoteException { mCallbackMap.forEach((cb, ex) -> handleVoidCallback(state, cb::onStateUpdated, ex)); diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java index 5727f9915fa8..3d293f7546a5 100644 --- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -52,10 +52,12 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.TreeMap; import java.util.regex.Pattern; /** @@ -181,7 +183,8 @@ public final class ApduServiceInfo implements Parcelable { this(info, onHost, description, staticAidGroups, dynamicAidGroups, requiresUnlock, requiresScreenOn, bannerResource, uid, settingsActivityName, offHost, staticOffHost, isEnabled, - new HashMap<String, Boolean>(), new HashMap<Pattern, Boolean>()); + new HashMap<String, Boolean>(), new TreeMap<>( + Comparator.comparing(Pattern::toString))); } /** @@ -317,7 +320,8 @@ public final class ApduServiceInfo implements Parcelable { mStaticAidGroups = new HashMap<String, AidGroup>(); mDynamicAidGroups = new HashMap<String, AidGroup>(); mAutoTransact = new HashMap<String, Boolean>(); - mAutoTransactPatterns = new HashMap<Pattern, Boolean>(); + mAutoTransactPatterns = new TreeMap<Pattern, Boolean>( + Comparator.comparing(Pattern::toString)); mOnHost = onHost; final int depth = parser.getDepth(); diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java index d8f04c50b695..eb28c3b9c930 100644 --- a/nfc/java/android/nfc/cardemulation/CardEmulation.java +++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java @@ -17,6 +17,7 @@ package android.nfc.cardemulation; import android.Manifest; +import android.annotation.CallbackExecutor; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; @@ -33,15 +34,18 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.nfc.ComponentNameAndUser; import android.nfc.Constants; import android.nfc.Flags; import android.nfc.INfcCardEmulation; +import android.nfc.INfcEventListener; import android.nfc.NfcAdapter; import android.os.Build; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; +import android.util.ArrayMap; import android.util.Log; import java.lang.annotation.Retention; @@ -50,6 +54,8 @@ import java.util.HashMap; import java.util.HexFormat; import java.util.List; import java.util.Locale; +import java.util.Objects; +import java.util.concurrent.Executor; import java.util.regex.Pattern; /** @@ -1076,4 +1082,107 @@ public final class CardEmulation { default -> throw new IllegalStateException("Unexpected value: " + route); }; } + + /** Listener for preferred service state changes. */ + @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) + public interface NfcEventListener { + /** + * This method is called when this package gains or loses preferred Nfc service status, + * either the Default Wallet Role holder (see {@link + * android.app.role.RoleManager#ROLE_WALLET}) or the preferred service of the foreground + * activity set with {@link #setPreferredService(Activity, ComponentName)} + * + * @param isPreferred true is this service has become the preferred Nfc service, false if it + * is no longer the preferred service + */ + @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) + default void onPreferredServiceChanged(boolean isPreferred) {} + + /** + * This method is called when observe mode has been enabled or disabled. + * + * @param isEnabled true if observe mode has been enabled, false if it has been disabled + */ + @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) + default void onObserveModeStateChanged(boolean isEnabled) {} + } + + private final ArrayMap<NfcEventListener, Executor> mNfcEventListeners = new ArrayMap<>(); + + final INfcEventListener mINfcEventListener = + new INfcEventListener.Stub() { + public void onPreferredServiceChanged(ComponentNameAndUser componentNameAndUser) { + if (!android.nfc.Flags.nfcEventListener()) { + return; + } + boolean isPreferred = + componentNameAndUser != null + && componentNameAndUser.getUserId() + == mContext.getUser().getIdentifier() + && componentNameAndUser.getComponentName() != null + && Objects.equals( + mContext.getPackageName(), + componentNameAndUser.getComponentName() + .getPackageName()); + synchronized (mNfcEventListeners) { + mNfcEventListeners.forEach( + (listener, executor) -> { + executor.execute( + () -> listener.onPreferredServiceChanged(isPreferred)); + }); + } + } + + public void onObserveModeStateChanged(boolean isEnabled) { + if (!android.nfc.Flags.nfcEventListener()) { + return; + } + synchronized (mNfcEventListeners) { + mNfcEventListeners.forEach( + (listener, executor) -> { + executor.execute( + () -> listener.onObserveModeStateChanged(isEnabled)); + }); + } + } + }; + + /** + * Register a listener for NFC Events. + * + * @param executor The Executor to run the call back with + * @param listener The listener to register + */ + @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) + public void registerNfcEventListener( + @NonNull @CallbackExecutor Executor executor, @NonNull NfcEventListener listener) { + if (!android.nfc.Flags.nfcEventListener()) { + return; + } + synchronized (mNfcEventListeners) { + mNfcEventListeners.put(listener, executor); + if (mNfcEventListeners.size() == 1) { + callService(() -> sService.registerNfcEventListener(mINfcEventListener)); + } + } + } + + /** + * Unregister a preferred service listener that was previously registered with {@link + * #registerNfcEventListener(Executor, NfcEventListener)} + * + * @param listener The previously registered listener to unregister + */ + @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) + public void unregisterNfcEventListener(@NonNull NfcEventListener listener) { + if (!android.nfc.Flags.nfcEventListener()) { + return; + } + synchronized (mNfcEventListeners) { + mNfcEventListeners.remove(listener); + if (mNfcEventListeners.size() == 0) { + callService(() -> sService.unregisterNfcEventListener(mINfcEventListener)); + } + } + } } diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java index cd8e19c54565..4f601f0704b4 100644 --- a/nfc/java/android/nfc/cardemulation/HostApduService.java +++ b/nfc/java/android/nfc/cardemulation/HostApduService.java @@ -239,15 +239,6 @@ public abstract class HostApduService extends Service { */ public static final int MSG_POLLING_LOOP = 4; - /** - * @hide - */ - public static final int MSG_OBSERVE_MODE_CHANGE = 5; - - /** - * @hide - */ - public static final int MSG_PREFERRED_SERVICE_CHANGED = 6; /** * @hide @@ -343,16 +334,6 @@ public abstract class HostApduService extends Service { processPollingFrames(pollingFrames); } break; - case MSG_OBSERVE_MODE_CHANGE: - if (android.nfc.Flags.nfcEventListener()) { - onObserveModeStateChanged(msg.arg1 == 1); - } - break; - case MSG_PREFERRED_SERVICE_CHANGED: - if (android.nfc.Flags.nfcEventListener()) { - onPreferredServiceChanged(msg.arg1 == 1); - } - break; default: super.handleMessage(msg); } @@ -462,25 +443,4 @@ public abstract class HostApduService extends Service { */ public abstract void onDeactivated(int reason); - - /** - * This method is called when this service is the preferred Nfc service and - * Observe mode has been enabled or disabled. - * - * @param isEnabled true if observe mode has been enabled, false if it has been disabled - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) - public void onObserveModeStateChanged(boolean isEnabled) { - - } - - /** - * This method is called when this service gains or loses preferred Nfc service status. - * - * @param isPreferred true is this service has become the preferred Nfc service, - * false if it is no longer the preferred service - */ - @FlaggedApi(android.nfc.Flags.FLAG_NFC_EVENT_LISTENER) - public void onPreferredServiceChanged(boolean isPreferred) { - } } diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig index 6a7e6939e773..34f020012d3c 100644 --- a/nfc/java/android/nfc/flags.aconfig +++ b/nfc/java/android/nfc/flags.aconfig @@ -165,3 +165,11 @@ flag { description: "Enabling security log for nfc state change" bug: "319934052" } + +flag { + name: "nfc_associated_role_services" + is_exported: true + namespace: "nfc" + description: "Share wallet role routing priority with associated services" + bug: "366243361" +} diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java index 121f5492a5ab..36960002e220 100644 --- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java +++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java @@ -41,6 +41,7 @@ public class LicenseHtmlLoaderCompat extends AsyncLoaderCompat<File> { "/system_ext/etc/NOTICE.xml.gz", "/vendor_dlkm/etc/NOTICE.xml.gz", "/odm_dlkm/etc/NOTICE.xml.gz", + "/system_dlkm/etc/NOTICE.xml.gz", }; static final String NOTICE_HTML_FILE_NAME = "NOTICE.html"; diff --git a/packages/SystemUI/animation/lib/OWNERS b/packages/SystemUI/animation/lib/OWNERS new file mode 100644 index 000000000000..7569419bf4a4 --- /dev/null +++ b/packages/SystemUI/animation/lib/OWNERS @@ -0,0 +1,3 @@ +#inherits OWNERS from SystemUI in addition to WEAR framework owners below +file:platform/frameworks/base:/WEAR_OWNERS + diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/OWNERS b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/OWNERS new file mode 100644 index 000000000000..a2001e66e55b --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/OWNERS @@ -0,0 +1 @@ +include /core/java/android/view/accessibility/OWNERS
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt index 349236551ecf..b2fcc434630c 100644 --- a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt +++ b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt @@ -45,7 +45,7 @@ open class DumpManager @Inject constructor() { /** See [registerCriticalDumpable]. */ fun registerCriticalDumpable(module: Dumpable) { - registerCriticalDumpable(module::class.java.canonicalName, module) + registerCriticalDumpable(module::class.java.name, module) } /** @@ -62,7 +62,7 @@ open class DumpManager @Inject constructor() { /** See [registerNormalDumpable]. */ fun registerNormalDumpable(module: Dumpable) { - registerNormalDumpable(module::class.java.canonicalName, module) + registerNormalDumpable(module::class.java.name, module) } /** @@ -104,13 +104,10 @@ open class DumpManager @Inject constructor() { dumpables[name] = DumpableEntry(module, name, priority) } - /** - * Same as the above override, but automatically uses the canonical class name as the dumpable - * name. - */ + /** Same as the above override, but automatically uses the class name as the dumpable name. */ @Synchronized fun registerDumpable(module: Dumpable) { - registerDumpable(module::class.java.canonicalName, module) + registerDumpable(module::class.java.name, module) } /** Unregisters a previously-registered dumpable. */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/DumpManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/dump/DumpManagerTest.kt index 6d5226f35e97..08881a08b511 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dump/DumpManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/dump/DumpManagerTest.kt @@ -22,6 +22,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.log.LogBuffer import com.android.systemui.log.table.TableLogBuffer import com.google.common.truth.Truth.assertThat +import java.io.PrintWriter import org.junit.Assert.assertThrows import org.junit.Before import org.junit.Test @@ -131,6 +132,21 @@ class DumpManagerTest : SysuiTestCase() { } @Test + fun registerDumpable_supportsAnonymousDumpables() { + val anonDumpable = + object : Dumpable { + override fun dump(pw: PrintWriter, args: Array<out String>) { + pw.println("AnonDumpable") + } + } + + // THEN registration with implicit names should succeed + dumpManager.registerCriticalDumpable(anonDumpable) + + // No exception thrown + } + + @Test fun getDumpables_returnsSafeCollection() { // GIVEN a variety of registered dumpables dumpManager.registerCriticalDumpable("dumpable1", dumpable1) diff --git a/packages/Vcn/TEST_MAPPING b/packages/Vcn/TEST_MAPPING new file mode 100644 index 000000000000..bde88fe9b717 --- /dev/null +++ b/packages/Vcn/TEST_MAPPING @@ -0,0 +1,10 @@ +{ + "postsubmit": [ + { + "name": "FrameworksVcnTests" + }, + { + "name": "CtsVcnTestCases" + } + ] +}
\ No newline at end of file diff --git a/packages/overlays/Android.bp b/packages/overlays/Android.bp index 5075f6322d1b..44abb9ffb34f 100644 --- a/packages/overlays/Android.bp +++ b/packages/overlays/Android.bp @@ -21,6 +21,7 @@ package { phony { name: "frameworks-base-overlays", + product_specific: true, required: [ "DisplayCutoutEmulationCornerOverlay", "DisplayCutoutEmulationDoubleOverlay", diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp index 9a5e623b6d45..4731cfbfc935 100644 --- a/ravenwood/Android.bp +++ b/ravenwood/Android.bp @@ -170,7 +170,7 @@ java_library { "hoststubgen-helper-runtime.ravenwood", "mockito-ravenwood-prebuilt", ], - visibility: ["//frameworks/base"], + visibility: [":__subpackages__"], jarjar_rules: ":ravenwood-services-jarjar-rules", } @@ -279,6 +279,15 @@ cc_defaults { shared_libs: [ "liblog", ], + visibility: ["//visibility:private"], +} + +cc_library_host_shared { + name: "libravenwood_initializer", + defaults: ["ravenwood_jni_defaults"], + srcs: [ + "runtime-jni/ravenwood_initializer.cpp", + ], } // We need this as a separate library because we need to overload the @@ -301,7 +310,6 @@ cc_library_host_shared { "libutils", "libcutils", ], - visibility: ["//frameworks/base"], } // For collecting the *stats.csv files in a known directory under out/host/linux-x86/testcases/. @@ -360,6 +368,239 @@ java_library { ], } +filegroup { + name: "ravenwood-data", + device_common_srcs: [ + ":system-build.prop", + ":framework-res", + ":ravenwood-empty-res", + ":framework-platform-compat-config", + ":services-platform-compat-config", + ], + device_first_srcs: [ + ":apex_icu.dat", + ], + visibility: ["//visibility:private"], +} + +// Keep in sync with build/make/target/product/generic/Android.bp +filegroup { + name: "ravenwood-fonts", + device_common_srcs: [ + ":AndroidClock.ttf", + ":CarroisGothicSC-Regular.ttf", + ":ComingSoon.ttf", + ":CutiveMono.ttf", + ":DancingScript-Regular.ttf", + ":DroidSansMono.ttf", + ":NotoColorEmoji.ttf", + ":NotoColorEmojiFlags.ttf", + ":NotoNaskhArabic-Bold.ttf", + ":NotoNaskhArabic-Regular.ttf", + ":NotoNaskhArabicUI-Bold.ttf", + ":NotoNaskhArabicUI-Regular.ttf", + ":NotoSansAdlam-VF.ttf", + ":NotoSansAhom-Regular.otf", + ":NotoSansAnatolianHieroglyphs-Regular.otf", + ":NotoSansArmenian-VF.ttf", + ":NotoSansAvestan-Regular.ttf", + ":NotoSansBalinese-Regular.ttf", + ":NotoSansBamum-Regular.ttf", + ":NotoSansBassaVah-Regular.otf", + ":NotoSansBatak-Regular.ttf", + ":NotoSansBengali-VF.ttf", + ":NotoSansBengaliUI-VF.ttf", + ":NotoSansBhaiksuki-Regular.otf", + ":NotoSansBrahmi-Regular.ttf", + ":NotoSansBuginese-Regular.ttf", + ":NotoSansBuhid-Regular.ttf", + ":NotoSansCJK-Regular.ttc", + ":NotoSansCanadianAboriginal-Regular.ttf", + ":NotoSansCarian-Regular.ttf", + ":NotoSansChakma-Regular.otf", + ":NotoSansCham-Bold.ttf", + ":NotoSansCham-Regular.ttf", + ":NotoSansCherokee-Regular.ttf", + ":NotoSansCoptic-Regular.ttf", + ":NotoSansCuneiform-Regular.ttf", + ":NotoSansCypriot-Regular.ttf", + ":NotoSansDeseret-Regular.ttf", + ":NotoSansDevanagari-VF.ttf", + ":NotoSansDevanagariUI-VF.ttf", + ":NotoSansEgyptianHieroglyphs-Regular.ttf", + ":NotoSansElbasan-Regular.otf", + ":NotoSansEthiopic-VF.ttf", + ":NotoSansGeorgian-VF.ttf", + ":NotoSansGlagolitic-Regular.ttf", + ":NotoSansGothic-Regular.ttf", + ":NotoSansGrantha-Regular.ttf", + ":NotoSansGujarati-Bold.ttf", + ":NotoSansGujarati-Regular.ttf", + ":NotoSansGujaratiUI-Bold.ttf", + ":NotoSansGujaratiUI-Regular.ttf", + ":NotoSansGunjalaGondi-Regular.otf", + ":NotoSansGurmukhi-VF.ttf", + ":NotoSansGurmukhiUI-VF.ttf", + ":NotoSansHanifiRohingya-Regular.otf", + ":NotoSansHanunoo-Regular.ttf", + ":NotoSansHatran-Regular.otf", + ":NotoSansHebrew-Bold.ttf", + ":NotoSansHebrew-Regular.ttf", + ":NotoSansImperialAramaic-Regular.ttf", + ":NotoSansInscriptionalPahlavi-Regular.ttf", + ":NotoSansInscriptionalParthian-Regular.ttf", + ":NotoSansJavanese-Regular.otf", + ":NotoSansKaithi-Regular.ttf", + ":NotoSansKannada-VF.ttf", + ":NotoSansKannadaUI-VF.ttf", + ":NotoSansKayahLi-Regular.ttf", + ":NotoSansKharoshthi-Regular.ttf", + ":NotoSansKhmer-VF.ttf", + ":NotoSansKhmerUI-Bold.ttf", + ":NotoSansKhmerUI-Regular.ttf", + ":NotoSansKhojki-Regular.otf", + ":NotoSansLao-Bold.ttf", + ":NotoSansLao-Regular.ttf", + ":NotoSansLaoUI-Bold.ttf", + ":NotoSansLaoUI-Regular.ttf", + ":NotoSansLepcha-Regular.ttf", + ":NotoSansLimbu-Regular.ttf", + ":NotoSansLinearA-Regular.otf", + ":NotoSansLinearB-Regular.ttf", + ":NotoSansLisu-Regular.ttf", + ":NotoSansLycian-Regular.ttf", + ":NotoSansLydian-Regular.ttf", + ":NotoSansMalayalam-VF.ttf", + ":NotoSansMalayalamUI-VF.ttf", + ":NotoSansMandaic-Regular.ttf", + ":NotoSansManichaean-Regular.otf", + ":NotoSansMarchen-Regular.otf", + ":NotoSansMasaramGondi-Regular.otf", + ":NotoSansMedefaidrin-VF.ttf", + ":NotoSansMeeteiMayek-Regular.ttf", + ":NotoSansMeroitic-Regular.otf", + ":NotoSansMiao-Regular.otf", + ":NotoSansModi-Regular.ttf", + ":NotoSansMongolian-Regular.ttf", + ":NotoSansMro-Regular.otf", + ":NotoSansMultani-Regular.otf", + ":NotoSansMyanmar-Bold.otf", + ":NotoSansMyanmar-Medium.otf", + ":NotoSansMyanmar-Regular.otf", + ":NotoSansMyanmarUI-Bold.otf", + ":NotoSansMyanmarUI-Medium.otf", + ":NotoSansMyanmarUI-Regular.otf", + ":NotoSansNKo-Regular.ttf", + ":NotoSansNabataean-Regular.otf", + ":NotoSansNewTaiLue-Regular.ttf", + ":NotoSansNewa-Regular.otf", + ":NotoSansOgham-Regular.ttf", + ":NotoSansOlChiki-Regular.ttf", + ":NotoSansOldItalic-Regular.ttf", + ":NotoSansOldNorthArabian-Regular.otf", + ":NotoSansOldPermic-Regular.otf", + ":NotoSansOldPersian-Regular.ttf", + ":NotoSansOldSouthArabian-Regular.ttf", + ":NotoSansOldTurkic-Regular.ttf", + ":NotoSansOriya-Bold.ttf", + ":NotoSansOriya-Regular.ttf", + ":NotoSansOriyaUI-Bold.ttf", + ":NotoSansOriyaUI-Regular.ttf", + ":NotoSansOsage-Regular.ttf", + ":NotoSansOsmanya-Regular.ttf", + ":NotoSansPahawhHmong-Regular.otf", + ":NotoSansPalmyrene-Regular.otf", + ":NotoSansPauCinHau-Regular.otf", + ":NotoSansPhagsPa-Regular.ttf", + ":NotoSansPhoenician-Regular.ttf", + ":NotoSansRejang-Regular.ttf", + ":NotoSansRunic-Regular.ttf", + ":NotoSansSamaritan-Regular.ttf", + ":NotoSansSaurashtra-Regular.ttf", + ":NotoSansSharada-Regular.otf", + ":NotoSansShavian-Regular.ttf", + ":NotoSansSinhala-VF.ttf", + ":NotoSansSinhalaUI-VF.ttf", + ":NotoSansSoraSompeng-Regular.otf", + ":NotoSansSoyombo-VF.ttf", + ":NotoSansSundanese-Regular.ttf", + ":NotoSansSylotiNagri-Regular.ttf", + ":NotoSansSymbols-Regular-Subsetted.ttf", + ":NotoSansSymbols-Regular-Subsetted2.ttf", + ":NotoSansSyriacEastern-Regular.ttf", + ":NotoSansSyriacEstrangela-Regular.ttf", + ":NotoSansSyriacWestern-Regular.ttf", + ":NotoSansTagalog-Regular.ttf", + ":NotoSansTagbanwa-Regular.ttf", + ":NotoSansTaiLe-Regular.ttf", + ":NotoSansTaiTham-Regular.ttf", + ":NotoSansTaiViet-Regular.ttf", + ":NotoSansTakri-VF.ttf", + ":NotoSansTamil-VF.ttf", + ":NotoSansTamilUI-VF.ttf", + ":NotoSansTelugu-VF.ttf", + ":NotoSansTeluguUI-VF.ttf", + ":NotoSansThaana-Bold.ttf", + ":NotoSansThaana-Regular.ttf", + ":NotoSansThai-Bold.ttf", + ":NotoSansThai-Regular.ttf", + ":NotoSansThaiUI-Bold.ttf", + ":NotoSansThaiUI-Regular.ttf", + ":NotoSansTifinagh-Regular.otf", + ":NotoSansUgaritic-Regular.ttf", + ":NotoSansVai-Regular.ttf", + ":NotoSansWancho-Regular.otf", + ":NotoSansWarangCiti-Regular.otf", + ":NotoSansYi-Regular.ttf", + ":NotoSerif-Bold.ttf", + ":NotoSerif-BoldItalic.ttf", + ":NotoSerif-Italic.ttf", + ":NotoSerif-Regular.ttf", + ":NotoSerifArmenian-VF.ttf", + ":NotoSerifBengali-VF.ttf", + ":NotoSerifCJK-Regular.ttc", + ":NotoSerifDevanagari-VF.ttf", + ":NotoSerifDogra-Regular.ttf", + ":NotoSerifEthiopic-VF.ttf", + ":NotoSerifGeorgian-VF.ttf", + ":NotoSerifGujarati-VF.ttf", + ":NotoSerifGurmukhi-VF.ttf", + ":NotoSerifHebrew-Bold.ttf", + ":NotoSerifHebrew-Regular.ttf", + ":NotoSerifHentaigana.ttf", + ":NotoSerifKannada-VF.ttf", + ":NotoSerifKhmer-Bold.otf", + ":NotoSerifKhmer-Regular.otf", + ":NotoSerifLao-Bold.ttf", + ":NotoSerifLao-Regular.ttf", + ":NotoSerifMalayalam-VF.ttf", + ":NotoSerifMyanmar-Bold.otf", + ":NotoSerifMyanmar-Regular.otf", + ":NotoSerifNyiakengPuachueHmong-VF.ttf", + ":NotoSerifSinhala-VF.ttf", + ":NotoSerifTamil-VF.ttf", + ":NotoSerifTelugu-VF.ttf", + ":NotoSerifThai-Bold.ttf", + ":NotoSerifThai-Regular.ttf", + ":NotoSerifTibetan-VF.ttf", + ":NotoSerifYezidi-VF.ttf", + ":Roboto-Regular.ttf", + ":RobotoFlex-Regular.ttf", + ":RobotoStatic-Regular.ttf", + ":SourceSansPro-Bold.ttf", + ":SourceSansPro-BoldItalic.ttf", + ":SourceSansPro-Italic.ttf", + ":SourceSansPro-Regular.ttf", + ":SourceSansPro-SemiBold.ttf", + ":SourceSansPro-SemiBoldItalic.ttf", + ], + device_first_srcs: [ + ":font_fallback.xml", + ":fonts.xml", + ], + visibility: ["//visibility:private"], +} + // JARs in "ravenwood-runtime" are set to the classpath, sorted alphabetically. // Rename some of the dependencies to make sure they're included in the intended order. @@ -386,13 +627,8 @@ java_library { android_ravenwood_libgroup { name: "ravenwood-runtime", - data: [ - ":system-build.prop", - ":framework-res", - ":ravenwood-empty-res", - ":framework-platform-compat-config", - ":services-platform-compat-config", - ], + data: [":ravenwood-data"], + fonts: [":ravenwood-fonts"], libs: [ "100-framework-minus-apex.ravenwood", "200-kxml2-android", @@ -431,6 +667,7 @@ android_ravenwood_libgroup { ], jni_libs: [ // Libraries has to be loaded in the following order + "libravenwood_initializer", "libravenwood_sysprop", "libravenwood_runtime", "libandroid_runtime", diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING index 72f62c5c29ee..a1243e37003e 100644 --- a/ravenwood/TEST_MAPPING +++ b/ravenwood/TEST_MAPPING @@ -5,8 +5,8 @@ { "name": "hoststubgen-test-tiny-test" }, { "name": "hoststubgen-invoke-test" }, { "name": "RavenwoodMockitoTest_device" }, - // TODO(b/371215487): Re-enable when the test is fixed. - // { "name": "RavenwoodBivalentTest_device" }, + { "name": "RavenwoodBivalentTest_device" }, + { "name": "RavenwoodBivalentTest_device_ravenizer" }, { "name": "RavenwoodBivalentInstTest_nonself_inst" }, { "name": "RavenwoodBivalentInstTest_self_inst_device" }, @@ -35,18 +35,18 @@ }, { "name": "CarLibHostUnitTest", - "host": true, - "keywords": ["automotive_code_coverage"] + "keywords": ["automotive_code_coverage"], + "host": true }, { "name": "CarServiceHostUnitTest", - "host": true, - "keywords": ["automotive_code_coverage"] + "keywords": ["automotive_code_coverage"], + "host": true }, { "name": "CarSystemUIRavenTests", - "host": true, - "keywords": ["automotive_code_coverage"] + "keywords": ["automotive_code_coverage"], + "host": true }, { "name": "CtsAccountManagerTestCasesRavenwood", @@ -65,6 +65,10 @@ "host": true }, { + "name": "CtsDeviceConfigTestCasesRavenwood", + "host": true + }, + { "name": "CtsGraphicsTestCasesRavenwood", "host": true }, diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java new file mode 100644 index 000000000000..869d854f7b23 --- /dev/null +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java @@ -0,0 +1,390 @@ +/* + * 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_VERBOSE_LOGGING; +import static com.android.ravenwood.common.RavenwoodCommonUtils.ensureIsPublicVoidMethod; + +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Bundle; +import android.platform.test.annotations.RavenwoodTestRunnerInitializing; +import android.platform.test.annotations.internal.InnerRunner; +import android.util.Log; + +import androidx.test.platform.app.InstrumentationRegistry; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runner.Runner; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.Filterable; +import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.Suite; +import org.junit.runners.model.Statement; +import org.junit.runners.model.TestClass; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.util.function.BiConsumer; + +/** + * A test runner used for Ravenwood. + * + * It will delegate to another runner specified with {@link InnerRunner} + * (default = {@link BlockJUnit4ClassRunner}) with the following features. + * - Add a called before the inner runner gets a chance to run. This can be used to initialize + * stuff used by the inner runner. + * - Add hook points with help from the four test rules such as {@link #sImplicitClassOuterRule}, + * which are also injected by the ravenizer tool. + * + * We use this runner to: + * - Initialize the Ravenwood environment. + * - Handle {@link android.platform.test.annotations.DisabledOnRavenwood}. + */ +public final class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase { + public static final String TAG = "Ravenwood"; + + /** Scope of a hook. */ + public enum Scope { + Class, + Instance, + } + + /** Order of a hook. */ + public enum Order { + Outer, + Inner, + } + + private record HookRule(Scope scope, Order order) implements TestRule { + @Override + public Statement apply(Statement base, Description description) { + return getCurrentRunner().wrapWithHooks(base, description, scope, order); + } + } + + // The following four rule instances will be injected to tests by the Ravenizer tool. + public static final TestRule sImplicitClassOuterRule = new HookRule(Scope.Class, Order.Outer); + public static final TestRule sImplicitClassInnerRule = new HookRule(Scope.Class, Order.Inner); + public static final TestRule sImplicitInstOuterRule = new HookRule(Scope.Instance, Order.Outer); + public static final TestRule sImplicitInstInnerRule = new HookRule(Scope.Instance, Order.Inner); + + /** Keeps track of the runner on the current thread. */ + private static final ThreadLocal<RavenwoodAwareTestRunner> sCurrentRunner = new ThreadLocal<>(); + + private static RavenwoodAwareTestRunner getCurrentRunner() { + var runner = sCurrentRunner.get(); + if (runner == null) { + throw new RuntimeException("Current test runner not set!"); + } + return runner; + } + + final Class<?> mTestJavaClass; + private final Runner mRealRunner; + private TestClass mTestClass = null; + + /** + * Stores internal states / methods associated with this runner that's only needed in + * junit-impl. + */ + final RavenwoodRunnerState mState = new RavenwoodRunnerState(this); + + /** + * Constructor. + */ + public RavenwoodAwareTestRunner(Class<?> testClass) { + RavenwoodRuntimeEnvironmentController.globalInitOnce(); + mTestJavaClass = testClass; + + /* + * If the class has @DisabledOnRavenwood, then we'll delegate to + * ClassSkippingTestRunner, which simply skips it. + * + * We need to do it before instantiating TestClass for b/367694651. + */ + if (!RavenwoodEnablementChecker.shouldRunClassOnRavenwood(testClass, true)) { + mRealRunner = new ClassSkippingTestRunner(testClass); + return; + } + + mTestClass = new TestClass(testClass); + + 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); + + mRealRunner = instantiateRealRunner(mTestClass); + + mState.enterTestRunner(); + } + + @Override + Runner getRealRunner() { + return mRealRunner; + } + + private void runAnnotatedMethodsOnRavenwood(Class<? extends Annotation> annotationClass, + Object instance) { + Log.v(TAG, "runAnnotatedMethodsOnRavenwood() " + annotationClass.getName()); + + for (var method : mTestClass.getAnnotatedMethods(annotationClass)) { + ensureIsPublicVoidMethod(method.getMethod(), /* isStatic=*/ instance == null); + + var methodDesc = method.getDeclaringClass().getName() + "." + + method.getMethod().toString(); + try { + method.getMethod().invoke(instance); + } catch (IllegalAccessException | InvocationTargetException e) { + throw logAndFail("Caught exception while running method " + methodDesc, e); + } + } + } + + @Override + public void run(RunNotifier realNotifier) { + final var notifier = new RavenwoodRunNotifier(realNotifier); + final var description = getDescription(); + + RavenwoodTestStats.getInstance().attachToRunNotifier(notifier); + + if (mRealRunner instanceof ClassSkippingTestRunner) { + Log.i(TAG, "onClassSkipped: description=" + description); + mRealRunner.run(notifier); + return; + } + + Log.v(TAG, "Starting " + mTestJavaClass.getCanonicalName()); + if (RAVENWOOD_VERBOSE_LOGGING) { + dumpDescription(description); + } + + // TODO(b/365976974): handle nested classes better + final boolean skipRunnerHook = + mRealRunnerTakesRunnerBuilder && mRealRunner instanceof Suite; + + sCurrentRunner.set(this); + try { + if (!skipRunnerHook) { + try { + mState.enterTestClass(); + } catch (Throwable th) { + notifier.reportBeforeTestFailure(description, th); + return; + } + } + + // Delegate to the inner runner. + mRealRunner.run(notifier); + } finally { + sCurrentRunner.remove(); + + if (!skipRunnerHook) { + try { + mState.exitTestClass(); + } catch (Throwable th) { + notifier.reportAfterTestFailure(th); + } + } + } + } + + private Statement wrapWithHooks(Statement base, Description description, Scope scope, + Order order) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + runWithHooks(description, scope, order, base); + } + }; + } + + private void runWithHooks(Description description, Scope scope, Order order, Statement s) + throws Throwable { + assumeTrue(onBefore(description, scope, order)); + try { + s.evaluate(); + onAfter(description, scope, order, null); + } catch (Throwable t) { + if (onAfter(description, scope, order, t)) { + throw t; + } + } + } + + /** + * A runner that simply skips a class. It still has to support {@link Filterable} + * because otherwise the result still says "SKIPPED" even when it's not included in the + * filter. + */ + private static class ClassSkippingTestRunner extends Runner implements Filterable { + private final Description mDescription; + private boolean mFilteredOut; + + ClassSkippingTestRunner(Class<?> testClass) { + mDescription = Description.createTestDescription(testClass, testClass.getSimpleName()); + mFilteredOut = false; + } + + @Override + public Description getDescription() { + return mDescription; + } + + @Override + public void run(RunNotifier notifier) { + if (mFilteredOut) { + return; + } + notifier.fireTestSuiteStarted(mDescription); + notifier.fireTestIgnored(mDescription); + notifier.fireTestSuiteFinished(mDescription); + } + + @Override + public void filter(Filter filter) throws NoTestsRemainException { + if (filter.shouldRun(mDescription)) { + mFilteredOut = false; + } else { + throw new NoTestsRemainException(); + } + } + } + + /** + * Called before a test / class. + * + * Return false if it should be skipped. + */ + private boolean onBefore(Description description, Scope scope, Order order) { + Log.v(TAG, "onBefore: description=" + description + ", " + scope + ", " + order); + + if (scope == Scope.Instance && order == Order.Outer) { + // Start of a test method. + mState.enterTestMethod(description); + } + + final var classDescription = getDescription(); + + // Class-level annotations are checked by the runner already, so we only check + // method-level annotations here. + if (scope == Scope.Instance && order == Order.Outer) { + if (!RavenwoodEnablementChecker.shouldEnableOnRavenwood(description, true)) { + return false; + } + } + return true; + } + + /** + * Called after a test / class. + * + * Return false if the exception should be ignored. + */ + private boolean onAfter(Description description, Scope scope, Order order, Throwable th) { + Log.v(TAG, "onAfter: description=" + description + ", " + scope + ", " + order + ", " + th); + + final var classDescription = getDescription(); + + if (scope == Scope.Instance && order == Order.Outer) { + // End of a test method. + mState.exitTestMethod(); + + } + + // If RUN_DISABLED_TESTS is set, and the method did _not_ throw, make it an error. + if (RavenwoodRule.private$ravenwood().isRunningDisabledTests() + && scope == Scope.Instance && order == Order.Outer) { + + boolean isTestEnabled = RavenwoodEnablementChecker.shouldEnableOnRavenwood( + description, false); + if (th == null) { + // Test passed. Is the test method supposed to be enabled? + if (isTestEnabled) { + // Enabled and didn't throw, okay. + return true; + } else { + // Disabled and didn't throw. We should report it. + fail("Test wasn't included under Ravenwood, but it actually " + + "passed under Ravenwood; consider updating annotations"); + return true; // unreachable. + } + } else { + // Test failed. + if (isTestEnabled) { + // Enabled but failed. We should throw the exception. + return true; + } else { + // Disabled and failed. Expected. Don't throw. + return false; + } + } + } + return true; + } + + /** + * Called by RavenwoodRule. + */ + static void onRavenwoodRuleEnter(Description description, RavenwoodRule rule) { + Log.v(TAG, "onRavenwoodRuleEnter: description=" + description); + getCurrentRunner().mState.enterRavenwoodRule(rule); + } + + /** + * Called by RavenwoodRule. + */ + static void onRavenwoodRuleExit(Description description, RavenwoodRule rule) { + Log.v(TAG, "onRavenwoodRuleExit: description=" + description); + getCurrentRunner().mState.exitRavenwoodRule(rule); + } + + private void dumpDescription(Description desc) { + dumpDescription(desc, "[TestDescription]=", " "); + } + + private void dumpDescription(Description desc, String header, String indent) { + Log.v(TAG, indent + header + desc); + + var children = desc.getChildren(); + var childrenIndent = " " + indent; + for (int i = 0; i < children.size(); i++) { + dumpDescription(children.get(i), "#" + i + ": ", childrenIndent); + } + } + + static volatile BiConsumer<String, Throwable> sCriticalErrorHandler = null; + + static void onCriticalError(@NonNull String message, @Nullable Throwable th) { + Log.e(TAG, "Critical error! " + message, th); + var handler = sCriticalErrorHandler; + if (handler == null) { + Log.e(TAG, "Ravenwood cannot continue. Killing self process.", th); + System.exit(1); + } + handler.accept(message, th); + } +} diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java deleted file mode 100644 index e0f9ec94a819..000000000000 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java +++ /dev/null @@ -1,209 +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 org.junit.Assert.fail; - -import android.os.Bundle; -import android.platform.test.ravenwood.RavenwoodAwareTestRunner.Order; -import android.platform.test.ravenwood.RavenwoodAwareTestRunner.Scope; -import android.platform.test.ravenwood.RavenwoodTestStats.Result; -import android.util.Log; - -import androidx.test.platform.app.InstrumentationRegistry; - -import org.junit.AssumptionViolatedException; -import org.junit.runner.Description; -import org.junit.runners.model.TestClass; - -/** - * Provide hook points created by {@link RavenwoodAwareTestRunner}. - * - * States are associated with each {@link RavenwoodAwareTestRunner} are stored in - * {@link RavenwoodRunnerState}, rather than as members of {@link RavenwoodAwareTestRunner}. - * See its javadoc for the reasons. - * - * All methods in this class must be called from the test main thread. - */ -public class RavenwoodAwareTestRunnerHook { - private static final String TAG = RavenwoodAwareTestRunner.TAG; - - private RavenwoodAwareTestRunnerHook() { - } - - /** - * Called before any code starts. Internally it will only initialize the environment once. - */ - public static void performGlobalInitialization() { - RavenwoodRuntimeEnvironmentController.globalInitOnce(); - } - - /** - * Called when a runner starts, before the inner runner gets a chance to run. - */ - public static void onRunnerInitializing(RavenwoodAwareTestRunner runner, TestClass testClass) { - Log.i(TAG, "onRunnerInitializing: testClass=" + testClass.getJavaClass() - + " runner=" + runner); - - // This is needed to make AndroidJUnit4ClassRunner happy. - InstrumentationRegistry.registerInstance(null, Bundle.EMPTY); - } - - /** - * Called when a whole test class is skipped. - */ - public static void onClassSkipped(Description description) { - Log.i(TAG, "onClassSkipped: description=" + description); - RavenwoodTestStats.getInstance().onClassSkipped(description); - } - - /** - * Called before the inner runner starts. - */ - public static void onBeforeInnerRunnerStart( - RavenwoodAwareTestRunner runner, Description description) throws Throwable { - Log.v(TAG, "onBeforeInnerRunnerStart: description=" + description); - - // Prepare the environment before the inner runner starts. - runner.mState.enterTestClass(description); - } - - /** - * Called after the inner runner finished. - */ - public static void onAfterInnerRunnerFinished( - RavenwoodAwareTestRunner runner, Description description) throws Throwable { - Log.v(TAG, "onAfterInnerRunnerFinished: description=" + description); - - RavenwoodTestStats.getInstance().onClassFinished(description); - runner.mState.exitTestClass(); - } - - /** - * Called before a test / class. - * - * Return false if it should be skipped. - */ - public static boolean onBefore(RavenwoodAwareTestRunner runner, Description description, - Scope scope, Order order) throws Throwable { - Log.v(TAG, "onBefore: description=" + description + ", " + scope + ", " + order); - - if (scope == Scope.Instance && order == Order.Outer) { - // Start of a test method. - runner.mState.enterTestMethod(description); - } - - final var classDescription = runner.mState.getClassDescription(); - - // Class-level annotations are checked by the runner already, so we only check - // method-level annotations here. - if (scope == Scope.Instance && order == Order.Outer) { - if (!RavenwoodEnablementChecker.shouldEnableOnRavenwood( - description, true)) { - RavenwoodTestStats.getInstance().onTestFinished( - classDescription, description, Result.Skipped); - return false; - } - } - return true; - } - - /** - * Called after a test / class. - * - * Return false if the exception should be ignored. - */ - public static boolean onAfter(RavenwoodAwareTestRunner runner, Description description, - Scope scope, Order order, Throwable th) { - Log.v(TAG, "onAfter: description=" + description + ", " + scope + ", " + order + ", " + th); - - final var classDescription = runner.mState.getClassDescription(); - - if (scope == Scope.Instance && order == Order.Outer) { - // End of a test method. - runner.mState.exitTestMethod(); - - final Result result; - if (th == null) { - result = Result.Passed; - } else if (th instanceof AssumptionViolatedException) { - result = Result.Skipped; - } else { - result = Result.Failed; - } - - RavenwoodTestStats.getInstance().onTestFinished(classDescription, description, result); - } - - // If RUN_DISABLED_TESTS is set, and the method did _not_ throw, make it an error. - if (RavenwoodRule.private$ravenwood().isRunningDisabledTests() - && scope == Scope.Instance && order == Order.Outer) { - - boolean isTestEnabled = RavenwoodEnablementChecker.shouldEnableOnRavenwood( - description, false); - if (th == null) { - // Test passed. Is the test method supposed to be enabled? - if (isTestEnabled) { - // Enabled and didn't throw, okay. - return true; - } else { - // Disabled and didn't throw. We should report it. - fail("Test wasn't included under Ravenwood, but it actually " - + "passed under Ravenwood; consider updating annotations"); - return true; // unreachable. - } - } else { - // Test failed. - if (isTestEnabled) { - // Enabled but failed. We should throw the exception. - return true; - } else { - // Disabled and failed. Expected. Don't throw. - return false; - } - } - } - return true; - } - - /** - * Called by {@link RavenwoodAwareTestRunner} to see if it should run a test class or not. - */ - public static boolean shouldRunClassOnRavenwood(Class<?> clazz) { - return RavenwoodEnablementChecker.shouldRunClassOnRavenwood(clazz, true); - } - - /** - * Called by RavenwoodRule. - */ - public static void onRavenwoodRuleEnter(RavenwoodAwareTestRunner runner, - Description description, RavenwoodRule rule) throws Throwable { - Log.v(TAG, "onRavenwoodRuleEnter: description=" + description); - - runner.mState.enterRavenwoodRule(rule); - } - - - /** - * Called by RavenwoodRule. - */ - public static void onRavenwoodRuleExit(RavenwoodAwareTestRunner runner, - Description description, RavenwoodRule rule) throws Throwable { - Log.v(TAG, "onRavenwoodRuleExit: description=" + description); - - runner.mState.exitRavenwoodRule(rule); - } -} diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigPrivate.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigPrivate.java new file mode 100644 index 000000000000..ffb642d60dcc --- /dev/null +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodConfigPrivate.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 android.platform.test.ravenwood; + +import android.annotation.Nullable; + +import java.util.function.BiConsumer; + +/** + * Contains Ravenwood private APIs. + */ +public class RavenwoodConfigPrivate { + private RavenwoodConfigPrivate() { + } + + /** + * Set a listener for onCriticalError(), for testing. If a listener is set, we won't call + * System.exit(). + */ + public static void setCriticalErrorHandler(@Nullable BiConsumer<String, Throwable> handler) { + RavenwoodAwareTestRunner.sCriticalErrorHandler = handler; + } +} diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java index d29b93c0c171..a208d6dce2ce 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java @@ -40,7 +40,7 @@ public final class RavenwoodNativeLoader { * See frameworks/base/core/jni/platform/host/HostRuntime.cpp */ private static final Class<?>[] sLibandroidClasses = { - android.util.Log.class, +// android.util.Log.class, // Not using native log: b/377377826 android.os.Parcel.class, android.os.Binder.class, android.os.SystemProperties.class, diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunNotifier.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunNotifier.java new file mode 100644 index 000000000000..69030350bf82 --- /dev/null +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunNotifier.java @@ -0,0 +1,227 @@ +/* + * 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 android.util.Log; + +import org.junit.AssumptionViolatedException; +import org.junit.runner.Description; +import org.junit.runner.Result; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; +import org.junit.runner.notification.RunNotifier; +import org.junit.runner.notification.StoppedByUserException; +import org.junit.runners.model.MultipleFailureException; + +import java.util.ArrayList; +import java.util.Stack; + +/** + * A run notifier that wraps another notifier and provides the following features: + * - Handle a failure that happened before testStarted and testEnded (typically that means + * it's from @BeforeClass or @AfterClass, or a @ClassRule) and deliver it as if + * individual tests in the class reported it. This is for b/364395552. + * + * - Logging. + */ +class RavenwoodRunNotifier extends RunNotifier { + private final RunNotifier mRealNotifier; + + private final Stack<Description> mSuiteStack = new Stack<>(); + private Description mCurrentSuite = null; + private final ArrayList<Throwable> mOutOfTestFailures = new ArrayList<>(); + + private boolean mBeforeTest = true; + private boolean mAfterTest = false; + + RavenwoodRunNotifier(RunNotifier realNotifier) { + mRealNotifier = realNotifier; + } + + private boolean isInTest() { + return !mBeforeTest && !mAfterTest; + } + + @Override + public void addListener(RunListener listener) { + mRealNotifier.addListener(listener); + } + + @Override + public void removeListener(RunListener listener) { + mRealNotifier.removeListener(listener); + } + + @Override + public void addFirstListener(RunListener listener) { + mRealNotifier.addFirstListener(listener); + } + + @Override + public void fireTestRunStarted(Description description) { + Log.i(RavenwoodAwareTestRunner.TAG, "testRunStarted: " + description); + mRealNotifier.fireTestRunStarted(description); + } + + @Override + public void fireTestRunFinished(Result result) { + Log.i(RavenwoodAwareTestRunner.TAG, "testRunFinished: " + + result.getRunCount() + "," + + result.getFailureCount() + "," + + result.getAssumptionFailureCount() + "," + + result.getIgnoreCount()); + mRealNotifier.fireTestRunFinished(result); + } + + @Override + public void fireTestSuiteStarted(Description description) { + Log.i(RavenwoodAwareTestRunner.TAG, "testSuiteStarted: " + description); + mRealNotifier.fireTestSuiteStarted(description); + + mBeforeTest = true; + mAfterTest = false; + + // Keep track of the current suite, needed if the outer test is a Suite, + // in which case its children are test classes. (not test methods) + mCurrentSuite = description; + mSuiteStack.push(description); + + mOutOfTestFailures.clear(); + } + + @Override + public void fireTestSuiteFinished(Description description) { + Log.i(RavenwoodAwareTestRunner.TAG, "testSuiteFinished: " + description); + mRealNotifier.fireTestSuiteFinished(description); + + maybeHandleOutOfTestFailures(); + + mBeforeTest = true; + mAfterTest = false; + + // Restore the upper suite. + mSuiteStack.pop(); + mCurrentSuite = mSuiteStack.size() == 0 ? null : mSuiteStack.peek(); + } + + @Override + public void fireTestStarted(Description description) throws StoppedByUserException { + Log.i(RavenwoodAwareTestRunner.TAG, "testStarted: " + description); + mRealNotifier.fireTestStarted(description); + + mAfterTest = false; + mBeforeTest = false; + } + + @Override + public void fireTestFailure(Failure failure) { + Log.i(RavenwoodAwareTestRunner.TAG, "testFailure: " + failure); + + if (isInTest()) { + mRealNotifier.fireTestFailure(failure); + } else { + mOutOfTestFailures.add(failure.getException()); + } + } + + @Override + public void fireTestAssumptionFailed(Failure failure) { + Log.i(RavenwoodAwareTestRunner.TAG, "testAssumptionFailed: " + failure); + + if (isInTest()) { + mRealNotifier.fireTestAssumptionFailed(failure); + } else { + mOutOfTestFailures.add(failure.getException()); + } + } + + @Override + public void fireTestIgnored(Description description) { + Log.i(RavenwoodAwareTestRunner.TAG, "testIgnored: " + description); + mRealNotifier.fireTestIgnored(description); + } + + @Override + public void fireTestFinished(Description description) { + Log.i(RavenwoodAwareTestRunner.TAG, "testFinished: " + description); + mRealNotifier.fireTestFinished(description); + + mAfterTest = true; + } + + @Override + public void pleaseStop() { + Log.w(RavenwoodAwareTestRunner.TAG, "pleaseStop:"); + mRealNotifier.pleaseStop(); + } + + /** + * At the end of each Suite, we handle failures happened out of test methods. + * (typically in @BeforeClass or @AfterClasses) + * + * This is to work around b/364395552. + */ + private boolean maybeHandleOutOfTestFailures() { + if (mOutOfTestFailures.size() == 0) { + return false; + } + Throwable th; + if (mOutOfTestFailures.size() == 1) { + th = mOutOfTestFailures.get(0); + } else { + th = new MultipleFailureException(mOutOfTestFailures); + } + if (mBeforeTest) { + reportBeforeTestFailure(mCurrentSuite, th); + return true; + } + if (mAfterTest) { + reportAfterTestFailure(th); + return true; + } + return false; + } + + public void reportBeforeTestFailure(Description suiteDesc, Throwable th) { + // If a failure happens befere running any tests, we'll need to pretend + // as if each test in the suite reported the failure, to work around b/364395552. + for (var child : suiteDesc.getChildren()) { + if (child.isSuite()) { + // If the chiil is still a "parent" -- a test class or a test suite + // -- propagate to its children. + mRealNotifier.fireTestSuiteStarted(child); + reportBeforeTestFailure(child, th); + mRealNotifier.fireTestSuiteFinished(child); + } else { + mRealNotifier.fireTestStarted(child); + Failure f = new Failure(child, th); + if (th instanceof AssumptionViolatedException) { + mRealNotifier.fireTestAssumptionFailed(f); + } else { + mRealNotifier.fireTestFailure(f); + } + mRealNotifier.fireTestFinished(child); + } + } + } + + public void reportAfterTestFailure(Throwable th) { + // Unfortunately, there's no good way to report it, so kill the own process. + RavenwoodAwareTestRunner.onCriticalError( + "Failures detected in @AfterClass, which would be swallowed by tradefed", + th); + } +} 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 03513ab0a2af..ec00e8fea887 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRunnerState.java @@ -20,8 +20,8 @@ import static com.android.ravenwood.common.RavenwoodCommonUtils.ensureIsPublicMe import static org.junit.Assert.fail; import android.annotation.Nullable; +import android.util.Log; -import com.android.internal.annotations.GuardedBy; import com.android.ravenwood.common.RavenwoodRuntimeException; import org.junit.ClassRule; @@ -29,9 +29,7 @@ import org.junit.Rule; import org.junit.rules.TestRule; import org.junit.runner.Description; -import java.io.IOException; import java.lang.reflect.Field; -import java.util.WeakHashMap; /** * Used to store various states associated with the current test runner that's inly needed @@ -45,10 +43,6 @@ import java.util.WeakHashMap; public final class RavenwoodRunnerState { private static final String TAG = "RavenwoodRunnerState"; - @GuardedBy("sStates") - private static final WeakHashMap<RavenwoodAwareTestRunner, RavenwoodRunnerState> sStates = - new WeakHashMap<>(); - private final RavenwoodAwareTestRunner mRunner; /** @@ -58,35 +52,65 @@ public final class RavenwoodRunnerState { mRunner = runner; } - private Description mClassDescription; - private Description mMethodDescription; - + /** + * The RavenwoodConfig used to configure the current Ravenwood environment. + * This can either come from mConfig or mRule. + */ private RavenwoodConfig mCurrentConfig; - private RavenwoodRule mCurrentRule; + /** + * 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 Description getClassDescription() { - return mClassDescription; + public RavenwoodConfig getConfig() { + return mCurrentConfig; } - public void enterTestClass(Description classDescription) throws IOException { - mClassDescription = classDescription; + public void enterTestRunner() { + Log.i(TAG, "enterTestRunner: " + mRunner); + + mHasRavenwoodRule = hasRavenwoodRule(mRunner.mTestJavaClass); + mConfig = extractConfiguration(mRunner.mTestJavaClass); - mHasRavenwoodRule = hasRavenwoodRule(mRunner.getTestClass().getJavaClass()); - mCurrentConfig = extractConfiguration(mRunner.getTestClass().getJavaClass()); + 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(mCurrentConfig); + RavenwoodRuntimeEnvironmentController.init(mRunner); } } - public void exitTestClass() { + public void enterTestClass() { + Log.i(TAG, "enterTestClass: " + mRunner.mTestJavaClass.getName()); + if (mCurrentConfig != null) { - try { + RavenwoodRuntimeEnvironmentController.init(mRunner); + } + } + + public void exitTestClass() { + Log.i(TAG, "exitTestClass: " + mRunner.mTestJavaClass.getName()); + try { + if (mCurrentConfig != null) { RavenwoodRuntimeEnvironmentController.reset(); - } finally { - mClassDescription = null; } + } finally { + mConfig = null; + mRule = null; } } @@ -96,55 +120,40 @@ public final class RavenwoodRunnerState { public void exitTestMethod() { mMethodDescription = null; + RavenwoodRuntimeEnvironmentController.reinit(); } - public void enterRavenwoodRule(RavenwoodRule rule) throws IOException { + 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 (mCurrentConfig != null) { - fail("RavenwoodConfig and RavenwoodRule cannot be used in the same class." - + " Suggest migrating to RavenwoodConfig."); - } - if (mCurrentRule != null) { + if (mRule != null) { fail("Multiple nesting RavenwoodRule's are detected in the same class," + " which is not supported."); } - mCurrentRule = rule; - RavenwoodRuntimeEnvironmentController.init(rule.getConfiguration()); + mRule = rule; + if (mCurrentConfig == null) { + mCurrentConfig = rule.getConfiguration(); + } + RavenwoodRuntimeEnvironmentController.init(mRunner); } public void exitRavenwoodRule(RavenwoodRule rule) { - if (mCurrentRule != rule) { - return; // This happens if the rule did _not_ take effect somehow. - } - - try { - RavenwoodRuntimeEnvironmentController.reset(); - } finally { - mCurrentRule = null; + if (mRule != rule) { + fail("RavenwoodRule did not take effect."); } + mRule = null; } /** * @return a configuration from a test class, if any. */ @Nullable - private RavenwoodConfig extractConfiguration(Class<?> testClass) { + private static RavenwoodConfig extractConfiguration(Class<?> testClass) { var field = findConfigurationField(testClass); if (field == null) { - if (mHasRavenwoodRule) { - // Should be handled by RavenwoodRule - return null; - } - - // If no RavenwoodConfig and no RavenwoodRule, return a default config - return new RavenwoodConfig.Builder().build(); - } - if (mHasRavenwoodRule) { - fail("RavenwoodConfig and RavenwoodRule cannot be used in the same class." - + " Suggest migrating to RavenwoodConfig."); + return null; } try { 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 c2806daf99a1..91778579ab28 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java @@ -40,6 +40,7 @@ import android.os.Build; import android.os.Bundle; import android.os.HandlerThread; import android.os.Looper; +import android.os.Process_ravenwood; import android.os.ServiceManager; import android.os.SystemProperties; import android.provider.DeviceConfig_host; @@ -50,8 +51,10 @@ import android.util.Log; import androidx.test.platform.app.InstrumentationRegistry; import com.android.hoststubgen.hosthelper.HostTestUtils; +import com.android.internal.annotations.GuardedBy; import com.android.internal.os.RuntimeInit; 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; @@ -84,6 +87,7 @@ public class RavenwoodRuntimeEnvironmentController { } private static final String MAIN_THREAD_NAME = "RavenwoodMain"; + private static final String LIBRAVENWOOD_INITIALIZER_NAME = "ravenwood_initializer"; private static final String RAVENWOOD_NATIVE_SYSPROP_NAME = "ravenwood_sysprop"; private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime"; private static final String RAVENWOOD_BUILD_PROP = @@ -137,23 +141,61 @@ public class RavenwoodRuntimeEnvironmentController { return res; } - private static RavenwoodConfig sConfig; - private static RavenwoodSystemProperties sProps; + private static final Object sInitializationLock = new Object(); + + @GuardedBy("sInitializationLock") private static boolean sInitialized = false; + @GuardedBy("sInitializationLock") + private static Throwable sExceptionFromGlobalInit; + + private static RavenwoodAwareTestRunner sRunner; + private static RavenwoodSystemProperties sProps; + /** * Initialize the global environment. */ public static void globalInitOnce() { - if (sInitialized) { - return; + synchronized (sInitializationLock) { + if (!sInitialized) { + // globalInitOnce() is called from class initializer, which cause + // this method to be called recursively, + sInitialized = true; + + // This is the first call. + try { + globalInitInner(); + } catch (Throwable th) { + Log.e(TAG, "globalInit() failed", th); + + sExceptionFromGlobalInit = th; + throw th; + } + } else { + // Subsequent calls. If the first call threw, just throw the same error, to prevent + // the test from running. + if (sExceptionFromGlobalInit != null) { + Log.e(TAG, "globalInit() failed re-throwing the same exception", + sExceptionFromGlobalInit); + + SneakyThrow.sneakyThrow(sExceptionFromGlobalInit); + } + } + } + } + + private static void globalInitInner() { + if (RAVENWOOD_VERBOSE_LOGGING) { + Log.v(TAG, "globalInit() called here...", new RuntimeException("NOT A CRASH")); } - sInitialized = true; + + // Some process-wide initialization. (maybe redirect stdout/stderr) + RavenwoodCommonUtils.loadJniLibrary(LIBRAVENWOOD_INITIALIZER_NAME); // We haven't initialized liblog yet, so directly write to System.out here. - RavenwoodCommonUtils.log(TAG, "globalInit()"); + RavenwoodCommonUtils.log(TAG, "globalInitInner()"); - // Load libravenwood_sysprop first + // Load libravenwood_sysprop before other libraries that may use SystemProperties. var libProp = RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_SYSPROP_NAME); System.load(libProp); RavenwoodRuntimeNative.reloadNativeLibrary(libProp); @@ -165,21 +207,27 @@ public class RavenwoodRuntimeEnvironmentController { RavenwoodSystemProperties.initialize(RAVENWOOD_BUILD_PROP); setSystemProperties(null); - // Make sure libandroid_runtime is loaded. - RavenwoodNativeLoader.loadFrameworkNativeCode(); - - // Redirect stdout/stdin to liblog. - RuntimeInit.redirectLogStreams(); - + // Do this after loading RAVENWOOD_NATIVE_RUNTIME_NAME (which backs Os.setenv()), + // before loadFrameworkNativeCode() (which uses $ANDROID_LOG_TAGS). if (RAVENWOOD_VERBOSE_LOGGING) { RavenwoodCommonUtils.log(TAG, "Force enabling verbose logging"); try { Os.setenv("ANDROID_LOG_TAGS", "*:v", true); } catch (ErrnoException e) { - // Shouldn't happen. + throw new RuntimeException(e); } } + // Make sure libandroid_runtime is loaded. + RavenwoodNativeLoader.loadFrameworkNativeCode(); + + // Redirect stdout/stdin to liblog. + RuntimeInit.redirectLogStreams(); + + // Touch some references early to ensure they're <clinit>'ed + Objects.requireNonNull(Build.TYPE); + Objects.requireNonNull(Build.VERSION.SDK); + System.setProperty(RAVENWOOD_VERSION_JAVA_SYSPROP, "1"); // This will let AndroidJUnit4 use the original runner. System.setProperty("android.junit.runner", @@ -191,12 +239,19 @@ public class RavenwoodRuntimeEnvironmentController { /** * Initialize the environment. */ - public static void init(RavenwoodConfig config) throws IOException { + public static void init(RavenwoodAwareTestRunner runner) { if (RAVENWOOD_VERBOSE_LOGGING) { - Log.i(TAG, "init() called here", new RuntimeException("STACKTRACE")); + Log.v(TAG, "init() called here: " + runner, new RuntimeException("STACKTRACE")); + } + if (sRunner == runner) { + return; + } + if (sRunner != null) { + reset(); } + sRunner = runner; try { - initInner(config); + initInner(runner.mState.getConfig()); } catch (Exception th) { Log.e(TAG, "init() failed", th); reset(); @@ -205,18 +260,16 @@ public class RavenwoodRuntimeEnvironmentController { } private static void initInner(RavenwoodConfig config) throws IOException { - if (sConfig != null) { - throw new RavenwoodRuntimeException("Internal error: init() called without reset()"); - } - sConfig = config; if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) { maybeThrowPendingUncaughtException(false); Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler); } - android.os.Process.init$ravenwood(config.mUid, config.mPid); + RavenwoodRuntimeState.sUid = config.mUid; + RavenwoodRuntimeState.sPid = config.mPid; + RavenwoodRuntimeState.sTargetSdkLevel = config.mTargetSdkLevel; sOriginalIdentityToken = Binder.clearCallingIdentity(); - Binder.restoreCallingIdentity(packBinderIdentityToken(false, config.mUid, config.mPid)); + reinit(); setSystemProperties(config.mSystemProperties); ServiceManager.init$ravenwood(); @@ -269,9 +322,7 @@ public class RavenwoodRuntimeEnvironmentController { config.mInstContext = instContext; config.mTargetContext = targetContext; - final Supplier<Resources> systemResourcesLoader = () -> { - return config.mState.loadResources(null); - }; + final Supplier<Resources> systemResourcesLoader = () -> config.mState.loadResources(null); config.mState.mSystemServerContext = new RavenwoodContext(ANDROID_PACKAGE_NAME, main, systemResourcesLoader); @@ -288,10 +339,14 @@ public class RavenwoodRuntimeEnvironmentController { RavenwoodRuntimeEnvironmentController::dumpStacks, TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); } + } - // Touch some references early to ensure they're <clinit>'ed - Objects.requireNonNull(Build.TYPE); - Objects.requireNonNull(Build.VERSION.SDK); + /** + * Partially re-initialize after each test method invocation + */ + public static void reinit() { + var config = sRunner.mState.getConfig(); + Binder.restoreCallingIdentity(packBinderIdentityToken(false, config.mUid, config.mPid)); } /** @@ -299,13 +354,13 @@ public class RavenwoodRuntimeEnvironmentController { */ public static void reset() { if (RAVENWOOD_VERBOSE_LOGGING) { - Log.i(TAG, "reset() called here", new RuntimeException("STACKTRACE")); + Log.v(TAG, "reset() called here", new RuntimeException("STACKTRACE")); } - if (sConfig == null) { + if (sRunner == null) { throw new RavenwoodRuntimeException("Internal error: reset() already called"); } - var config = sConfig; - sConfig = null; + var config = sRunner.mState.getConfig(); + sRunner = null; if (ENABLE_TIMEOUT_STACKS) { sPendingTimeout.cancel(false); @@ -339,8 +394,8 @@ public class RavenwoodRuntimeEnvironmentController { if (sOriginalIdentityToken != -1) { Binder.restoreCallingIdentity(sOriginalIdentityToken); } - android.os.Process.reset$ravenwood(); - + RavenwoodRuntimeState.reset(); + Process_ravenwood.reset(); DeviceConfig_host.reset(); try { diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java index b4b871524291..787058545fed 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodTestStats.java @@ -18,6 +18,9 @@ package android.platform.test.ravenwood; import android.util.Log; import org.junit.runner.Description; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunListener; +import org.junit.runner.notification.RunNotifier; import java.io.File; import java.io.IOException; @@ -27,7 +30,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; /** @@ -39,7 +42,7 @@ import java.util.Map; */ public class RavenwoodTestStats { private static final String TAG = "RavenwoodTestStats"; - private static final String HEADER = "Module,Class,ClassDesc,Passed,Failed,Skipped"; + private static final String HEADER = "Module,Class,OuterClass,Passed,Failed,Skipped"; private static RavenwoodTestStats sInstance; @@ -66,7 +69,7 @@ public class RavenwoodTestStats { private final PrintWriter mOutputWriter; private final String mTestModuleName; - public final Map<Description, Map<Description, Result>> mStats = new HashMap<>(); + public final Map<String, Map<String, Result>> mStats = new LinkedHashMap<>(); /** Ctor */ public RavenwoodTestStats() { @@ -90,9 +93,7 @@ public class RavenwoodTestStats { // Create the "latest" symlink. Path symlink = Paths.get(tmpdir, basename + "latest.csv"); try { - if (Files.exists(symlink)) { - Files.delete(symlink); - } + Files.deleteIfExists(symlink); Files.createSymbolicLink(symlink, Paths.get(mOutputFile.getName())); } catch (IOException e) { @@ -117,75 +118,129 @@ public class RavenwoodTestStats { return cwd.getName(); } - private void addResult(Description classDescription, Description methodDescription, + private void addResult(String className, String methodName, Result result) { - mStats.compute(classDescription, (classDesc, value) -> { + mStats.compute(className, (className_, value) -> { if (value == null) { - value = new HashMap<>(); + value = new LinkedHashMap<>(); + } + // If the result is already set, don't overwrite it. + if (!value.containsKey(methodName)) { + value.put(methodName, result); } - value.put(methodDescription, result); return value; }); } /** - * Call it when a test class is skipped. - */ - public void onClassSkipped(Description classDescription) { - addResult(classDescription, Description.EMPTY, Result.Skipped); - onClassFinished(classDescription); - } - - /** * Call it when a test method is finished. */ - public void onTestFinished(Description classDescription, Description testDescription, - Result result) { - addResult(classDescription, testDescription, result); + private void onTestFinished(String className, String testName, Result result) { + addResult(className, testName, result); } /** - * Call it when a test class is finished. + * Dump all the results and clear it. */ - public void onClassFinished(Description classDescription) { - int passed = 0; - int skipped = 0; - int failed = 0; - var stats = mStats.get(classDescription); - if (stats == null) { - return; - } - for (var e : stats.values()) { - switch (e) { - case Passed: passed++; break; - case Skipped: skipped++; break; - case Failed: failed++; break; + private void dumpAllAndClear() { + for (var entry : mStats.entrySet()) { + int passed = 0; + int skipped = 0; + int failed = 0; + var className = entry.getKey(); + + for (var e : entry.getValue().values()) { + switch (e) { + case Passed: + passed++; + break; + case Skipped: + skipped++; + break; + case Failed: + failed++; + break; + } } + + mOutputWriter.printf("%s,%s,%s,%d,%d,%d\n", + mTestModuleName, className, getOuterClassName(className), + passed, failed, skipped); } + mOutputWriter.flush(); + mStats.clear(); + } - var testClass = extractTestClass(classDescription); + private static String getOuterClassName(String className) { + // Just delete the '$', because I'm not sure if the className we get here is actaully a + // valid class name that does exist. (it might have a parameter name, etc?) + int p = className.indexOf('$'); + if (p < 0) { + return className; + } + return className.substring(0, p); + } - mOutputWriter.printf("%s,%s,%s,%d,%d,%d\n", - mTestModuleName, (testClass == null ? "?" : testClass.getCanonicalName()), - classDescription, passed, failed, skipped); - mOutputWriter.flush(); + public void attachToRunNotifier(RunNotifier notifier) { + notifier.addListener(mRunListener); } - /** - * Try to extract the class from a description, which is needed because - * ParameterizedAndroidJunit4's description doesn't contain a class. - */ - private Class<?> extractTestClass(Description desc) { - if (desc.getTestClass() != null) { - return desc.getTestClass(); + private final RunListener mRunListener = new RunListener() { + @Override + public void testSuiteStarted(Description description) { + Log.d(TAG, "testSuiteStarted: " + description); } - // Look into the children. - for (var child : desc.getChildren()) { - var fromChild = extractTestClass(child); - if (fromChild != null) { - return fromChild; - } + + @Override + public void testSuiteFinished(Description description) { + Log.d(TAG, "testSuiteFinished: " + description); } - return null; - } + + @Override + public void testRunStarted(Description description) { + Log.d(TAG, "testRunStarted: " + description); + } + + @Override + public void testRunFinished(org.junit.runner.Result result) { + Log.d(TAG, "testRunFinished: " + result); + + dumpAllAndClear(); + } + + @Override + public void testStarted(Description description) { + Log.d(TAG, " testStarted: " + description); + } + + @Override + public void testFinished(Description description) { + Log.d(TAG, " testFinished: " + description); + + // Send "Passed", but if there's already another result sent for this, this won't + // override it. + onTestFinished(description.getClassName(), description.getMethodName(), Result.Passed); + } + + @Override + public void testFailure(Failure failure) { + Log.d(TAG, " testFailure: " + failure); + + var description = failure.getDescription(); + onTestFinished(description.getClassName(), description.getMethodName(), Result.Failed); + } + + @Override + public void testAssumptionFailure(Failure failure) { + Log.d(TAG, " testAssumptionFailure: " + failure); + var description = failure.getDescription(); + onTestFinished(description.getClassName(), description.getMethodName(), Result.Skipped); + } + + @Override + public void testIgnored(Description description) { + Log.d(TAG, " testIgnored: " + description); + onTestFinished(description.getClassName(), description.getMethodName(), Result.Skipped); + } + }; } diff --git a/ravenwood/junit-src/android/platform/test/annotations/RavenwoodTestRunnerInitializing.java b/ravenwood/junit-src/android/platform/test/annotations/RavenwoodTestRunnerInitializing.java new file mode 100644 index 000000000000..3bba27ab8f1d --- /dev/null +++ b/ravenwood/junit-src/android/platform/test/annotations/RavenwoodTestRunnerInitializing.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 android.platform.test.annotations; + +import static java.lang.annotation.ElementType.METHOD; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * An annotation similar to JUnit's BeforeClass, but this gets executed before + * the inner runner is instantiated, and only on Ravenwood. + * It can be used to initialize what's needed by the inner runner. + */ +@Target({METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface RavenwoodTestRunnerInitializing { +} diff --git a/services/core/java/com/android/server/integrity/serializer/RuleSerializeException.java b/ravenwood/junit-src/android/platform/test/annotations/internal/InnerRunner.java index 60cfc4876414..dde53a5ea03c 100644 --- a/services/core/java/com/android/server/integrity/serializer/RuleSerializeException.java +++ b/ravenwood/junit-src/android/platform/test/annotations/internal/InnerRunner.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -13,20 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package android.platform.test.annotations.internal; -package com.android.server.integrity.serializer; +import static java.lang.annotation.ElementType.TYPE; -import android.annotation.NonNull; +import org.junit.runner.Runner; -/** - * Thrown when rule serialization fails. - */ -public class RuleSerializeException extends Exception { - public RuleSerializeException(@NonNull String message) { - super(message); - } +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; - public RuleSerializeException(@NonNull String message, @NonNull Throwable cause) { - super(message, cause); - } +@Inherited +@Target({TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface InnerRunner { + Class<? extends Runner> value(); } diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java deleted file mode 100644 index 5ba972df1193..000000000000 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java +++ /dev/null @@ -1,733 +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_VERBOSE_LOGGING; -import static com.android.ravenwood.common.RavenwoodCommonUtils.ensureIsPublicVoidMethod; -import static com.android.ravenwood.common.RavenwoodCommonUtils.isOnRavenwood; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.ElementType.TYPE; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.util.Log; - -import org.junit.Assume; -import org.junit.AssumptionViolatedException; -import org.junit.internal.builders.AllDefaultPossibilitiesBuilder; -import org.junit.rules.TestRule; -import org.junit.runner.Description; -import org.junit.runner.Result; -import org.junit.runner.Runner; -import org.junit.runner.manipulation.Filter; -import org.junit.runner.manipulation.Filterable; -import org.junit.runner.manipulation.InvalidOrderingException; -import org.junit.runner.manipulation.NoTestsRemainException; -import org.junit.runner.manipulation.Orderable; -import org.junit.runner.manipulation.Orderer; -import org.junit.runner.manipulation.Sortable; -import org.junit.runner.manipulation.Sorter; -import org.junit.runner.notification.Failure; -import org.junit.runner.notification.RunListener; -import org.junit.runner.notification.RunNotifier; -import org.junit.runner.notification.StoppedByUserException; -import org.junit.runners.BlockJUnit4ClassRunner; -import org.junit.runners.Suite; -import org.junit.runners.model.MultipleFailureException; -import org.junit.runners.model.RunnerBuilder; -import org.junit.runners.model.Statement; -import org.junit.runners.model.TestClass; - -import java.lang.annotation.Annotation; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Stack; -import java.util.function.BiConsumer; - -/** - * A test runner used for Ravenwood. - * - * It will delegate to another runner specified with {@link InnerRunner} - * (default = {@link BlockJUnit4ClassRunner}) with the following features. - * - Add a {@link RavenwoodAwareTestRunnerHook#onRunnerInitializing} hook, which is called before - * the inner runner gets a chance to run. This can be used to initialize stuff used by the - * inner runner. - * - Add hook points, which are handed by RavenwoodAwareTestRunnerHook, with help from - * the four test rules such as {@link #sImplicitClassOuterRule}, which are also injected by - * the ravenizer tool. - * - * We use this runner to: - * - Initialize the bare minimum environmnet just to be enough to make the actual test runners - * happy. - * - Handle {@link android.platform.test.annotations.DisabledOnRavenwood}. - * - * This class is built such that it can also be used on a real device, but in that case - * it will basically just delegate to the inner wrapper, and won't do anything special. - * (no hooks, etc.) - */ -public final class RavenwoodAwareTestRunner extends Runner implements Filterable, Orderable { - public static final String TAG = "Ravenwood"; - - @Inherited - @Target({TYPE}) - @Retention(RetentionPolicy.RUNTIME) - public @interface InnerRunner { - Class<? extends Runner> value(); - } - - /** - * An annotation similar to JUnit's BeforeClass, but this gets executed before - * the inner runner is instantiated, and only on Ravenwood. - * It can be used to initialize what's needed by the inner runner. - */ - @Target({METHOD}) - @Retention(RetentionPolicy.RUNTIME) - public @interface RavenwoodTestRunnerInitializing { - } - - /** Scope of a hook. */ - public enum Scope { - Class, - Instance, - } - - /** Order of a hook. */ - public enum Order { - Outer, - Inner, - } - - // The following four rule instances will be injected to tests by the Ravenizer tool. - private static class RavenwoodClassOuterRule implements TestRule { - @Override - public Statement apply(Statement base, Description description) { - return getCurrentRunner().wrapWithHooks(base, description, Scope.Class, Order.Outer); - } - } - - private static class RavenwoodClassInnerRule implements TestRule { - @Override - public Statement apply(Statement base, Description description) { - return getCurrentRunner().wrapWithHooks(base, description, Scope.Class, Order.Inner); - } - } - - private static class RavenwoodInstanceOuterRule implements TestRule { - @Override - public Statement apply(Statement base, Description description) { - return getCurrentRunner().wrapWithHooks( - base, description, Scope.Instance, Order.Outer); - } - } - - private static class RavenwoodInstanceInnerRule implements TestRule { - @Override - public Statement apply(Statement base, Description description) { - return getCurrentRunner().wrapWithHooks( - base, description, Scope.Instance, Order.Inner); - } - } - - public static final TestRule sImplicitClassOuterRule = new RavenwoodClassOuterRule(); - public static final TestRule sImplicitClassInnerRule = new RavenwoodClassInnerRule(); - public static final TestRule sImplicitInstOuterRule = new RavenwoodInstanceOuterRule(); - public static final TestRule sImplicitInstInnerRule = new RavenwoodInstanceOuterRule(); - - public static final String IMPLICIT_CLASS_OUTER_RULE_NAME = "sImplicitClassOuterRule"; - public static final String IMPLICIT_CLASS_INNER_RULE_NAME = "sImplicitClassInnerRule"; - public static final String IMPLICIT_INST_OUTER_RULE_NAME = "sImplicitInstOuterRule"; - public static final String IMPLICIT_INST_INNER_RULE_NAME = "sImplicitInstInnerRule"; - - /** Keeps track of the runner on the current thread. */ - private static final ThreadLocal<RavenwoodAwareTestRunner> sCurrentRunner = new ThreadLocal<>(); - - static RavenwoodAwareTestRunner getCurrentRunner() { - var runner = sCurrentRunner.get(); - if (runner == null) { - throw new RuntimeException("Current test runner not set!"); - } - return runner; - } - - private final Class<?> mTestJavaClass; - private TestClass mTestClass = null; - private Runner mRealRunner = null; - private Description mDescription = null; - private Throwable mExceptionInConstructor = null; - private boolean mRealRunnerTakesRunnerBuilder = false; - - /** - * Stores internal states / methods associated with this runner that's only needed in - * junit-impl. - */ - final RavenwoodRunnerState mState = new RavenwoodRunnerState(this); - - private Error logAndFail(String message, Throwable exception) { - Log.e(TAG, message, exception); - throw new AssertionError(message, exception); - } - - public TestClass getTestClass() { - return mTestClass; - } - - /** - * Constructor. - */ - public RavenwoodAwareTestRunner(Class<?> testClass) { - mTestJavaClass = testClass; - try { - performGlobalInitialization(); - - /* - * If the class has @DisabledOnRavenwood, then we'll delegate to - * ClassSkippingTestRunner, which simply skips it. - * - * We need to do it before instantiating TestClass for b/367694651. - */ - if (isOnRavenwood() && !RavenwoodAwareTestRunnerHook.shouldRunClassOnRavenwood( - testClass)) { - mRealRunner = new ClassSkippingTestRunner(testClass); - mDescription = mRealRunner.getDescription(); - return; - } - - mTestClass = new TestClass(testClass); - - Log.v(TAG, "RavenwoodAwareTestRunner starting for " + testClass.getCanonicalName()); - - onRunnerInitializing(); - - // Find the real runner. - final Class<? extends Runner> realRunnerClass; - final InnerRunner innerRunnerAnnotation = mTestClass.getAnnotation(InnerRunner.class); - if (innerRunnerAnnotation != null) { - realRunnerClass = innerRunnerAnnotation.value(); - } else { - // Default runner. - realRunnerClass = BlockJUnit4ClassRunner.class; - } - - try { - Log.i(TAG, "Initializing the inner runner: " + realRunnerClass); - - mRealRunner = instantiateRealRunner(realRunnerClass, testClass); - mDescription = mRealRunner.getDescription(); - - } catch (InstantiationException | IllegalAccessException - | InvocationTargetException | NoSuchMethodException e) { - throw logAndFail("Failed to instantiate " + realRunnerClass, e); - } - } catch (Throwable th) { - // If we throw in the constructor, Tradefed may not report it and just ignore the class, - // so record it and throw it when the test actually started. - Log.e(TAG, "Fatal: Exception detected in constructor", th); - mExceptionInConstructor = new RuntimeException("Exception detected in constructor", - th); - mDescription = Description.createTestDescription(testClass, "Constructor"); - - // This is for testing if tradefed is fixed. - if ("1".equals(System.getenv("RAVENWOOD_THROW_EXCEPTION_IN_TEST_RUNNER"))) { - throw th; - } - } - } - - private Runner instantiateRealRunner( - Class<? extends Runner> realRunnerClass, - Class<?> testClass) - throws NoSuchMethodException, InvocationTargetException, InstantiationException, - IllegalAccessException { - try { - return realRunnerClass.getConstructor(Class.class).newInstance(testClass); - } catch (NoSuchMethodException e) { - var constructor = realRunnerClass.getConstructor(Class.class, RunnerBuilder.class); - mRealRunnerTakesRunnerBuilder = true; - return constructor.newInstance(testClass, new AllDefaultPossibilitiesBuilder()); - } - } - - private void performGlobalInitialization() { - if (!isOnRavenwood()) { - return; - } - RavenwoodAwareTestRunnerHook.performGlobalInitialization(); - } - - /** - * Run the bare minimum setup to initialize the wrapped runner. - */ - // This method is called by the ctor, so never make it virtual. - private void onRunnerInitializing() { - if (!isOnRavenwood()) { - return; - } - - RavenwoodAwareTestRunnerHook.onRunnerInitializing(this, mTestClass); - - // Hook point to allow more customization. - runAnnotatedMethodsOnRavenwood(RavenwoodTestRunnerInitializing.class, null); - } - - private void runAnnotatedMethodsOnRavenwood(Class<? extends Annotation> annotationClass, - Object instance) { - if (!isOnRavenwood()) { - return; - } - Log.v(TAG, "runAnnotatedMethodsOnRavenwood() " + annotationClass.getName()); - - for (var method : getTestClass().getAnnotatedMethods(annotationClass)) { - ensureIsPublicVoidMethod(method.getMethod(), /* isStatic=*/ instance == null); - - var methodDesc = method.getDeclaringClass().getName() + "." - + method.getMethod().toString(); - try { - method.getMethod().invoke(instance); - } catch (IllegalAccessException | InvocationTargetException e) { - throw logAndFail("Caught exception while running method " + methodDesc, e); - } - } - } - - @Override - public Description getDescription() { - return mDescription; - } - - @Override - public void run(RunNotifier realNotifier) { - final RavenwoodRunNotifier notifier = new RavenwoodRunNotifier(realNotifier); - - if (mRealRunner instanceof ClassSkippingTestRunner) { - mRealRunner.run(notifier); - RavenwoodAwareTestRunnerHook.onClassSkipped(getDescription()); - return; - } - - Log.v(TAG, "Starting " + mTestJavaClass.getCanonicalName()); - if (RAVENWOOD_VERBOSE_LOGGING) { - dumpDescription(getDescription()); - } - - if (maybeReportExceptionFromConstructor(notifier)) { - return; - } - - // TODO(b/365976974): handle nested classes better - final boolean skipRunnerHook = - mRealRunnerTakesRunnerBuilder && mRealRunner instanceof Suite; - - sCurrentRunner.set(this); - try { - if (!skipRunnerHook) { - try { - RavenwoodAwareTestRunnerHook.onBeforeInnerRunnerStart( - this, getDescription()); - } catch (Throwable th) { - notifier.reportBeforeTestFailure(getDescription(), th); - return; - } - } - - // Delegate to the inner runner. - mRealRunner.run(notifier); - } finally { - sCurrentRunner.remove(); - - if (!skipRunnerHook) { - try { - RavenwoodAwareTestRunnerHook.onAfterInnerRunnerFinished( - this, getDescription()); - } catch (Throwable th) { - notifier.reportAfterTestFailure(th); - } - } - } - } - - /** Throw the exception detected in the constructor, if any. */ - private boolean maybeReportExceptionFromConstructor(RunNotifier notifier) { - if (mExceptionInConstructor == null) { - return false; - } - notifier.fireTestStarted(mDescription); - notifier.fireTestFailure(new Failure(mDescription, mExceptionInConstructor)); - notifier.fireTestFinished(mDescription); - - return true; - } - - @Override - public void filter(Filter filter) throws NoTestsRemainException { - if (mRealRunner instanceof Filterable r) { - r.filter(filter); - } - } - - @Override - public void order(Orderer orderer) throws InvalidOrderingException { - if (mRealRunner instanceof Orderable r) { - r.order(orderer); - } - } - - @Override - public void sort(Sorter sorter) { - if (mRealRunner instanceof Sortable r) { - r.sort(sorter); - } - } - - private Statement wrapWithHooks(Statement base, Description description, Scope scope, - Order order) { - if (!isOnRavenwood()) { - return base; - } - return new Statement() { - @Override - public void evaluate() throws Throwable { - runWithHooks(description, scope, order, base); - } - }; - } - - private void runWithHooks(Description description, Scope scope, Order order, Runnable r) - throws Throwable { - runWithHooks(description, scope, order, new Statement() { - @Override - public void evaluate() throws Throwable { - r.run(); - } - }); - } - - private void runWithHooks(Description description, Scope scope, Order order, Statement s) - throws Throwable { - if (isOnRavenwood()) { - Assume.assumeTrue( - RavenwoodAwareTestRunnerHook.onBefore(this, description, scope, order)); - } - try { - s.evaluate(); - if (isOnRavenwood()) { - RavenwoodAwareTestRunnerHook.onAfter(this, description, scope, order, null); - } - } catch (Throwable t) { - boolean shouldThrow = true; - if (isOnRavenwood()) { - shouldThrow = RavenwoodAwareTestRunnerHook.onAfter( - this, description, scope, order, t); - } - if (shouldThrow) { - throw t; - } - } - } - - /** - * A runner that simply skips a class. It still has to support {@link Filterable} - * because otherwise the result still says "SKIPPED" even when it's not included in the - * filter. - */ - private static class ClassSkippingTestRunner extends Runner implements Filterable { - private final Description mDescription; - private boolean mFilteredOut; - - ClassSkippingTestRunner(Class<?> testClass) { - mDescription = Description.createTestDescription(testClass, testClass.getSimpleName()); - mFilteredOut = false; - } - - @Override - public Description getDescription() { - return mDescription; - } - - @Override - public void run(RunNotifier notifier) { - if (mFilteredOut) { - return; - } - notifier.fireTestSuiteStarted(mDescription); - notifier.fireTestIgnored(mDescription); - notifier.fireTestSuiteFinished(mDescription); - } - - @Override - public void filter(Filter filter) throws NoTestsRemainException { - if (filter.shouldRun(mDescription)) { - mFilteredOut = false; - } else { - throw new NoTestsRemainException(); - } - } - } - - private void dumpDescription(Description desc) { - dumpDescription(desc, "[TestDescription]=", " "); - } - - private void dumpDescription(Description desc, String header, String indent) { - Log.v(TAG, indent + header + desc); - - var children = desc.getChildren(); - var childrenIndent = " " + indent; - for (int i = 0; i < children.size(); i++) { - dumpDescription(children.get(i), "#" + i + ": ", childrenIndent); - } - } - - /** - * A run notifier that wraps another notifier and provides the following features: - * - Handle a failure that happened before testStarted and testEnded (typically that means - * it's from @BeforeClass or @AfterClass, or a @ClassRule) and deliver it as if - * individual tests in the class reported it. This is for b/364395552. - * - * - Logging. - */ - private class RavenwoodRunNotifier extends RunNotifier { - private final RunNotifier mRealNotifier; - - private final Stack<Description> mSuiteStack = new Stack<>(); - private Description mCurrentSuite = null; - private final ArrayList<Throwable> mOutOfTestFailures = new ArrayList<>(); - - private boolean mBeforeTest = true; - private boolean mAfterTest = false; - - private RavenwoodRunNotifier(RunNotifier realNotifier) { - mRealNotifier = realNotifier; - } - - private boolean isInTest() { - return !mBeforeTest && !mAfterTest; - } - - @Override - public void addListener(RunListener listener) { - mRealNotifier.addListener(listener); - } - - @Override - public void removeListener(RunListener listener) { - mRealNotifier.removeListener(listener); - } - - @Override - public void addFirstListener(RunListener listener) { - mRealNotifier.addFirstListener(listener); - } - - @Override - public void fireTestRunStarted(Description description) { - Log.i(TAG, "testRunStarted: " + description); - mRealNotifier.fireTestRunStarted(description); - } - - @Override - public void fireTestRunFinished(Result result) { - Log.i(TAG, "testRunFinished: " - + result.getRunCount() + "," - + result.getFailureCount() + "," - + result.getAssumptionFailureCount() + "," - + result.getIgnoreCount()); - mRealNotifier.fireTestRunFinished(result); - } - - @Override - public void fireTestSuiteStarted(Description description) { - Log.i(TAG, "testSuiteStarted: " + description); - mRealNotifier.fireTestSuiteStarted(description); - - mBeforeTest = true; - mAfterTest = false; - - // Keep track of the current suite, needed if the outer test is a Suite, - // in which case its children are test classes. (not test methods) - mCurrentSuite = description; - mSuiteStack.push(description); - - mOutOfTestFailures.clear(); - } - - @Override - public void fireTestSuiteFinished(Description description) { - Log.i(TAG, "testSuiteFinished: " + description); - mRealNotifier.fireTestSuiteFinished(description); - - maybeHandleOutOfTestFailures(); - - mBeforeTest = true; - mAfterTest = false; - - // Restore the upper suite. - mSuiteStack.pop(); - mCurrentSuite = mSuiteStack.size() == 0 ? null : mSuiteStack.peek(); - } - - @Override - public void fireTestStarted(Description description) throws StoppedByUserException { - Log.i(TAG, "testStarted: " + description); - mRealNotifier.fireTestStarted(description); - - mAfterTest = false; - mBeforeTest = false; - } - - @Override - public void fireTestFailure(Failure failure) { - Log.i(TAG, "testFailure: " + failure); - - if (isInTest()) { - mRealNotifier.fireTestFailure(failure); - } else { - mOutOfTestFailures.add(failure.getException()); - } - } - - @Override - public void fireTestAssumptionFailed(Failure failure) { - Log.i(TAG, "testAssumptionFailed: " + failure); - - if (isInTest()) { - mRealNotifier.fireTestAssumptionFailed(failure); - } else { - mOutOfTestFailures.add(failure.getException()); - } - } - - @Override - public void fireTestIgnored(Description description) { - Log.i(TAG, "testIgnored: " + description); - mRealNotifier.fireTestIgnored(description); - } - - @Override - public void fireTestFinished(Description description) { - Log.i(TAG, "testFinished: " + description); - mRealNotifier.fireTestFinished(description); - - mAfterTest = true; - } - - @Override - public void pleaseStop() { - Log.w(TAG, "pleaseStop:"); - mRealNotifier.pleaseStop(); - } - - /** - * At the end of each Suite, we handle failures happened out of test methods. - * (typically in @BeforeClass or @AfterClasses) - * - * This is to work around b/364395552. - */ - private boolean maybeHandleOutOfTestFailures() { - if (mOutOfTestFailures.size() == 0) { - return false; - } - Throwable th; - if (mOutOfTestFailures.size() == 1) { - th = mOutOfTestFailures.get(0); - } else { - th = new MultipleFailureException(mOutOfTestFailures); - } - if (mBeforeTest) { - reportBeforeTestFailure(mCurrentSuite, th); - return true; - } - if (mAfterTest) { - reportAfterTestFailure(th); - return true; - } - return false; - } - - public void reportBeforeTestFailure(Description suiteDesc, Throwable th) { - // If a failure happens befere running any tests, we'll need to pretend - // as if each test in the suite reported the failure, to work around b/364395552. - for (var child : suiteDesc.getChildren()) { - if (child.isSuite()) { - // If the chiil is still a "parent" -- a test class or a test suite - // -- propagate to its children. - mRealNotifier.fireTestSuiteStarted(child); - reportBeforeTestFailure(child, th); - mRealNotifier.fireTestSuiteFinished(child); - } else { - mRealNotifier.fireTestStarted(child); - Failure f = new Failure(child, th); - if (th instanceof AssumptionViolatedException) { - mRealNotifier.fireTestAssumptionFailed(f); - } else { - mRealNotifier.fireTestFailure(f); - } - mRealNotifier.fireTestFinished(child); - } - } - } - - public void reportAfterTestFailure(Throwable th) { - // Unfortunately, there's no good way to report it, so kill the own process. - onCriticalError( - "Failures detected in @AfterClass, which would be swallowed by tradefed", - th); - } - } - - private static volatile BiConsumer<String, Throwable> sCriticalErrorHanler; - - private void onCriticalError(@NonNull String message, @Nullable Throwable th) { - Log.e(TAG, "Critical error! " + message, th); - var handler = sCriticalErrorHanler; - if (handler == null) { - handler = sDefaultCriticalErrorHandler; - } - handler.accept(message, th); - } - - private static BiConsumer<String, Throwable> sDefaultCriticalErrorHandler = (message, th) -> { - Log.e(TAG, "Ravenwood cannot continue. Killing self process.", th); - System.exit(1); - }; - - /** - * Contains Ravenwood private APIs. - */ - public static class RavenwoodPrivate { - private RavenwoodPrivate() { - } - - /** - * Set a listener for onCriticalError(), for testing. If a listener is set, we won't call - * System.exit(). - */ - public void setCriticalErrorHandler( - @Nullable BiConsumer<String, Throwable> handler) { - sCriticalErrorHanler = handler; - } - } - - private static final RavenwoodPrivate sRavenwoodPrivate = new RavenwoodPrivate(); - - public static RavenwoodPrivate private$ravenwood() { - return sRavenwoodPrivate; - } -} diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java new file mode 100644 index 000000000000..31a14164bd51 --- /dev/null +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerBase.java @@ -0,0 +1,101 @@ +/* + * 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 android.platform.test.annotations.internal.InnerRunner; +import android.util.Log; + +import org.junit.internal.builders.AllDefaultPossibilitiesBuilder; +import org.junit.runner.Description; +import org.junit.runner.Runner; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.Filterable; +import org.junit.runner.manipulation.InvalidOrderingException; +import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runner.manipulation.Orderable; +import org.junit.runner.manipulation.Orderer; +import org.junit.runner.manipulation.Sortable; +import org.junit.runner.manipulation.Sorter; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.BlockJUnit4ClassRunner; +import org.junit.runners.model.RunnerBuilder; +import org.junit.runners.model.TestClass; + +abstract class RavenwoodAwareTestRunnerBase extends Runner implements Filterable, Orderable { + private static final String TAG = "Ravenwood"; + + boolean mRealRunnerTakesRunnerBuilder = false; + + abstract Runner getRealRunner(); + + final Runner instantiateRealRunner(TestClass testClass) { + // Find the real runner. + final Class<? extends Runner> runnerClass; + final InnerRunner innerRunnerAnnotation = testClass.getAnnotation(InnerRunner.class); + if (innerRunnerAnnotation != null) { + runnerClass = innerRunnerAnnotation.value(); + } else { + // Default runner. + runnerClass = BlockJUnit4ClassRunner.class; + } + + try { + Log.i(TAG, "Initializing the inner runner: " + runnerClass); + try { + return runnerClass.getConstructor(Class.class) + .newInstance(testClass.getJavaClass()); + } catch (NoSuchMethodException e) { + var constructor = runnerClass.getConstructor(Class.class, RunnerBuilder.class); + mRealRunnerTakesRunnerBuilder = true; + return constructor.newInstance( + testClass.getJavaClass(), new AllDefaultPossibilitiesBuilder()); + } + } catch (ReflectiveOperationException e) { + throw logAndFail("Failed to instantiate " + runnerClass, e); + } + } + + final Error logAndFail(String message, Throwable exception) { + Log.e(TAG, message, exception); + return new AssertionError(message, exception); + } + + @Override + public final Description getDescription() { + return getRealRunner().getDescription(); + } + + @Override + public final void filter(Filter filter) throws NoTestsRemainException { + if (getRealRunner() instanceof Filterable r) { + r.filter(filter); + } + } + + @Override + public final void order(Orderer orderer) throws InvalidOrderingException { + if (getRealRunner() instanceof Orderable r) { + r.order(orderer); + } + } + + @Override + public final void sort(Sorter sorter) { + if (getRealRunner() instanceof Sortable r) { + r.sort(sorter); + } + } +} diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java index 37b0abcceede..d8f2b705d539 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodConfig.java @@ -23,6 +23,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Instrumentation; import android.content.Context; +import android.os.Build; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -67,7 +68,7 @@ public final class RavenwoodConfig { String mTargetPackageName; int mMinSdkLevel; - int mTargetSdkLevel; + int mTargetSdkLevel = Build.VERSION_CODES.CUR_DEVELOPMENT; boolean mProvideMainThread = false; diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java index 93a6806ed1f4..3d6ac0f37050 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java @@ -24,6 +24,8 @@ import android.app.Instrumentation; import android.content.Context; import android.platform.test.annotations.DisabledOnRavenwood; +import androidx.test.platform.app.InstrumentationRegistry; + import com.android.ravenwood.common.RavenwoodCommonUtils; import org.junit.rules.TestRule; @@ -219,8 +221,7 @@ public final class RavenwoodRule implements TestRule { */ @Deprecated public Context getContext() { - return Objects.requireNonNull(mConfiguration.mInstContext, - "Context is only available during @Test execution"); + return InstrumentationRegistry.getInstrumentation().getContext(); } /** @@ -230,8 +231,7 @@ public final class RavenwoodRule implements TestRule { */ @Deprecated public Instrumentation getInstrumentation() { - return Objects.requireNonNull(mConfiguration.mInstrumentation, - "Instrumentation is only available during @Test execution"); + return InstrumentationRegistry.getInstrumentation(); } @Override @@ -242,15 +242,11 @@ public final class RavenwoodRule implements TestRule { return new Statement() { @Override public void evaluate() throws Throwable { - RavenwoodAwareTestRunnerHook.onRavenwoodRuleEnter( - RavenwoodAwareTestRunner.getCurrentRunner(), description, - RavenwoodRule.this); + RavenwoodAwareTestRunner.onRavenwoodRuleEnter(description, RavenwoodRule.this); try { base.evaluate(); } finally { - RavenwoodAwareTestRunnerHook.onRavenwoodRuleExit( - RavenwoodAwareTestRunner.getCurrentRunner(), description, - RavenwoodRule.this); + RavenwoodAwareTestRunner.onRavenwoodRuleExit(description, RavenwoodRule.this); } } }; diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java new file mode 100644 index 000000000000..b4b751788c6e --- /dev/null +++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java @@ -0,0 +1,70 @@ +/* + * 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 android.util.Log; + +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runner.Runner; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.model.Statement; +import org.junit.runners.model.TestClass; + +/** + * A simple pass-through runner that just delegates to the inner runner without doing + * anything special (no hooks, etc.). + * + * This is only used when a real device-side test has Ravenizer enabled. + */ +public class RavenwoodAwareTestRunner extends RavenwoodAwareTestRunnerBase { + private static final String TAG = "Ravenwood"; + + private static class NopRule implements TestRule { + @Override + public Statement apply(Statement base, Description description) { + return base; + } + } + + public static final TestRule sImplicitClassOuterRule = new NopRule(); + public static final TestRule sImplicitClassInnerRule = sImplicitClassOuterRule; + public static final TestRule sImplicitInstOuterRule = sImplicitClassOuterRule; + public static final TestRule sImplicitInstInnerRule = sImplicitClassOuterRule; + + private final Runner mRealRunner; + + public RavenwoodAwareTestRunner(Class<?> clazz) { + Log.v(TAG, "RavenwoodAwareTestRunner starting for " + clazz.getCanonicalName()); + mRealRunner = instantiateRealRunner(new TestClass(clazz)); + } + + @Override + Runner getRealRunner() { + return mRealRunner; + } + + @Override + public void run(RunNotifier notifier) { + mRealRunner.run(notifier); + } + + static void onRavenwoodRuleEnter(Description description, RavenwoodRule rule) { + } + + static void onRavenwoodRuleExit(Description description, RavenwoodRule rule) { + } +} diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java deleted file mode 100644 index aa8c29936082..000000000000 --- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java +++ /dev/null @@ -1,101 +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 android.platform.test.ravenwood.RavenwoodAwareTestRunner.Order; -import android.platform.test.ravenwood.RavenwoodAwareTestRunner.Scope; - -import org.junit.runner.Description; -import org.junit.runners.model.TestClass; - -/** - * Provide hook points created by {@link RavenwoodAwareTestRunner}. This is a version - * that's used on a device side test. - * - * All methods are no-op in real device tests. - * - * TODO: Use some kind of factory to provide different implementation for the device test - * and the ravenwood test. - */ -public class RavenwoodAwareTestRunnerHook { - private RavenwoodAwareTestRunnerHook() { - } - - /** - * Called before any code starts. Internally it will only initialize the environment once. - */ - public static void performGlobalInitialization() { - } - - /** - * Called when a runner starts, before the inner runner gets a chance to run. - */ - public static void onRunnerInitializing(RavenwoodAwareTestRunner runner, TestClass testClass) { - } - - /** - * Called when a whole test class is skipped. - */ - public static void onClassSkipped(Description description) { - } - - /** - * Called before the inner runner starts. - */ - public static void onBeforeInnerRunnerStart( - RavenwoodAwareTestRunner runner, Description description) throws Throwable { - } - - /** - * Called after the inner runner finished. - */ - public static void onAfterInnerRunnerFinished( - RavenwoodAwareTestRunner runner, Description description) throws Throwable { - } - - /** - * Called before a test / class. - * - * Return false if it should be skipped. - */ - public static boolean onBefore(RavenwoodAwareTestRunner runner, Description description, - Scope scope, Order order) throws Throwable { - return true; - } - - public static void onRavenwoodRuleEnter(RavenwoodAwareTestRunner runner, - Description description, RavenwoodRule rule) throws Throwable { - } - - public static void onRavenwoodRuleExit(RavenwoodAwareTestRunner runner, - Description description, RavenwoodRule rule) throws Throwable { - } - - - /** - * Called after a test / class. - * - * Return false if the exception should be ignored. - */ - public static boolean onAfter(RavenwoodAwareTestRunner runner, Description description, - Scope scope, Order order, Throwable th) { - return true; - } - - public static boolean shouldRunClassOnRavenwood(Class<?> clazz) { - return true; - } -} diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java index 43a28ba72ec9..7d3d8b9f7974 100644 --- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java +++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodConfigState.java @@ -15,7 +15,7 @@ */ package android.platform.test.ravenwood; -/** Stub class. The actual implementaetion is in junit-impl-src. */ +/** Stub class. The actual implementation is in junit-impl-src. */ public class RavenwoodConfigState { public RavenwoodConfigState(RavenwoodConfig config) { } diff --git a/ravenwood/runtime-helper-src/framework/android/os/Process_ravenwood.java b/ravenwood/runtime-helper-src/framework/android/os/Process_ravenwood.java new file mode 100644 index 000000000000..3c6a4d78d1d9 --- /dev/null +++ b/ravenwood/runtime-helper-src/framework/android/os/Process_ravenwood.java @@ -0,0 +1,70 @@ +/* + * 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.os; + +import android.util.Pair; + +public class Process_ravenwood { + + private static volatile ThreadLocal<Pair<Integer, Boolean>> sThreadPriority; + + static { + reset(); + } + + public static void reset() { + // Reset the thread local variable + sThreadPriority = ThreadLocal.withInitial( + () -> Pair.create(Process.THREAD_PRIORITY_DEFAULT, true)); + } + + /** + * Called by {@link Process#setThreadPriority(int, int)} + */ + public static void setThreadPriority(int tid, int priority) { + if (Process.myTid() == tid) { + boolean backgroundOk = sThreadPriority.get().second; + if (priority >= Process.THREAD_PRIORITY_BACKGROUND && !backgroundOk) { + throw new IllegalArgumentException( + "Priority " + priority + " blocked by setCanSelfBackground()"); + } + sThreadPriority.set(Pair.create(priority, backgroundOk)); + } else { + throw new UnsupportedOperationException( + "Cross-thread priority management not yet available in Ravenwood"); + } + } + + /** + * Called by {@link Process#setCanSelfBackground(boolean)} + */ + public static void setCanSelfBackground(boolean backgroundOk) { + int priority = sThreadPriority.get().first; + sThreadPriority.set(Pair.create(priority, backgroundOk)); + } + + /** + * Called by {@link Process#getThreadPriority(int)} + */ + public static int getThreadPriority(int tid) { + if (Process.myTid() == tid) { + return sThreadPriority.get().first; + } else { + throw new UnsupportedOperationException( + "Cross-thread priority management not yet available in Ravenwood"); + } + } +} diff --git a/ravenwood/runtime-helper-src/framework/com/android/internal/ravenwood/RavenwoodEnvironment_host.java b/ravenwood/runtime-helper-src/framework/com/android/internal/ravenwood/RavenwoodEnvironment_host.java index e12ff240c4d9..b65668b67e03 100644 --- a/ravenwood/runtime-helper-src/framework/com/android/internal/ravenwood/RavenwoodEnvironment_host.java +++ b/ravenwood/runtime-helper-src/framework/com/android/internal/ravenwood/RavenwoodEnvironment_host.java @@ -23,14 +23,6 @@ public class RavenwoodEnvironment_host { } /** - * Called from {@link RavenwoodEnvironment#ensureRavenwoodInitialized()}. - */ - public static void ensureRavenwoodInitialized() { - // Initialization is now done by RavenwoodAwareTestRunner. - // Should we remove it? - } - - /** * Called from {@link RavenwoodEnvironment#getRavenwoodRuntimePath()}. */ public static String getRavenwoodRuntimePath(RavenwoodEnvironment env) { diff --git a/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java b/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java index c94ef31a5e5e..02981713674d 100644 --- a/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java +++ b/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java @@ -16,6 +16,7 @@ package android.system; import com.android.ravenwood.RavenwoodRuntimeNative; +import com.android.ravenwood.RavenwoodRuntimeState; import com.android.ravenwood.common.JvmWorkaround; import java.io.FileDescriptor; @@ -97,4 +98,16 @@ public final class Os { public static void setenv(String name, String value, boolean overwrite) throws ErrnoException { RavenwoodRuntimeNative.setenv(name, value, overwrite); } + + public static int getpid() { + return RavenwoodRuntimeState.sPid; + } + + public static int getuid() { + return RavenwoodRuntimeState.sUid; + } + + public static int gettid() { + return RavenwoodRuntimeNative.gettid(); + } } 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 f13189f6f8be..7b940b423b69 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 @@ -58,6 +58,8 @@ public class RavenwoodRuntimeNative { public static native void clearSystemProperties(); + public static native int gettid(); + public static long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException { return nLseek(JvmWorkaround.getInstance().getFdInt(fd), offset, whence); } diff --git a/services/core/java/com/android/server/integrity/parser/RuleParseException.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeState.java index c0f36a66528a..175e020d61d6 100644 --- a/services/core/java/com/android/server/integrity/parser/RuleParseException.java +++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeState.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -13,20 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.ravenwood; -package com.android.server.integrity.parser; +public class RavenwoodRuntimeState { + // This must match VMRuntime.SDK_VERSION_CUR_DEVELOPMENT. + public static final int CUR_DEVELOPMENT = 10000; -import android.annotation.NonNull; + public static volatile int sUid; + public static volatile int sPid; + public static volatile int sTargetSdkLevel; -/** - * Thrown when rule parsing fails. - */ -public class RuleParseException extends Exception { - public RuleParseException(@NonNull String message) { - super(message); + static { + reset(); } - public RuleParseException(@NonNull String message, @NonNull Throwable cause) { - super(message, cause); + public static void reset() { + sUid = -1; + sPid = -1; + sTargetSdkLevel = CUR_DEVELOPMENT; } } diff --git a/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/VMRuntime.java b/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/VMRuntime.java index ba89f71dde8a..eaadac6a8b92 100644 --- a/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/VMRuntime.java +++ b/ravenwood/runtime-helper-src/libcore-fake/dalvik/system/VMRuntime.java @@ -19,6 +19,7 @@ package dalvik.system; // The original is here: // $ANDROID_BUILD_TOP/libcore/libart/src/main/java/dalvik/system/VMRuntime.java +import com.android.ravenwood.RavenwoodRuntimeState; import com.android.ravenwood.common.JvmWorkaround; import java.lang.reflect.Array; @@ -52,4 +53,8 @@ public class VMRuntime { public long addressOf(Object obj) { return JvmWorkaround.getInstance().addressOf(obj); } + + public int getTargetSdkVersion() { + return RavenwoodRuntimeState.sTargetSdkLevel; + } } diff --git a/ravenwood/runtime-jni/ravenwood_initializer.cpp b/ravenwood/runtime-jni/ravenwood_initializer.cpp new file mode 100644 index 000000000000..89fb7c3c3510 --- /dev/null +++ b/ravenwood/runtime-jni/ravenwood_initializer.cpp @@ -0,0 +1,50 @@ +/* + * 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. + */ + + /* + * This file is compiled into a single SO file, which we load at the very first. + * We can do process-wide initialization here. + */ + +#include <fcntl.h> +#include <unistd.h> + +#include "jni_helper.h" + +static void maybeRedirectLog() { + auto ravenwoodLogOut = getenv("RAVENWOOD_LOG_OUT"); + if (ravenwoodLogOut == NULL) { + return; + } + ALOGI("RAVENWOOD_LOG_OUT set. Redirecting output to %s", ravenwoodLogOut); + + // Redirect stdin / stdout to /dev/tty. + int ttyFd = open(ravenwoodLogOut, O_WRONLY | O_APPEND); + if (ttyFd == -1) { + ALOGW("$RAVENWOOD_LOG_OUT is set to %s, but failed to open: %s ", ravenwoodLogOut, + strerror(errno)); + return; + } + dup2(ttyFd, 1); + dup2(ttyFd, 2); +} + +extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) { + ALOGI("%s: JNI_OnLoad", __FILE__); + + maybeRedirectLog(); + return JNI_VERSION_1_4; +} diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp index 3ff08483c956..c1993f691686 100644 --- a/ravenwood/runtime-jni/ravenwood_runtime.cpp +++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp @@ -17,6 +17,7 @@ #include <fcntl.h> #include <string.h> #include <sys/stat.h> +#include <sys/syscall.h> #include <unistd.h> #include <utils/misc.h> @@ -173,6 +174,12 @@ static void Linux_setenv(JNIEnv* env, jobject, jstring javaName, jstring javaVal throwIfMinusOne(env, "setenv", setenv(name.c_str(), value.c_str(), overwrite ? 1 : 0)); } + +static jint Linux_gettid(JNIEnv* env, jobject) { + // gettid(2() was added in glibc 2.30 but Android uses an older version in prebuilt. + return syscall(__NR_gettid); +} + // ---- Registration ---- extern void register_android_system_OsConstants(JNIEnv* env); @@ -189,6 +196,7 @@ static const JNINativeMethod sMethods[] = { "stat", "(Ljava/lang/String;)Landroid/system/StructStat;", (void*)Linux_stat }, { "nOpen", "(Ljava/lang/String;II)I", (void*)Linux_open }, { "setenv", "(Ljava/lang/String;Ljava/lang/String;Z)V", (void*)Linux_setenv }, + { "gettid", "()I", (void*)Linux_gettid }, }; extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) { diff --git a/ravenwood/scripts/run-ravenwood-tests.sh b/ravenwood/scripts/run-ravenwood-tests.sh index 5d623e0b6c36..1910100a7f5d 100755 --- a/ravenwood/scripts/run-ravenwood-tests.sh +++ b/ravenwood/scripts/run-ravenwood-tests.sh @@ -18,12 +18,38 @@ # Options: # # -s: "Smoke" test -- skip slow tests (SysUI, ICU) +# +# -x PCRE: Specify exclusion filter in PCRE +# Example: -x '^(Cts|hoststub)' # Exclude CTS and hoststubgen tests. +# +# -f PCRE: Specify inclusion filter in PCRE + + +# Regex to identify slow tests, in PCRE +SLOW_TEST_RE='^(SystemUiRavenTests|CtsIcuTestCasesRavenwood|CarSystemUIRavenTests)$' smoke=0 -while getopts "s" opt; do +include_re="" +exclude_re="" +smoke_exclude_re="" +dry_run="" +while getopts "sx:f:d" opt; do case "$opt" in s) - smoke=1 + # Remove slow tests. + smoke_exclude_re="$SLOW_TEST_RE" + ;; + x) + # Take a PCRE from the arg, and use it as an exclusion filter. + exclude_re="$OPTARG" + ;; + f) + # Take a PCRE from the arg, and use it as an inclusion filter. + include_re="$OPTARG" + ;; + d) + # Dry run + dry_run="echo" ;; '?') exit 1 @@ -35,21 +61,46 @@ shift $(($OPTIND - 1)) all_tests=(hoststubgentest tiny-framework-dump-test hoststubgen-invoke-test ravenwood-stats-checker) all_tests+=( $(${0%/*}/list-ravenwood-tests.sh) ) -# Regex to identify slow tests, in PCRE -slow_tests_re='^(SystemUiRavenTests|CtsIcuTestCasesRavenwood)$' - -if (( $smoke )) ; then - # Remove the slow tests. - all_tests=( $( - for t in "${all_tests[@]}"; do - echo $t | grep -vP "$slow_tests_re" - done - ) ) -fi +filter() { + local re="$1" + local grep_arg="$2" + if [[ "$re" == "" ]] ; then + cat # No filtering + else + grep $grep_arg -iP "$re" + fi +} -run() { - echo "Running: $*" - "${@}" +filter_in() { + filter "$1" } -run ${ATEST:-atest} "${all_tests[@]}" +filter_out() { + filter "$1" -v +} + + +# Remove the slow tests. +targets=( $( + for t in "${all_tests[@]}"; do + echo $t | filter_in "$include_re" | filter_out "$smoke_exclude_re" | filter_out "$exclude_re" + done +) ) + +# Show the target tests + +echo "Target tests:" +for t in "${targets[@]}"; do + echo " $t" +done + +# Calculate the removed tests. + +diff="$(diff <(echo "${all_tests[@]}" | tr ' ' '\n') <(echo "${targets[@]}" | tr ' ' '\n') )" + +if [[ "$diff" != "" ]]; then + echo "Excluded tests:" + echo "$diff" +fi + +$dry_run ${ATEST:-atest} "${targets[@]}" diff --git a/ravenwood/scripts/update-test-mapping.sh b/ravenwood/scripts/update-test-mapping.sh index e478b50cc2b9..ab37baf01ea5 100755 --- a/ravenwood/scripts/update-test-mapping.sh +++ b/ravenwood/scripts/update-test-mapping.sh @@ -23,6 +23,14 @@ set -e # Tests that shouldn't be in presubmit. EXEMPT='^(SystemUiRavenTests)$' +is_car() { + local module="$1" + + # If the module name starts with "Car", then it's a test for "Car". + [[ "$module" =~ ^Car ]] + return $? +} + main() { local script_name="${0##*/}" local script_dir="${0%/*}" @@ -62,6 +70,10 @@ main() { fi echo " {" echo " \"name\": \"${tests[$i]}\"," + if is_car "${tests[$i]}"; then + echo ' "keywords": ["automotive_code_coverage"],' + fi + echo " \"host\": true" echo " }$comma" diff --git a/ravenwood/tests/bivalenttest/Android.bp b/ravenwood/tests/bivalenttest/Android.bp index d7f4b3e2955d..4895a1a6d1a2 100644 --- a/ravenwood/tests/bivalenttest/Android.bp +++ b/ravenwood/tests/bivalenttest/Android.bp @@ -31,9 +31,8 @@ cc_library_shared { ], } -android_ravenwood_test { - name: "RavenwoodBivalentTest", - +java_defaults { + name: "ravenwood-bivalent-defaults", static_libs: [ "androidx.annotation_annotation", "androidx.test.ext.junit", @@ -44,46 +43,56 @@ android_ravenwood_test { // To make sure it won't cause VerifyError (b/324063814) "platformprotosnano", + + "com.android.internal.os.flags-aconfig-java", ], srcs: [ "test/**/*.java", + "test/**/*.kt", ], jni_libs: [ "libravenwoodbivalenttest_jni", ], - auto_gen_config: true, } -android_test { - name: "RavenwoodBivalentTest_device", - - srcs: [ - "test/**/*.java", - ], +java_defaults { + name: "ravenwood-bivalent-device-defaults", + defaults: ["ravenwood-bivalent-defaults"], // TODO(b/371215487): migrate bivalenttest.ravenizer tests to another architecture exclude_srcs: [ "test/**/ravenizer/*.java", + "test/**/ravenizer/*.kt", ], static_libs: [ "junit", "truth", - - "androidx.annotation_annotation", - "androidx.test.ext.junit", - "androidx.test.rules", - - "junit-params", - "platform-parametric-runner-lib", - + "flag-junit", "ravenwood-junit", ], - jni_libs: [ - "libravenwoodbivalenttest_jni", - ], test_suites: [ "device-tests", ], optimize: { enabled: false, }, + test_config_template: "AndroidTestTemplate.xml", +} + +android_ravenwood_test { + name: "RavenwoodBivalentTest", + defaults: ["ravenwood-bivalent-defaults"], + auto_gen_config: true, +} + +android_test { + name: "RavenwoodBivalentTest_device", + defaults: ["ravenwood-bivalent-device-defaults"], +} + +android_test { + name: "RavenwoodBivalentTest_device_ravenizer", + defaults: ["ravenwood-bivalent-device-defaults"], + ravenizer: { + enabled: true, + }, } diff --git a/ravenwood/tests/bivalenttest/AndroidTest.xml b/ravenwood/tests/bivalenttest/AndroidTestTemplate.xml index 9e5dd11b60cb..8f1a92c0ae17 100644 --- a/ravenwood/tests/bivalenttest/AndroidTest.xml +++ b/ravenwood/tests/bivalenttest/AndroidTestTemplate.xml @@ -19,7 +19,7 @@ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> - <option name="test-file-name" value="RavenwoodBivalentTest_device.apk" /> + <option name="test-file-name" value="{MODULE}.apk"/> </target_preparer> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/aconfig/RavenwoodAconfigFlagTest.kt b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/aconfig/RavenwoodAconfigFlagTest.kt new file mode 100644 index 000000000000..fd6d6fb66465 --- /dev/null +++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/aconfig/RavenwoodAconfigFlagTest.kt @@ -0,0 +1,102 @@ +/* + * 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.aconfig + +import android.platform.test.annotations.EnableFlags +import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.annotations.RequiresFlagsEnabled +import android.platform.test.flag.junit.DeviceFlagsValueProvider +import android.platform.test.flag.junit.SetFlagsRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.internal.os.Flags +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.Ignore +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + + +@RunWith(AndroidJUnit4::class) +class RavenwoodAconfigSimpleReadTests { + @Test + fun testFalseFlags() { + assertFalse(Flags.ravenwoodFlagRo1()) + assertFalse(Flags.ravenwoodFlagRw1()) + } + + @Test + @Ignore // TODO: Enable this test after rolling out the "2" flags. + fun testTrueFlags() { + assertTrue(Flags.ravenwoodFlagRo2()) + assertTrue(Flags.ravenwoodFlagRw2()) + } +} + +@RunWith(AndroidJUnit4::class) +class RavenwoodAconfigCheckFlagsRuleTests { + @Rule + @JvmField + val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule() + + @Test + @RequiresFlagsEnabled(Flags.FLAG_RAVENWOOD_FLAG_RO_1) + fun testRequireFlagsEnabledRo() { + fail("This test shouldn't be executed") + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_RAVENWOOD_FLAG_RW_1) + fun testRequireFlagsEnabledRw() { + fail("This test shouldn't be executed") + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_RAVENWOOD_FLAG_RO_2) + @Ignore // TODO: Enable this test after rolling out the "2" flags. + fun testRequireFlagsDisabledRo() { + fail("This test shouldn't be executed") + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_RAVENWOOD_FLAG_RW_2) + @Ignore // TODO: Enable this test after rolling out the "2" flags. + fun testRequireFlagsDisabledRw() { + fail("This test shouldn't be executed") + } +} + +@RunWith(AndroidJUnit4::class) +class RavenwoodAconfigSetFlagsRuleWithDefaultTests { + @Rule + @JvmField + val setFlagsRule = SetFlagsRule() + + @Test + @EnableFlags(Flags.FLAG_RAVENWOOD_FLAG_RO_1) + fun testSetRoFlag() { + assertTrue(Flags.ravenwoodFlagRo1()) + assertFalse(Flags.ravenwoodFlagRw1()) + } + + @Test + @EnableFlags(Flags.FLAG_RAVENWOOD_FLAG_RW_1) + fun testSetRwFlag() { + assertFalse(Flags.ravenwoodFlagRo1()) + assertTrue(Flags.ravenwoodFlagRw1()) + } +} diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodAwareTestRunnerTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodAwareTestRunnerTest.java index d7c2c6cd73a8..4e21f8605163 100644 --- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodAwareTestRunnerTest.java +++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodAwareTestRunnerTest.java @@ -16,12 +16,15 @@ package com.android.ravenwoodtest.bivalenttest.ravenizer; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import android.platform.test.annotations.DisabledOnRavenwood; -import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing; +import android.platform.test.annotations.RavenwoodTestRunnerInitializing; import android.platform.test.ravenwood.RavenwoodRule; import android.util.Log; +import androidx.test.platform.app.InstrumentationRegistry; + import junitparams.JUnitParamsRunner; import junitparams.Parameters; @@ -30,6 +33,8 @@ import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.List; + /** * Make sure RavenwoodAwareTestRunnerTest properly delegates to the original runner, * and also run the special annotated methods. @@ -61,13 +66,22 @@ public class RavenwoodAwareTestRunnerTest { sCallTracker.incrementMethodCallCount(); } + public RavenwoodAwareTestRunnerTest() { + // Make sure the environment is already initialized when the constructor is called + assertNotNull(InstrumentationRegistry.getInstrumentation()); + } + @Test public void test1() { sCallTracker.incrementMethodCallCount(); } + public static List<String> testParams() { + return List.of("foo", "bar"); + } + @Test - @Parameters({"foo", "bar"}) + @Parameters(method = "testParams") public void testWithParams(String arg) { sCallTracker.incrementMethodCallCount(); } diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java index 9d878f444e5e..77a807d5e1e5 100644 --- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java +++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodNoRavenizerTest.java @@ -16,7 +16,7 @@ package com.android.ravenwoodtest.bivalenttest.ravenizer; import android.platform.test.annotations.NoRavenizer; -import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing; +import android.platform.test.annotations.RavenwoodTestRunnerInitializing; import org.junit.Test; diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsReallyDisabledTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsReallyDisabledTest.java index c77841b1b55a..e6e617b401b6 100644 --- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsReallyDisabledTest.java +++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsReallyDisabledTest.java @@ -18,7 +18,7 @@ package com.android.ravenwoodtest.bivalenttest.ravenizer; import static org.junit.Assert.fail; import android.platform.test.annotations.DisabledOnRavenwood; -import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing; +import android.platform.test.annotations.RavenwoodTestRunnerInitializing; import android.platform.test.ravenwood.RavenwoodRule; import android.util.Log; diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsTest.java b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsTest.java index ea1a29d57482..ef18c82b6d79 100644 --- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsTest.java +++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/ravenizer/RavenwoodRunDisabledTestsTest.java @@ -18,7 +18,7 @@ package com.android.ravenwoodtest.bivalenttest.ravenizer; import static org.junit.Assert.fail; import android.platform.test.annotations.DisabledOnRavenwood; -import android.platform.test.ravenwood.RavenwoodAwareTestRunner.RavenwoodTestRunnerInitializing; +import android.platform.test.annotations.RavenwoodTestRunnerInitializing; import android.platform.test.ravenwood.RavenwoodRule; import android.util.Log; diff --git a/ravenwood/tests/coretest/Android.bp b/ravenwood/tests/coretest/Android.bp index 412744eb9d34..9dd7cc683719 100644 --- a/ravenwood/tests/coretest/Android.bp +++ b/ravenwood/tests/coretest/Android.bp @@ -16,11 +16,14 @@ android_ravenwood_test { "androidx.test.rules", "junit-params", "platform-parametric-runner-lib", - "truth", // This library should be removed by Ravenizer "mockito-target-minus-junit4", ], + libs: [ + // We access internal private classes + "ravenwood-junit-impl", + ], srcs: [ "test/**/*.java", "test/**/*.kt", diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java index bd013133d3a4..6720e45939fb 100644 --- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java +++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerCallbackTest.java @@ -337,9 +337,9 @@ public class RavenwoodRunnerCallbackTest extends RavenwoodRunnerTestBase { @Expected(""" testRunStarted: classes testSuiteStarted: classes - testStarted: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ExceptionFromInnerRunnerConstructorTest) - testFailure: Exception detected in constructor - testFinished: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ExceptionFromInnerRunnerConstructorTest) + testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ExceptionFromInnerRunnerConstructorTest) + testFailure: Failed to instantiate class androidx.test.ext.junit.runners.AndroidJUnit4 + testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$ExceptionFromInnerRunnerConstructorTest) testSuiteFinished: classes testRunFinished: 1,1,0,0 """) @@ -439,9 +439,9 @@ public class RavenwoodRunnerCallbackTest extends RavenwoodRunnerTestBase { @Expected(""" testRunStarted: classes testSuiteStarted: classes - testStarted: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BrokenRunnerTest) - testFailure: Exception detected in constructor - testFinished: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BrokenRunnerTest) + testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BrokenRunnerTest) + testFailure: Failed to instantiate class com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BrokenTestRunner + testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerCallbackTest$BrokenRunnerTest) testSuiteFinished: classes testRunFinished: 1,1,0,0 """) diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java index 73ea64f57997..02d10732245d 100644 --- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java +++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerConfigValidationTest.java @@ -106,17 +106,11 @@ public class RavenwoodRunnerConfigValidationTest extends RavenwoodRunnerTestBase @Expected(""" testRunStarted: classes testSuiteStarted: classes - testStarted: testMethod1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest) + testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest) testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest.sConfig expected to be public static - testFinished: testMethod1(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest) - testStarted: testMethod2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest) - testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest.sConfig expected to be public static - testFinished: testMethod2(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest) - testStarted: testMethod3(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest) - testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest.sConfig expected to be public static - testFinished: testMethod3(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest) + testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ErrorMustBeReportedFromEachTest) testSuiteFinished: classes - testRunFinished: 3,3,0,0 + testRunFinished: 1,1,0,0 """) // CHECKSTYLE:ON public static class ErrorMustBeReportedFromEachTest { @@ -145,9 +139,9 @@ public class RavenwoodRunnerConfigValidationTest extends RavenwoodRunnerTestBase @Expected(""" testRunStarted: classes testSuiteStarted: classes - testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest) + testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest) testFailure: Class com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest.DuplicateConfigTest has multiple fields with @RavenwoodConfig.Config - testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest) + testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$DuplicateConfigTest) testSuiteFinished: classes testRunFinished: 1,1,0,0 """) @@ -175,9 +169,9 @@ public class RavenwoodRunnerConfigValidationTest extends RavenwoodRunnerTestBase @Expected(""" testRunStarted: classes testSuiteStarted: classes - testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest) + testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest) testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest.sConfig expected to be public static - testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest) + testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonStaticConfigTest) testSuiteFinished: classes testRunFinished: 1,1,0,0 """) @@ -201,9 +195,9 @@ public class RavenwoodRunnerConfigValidationTest extends RavenwoodRunnerTestBase @Expected(""" testRunStarted: classes testSuiteStarted: classes - testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest) + testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest) testFailure: com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest.sConfig expected to be public static - testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest) + testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$NonPublicConfigTest) testSuiteFinished: classes testRunFinished: 1,1,0,0 """) @@ -227,9 +221,9 @@ public class RavenwoodRunnerConfigValidationTest extends RavenwoodRunnerTestBase @Expected(""" testRunStarted: classes testSuiteStarted: classes - testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest) + 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: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest) + testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WrongTypeConfigTest) testSuiteFinished: classes testRunFinished: 1,1,0,0 """) @@ -282,9 +276,9 @@ public class RavenwoodRunnerConfigValidationTest extends RavenwoodRunnerTestBase @Expected(""" testRunStarted: classes testSuiteStarted: classes - testStarted: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest) + testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest) testFailure: RavenwoodConfig and RavenwoodRule cannot be used in the same class. Suggest migrating to RavenwoodConfig. - testFinished: testConfig(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest) + testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithInstanceRuleTest) testSuiteFinished: classes testRunFinished: 1,1,0,0 """) @@ -311,9 +305,9 @@ public class RavenwoodRunnerConfigValidationTest extends RavenwoodRunnerTestBase @Expected(""" testRunStarted: classes testSuiteStarted: classes - testStarted: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest) - testFailure: Exception detected in constructor - testFinished: Constructor(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$WithStaticRuleTest) + 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 """) @@ -400,9 +394,9 @@ public class RavenwoodRunnerConfigValidationTest extends RavenwoodRunnerTestBase @Expected(""" testRunStarted: classes testSuiteStarted: classes - testStarted: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest) + testStarted: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest) testFailure: RavenwoodConfig and RavenwoodRule cannot be used in the same class. Suggest migrating to RavenwoodConfig. - testFinished: test(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest) + testFinished: initializationError(com.android.ravenwoodtest.runnercallbacktests.RavenwoodRunnerConfigValidationTest$ConfigWithRuleInBaseClassTest) testSuiteFinished: classes testRunFinished: 1,1,0,0 """) diff --git a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java index 9a6934bf17c5..f7a2198a9bc4 100644 --- a/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java +++ b/ravenwood/tests/coretest/test/com/android/ravenwoodtest/runnercallbacktests/RavenwoodRunnerTestBase.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertWithMessage; import android.platform.test.annotations.NoRavenizer; import android.platform.test.ravenwood.RavenwoodAwareTestRunner; +import android.platform.test.ravenwood.RavenwoodConfigPrivate; import android.util.Log; import junitparams.JUnitParamsRunner; @@ -137,15 +138,14 @@ public abstract class RavenwoodRunnerTestBase { // Set a listener to critical errors. This will also prevent // {@link RavenwoodAwareTestRunner} from calling System.exit() when there's // a critical error. - RavenwoodAwareTestRunner.private$ravenwood().setCriticalErrorHandler( - listener.sCriticalErrorListener); + RavenwoodConfigPrivate.setCriticalErrorHandler(listener.sCriticalErrorListener); try { // Run the test class. junitCore.run(testClazz); } finally { // Clear the critical error listener. - RavenwoodAwareTestRunner.private$ravenwood().setCriticalErrorHandler(null); + RavenwoodConfigPrivate.setCriticalErrorHandler(null); } // Check the result. diff --git a/ravenwood/tests/runtime-test/Android.bp b/ravenwood/tests/runtime-test/Android.bp index 410292001670..0c0df1f993aa 100644 --- a/ravenwood/tests/runtime-test/Android.bp +++ b/ravenwood/tests/runtime-test/Android.bp @@ -10,6 +10,9 @@ package { android_ravenwood_test { name: "RavenwoodRuntimeTest", + libs: [ + "ravenwood-helper-runtime", + ], static_libs: [ "androidx.annotation_annotation", "androidx.test.ext.junit", 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 new file mode 100644 index 000000000000..8e04b698c9d9 --- /dev/null +++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/IdentityTest.java @@ -0,0 +1,66 @@ +/* + * 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 android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; +import static android.os.Process.FIRST_APPLICATION_UID; + +import static org.junit.Assert.assertEquals; + +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; + +import dalvik.system.VMRuntime; + +import org.junit.Test; + +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); + assertEquals(FIRST_APPLICATION_UID, Os.getuid()); + assertEquals(FIRST_APPLICATION_UID, Process.myUid()); + assertEquals(FIRST_APPLICATION_UID, Binder.getCallingUid()); + } + + @Test + public void testPid() { + int pid = RavenwoodRuntimeState.sPid; + assertEquals(pid, Os.getpid()); + assertEquals(pid, Process.myPid()); + assertEquals(pid, Binder.getCallingPid()); + } + + @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()); + } +} diff --git a/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java index c2230c739ccf..c55506a0a10a 100644 --- a/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java +++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/OsTest.java @@ -24,6 +24,8 @@ import static android.system.OsConstants.S_ISREG; import static android.system.OsConstants.S_ISSOCK; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; import static java.nio.file.LinkOption.NOFOLLOW_LINKS; @@ -51,10 +53,12 @@ import java.nio.file.attribute.PosixFileAttributes; import java.nio.file.attribute.PosixFilePermission; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) public class OsTest { + public interface ConsumerWithThrow<T> { void accept(T var1) throws Exception; } @@ -165,6 +169,35 @@ public class OsTest { }); } + private static class TestThread extends Thread { + + final CountDownLatch mLatch = new CountDownLatch(1); + int mTid; + + TestThread() { + setDaemon(true); + } + + @Override + public void run() { + mTid = Os.gettid(); + mLatch.countDown(); + } + } + + @Test + public void testGetTid() throws InterruptedException { + var t1 = new TestThread(); + var t2 = new TestThread(); + t1.start(); + t2.start(); + // Wait for thread execution + assertTrue(t1.mLatch.await(1, TimeUnit.SECONDS)); + assertTrue(t2.mLatch.await(1, TimeUnit.SECONDS)); + // Make sure the tid is unique per-thread + assertNotEquals(t1.mTid, t2.mTid); + } + // Verify StructStat values from libcore against native JVM PosixFileAttributes private static void assertAttributesEqual(PosixFileAttributes attr, StructStat stat) { assertEquals(attr.lastModifiedTime(), convertTimespecToFileTime(stat.st_mtim)); diff --git a/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/ProcessTest.java b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/ProcessTest.java new file mode 100644 index 000000000000..d25b5c19f351 --- /dev/null +++ b/ravenwood/tests/runtime-test/test/com/android/ravenwoodtest/runtimetest/ProcessTest.java @@ -0,0 +1,54 @@ +/* + * 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 android.os.Process.THREAD_PRIORITY_BACKGROUND; +import static android.os.Process.THREAD_PRIORITY_DEFAULT; +import static android.os.Process.THREAD_PRIORITY_FOREGROUND; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import android.os.Process; +import android.system.Os; + +import org.junit.Test; + +public class ProcessTest { + + @Test + public void testGetUidPidTid() { + assertEquals(Os.getuid(), Process.myUid()); + assertEquals(Os.getpid(), Process.myPid()); + assertEquals(Os.gettid(), Process.myTid()); + } + + @Test + public void testThreadPriority() { + assertThrows(UnsupportedOperationException.class, + () -> Process.getThreadPriority(Process.myTid() + 1)); + assertThrows(UnsupportedOperationException.class, + () -> Process.setThreadPriority(Process.myTid() + 1, THREAD_PRIORITY_DEFAULT)); + assertEquals(THREAD_PRIORITY_DEFAULT, Process.getThreadPriority(Process.myTid())); + Process.setThreadPriority(THREAD_PRIORITY_FOREGROUND); + assertEquals(THREAD_PRIORITY_FOREGROUND, Process.getThreadPriority(Process.myTid())); + Process.setCanSelfBackground(false); + Process.setThreadPriority(THREAD_PRIORITY_DEFAULT); + assertEquals(THREAD_PRIORITY_DEFAULT, Process.getThreadPriority(Process.myTid())); + assertThrows(IllegalArgumentException.class, + () -> Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND)); + } +} diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt index 9c8638930df9..a26fe66da2ab 100644 --- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt +++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt @@ -359,3 +359,6 @@ com.android.server.SystemService com.android.server.SystemServiceManager com.android.server.utils.TimingsTraceAndSlog + +android.os.IpcDataCache +android.app.PropertyInvalidatedCache diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt index a02082d12934..f47aaba8ef22 100644 --- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/asm/AsmUtils.kt @@ -327,6 +327,10 @@ fun ClassNode.isSynthetic(): Boolean { return (this.access and Opcodes.ACC_SYNTHETIC) != 0 } +fun ClassNode.isAbstract(): Boolean { + return (this.access and Opcodes.ACC_ABSTRACT) != 0 +} + fun MethodNode.isSynthetic(): Boolean { return (this.access and Opcodes.ACC_SYNTHETIC) != 0 } diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt index 32dcbe546f3c..a0e5599c0a7c 100644 --- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt +++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/RavenizerOptions.kt @@ -46,7 +46,7 @@ class RavenizerOptions( var enableValidation: SetOnce<Boolean> = SetOnce(true), /** Whether the validation failure is fatal or not. */ - var fatalValidation: SetOnce<Boolean> = SetOnce(false), + var fatalValidation: SetOnce<Boolean> = SetOnce(true), /** Whether to remove mockito and dexmaker classes. */ var stripMockito: SetOnce<Boolean> = SetOnce(false), diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt index 37a797528e13..6092fcc9402d 100644 --- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt +++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Utils.kt @@ -15,6 +15,7 @@ */ package com.android.platform.test.ravenwood.ravenizer +import android.platform.test.annotations.internal.InnerRunner import android.platform.test.annotations.NoRavenizer import android.platform.test.ravenwood.RavenwoodAwareTestRunner import com.android.hoststubgen.asm.ClassNodes @@ -39,7 +40,7 @@ val testAnotType = TypeHolder(org.junit.Test::class.java) val ruleAnotType = TypeHolder(org.junit.Rule::class.java) val classRuleAnotType = TypeHolder(org.junit.ClassRule::class.java) val runWithAnotType = TypeHolder(RunWith::class.java) -val innerRunnerAnotType = TypeHolder(RavenwoodAwareTestRunner.InnerRunner::class.java) +val innerRunnerAnotType = TypeHolder(InnerRunner::class.java) val noRavenizerAnotType = TypeHolder(NoRavenizer::class.java) val testRuleType = TypeHolder(TestRule::class.java) diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt index 27092d28ae5e..8ec0932d89dd 100644 --- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt +++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Validator.kt @@ -16,10 +16,12 @@ package com.android.platform.test.ravenwood.ravenizer import com.android.hoststubgen.asm.ClassNodes +import com.android.hoststubgen.asm.isAbstract import com.android.hoststubgen.asm.startsWithAny import com.android.hoststubgen.asm.toHumanReadableClassName import com.android.hoststubgen.log import org.objectweb.asm.tree.ClassNode +import java.util.regex.Pattern fun validateClasses(classes: ClassNodes): Boolean { var allOk = true @@ -41,25 +43,35 @@ fun checkClass(cn: ClassNode, classes: ClassNodes): Boolean { } var allOk = true + log.i("Checking ${cn.name.toHumanReadableClassName()}") + // See if there's any class that extends a legacy base class. // But ignore the base classes in android.test. - if (!cn.name.startsWithAny("android/test/")) { - allOk = checkSuperClass(cn, cn, classes) && allOk + if (!cn.isAbstract() && !cn.name.startsWith("android/test/") + && !isAllowListedLegacyTest(cn) + ) { + allOk = checkSuperClassForJunit3(cn, cn, classes) && allOk } return allOk } -fun checkSuperClass(targetClass: ClassNode, currentClass: ClassNode, classes: ClassNodes): Boolean { +fun checkSuperClassForJunit3( + targetClass: ClassNode, + currentClass: ClassNode, + classes: ClassNodes, +): Boolean { if (currentClass.superName == null || currentClass.superName == "java/lang/Object") { return true // No parent class } + // Make sure the class doesn't extend a junit3 TestCase class. if (currentClass.superName.isLegacyTestBaseClass()) { log.e("Error: Class ${targetClass.name.toHumanReadableClassName()} extends" - + " a legacy test class ${currentClass.superName.toHumanReadableClassName()}.") + + " a legacy test class ${currentClass.superName.toHumanReadableClassName()}" + + ", which is not supported on Ravenwood. Please migrate to Junit4 syntax.") return false } classes.findClass(currentClass.superName)?.let { - return checkSuperClass(targetClass, it, classes) + return checkSuperClassForJunit3(targetClass, it, classes) } // Super class not found. // log.w("Class ${currentClass.superName} not found.") @@ -73,9 +85,64 @@ fun String.isLegacyTestBaseClass(): Boolean { return this.startsWithAny( "junit/framework/TestCase", - // In case the test doesn't statically include JUnit, we need + // In case the test doesn't statically include JUnit, we need the following. "android/test/AndroidTestCase", "android/test/InstrumentationTestCase", "android/test/InstrumentationTestSuite", ) } + +private val allowListedLegacyTests = setOf( +// List of existing test classes that use the JUnit3 syntax. We exempt them for now, but +// will reject any more of them. +// +// Note, we want internal class names, but for convenience, we use '.'s and '%'s here +// and replace them later. (a '$' would be parsed as a string template.) + *""" +android.util.proto.cts.DebuggingTest +android.util.proto.cts.EncodedBufferTest +android.util.proto.cts.ProtoOutputStreamBoolTest +android.util.proto.cts.ProtoOutputStreamBytesTest +android.util.proto.cts.ProtoOutputStreamDoubleTest +android.util.proto.cts.ProtoOutputStreamEnumTest +android.util.proto.cts.ProtoOutputStreamFixed32Test +android.util.proto.cts.ProtoOutputStreamFixed64Test +android.util.proto.cts.ProtoOutputStreamFloatTest +android.util.proto.cts.ProtoOutputStreamInt32Test +android.util.proto.cts.ProtoOutputStreamInt64Test +android.util.proto.cts.ProtoOutputStreamObjectTest +android.util.proto.cts.ProtoOutputStreamSFixed32Test +android.util.proto.cts.ProtoOutputStreamSFixed64Test +android.util.proto.cts.ProtoOutputStreamSInt32Test +android.util.proto.cts.ProtoOutputStreamSInt64Test +android.util.proto.cts.ProtoOutputStreamStringTest +android.util.proto.cts.ProtoOutputStreamSwitchedWriteTest +android.util.proto.cts.ProtoOutputStreamTagTest +android.util.proto.cts.ProtoOutputStreamUInt32Test +android.util.proto.cts.ProtoOutputStreamUInt64Test + +android.os.cts.BadParcelableExceptionTest +android.os.cts.DeadObjectExceptionTest +android.os.cts.ParcelFormatExceptionTest +android.os.cts.PatternMatcherTest +android.os.cts.RemoteExceptionTest + +android.os.storage.StorageManagerBaseTest +android.os.storage.StorageManagerIntegrationTest +android.util.LogTest%PerformanceTest + +com.android.server.power.stats.BatteryStatsCounterTest +com.android.server.power.stats.BatteryStatsDualTimerTest +com.android.server.power.stats.BatteryStatsDurationTimerTest +com.android.server.power.stats.BatteryStatsSamplingTimerTest +com.android.server.power.stats.BatteryStatsStopwatchTimerTest +com.android.server.power.stats.BatteryStatsTimeBaseTest +com.android.server.power.stats.BatteryStatsTimerTest + + """.trim().replace('%', '$').replace('.', '/') + .split(Pattern.compile("""\s+""")).toTypedArray() +) + +private fun isAllowListedLegacyTest(targetClass: ClassNode): Boolean { + return allowListedLegacyTests.contains(targetClass.name) +}
\ No newline at end of file diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt index cf6d6f6bcae3..81fe3da8d954 100644 --- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt +++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/adapter/RunnerRewritingAdapter.kt @@ -15,7 +15,6 @@ */ package com.android.platform.test.ravenwood.ravenizer.adapter -import android.platform.test.ravenwood.RavenwoodAwareTestRunner import com.android.hoststubgen.ClassParseException import com.android.hoststubgen.asm.CLASS_INITIALIZER_DESC import com.android.hoststubgen.asm.CLASS_INITIALIZER_NAME @@ -28,8 +27,8 @@ import com.android.hoststubgen.log import com.android.hoststubgen.visitors.OPCODE_VERSION import com.android.platform.test.ravenwood.ravenizer.RavenizerInternalException import com.android.platform.test.ravenwood.ravenizer.classRuleAnotType -import com.android.platform.test.ravenwood.ravenizer.isTestLookingClass import com.android.platform.test.ravenwood.ravenizer.innerRunnerAnotType +import com.android.platform.test.ravenwood.ravenizer.isTestLookingClass import com.android.platform.test.ravenwood.ravenizer.noRavenizerAnotType import com.android.platform.test.ravenwood.ravenizer.ravenwoodTestRunnerType import com.android.platform.test.ravenwood.ravenizer.ruleAnotType @@ -50,7 +49,7 @@ import org.objectweb.asm.tree.ClassNode * Class visitor to update the RunWith and inject some necessary rules. * * - Change the @RunWith(RavenwoodAwareTestRunner.class). - * - If the original class has a @RunWith(...), then change it to an @OrigRunWith(...). + * - If the original class has a @RunWith(...), then change it to an @InnerRunner(...). * - Add RavenwoodAwareTestRunner's member rules as junit rules. * - Update the order of the existing JUnit rules to make sure they don't use the MIN or MAX. */ @@ -146,7 +145,7 @@ class RunnerRewritingAdapter private constructor( /** * Inject `@RunWith(RavenwoodAwareTestRunner.class)`. If the class already has - * a `@RunWith`, then change it to add a `@OrigRunWith`. + * a `@RunWith`, then change it to add a `@InnerRunner`. */ private fun injectRunWithAnnotation() { // Extract the original RunWith annotation and its value. @@ -172,7 +171,7 @@ class RunnerRewritingAdapter private constructor( + " in class ${classInternalName.toHumanReadableClassName()}") } - // Inject an @OrigRunWith. + // Inject an @InnerRunner. visitAnnotation(innerRunnerAnotType.desc, true)!!.let { av -> av.visit("value", runWithClass) av.visitEnd() @@ -302,7 +301,7 @@ class RunnerRewritingAdapter private constructor( override fun visitCode() { visitFieldInsn(Opcodes.GETSTATIC, ravenwoodTestRunnerType.internlName, - RavenwoodAwareTestRunner.IMPLICIT_CLASS_OUTER_RULE_NAME, + IMPLICIT_CLASS_OUTER_RULE_NAME, testRuleType.desc ) visitFieldInsn(Opcodes.PUTSTATIC, @@ -313,7 +312,7 @@ class RunnerRewritingAdapter private constructor( visitFieldInsn(Opcodes.GETSTATIC, ravenwoodTestRunnerType.internlName, - RavenwoodAwareTestRunner.IMPLICIT_CLASS_INNER_RULE_NAME, + IMPLICIT_CLASS_INNER_RULE_NAME, testRuleType.desc ) visitFieldInsn(Opcodes.PUTSTATIC, @@ -361,7 +360,7 @@ class RunnerRewritingAdapter private constructor( visitVarInsn(ALOAD, 0) visitFieldInsn(Opcodes.GETSTATIC, ravenwoodTestRunnerType.internlName, - RavenwoodAwareTestRunner.IMPLICIT_INST_OUTER_RULE_NAME, + IMPLICIT_INST_OUTER_RULE_NAME, testRuleType.desc ) visitFieldInsn(Opcodes.PUTFIELD, @@ -373,7 +372,7 @@ class RunnerRewritingAdapter private constructor( visitVarInsn(ALOAD, 0) visitFieldInsn(Opcodes.GETSTATIC, ravenwoodTestRunnerType.internlName, - RavenwoodAwareTestRunner.IMPLICIT_INST_INNER_RULE_NAME, + IMPLICIT_INST_INNER_RULE_NAME, testRuleType.desc ) visitFieldInsn(Opcodes.PUTFIELD, @@ -435,6 +434,11 @@ class RunnerRewritingAdapter private constructor( } companion object { + const val IMPLICIT_CLASS_OUTER_RULE_NAME = "sImplicitClassOuterRule" + const val IMPLICIT_CLASS_INNER_RULE_NAME = "sImplicitClassInnerRule" + const val IMPLICIT_INST_OUTER_RULE_NAME = "sImplicitInstOuterRule" + const val IMPLICIT_INST_INNER_RULE_NAME = "sImplicitInstInnerRule" + fun shouldProcess(classes: ClassNodes, className: String): Boolean { if (!isTestLookingClass(classes, className)) { return false @@ -463,4 +467,4 @@ class RunnerRewritingAdapter private constructor( } } } -}
\ No newline at end of file +} diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 55677078f939..0c99fcfd221a 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -209,6 +209,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); + if (action == null) { + return; + } + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); if (DEBUG) { diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java index 2e9a4dcb45aa..a10039f9bf6c 100644 --- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java +++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java @@ -462,7 +462,9 @@ public final class AutoFillUI { @Override public void onShown() { - mCallback.onShown(UI_TYPE_DIALOG, response.getDatasets().size()); + if (mCallback != null) { + mCallback.onShown(UI_TYPE_DIALOG, response.getDatasets().size()); + } } @Override @@ -511,7 +513,9 @@ public final class AutoFillUI { @Override public void startIntentSender(IntentSender intentSender) { - mCallback.startIntentSenderAndFinishSession(intentSender); + if (mCallback != null) { + mCallback.startIntentSenderAndFinishSession(intentSender); + } } private void log(int type) { diff --git a/services/core/java/com/android/server/SerialService.java b/services/core/java/com/android/server/SerialService.java index 82c2038d8011..71885fdc9c81 100644 --- a/services/core/java/com/android/server/SerialService.java +++ b/services/core/java/com/android/server/SerialService.java @@ -18,22 +18,30 @@ package com.android.server; import android.annotation.EnforcePermission; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.hardware.ISerialManager; import android.hardware.SerialManagerInternal; import android.os.ParcelFileDescriptor; import android.os.PermissionEnforcer; +import android.system.ErrnoException; +import android.system.Os; +import android.system.OsConstants; +import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import java.io.File; +import java.io.FileDescriptor; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.function.Supplier; @android.ravenwood.annotation.RavenwoodKeepWholeClass public class SerialService extends ISerialManager.Stub { + private static final String TAG = "SerialService"; + private final Context mContext; @GuardedBy("mSerialPorts") @@ -50,7 +58,7 @@ public class SerialService extends ISerialManager.Stub { final String[] serialPorts = getSerialPorts(context); for (String serialPort : serialPorts) { mSerialPorts.put(serialPort, () -> { - return native_open(serialPort); + return tryOpen(serialPort); }); } } @@ -135,5 +143,14 @@ public class SerialService extends ISerialManager.Stub { } }; - private native ParcelFileDescriptor native_open(String path); + private static @Nullable ParcelFileDescriptor tryOpen(String path) { + try { + FileDescriptor fd = Os.open(path, OsConstants.O_RDWR | OsConstants.O_NOCTTY, 0); + return new ParcelFileDescriptor(fd); + } catch (ErrnoException e) { + Slog.e(TAG, "Could not open: " + path, e); + // We return null to preserve API semantics from earlier implementation variants. + return null; + } + } } diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java index 8b619a40f19b..9b987e9850c4 100644 --- a/services/core/java/com/android/server/SystemConfig.java +++ b/services/core/java/com/android/server/SystemConfig.java @@ -670,6 +670,17 @@ public class SystemConfig { } private void readAllPermissions() { + readAllPermissionsFromXml(); + readAllPermissionsFromEnvironment(); + + // Apply global feature removal last, after all features have been read. + // This only needs to happen once. + for (String featureName : mUnavailableFeatures) { + removeFeature(featureName); + } + } + + private void readAllPermissionsFromXml() { final XmlPullParser parser = Xml.newPullParser(); // Read configuration from system @@ -1732,7 +1743,13 @@ public class SystemConfig { } finally { IoUtils.closeQuietly(permReader); } + } + // Add features or permission dependent on global system properties (as + // opposed to XML permission files). + // This only needs to be called once after all features have been parsed + // from various partition/apex sources. + private void readAllPermissionsFromEnvironment() { // Some devices can be field-converted to FBE, so offer to splice in // those features if not already defined by the static config if (StorageManager.isFileEncrypted()) { @@ -1773,10 +1790,6 @@ public class SystemConfig { addFeature(PackageManager.FEATURE_EROFS_LEGACY, 0); } } - - for (String featureName : mUnavailableFeatures) { - removeFeature(featureName); - } } private @Nullable SignedPackage parseEnhancedConfirmationTrustedPackage(XmlPullParser parser, diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 51c768b80eff..06e6c8b1ec53 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -48,6 +48,7 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; +import android.net.vcn.Flags; import android.net.vcn.IVcnManagementService; import android.net.vcn.IVcnStatusCallback; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; @@ -524,6 +525,9 @@ public class VcnManagementService extends IVcnManagementService.Stub { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); + if (action == null) { + return; + } switch (action) { case Intent.ACTION_PACKAGE_ADDED: // Fallthrough @@ -878,6 +882,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { private void garbageCollectAndWriteVcnConfigsLocked() { final SubscriptionManager subMgr = mContext.getSystemService(SubscriptionManager.class); + final Set<ParcelUuid> subGroups = mLastSnapshot.getAllSubscriptionGroups(); boolean shouldWrite = false; @@ -885,11 +890,20 @@ public class VcnManagementService extends IVcnManagementService.Stub { while (configsIterator.hasNext()) { final ParcelUuid subGrp = configsIterator.next(); - final List<SubscriptionInfo> subscriptions = subMgr.getSubscriptionsInGroup(subGrp); - if (subscriptions == null || subscriptions.isEmpty()) { - // Trim subGrps with no more subscriptions; must have moved to another subGrp - configsIterator.remove(); - shouldWrite = true; + if (Flags.fixConfigGarbageCollection()) { + if (!subGroups.contains(subGrp)) { + // Trim subGrps with no more subscriptions; must have moved to another subGrp + logDbg("Garbage collect VcnConfig for group=" + subGrp); + configsIterator.remove(); + shouldWrite = true; + } + } else { + final List<SubscriptionInfo> subscriptions = subMgr.getSubscriptionsInGroup(subGrp); + if (subscriptions == null || subscriptions.isEmpty()) { + // Trim subGrps with no more subscriptions; must have moved to another subGrp + configsIterator.remove(); + shouldWrite = true; + } } } @@ -1094,13 +1108,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { synchronized (mLock) { final Vcn vcn = mVcns.get(subGrp); final VcnConfig vcnConfig = mConfigs.get(subGrp); - if (vcn != null) { - if (vcnConfig == null) { - // TODO: b/284381334 Investigate for the root cause of this issue - // and handle it properly - logWtf("Vcn instance exists but VcnConfig does not for " + subGrp); - } - + if (vcn != null && vcnConfig != null) { if (vcn.getStatus() == VCN_STATUS_CODE_ACTIVE) { isVcnManagedNetwork = true; } @@ -1120,6 +1128,8 @@ public class VcnManagementService extends IVcnManagementService.Stub { } } } + } else if (vcn != null && vcnConfig == null) { + logWtf("Vcn instance exists but VcnConfig does not for " + subGrp); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index f549c7b2d303..4a4a1b4de0f0 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2874,8 +2874,12 @@ public class ActivityManagerService extends IActivityManager.Stub // Add common services. // IMPORTANT: Before adding services here, make sure ephemeral apps can access them too. // Enable the check in ApplicationThread.bindApplication() to make sure. - if (!android.server.Flags.removeJavaServiceManagerCache()) { - addServiceToMap(mAppBindArgs, "permissionmgr"); + + // Removing User Service and App Ops Service from cache breaks boot for auto. + // Removing permissionmgr breaks tests for Android Auto due to SELinux restrictions. + // TODO: fix SELinux restrictions and remove caching for Android Auto. + if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) + || !android.server.Flags.removeJavaServiceManagerCache()) { addServiceToMap(mAppBindArgs, Context.ALARM_SERVICE); addServiceToMap(mAppBindArgs, Context.DISPLAY_SERVICE); addServiceToMap(mAppBindArgs, Context.NETWORKMANAGEMENT_SERVICE); @@ -2896,12 +2900,12 @@ public class ActivityManagerService extends IActivityManager.Stub // See b/79378449 // Getting the window service and package service binder from servicemanager // is blocked for Apps. However they are necessary for apps. - // Removing User Service and App Ops Service from cache breaks boot for auto. // TODO: remove exception - addServiceToMap(mAppBindArgs, Context.APP_OPS_SERVICE); addServiceToMap(mAppBindArgs, "package"); addServiceToMap(mAppBindArgs, Context.WINDOW_SERVICE); addServiceToMap(mAppBindArgs, Context.USER_SERVICE); + addServiceToMap(mAppBindArgs, "permissionmgr"); + addServiceToMap(mAppBindArgs, Context.APP_OPS_SERVICE); } return mAppBindArgs; } @@ -5235,6 +5239,11 @@ public class ActivityManagerService extends IActivityManager.Stub mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (action == null) { + return; + } + String[] pkgs = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES); if (pkgs != null) { for (String pkg : pkgs) { diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java index 4c87e1ce357c..c036605b029f 100644 --- a/services/core/java/com/android/server/am/AppRestrictionController.java +++ b/services/core/java/com/android/server/am/AppRestrictionController.java @@ -373,7 +373,10 @@ public final class AppRestrictionController { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); - switch (intent.getAction()) { + if (action == null) { + return; + } + switch (action) { case Intent.ACTION_PACKAGE_ADDED: { if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); diff --git a/services/core/java/com/android/server/appbinding/AppBindingService.java b/services/core/java/com/android/server/appbinding/AppBindingService.java index 5db6dc7ccc15..6ccb3ee8bcc9 100644 --- a/services/core/java/com/android/server/appbinding/AppBindingService.java +++ b/services/core/java/com/android/server/appbinding/AppBindingService.java @@ -235,6 +235,9 @@ public class AppBindingService extends Binder { } final String action = intent.getAction(); + if (action == null) { + return; + } if (Intent.ACTION_USER_REMOVED.equals(action)) { onUserRemoved(userId); diff --git a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java index 01f770b1e89f..9fa5da47aae7 100644 --- a/services/core/java/com/android/server/audio/LoudnessCodecHelper.java +++ b/services/core/java/com/android/server/audio/LoudnessCodecHelper.java @@ -133,6 +133,9 @@ public class LoudnessCodecHelper { private static final EventLogger sLogger = new EventLogger( AudioService.LOG_NB_EVENTS_LOUDNESS_CODEC, "Loudness updates"); + private final Object mDispatcherLock = new Object(); + + @GuardedBy("mDispatcherLock") private final LoudnessRemoteCallbackList mLoudnessUpdateDispatchers = new LoudnessRemoteCallbackList(this); @@ -339,12 +342,16 @@ public class LoudnessCodecHelper { } void registerLoudnessCodecUpdatesDispatcher(ILoudnessCodecUpdatesDispatcher dispatcher) { - mLoudnessUpdateDispatchers.register(dispatcher, Binder.getCallingPid()); + synchronized (mDispatcherLock) { + mLoudnessUpdateDispatchers.register(dispatcher, Binder.getCallingPid()); + } } void unregisterLoudnessCodecUpdatesDispatcher( ILoudnessCodecUpdatesDispatcher dispatcher) { - mLoudnessUpdateDispatchers.unregister(dispatcher); + synchronized (mDispatcherLock) { + mLoudnessUpdateDispatchers.unregister(dispatcher); + } } void startLoudnessCodecUpdates(int sessionId) { @@ -640,17 +647,20 @@ public class LoudnessCodecHelper { Log.d(TAG, "dispatchNewLoudnessParameters: sessionId " + sessionId + " bundle: " + bundle); } - final int nbDispatchers = mLoudnessUpdateDispatchers.beginBroadcast(); - for (int i = 0; i < nbDispatchers; ++i) { - try { - mLoudnessUpdateDispatchers.getBroadcastItem(i) - .dispatchLoudnessCodecParameterChange(sessionId, bundle); - } catch (RemoteException e) { - Log.e(TAG, "Error dispatching for sessionId " + sessionId + " bundle: " + bundle, - e); + synchronized (mDispatcherLock) { + final int nbDispatchers = mLoudnessUpdateDispatchers.beginBroadcast(); + for (int i = 0; i < nbDispatchers; ++i) { + try { + mLoudnessUpdateDispatchers.getBroadcastItem(i) + .dispatchLoudnessCodecParameterChange(sessionId, bundle); + } catch (RemoteException e) { + Log.e(TAG, + "Error dispatching for sessionId " + sessionId + " bundle: " + bundle, + e); + } } + mLoudnessUpdateDispatchers.finishBroadcast(); } - mLoudnessUpdateDispatchers.finishBroadcast(); } @GuardedBy("mLock") diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index bbe7b2b038c9..8436c809337b 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -1975,7 +1975,6 @@ public class HdmiControlService extends SystemService { void setAudioStatus(boolean mute, int volume) { if (!isTvDeviceEnabled() || !tv().isSystemAudioActivated() - || !tv().isArcEstablished() // Don't update TV volume when SAM is on and ARC is off || getHdmiCecVolumeControl() == HdmiControlManager.VOLUME_CONTROL_DISABLED) { return; diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java index 97c32b986d38..49934126ab8c 100644 --- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java +++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java @@ -255,17 +255,6 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener { } } - private static boolean isCompatibleLocale(Locale systemLocale, Locale keyboardLocale) { - // Different languages are never compatible - if (!systemLocale.getLanguage().equals(keyboardLocale.getLanguage())) { - return false; - } - // If both the system and the keyboard layout have a country specifier, they must be equal. - return TextUtils.isEmpty(systemLocale.getCountry()) - || TextUtils.isEmpty(keyboardLocale.getCountry()) - || systemLocale.getCountry().equals(keyboardLocale.getCountry()); - } - @MainThread private void updateKeyboardLayouts() { // Scan all input devices state for keyboard layouts that have been uninstalled. @@ -953,21 +942,33 @@ class KeyboardLayoutManager implements InputManager.InputDeviceListener { return; } + List<String> layoutNames = new ArrayList<>(); + for (String layoutDesc : config.getConfiguredLayouts()) { + KeyboardLayout kl = getKeyboardLayout(layoutDesc); + if (kl == null) { + // b/349033234: Weird state with stale keyboard layout configured. + // Possibly due to race condition between KCM providing package being removed and + // corresponding layouts being removed from Datastore and cache. + // {@see updateKeyboardLayouts()} + // + // Ideally notification will be correctly shown after the keyboard layouts are + // configured again with the new package state. + return; + } + layoutNames.add(kl.getLabel()); + } showKeyboardLayoutNotification( r.getString( R.string.keyboard_layout_notification_selected_title, inputDevice.getName()), - createConfiguredNotificationText(mContext, config.getConfiguredLayouts()), + createConfiguredNotificationText(mContext, layoutNames), inputDevice); } @MainThread private String createConfiguredNotificationText(@NonNull Context context, - @NonNull Set<String> selectedLayouts) { + @NonNull List<String> layoutNames) { final Resources r = context.getResources(); - List<String> layoutNames = new ArrayList<>(); - selectedLayouts.forEach( - (layoutDesc) -> layoutNames.add(getKeyboardLayout(layoutDesc).getLabel())); Collections.sort(layoutNames); switch (layoutNames.size()) { case 1: diff --git a/services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java b/services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java deleted file mode 100644 index f09e035ecc78..000000000000 --- a/services/core/java/com/android/server/integrity/parser/BinaryFileOperations.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.integrity.parser; - -import static com.android.server.integrity.model.ComponentBitSize.IS_HASHED_BITS; -import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS; - -import android.content.integrity.IntegrityUtils; - -import com.android.server.integrity.model.BitInputStream; - -import java.io.IOException; -import java.nio.ByteBuffer; - -/** - * Helper methods for reading standard data structures from {@link BitInputStream}. - */ -public class BinaryFileOperations { - - /** - * Read an string value with the given size and hash status from a {@code BitInputStream}. - * - * If the value is hashed, get the hex-encoding of the value. Serialized values are in raw form. - * All hashed values are hex-encoded. - */ - public static String getStringValue(BitInputStream bitInputStream) throws IOException { - boolean isHashedValue = bitInputStream.getNext(IS_HASHED_BITS) == 1; - int valueSize = bitInputStream.getNext(VALUE_SIZE_BITS); - return getStringValue(bitInputStream, valueSize, isHashedValue); - } - - /** - * Read an string value with the given size and hash status from a {@code BitInputStream}. - * - * If the value is hashed, get the hex-encoding of the value. Serialized values are in raw form. - * All hashed values are hex-encoded. - */ - public static String getStringValue( - BitInputStream bitInputStream, int valueSize, boolean isHashedValue) - throws IOException { - if (!isHashedValue) { - StringBuilder value = new StringBuilder(); - while (valueSize-- > 0) { - value.append((char) bitInputStream.getNext(/* numOfBits= */ 8)); - } - return value.toString(); - } - ByteBuffer byteBuffer = ByteBuffer.allocate(valueSize); - while (valueSize-- > 0) { - byteBuffer.put((byte) (bitInputStream.getNext(/* numOfBits= */ 8) & 0xFF)); - } - return IntegrityUtils.getHexDigest(byteBuffer.array()); - } - - /** Read an integer value from a {@code BitInputStream}. */ - public static int getIntValue(BitInputStream bitInputStream) throws IOException { - return bitInputStream.getNext(/* numOfBits= */ 32); - } - - /** Read an boolean value from a {@code BitInputStream}. */ - public static boolean getBooleanValue(BitInputStream bitInputStream) throws IOException { - return bitInputStream.getNext(/* numOfBits= */ 1) == 1; - } -} diff --git a/services/core/java/com/android/server/integrity/parser/LimitInputStream.java b/services/core/java/com/android/server/integrity/parser/LimitInputStream.java deleted file mode 100644 index a91bbb7dbae1..000000000000 --- a/services/core/java/com/android/server/integrity/parser/LimitInputStream.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.integrity.parser; - -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; - -/** An {@link InputStream} that basically truncates another {@link InputStream} */ -public class LimitInputStream extends FilterInputStream { - private int mReadBytes; - private final int mLimit; - - public LimitInputStream(InputStream in, int limit) { - super(in); - if (limit < 0) { - throw new IllegalArgumentException("limit " + limit + " cannot be negative"); - } - mReadBytes = 0; - mLimit = limit; - } - - @Override - public int available() throws IOException { - return Math.min(super.available(), mLimit - mReadBytes); - } - - @Override - public int read() throws IOException { - if (mReadBytes == mLimit) { - return -1; - } - mReadBytes++; - return super.read(); - } - - @Override - public int read(byte[] b) throws IOException { - return read(b, 0, b.length); - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - if (len <= 0) { - return 0; - } - int available = available(); - if (available <= 0) { - return -1; - } - int result = super.read(b, off, Math.min(len, available)); - mReadBytes += result; - return result; - } - - @Override - public long skip(long n) throws IOException { - if (n <= 0) { - return 0; - } - int available = available(); - if (available <= 0) { - return 0; - } - int bytesToSkip = (int) Math.min(available, n); - long bytesSkipped = super.skip(bytesToSkip); - mReadBytes += (int) bytesSkipped; - return bytesSkipped; - } -} diff --git a/services/core/java/com/android/server/integrity/parser/RandomAccessInputStream.java b/services/core/java/com/android/server/integrity/parser/RandomAccessInputStream.java deleted file mode 100644 index 206e6a1f0197..000000000000 --- a/services/core/java/com/android/server/integrity/parser/RandomAccessInputStream.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.integrity.parser; - -import java.io.IOException; -import java.io.InputStream; - -/** A wrapper around {@link RandomAccessObject} to turn it into a {@link InputStream}. */ -public class RandomAccessInputStream extends InputStream { - - private final RandomAccessObject mRandomAccessObject; - - private int mPosition; - - public RandomAccessInputStream(RandomAccessObject object) throws IOException { - mRandomAccessObject = object; - mPosition = 0; - } - - /** Returns the position of the file pointer. */ - public int getPosition() { - return mPosition; - } - - /** See {@link RandomAccessObject#seek(int)} */ - public void seek(int position) throws IOException { - mRandomAccessObject.seek(position); - mPosition = position; - } - - @Override - public int available() throws IOException { - return mRandomAccessObject.length() - mPosition; - } - - @Override - public void close() throws IOException { - mRandomAccessObject.close(); - } - - @Override - public int read() throws IOException { - if (available() <= 0) { - return -1; - } - mPosition++; - return mRandomAccessObject.read(); - } - - @Override - public int read(byte[] b) throws IOException { - return read(b, 0, b.length); - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - if (len <= 0) { - return 0; - } - int available = available(); - if (available <= 0) { - return -1; - } - int result = mRandomAccessObject.read(b, off, Math.min(len, available)); - mPosition += result; - return result; - } - - @Override - public long skip(long n) throws IOException { - if (n <= 0) { - return 0; - } - int available = available(); - if (available <= 0) { - return 0; - } - int skipAmount = (int) Math.min(available, n); - mPosition += skipAmount; - mRandomAccessObject.seek(mPosition); - return skipAmount; - } -} diff --git a/services/core/java/com/android/server/integrity/parser/RandomAccessObject.java b/services/core/java/com/android/server/integrity/parser/RandomAccessObject.java deleted file mode 100644 index d9b2e38b0062..000000000000 --- a/services/core/java/com/android/server/integrity/parser/RandomAccessObject.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.integrity.parser; - -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.ByteBuffer; - -/** An interface for random access objects like RandomAccessFile or byte arrays. */ -public abstract class RandomAccessObject { - - /** See {@link RandomAccessFile#seek(long)}. */ - public abstract void seek(int position) throws IOException; - - /** See {@link RandomAccessFile#read()}. */ - public abstract int read() throws IOException; - - /** See {@link RandomAccessFile#read(byte[], int, int)}. */ - public abstract int read(byte[] bytes, int off, int len) throws IOException; - - /** See {@link RandomAccessFile#close()}. */ - public abstract void close() throws IOException; - - /** See {@link java.io.RandomAccessFile#length()}. */ - public abstract int length(); - - /** Static constructor from a file. */ - public static RandomAccessObject ofFile(File file) throws IOException { - return new RandomAccessFileObject(file); - } - - /** Static constructor from a byte array. */ - public static RandomAccessObject ofBytes(byte[] bytes) { - return new RandomAccessByteArrayObject(bytes); - } - - private static class RandomAccessFileObject extends RandomAccessObject { - private final RandomAccessFile mRandomAccessFile; - // We cache the length since File.length() invokes file IO. - private final int mLength; - - RandomAccessFileObject(File file) throws IOException { - long length = file.length(); - if (length > Integer.MAX_VALUE) { - throw new IOException("Unsupported file size (too big) " + length); - } - - mRandomAccessFile = new RandomAccessFile(file, /* mode= */ "r"); - mLength = (int) length; - } - - @Override - public void seek(int position) throws IOException { - mRandomAccessFile.seek(position); - } - - @Override - public int read() throws IOException { - return mRandomAccessFile.read(); - } - - @Override - public int read(byte[] bytes, int off, int len) throws IOException { - return mRandomAccessFile.read(bytes, off, len); - } - - @Override - public void close() throws IOException { - mRandomAccessFile.close(); - } - - @Override - public int length() { - return mLength; - } - } - - private static class RandomAccessByteArrayObject extends RandomAccessObject { - - private final ByteBuffer mBytes; - - RandomAccessByteArrayObject(byte[] bytes) { - mBytes = ByteBuffer.wrap(bytes); - } - - @Override - public void seek(int position) throws IOException { - mBytes.position(position); - } - - @Override - public int read() throws IOException { - if (!mBytes.hasRemaining()) { - return -1; - } - - return mBytes.get() & 0xFF; - } - - @Override - public int read(byte[] bytes, int off, int len) throws IOException { - int bytesToCopy = Math.min(len, mBytes.remaining()); - if (bytesToCopy <= 0) { - return 0; - } - mBytes.get(bytes, off, len); - return bytesToCopy; - } - - @Override - public void close() throws IOException {} - - @Override - public int length() { - return mBytes.capacity(); - } - } -} diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java deleted file mode 100644 index ea3a3d5f1c60..000000000000 --- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity.parser; - -import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START; -import static com.android.server.integrity.model.ComponentBitSize.BYTE_BITS; -import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END; -import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START; -import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS; -import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS; -import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS; -import static com.android.server.integrity.model.ComponentBitSize.INSTALLER_ALLOWED_BY_MANIFEST_START; -import static com.android.server.integrity.model.ComponentBitSize.IS_HASHED_BITS; -import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS; -import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS; -import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS; -import static com.android.server.integrity.model.ComponentBitSize.SIGNAL_BIT; -import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS; -import static com.android.server.integrity.parser.BinaryFileOperations.getBooleanValue; -import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue; -import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue; - -import android.content.integrity.AtomicFormula; -import android.content.integrity.CompoundFormula; -import android.content.integrity.InstallerAllowedByManifestFormula; -import android.content.integrity.IntegrityFormula; -import android.content.integrity.Rule; - -import com.android.server.integrity.model.BitInputStream; - -import java.io.BufferedInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** A helper class to parse rules into the {@link Rule} model from Binary representation. */ -public class RuleBinaryParser implements RuleParser { - - @Override - public List<Rule> parse(byte[] ruleBytes) throws RuleParseException { - return parse(RandomAccessObject.ofBytes(ruleBytes), Collections.emptyList()); - } - - @Override - public List<Rule> parse(RandomAccessObject randomAccessObject, List<RuleIndexRange> indexRanges) - throws RuleParseException { - try (RandomAccessInputStream randomAccessInputStream = - new RandomAccessInputStream(randomAccessObject)) { - return parseRules(randomAccessInputStream, indexRanges); - } catch (Exception e) { - throw new RuleParseException(e.getMessage(), e); - } - } - - private List<Rule> parseRules( - RandomAccessInputStream randomAccessInputStream, List<RuleIndexRange> indexRanges) - throws IOException { - - // Read the rule binary file format version. - randomAccessInputStream.skip(FORMAT_VERSION_BITS / BYTE_BITS); - - return indexRanges.isEmpty() - ? parseAllRules(randomAccessInputStream) - : parseIndexedRules(randomAccessInputStream, indexRanges); - } - - private List<Rule> parseAllRules(RandomAccessInputStream randomAccessInputStream) - throws IOException { - List<Rule> parsedRules = new ArrayList<>(); - - BitInputStream inputStream = - new BitInputStream(new BufferedInputStream(randomAccessInputStream)); - while (inputStream.hasNext()) { - if (inputStream.getNext(SIGNAL_BIT) == 1) { - parsedRules.add(parseRule(inputStream)); - } - } - - return parsedRules; - } - - private List<Rule> parseIndexedRules( - RandomAccessInputStream randomAccessInputStream, List<RuleIndexRange> indexRanges) - throws IOException { - List<Rule> parsedRules = new ArrayList<>(); - - for (RuleIndexRange range : indexRanges) { - randomAccessInputStream.seek(range.getStartIndex()); - - BitInputStream inputStream = - new BitInputStream( - new BufferedInputStream( - new LimitInputStream( - randomAccessInputStream, - range.getEndIndex() - range.getStartIndex()))); - - // Read the rules until we reach the end index. available() here is not reliable. - while (inputStream.hasNext()) { - if (inputStream.getNext(SIGNAL_BIT) == 1) { - parsedRules.add(parseRule(inputStream)); - } - } - } - - return parsedRules; - } - - private Rule parseRule(BitInputStream bitInputStream) throws IOException { - IntegrityFormula formula = parseFormula(bitInputStream); - int effect = bitInputStream.getNext(EFFECT_BITS); - - if (bitInputStream.getNext(SIGNAL_BIT) != 1) { - throw new IllegalArgumentException("A rule must end with a '1' bit."); - } - - return new Rule(formula, effect); - } - - private IntegrityFormula parseFormula(BitInputStream bitInputStream) throws IOException { - int separator = bitInputStream.getNext(SEPARATOR_BITS); - switch (separator) { - case ATOMIC_FORMULA_START: - return parseAtomicFormula(bitInputStream); - case COMPOUND_FORMULA_START: - return parseCompoundFormula(bitInputStream); - case COMPOUND_FORMULA_END: - return null; - case INSTALLER_ALLOWED_BY_MANIFEST_START: - return new InstallerAllowedByManifestFormula(); - default: - throw new IllegalArgumentException( - String.format("Unknown formula separator: %s", separator)); - } - } - - private CompoundFormula parseCompoundFormula(BitInputStream bitInputStream) throws IOException { - int connector = bitInputStream.getNext(CONNECTOR_BITS); - List<IntegrityFormula> formulas = new ArrayList<>(); - - IntegrityFormula parsedFormula = parseFormula(bitInputStream); - while (parsedFormula != null) { - formulas.add(parsedFormula); - parsedFormula = parseFormula(bitInputStream); - } - - return new CompoundFormula(connector, formulas); - } - - private AtomicFormula parseAtomicFormula(BitInputStream bitInputStream) throws IOException { - int key = bitInputStream.getNext(KEY_BITS); - int operator = bitInputStream.getNext(OPERATOR_BITS); - - switch (key) { - case AtomicFormula.PACKAGE_NAME: - case AtomicFormula.APP_CERTIFICATE: - case AtomicFormula.APP_CERTIFICATE_LINEAGE: - case AtomicFormula.INSTALLER_NAME: - case AtomicFormula.INSTALLER_CERTIFICATE: - case AtomicFormula.STAMP_CERTIFICATE_HASH: - boolean isHashedValue = bitInputStream.getNext(IS_HASHED_BITS) == 1; - int valueSize = bitInputStream.getNext(VALUE_SIZE_BITS); - String stringValue = getStringValue(bitInputStream, valueSize, isHashedValue); - return new AtomicFormula.StringAtomicFormula(key, stringValue, isHashedValue); - case AtomicFormula.VERSION_CODE: - // TODO(b/147880712): temporary hack until our input handles long - long upper = getIntValue(bitInputStream); - long lower = getIntValue(bitInputStream); - long longValue = (upper << 32) | lower; - return new AtomicFormula.LongAtomicFormula(key, operator, longValue); - case AtomicFormula.PRE_INSTALLED: - case AtomicFormula.STAMP_TRUSTED: - boolean booleanValue = getBooleanValue(bitInputStream); - return new AtomicFormula.BooleanAtomicFormula(key, booleanValue); - default: - throw new IllegalArgumentException(String.format("Unknown key: %d", key)); - } - } -} diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java b/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java deleted file mode 100644 index 595a035baf1d..000000000000 --- a/services/core/java/com/android/server/integrity/parser/RuleIndexRange.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.integrity.parser; - -import android.annotation.Nullable; - -/** - * A wrapper class to represent an indexing range that is identified by the {@link - * RuleIndexingController}. - */ -public class RuleIndexRange { - private int mStartIndex; - private int mEndIndex; - - /** Constructor with start and end indexes. */ - public RuleIndexRange(int startIndex, int endIndex) { - this.mStartIndex = startIndex; - this.mEndIndex = endIndex; - } - - /** Returns the startIndex. */ - public int getStartIndex() { - return mStartIndex; - } - - /** Returns the end index. */ - public int getEndIndex() { - return mEndIndex; - } - - @Override - public boolean equals(@Nullable Object object) { - return mStartIndex == ((RuleIndexRange) object).getStartIndex() - && mEndIndex == ((RuleIndexRange) object).getEndIndex(); - } - - @Override - public String toString() { - return String.format("Range{%d, %d}", mStartIndex, mEndIndex); - } -} diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java deleted file mode 100644 index 348a03be7317..000000000000 --- a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.integrity.parser; - -import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY; -import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY; -import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue; -import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue; - -import android.content.integrity.AppInstallMetadata; - -import com.android.server.integrity.model.BitInputStream; - -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.stream.Collectors; - -/** Helper class to identify the necessary indexes that needs to be read. */ -public class RuleIndexingController { - - private static LinkedHashMap<String, Integer> sPackageNameBasedIndexes; - private static LinkedHashMap<String, Integer> sAppCertificateBasedIndexes; - private static LinkedHashMap<String, Integer> sUnindexedRuleIndexes; - - /** - * Provide the indexing file to read and the object will be constructed by reading and - * identifying the indexes. - */ - public RuleIndexingController(InputStream inputStream) throws IOException { - BitInputStream bitInputStream = new BitInputStream(inputStream); - sPackageNameBasedIndexes = getNextIndexGroup(bitInputStream); - sAppCertificateBasedIndexes = getNextIndexGroup(bitInputStream); - sUnindexedRuleIndexes = getNextIndexGroup(bitInputStream); - } - - /** - * Returns a list of integers with the starting and ending bytes of the rules that needs to be - * read and evaluated. - */ - public List<RuleIndexRange> identifyRulesToEvaluate(AppInstallMetadata appInstallMetadata) { - List<RuleIndexRange> indexRanges = new ArrayList<>(); - - // Add the range for package name indexes rules. - indexRanges.add( - searchIndexingKeysRangeContainingKey( - sPackageNameBasedIndexes, appInstallMetadata.getPackageName())); - - // Add the range for app certificate indexes rules of all certificates. - for (String appCertificate : appInstallMetadata.getAppCertificates()) { - indexRanges.add( - searchIndexingKeysRangeContainingKey( - sAppCertificateBasedIndexes, appCertificate)); - } - - // Add the range for unindexed rules. - indexRanges.add( - new RuleIndexRange( - sUnindexedRuleIndexes.get(START_INDEXING_KEY), - sUnindexedRuleIndexes.get(END_INDEXING_KEY))); - - return indexRanges; - } - - private LinkedHashMap<String, Integer> getNextIndexGroup(BitInputStream bitInputStream) - throws IOException { - LinkedHashMap<String, Integer> keyToIndexMap = new LinkedHashMap<>(); - while (bitInputStream.hasNext()) { - String key = getStringValue(bitInputStream); - int value = getIntValue(bitInputStream); - - keyToIndexMap.put(key, value); - - if (key.matches(END_INDEXING_KEY)) { - break; - } - } - if (keyToIndexMap.size() < 2) { - throw new IllegalStateException("Indexing file is corrupt."); - } - return keyToIndexMap; - } - - private static RuleIndexRange searchIndexingKeysRangeContainingKey( - LinkedHashMap<String, Integer> indexMap, String searchedKey) { - List<String> keys = indexMap.keySet().stream().collect(Collectors.toList()); - List<String> identifiedKeyRange = - searchKeysRangeContainingKey(keys, searchedKey, 0, keys.size() - 1); - return new RuleIndexRange( - indexMap.get(identifiedKeyRange.get(0)), indexMap.get(identifiedKeyRange.get(1))); - } - - private static List<String> searchKeysRangeContainingKey( - List<String> sortedKeyList, String key, int startIndex, int endIndex) { - if (endIndex <= startIndex) { - throw new IllegalStateException("Indexing file is corrupt."); - } - if (endIndex - startIndex == 1) { - return Arrays.asList(sortedKeyList.get(startIndex), sortedKeyList.get(endIndex)); - } - - int midKeyIndex = startIndex + ((endIndex - startIndex) / 2); - String midKey = sortedKeyList.get(midKeyIndex); - - if (key.compareTo(midKey) >= 0) { - return searchKeysRangeContainingKey(sortedKeyList, key, midKeyIndex, endIndex); - } else { - return searchKeysRangeContainingKey(sortedKeyList, key, startIndex, midKeyIndex); - } - } -} diff --git a/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java b/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java deleted file mode 100644 index e831e40e70d1..000000000000 --- a/services/core/java/com/android/server/integrity/parser/RuleMetadataParser.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity.parser; - -import android.annotation.Nullable; -import android.util.Xml; - -import com.android.modules.utils.TypedXmlPullParser; -import com.android.server.integrity.model.RuleMetadata; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -import java.io.IOException; -import java.io.InputStream; - -/** Helper class for parsing rule metadata. */ -public class RuleMetadataParser { - - public static final String RULE_PROVIDER_TAG = "P"; - public static final String VERSION_TAG = "V"; - - /** Parse the rule metadata from an input stream. */ - @Nullable - public static RuleMetadata parse(InputStream inputStream) - throws XmlPullParserException, IOException { - - String ruleProvider = ""; - String version = ""; - - TypedXmlPullParser xmlPullParser = Xml.resolvePullParser(inputStream); - - int eventType; - while ((eventType = xmlPullParser.next()) != XmlPullParser.END_DOCUMENT) { - if (eventType == XmlPullParser.START_TAG) { - String tag = xmlPullParser.getName(); - switch (tag) { - case RULE_PROVIDER_TAG: - ruleProvider = xmlPullParser.nextText(); - break; - case VERSION_TAG: - version = xmlPullParser.nextText(); - break; - default: - throw new IllegalStateException("Unknown tag in metadata: " + tag); - } - } - } - - return new RuleMetadata(ruleProvider, version); - } -} diff --git a/services/core/java/com/android/server/integrity/parser/RuleParser.java b/services/core/java/com/android/server/integrity/parser/RuleParser.java deleted file mode 100644 index 126dacc4fa40..000000000000 --- a/services/core/java/com/android/server/integrity/parser/RuleParser.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity.parser; - -import android.content.integrity.Rule; - -import java.util.List; - -/** A helper class to parse rules into the {@link Rule} model. */ -public interface RuleParser { - - /** Parse rules from bytes. */ - List<Rule> parse(byte[] ruleBytes) throws RuleParseException; - - /** Parse rules from an input stream. */ - List<Rule> parse(RandomAccessObject randomAccessObject, List<RuleIndexRange> ruleIndexRanges) - throws RuleParseException; -} diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java deleted file mode 100644 index 8ba5870aef0f..000000000000 --- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity.serializer; - -import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START; -import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END; -import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START; -import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS; -import static com.android.server.integrity.model.ComponentBitSize.DEFAULT_FORMAT_VERSION; -import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS; -import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS; -import static com.android.server.integrity.model.ComponentBitSize.INSTALLER_ALLOWED_BY_MANIFEST_START; -import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS; -import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS; -import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS; -import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS; -import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY; -import static com.android.server.integrity.model.IndexingFileConstants.INDEXING_BLOCK_SIZE; -import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY; -import static com.android.server.integrity.serializer.RuleIndexingDetails.APP_CERTIFICATE_INDEXED; -import static com.android.server.integrity.serializer.RuleIndexingDetails.NOT_INDEXED; -import static com.android.server.integrity.serializer.RuleIndexingDetails.PACKAGE_NAME_INDEXED; - -import android.content.integrity.AtomicFormula; -import android.content.integrity.CompoundFormula; -import android.content.integrity.InstallerAllowedByManifestFormula; -import android.content.integrity.IntegrityFormula; -import android.content.integrity.IntegrityUtils; -import android.content.integrity.Rule; - -import com.android.internal.util.Preconditions; -import com.android.server.integrity.model.BitOutputStream; -import com.android.server.integrity.model.ByteTrackedOutputStream; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; - -/** A helper class to serialize rules from the {@link Rule} model to Binary representation. */ -public class RuleBinarySerializer implements RuleSerializer { - static final int TOTAL_RULE_SIZE_LIMIT = 200000; - static final int INDEXED_RULE_SIZE_LIMIT = 100000; - static final int NONINDEXED_RULE_SIZE_LIMIT = 1000; - - // Get the byte representation for a list of rules. - @Override - public byte[] serialize(List<Rule> rules, Optional<Integer> formatVersion) - throws RuleSerializeException { - try { - ByteArrayOutputStream rulesOutputStream = new ByteArrayOutputStream(); - serialize(rules, formatVersion, rulesOutputStream, new ByteArrayOutputStream()); - return rulesOutputStream.toByteArray(); - } catch (Exception e) { - throw new RuleSerializeException(e.getMessage(), e); - } - } - - // Get the byte representation for a list of rules, and write them to an output stream. - @Override - public void serialize( - List<Rule> rules, - Optional<Integer> formatVersion, - OutputStream rulesFileOutputStream, - OutputStream indexingFileOutputStream) - throws RuleSerializeException { - try { - if (rules == null) { - throw new IllegalArgumentException("Null rules cannot be serialized."); - } - - if (rules.size() > TOTAL_RULE_SIZE_LIMIT) { - throw new IllegalArgumentException("Too many rules provided: " + rules.size()); - } - - // Determine the indexing groups and the order of the rules within each indexed group. - Map<Integer, Map<String, List<Rule>>> indexedRules = - RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets(rules); - - // Validate the rule blocks are not larger than expected limits. - verifySize(indexedRules.get(PACKAGE_NAME_INDEXED), INDEXED_RULE_SIZE_LIMIT); - verifySize(indexedRules.get(APP_CERTIFICATE_INDEXED), INDEXED_RULE_SIZE_LIMIT); - verifySize(indexedRules.get(NOT_INDEXED), NONINDEXED_RULE_SIZE_LIMIT); - - // Serialize the rules. - ByteTrackedOutputStream ruleFileByteTrackedOutputStream = - new ByteTrackedOutputStream(rulesFileOutputStream); - serializeRuleFileMetadata(formatVersion, ruleFileByteTrackedOutputStream); - LinkedHashMap<String, Integer> packageNameIndexes = - serializeRuleList( - indexedRules.get(PACKAGE_NAME_INDEXED), - ruleFileByteTrackedOutputStream); - LinkedHashMap<String, Integer> appCertificateIndexes = - serializeRuleList( - indexedRules.get(APP_CERTIFICATE_INDEXED), - ruleFileByteTrackedOutputStream); - LinkedHashMap<String, Integer> unindexedRulesIndexes = - serializeRuleList( - indexedRules.get(NOT_INDEXED), ruleFileByteTrackedOutputStream); - - // Serialize their indexes. - BitOutputStream indexingBitOutputStream = new BitOutputStream(indexingFileOutputStream); - serializeIndexGroup(packageNameIndexes, indexingBitOutputStream, /* isIndexed= */ true); - serializeIndexGroup( - appCertificateIndexes, indexingBitOutputStream, /* isIndexed= */ true); - serializeIndexGroup( - unindexedRulesIndexes, indexingBitOutputStream, /* isIndexed= */ false); - indexingBitOutputStream.flush(); - } catch (Exception e) { - throw new RuleSerializeException(e.getMessage(), e); - } - } - - private void verifySize(Map<String, List<Rule>> ruleListMap, int ruleSizeLimit) { - int totalRuleCount = - ruleListMap.values().stream() - .map(list -> list.size()) - .collect(Collectors.summingInt(Integer::intValue)); - if (totalRuleCount > ruleSizeLimit) { - throw new IllegalArgumentException( - "Too many rules provided in the indexing group. Provided " - + totalRuleCount - + " limit " - + ruleSizeLimit); - } - } - - private void serializeRuleFileMetadata( - Optional<Integer> formatVersion, ByteTrackedOutputStream outputStream) - throws IOException { - int formatVersionValue = formatVersion.orElse(DEFAULT_FORMAT_VERSION); - - BitOutputStream bitOutputStream = new BitOutputStream(outputStream); - bitOutputStream.setNext(FORMAT_VERSION_BITS, formatVersionValue); - bitOutputStream.flush(); - } - - private LinkedHashMap<String, Integer> serializeRuleList( - Map<String, List<Rule>> rulesMap, ByteTrackedOutputStream outputStream) - throws IOException { - Preconditions.checkArgument( - rulesMap != null, "serializeRuleList should never be called with null rule list."); - - BitOutputStream bitOutputStream = new BitOutputStream(outputStream); - LinkedHashMap<String, Integer> indexMapping = new LinkedHashMap(); - indexMapping.put(START_INDEXING_KEY, outputStream.getWrittenBytesCount()); - - List<String> sortedKeys = rulesMap.keySet().stream().sorted().collect(Collectors.toList()); - int indexTracker = 0; - for (String key : sortedKeys) { - if (indexTracker >= INDEXING_BLOCK_SIZE) { - indexMapping.put(key, outputStream.getWrittenBytesCount()); - indexTracker = 0; - } - - for (Rule rule : rulesMap.get(key)) { - serializeRule(rule, bitOutputStream); - bitOutputStream.flush(); - indexTracker++; - } - } - indexMapping.put(END_INDEXING_KEY, outputStream.getWrittenBytesCount()); - - return indexMapping; - } - - private void serializeRule(Rule rule, BitOutputStream bitOutputStream) throws IOException { - if (rule == null) { - throw new IllegalArgumentException("Null rule can not be serialized"); - } - - // Start with a '1' bit to mark the start of a rule. - bitOutputStream.setNext(); - - serializeFormula(rule.getFormula(), bitOutputStream); - bitOutputStream.setNext(EFFECT_BITS, rule.getEffect()); - - // End with a '1' bit to mark the end of a rule. - bitOutputStream.setNext(); - } - - private void serializeFormula(IntegrityFormula formula, BitOutputStream bitOutputStream) - throws IOException { - if (formula instanceof AtomicFormula) { - serializeAtomicFormula((AtomicFormula) formula, bitOutputStream); - } else if (formula instanceof CompoundFormula) { - serializeCompoundFormula((CompoundFormula) formula, bitOutputStream); - } else if (formula instanceof InstallerAllowedByManifestFormula) { - bitOutputStream.setNext(SEPARATOR_BITS, INSTALLER_ALLOWED_BY_MANIFEST_START); - } else { - throw new IllegalArgumentException( - String.format("Invalid formula type: %s", formula.getClass())); - } - } - - private void serializeCompoundFormula( - CompoundFormula compoundFormula, BitOutputStream bitOutputStream) throws IOException { - if (compoundFormula == null) { - throw new IllegalArgumentException("Null compound formula can not be serialized"); - } - - bitOutputStream.setNext(SEPARATOR_BITS, COMPOUND_FORMULA_START); - bitOutputStream.setNext(CONNECTOR_BITS, compoundFormula.getConnector()); - for (IntegrityFormula formula : compoundFormula.getFormulas()) { - serializeFormula(formula, bitOutputStream); - } - bitOutputStream.setNext(SEPARATOR_BITS, COMPOUND_FORMULA_END); - } - - private void serializeAtomicFormula( - AtomicFormula atomicFormula, BitOutputStream bitOutputStream) throws IOException { - if (atomicFormula == null) { - throw new IllegalArgumentException("Null atomic formula can not be serialized"); - } - - bitOutputStream.setNext(SEPARATOR_BITS, ATOMIC_FORMULA_START); - bitOutputStream.setNext(KEY_BITS, atomicFormula.getKey()); - if (atomicFormula.getTag() == AtomicFormula.STRING_ATOMIC_FORMULA_TAG) { - AtomicFormula.StringAtomicFormula stringAtomicFormula = - (AtomicFormula.StringAtomicFormula) atomicFormula; - bitOutputStream.setNext(OPERATOR_BITS, AtomicFormula.EQ); - serializeStringValue( - stringAtomicFormula.getValue(), - stringAtomicFormula.getIsHashedValue(), - bitOutputStream); - } else if (atomicFormula.getTag() == AtomicFormula.LONG_ATOMIC_FORMULA_TAG) { - AtomicFormula.LongAtomicFormula longAtomicFormula = - (AtomicFormula.LongAtomicFormula) atomicFormula; - bitOutputStream.setNext(OPERATOR_BITS, longAtomicFormula.getOperator()); - // TODO(b/147880712): Temporary hack until we support long values in bitOutputStream - long value = longAtomicFormula.getValue(); - serializeIntValue((int) (value >>> 32), bitOutputStream); - serializeIntValue((int) value, bitOutputStream); - } else if (atomicFormula.getTag() == AtomicFormula.BOOLEAN_ATOMIC_FORMULA_TAG) { - AtomicFormula.BooleanAtomicFormula booleanAtomicFormula = - (AtomicFormula.BooleanAtomicFormula) atomicFormula; - bitOutputStream.setNext(OPERATOR_BITS, AtomicFormula.EQ); - serializeBooleanValue(booleanAtomicFormula.getValue(), bitOutputStream); - } else { - throw new IllegalArgumentException( - String.format("Invalid atomic formula type: %s", atomicFormula.getClass())); - } - } - - private void serializeIndexGroup( - LinkedHashMap<String, Integer> indexes, - BitOutputStream bitOutputStream, - boolean isIndexed) - throws IOException { - // Output the starting location of this indexing group. - serializeStringValue(START_INDEXING_KEY, /* isHashedValue= */ false, bitOutputStream); - serializeIntValue(indexes.get(START_INDEXING_KEY), bitOutputStream); - - // If the group is indexed, output the locations of the indexes. - if (isIndexed) { - for (Map.Entry<String, Integer> entry : indexes.entrySet()) { - if (!entry.getKey().equals(START_INDEXING_KEY) - && !entry.getKey().equals(END_INDEXING_KEY)) { - serializeStringValue( - entry.getKey(), /* isHashedValue= */ false, bitOutputStream); - serializeIntValue(entry.getValue(), bitOutputStream); - } - } - } - - // Output the end location of this indexing group. - serializeStringValue(END_INDEXING_KEY, /*isHashedValue= */ false, bitOutputStream); - serializeIntValue(indexes.get(END_INDEXING_KEY), bitOutputStream); - } - - private void serializeStringValue( - String value, boolean isHashedValue, BitOutputStream bitOutputStream) - throws IOException { - if (value == null) { - throw new IllegalArgumentException("String value can not be null."); - } - byte[] valueBytes = getBytesForString(value, isHashedValue); - - bitOutputStream.setNext(isHashedValue); - bitOutputStream.setNext(VALUE_SIZE_BITS, valueBytes.length); - for (byte valueByte : valueBytes) { - bitOutputStream.setNext(/* numOfBits= */ 8, valueByte); - } - } - - private void serializeIntValue(int value, BitOutputStream bitOutputStream) throws IOException { - bitOutputStream.setNext(/* numOfBits= */ 32, value); - } - - private void serializeBooleanValue(boolean value, BitOutputStream bitOutputStream) - throws IOException { - bitOutputStream.setNext(value); - } - - // Get the byte array for a value. - // If the value is not hashed, use its byte array form directly. - // If the value is hashed, get the raw form decoding of the value. All hashed values are - // hex-encoded. Serialized values are in raw form. - private static byte[] getBytesForString(String value, boolean isHashedValue) { - if (!isHashedValue) { - return value.getBytes(StandardCharsets.UTF_8); - } - return IntegrityUtils.getBytesFromHexDigest(value); - } -} diff --git a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetails.java b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetails.java deleted file mode 100644 index 2cbd4ede5214..000000000000 --- a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetails.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity.serializer; - -import android.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** Holds the indexing type and indexing key of a given formula. */ -class RuleIndexingDetails { - - static final int NOT_INDEXED = 0; - static final int PACKAGE_NAME_INDEXED = 1; - static final int APP_CERTIFICATE_INDEXED = 2; - - static final String DEFAULT_RULE_KEY = "N/A"; - - /** Represents which indexed file the rule should be located. */ - @IntDef( - value = { - NOT_INDEXED, - PACKAGE_NAME_INDEXED, - APP_CERTIFICATE_INDEXED - }) - @Retention(RetentionPolicy.SOURCE) - public @interface IndexType { - } - - private @IndexType int mIndexType; - private String mRuleKey; - - /** Constructor without a ruleKey for {@code NOT_INDEXED}. */ - RuleIndexingDetails(@IndexType int indexType) { - this.mIndexType = indexType; - this.mRuleKey = DEFAULT_RULE_KEY; - } - - /** Constructor with a ruleKey for indexed rules. */ - RuleIndexingDetails(@IndexType int indexType, String ruleKey) { - this.mIndexType = indexType; - this.mRuleKey = ruleKey; - } - - /** Returns the indexing type for the rule. */ - @IndexType - public int getIndexType() { - return mIndexType; - } - - /** Returns the identified rule key. */ - public String getRuleKey() { - return mRuleKey; - } -} diff --git a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java deleted file mode 100644 index e7235591fb9b..000000000000 --- a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity.serializer; - -import static com.android.server.integrity.serializer.RuleIndexingDetails.APP_CERTIFICATE_INDEXED; -import static com.android.server.integrity.serializer.RuleIndexingDetails.NOT_INDEXED; -import static com.android.server.integrity.serializer.RuleIndexingDetails.PACKAGE_NAME_INDEXED; - -import android.content.integrity.AtomicFormula; -import android.content.integrity.CompoundFormula; -import android.content.integrity.IntegrityFormula; -import android.content.integrity.Rule; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -/** A helper class for identifying the indexing type and key of a given rule. */ -class RuleIndexingDetailsIdentifier { - - /** - * Splits a given rule list into three indexing categories. Each rule category is returned as a - * TreeMap that is sorted by their indexing keys -- where keys correspond to package name for - * PACKAGE_NAME_INDEXED rules, app certificate for APP_CERTIFICATE_INDEXED rules and N/A for - * NOT_INDEXED rules. - */ - public static Map<Integer, Map<String, List<Rule>>> splitRulesIntoIndexBuckets( - List<Rule> rules) { - if (rules == null) { - throw new IllegalArgumentException( - "Index buckets cannot be created for null rule list."); - } - - Map<Integer, Map<String, List<Rule>>> typeOrganizedRuleMap = new HashMap(); - typeOrganizedRuleMap.put(NOT_INDEXED, new HashMap()); - typeOrganizedRuleMap.put(PACKAGE_NAME_INDEXED, new HashMap<>()); - typeOrganizedRuleMap.put(APP_CERTIFICATE_INDEXED, new HashMap<>()); - - // Split the rules into the appropriate indexed pattern. The Tree Maps help us to keep the - // entries sorted by their index key. - for (Rule rule : rules) { - RuleIndexingDetails indexingDetails; - try { - indexingDetails = getIndexingDetails(rule.getFormula()); - } catch (Exception e) { - throw new IllegalArgumentException( - String.format("Malformed rule identified. [%s]", rule.toString())); - } - - int ruleIndexType = indexingDetails.getIndexType(); - String ruleKey = indexingDetails.getRuleKey(); - - if (!typeOrganizedRuleMap.get(ruleIndexType).containsKey(ruleKey)) { - typeOrganizedRuleMap.get(ruleIndexType).put(ruleKey, new ArrayList()); - } - - typeOrganizedRuleMap.get(ruleIndexType).get(ruleKey).add(rule); - } - - return typeOrganizedRuleMap; - } - - private static RuleIndexingDetails getIndexingDetails(IntegrityFormula formula) { - switch (formula.getTag()) { - case IntegrityFormula.COMPOUND_FORMULA_TAG: - return getIndexingDetailsForCompoundFormula((CompoundFormula) formula); - case IntegrityFormula.STRING_ATOMIC_FORMULA_TAG: - return getIndexingDetailsForStringAtomicFormula( - (AtomicFormula.StringAtomicFormula) formula); - case IntegrityFormula.LONG_ATOMIC_FORMULA_TAG: - case IntegrityFormula.INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG: - case IntegrityFormula.BOOLEAN_ATOMIC_FORMULA_TAG: - // Package name and app certificate related formulas are string atomic formulas. - return new RuleIndexingDetails(NOT_INDEXED); - default: - throw new IllegalArgumentException( - String.format("Invalid formula tag type: %s", formula.getTag())); - } - } - - private static RuleIndexingDetails getIndexingDetailsForCompoundFormula( - CompoundFormula compoundFormula) { - int connector = compoundFormula.getConnector(); - List<IntegrityFormula> formulas = compoundFormula.getFormulas(); - - switch (connector) { - case CompoundFormula.AND: - case CompoundFormula.OR: - // If there is a package name related atomic rule, return package name indexed. - Optional<RuleIndexingDetails> packageNameRule = - formulas.stream() - .map(formula -> getIndexingDetails(formula)) - .filter(ruleIndexingDetails -> ruleIndexingDetails.getIndexType() - == PACKAGE_NAME_INDEXED) - .findAny(); - if (packageNameRule.isPresent()) { - return packageNameRule.get(); - } - - // If there is an app certificate related atomic rule but no package name related - // atomic rule, return app certificate indexed. - Optional<RuleIndexingDetails> appCertificateRule = - formulas.stream() - .map(formula -> getIndexingDetails(formula)) - .filter(ruleIndexingDetails -> ruleIndexingDetails.getIndexType() - == APP_CERTIFICATE_INDEXED) - .findAny(); - if (appCertificateRule.isPresent()) { - return appCertificateRule.get(); - } - - // Do not index when there is not package name or app certificate indexing. - return new RuleIndexingDetails(NOT_INDEXED); - default: - // Having a NOT operator in the indexing messes up the indexing; e.g., deny - // installation if app certificate is NOT X (should not be indexed with app cert - // X). We will not keep these rules indexed. - // Also any other type of unknown operators will not be indexed. - return new RuleIndexingDetails(NOT_INDEXED); - } - } - - private static RuleIndexingDetails getIndexingDetailsForStringAtomicFormula( - AtomicFormula.StringAtomicFormula atomicFormula) { - switch (atomicFormula.getKey()) { - case AtomicFormula.PACKAGE_NAME: - return new RuleIndexingDetails(PACKAGE_NAME_INDEXED, atomicFormula.getValue()); - case AtomicFormula.APP_CERTIFICATE: - return new RuleIndexingDetails(APP_CERTIFICATE_INDEXED, atomicFormula.getValue()); - default: - return new RuleIndexingDetails(NOT_INDEXED); - } - } -} - diff --git a/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java deleted file mode 100644 index 022b4b8cb7eb..000000000000 --- a/services/core/java/com/android/server/integrity/serializer/RuleMetadataSerializer.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity.serializer; - -import static com.android.server.integrity.parser.RuleMetadataParser.RULE_PROVIDER_TAG; -import static com.android.server.integrity.parser.RuleMetadataParser.VERSION_TAG; - -import android.util.Xml; - -import com.android.modules.utils.TypedXmlSerializer; -import com.android.server.integrity.model.RuleMetadata; - -import org.xmlpull.v1.XmlSerializer; - -import java.io.IOException; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; - -/** Helper class for writing rule metadata. */ -public class RuleMetadataSerializer { - /** Serialize the rule metadata to an output stream. */ - public static void serialize(RuleMetadata ruleMetadata, OutputStream outputStream) - throws IOException { - TypedXmlSerializer xmlSerializer = Xml.resolveSerializer(outputStream); - - serializeTaggedValue(xmlSerializer, RULE_PROVIDER_TAG, ruleMetadata.getRuleProvider()); - serializeTaggedValue(xmlSerializer, VERSION_TAG, ruleMetadata.getVersion()); - - xmlSerializer.endDocument(); - } - - private static void serializeTaggedValue(TypedXmlSerializer xmlSerializer, String tag, - String value) throws IOException { - xmlSerializer.startTag(/* namespace= */ null, tag); - xmlSerializer.text(value); - xmlSerializer.endTag(/* namespace= */ null, tag); - } -} diff --git a/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java deleted file mode 100644 index 2941856915a8..000000000000 --- a/services/core/java/com/android/server/integrity/serializer/RuleSerializer.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity.serializer; - -import android.content.integrity.Rule; - -import java.io.OutputStream; -import java.util.List; -import java.util.Optional; - -/** A helper class to serialize rules from the {@link Rule} model. */ -public interface RuleSerializer { - - /** Serialize rules to an output stream */ - void serialize( - List<Rule> rules, - Optional<Integer> formatVersion, - OutputStream ruleFileOutputStream, - OutputStream indexingFileOutputStream) - throws RuleSerializeException; - - /** Serialize rules to a ByteArray. */ - byte[] serialize(List<Rule> rules, Optional<Integer> formatVersion) - throws RuleSerializeException; -} diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index bbdac5636fa4..c314ab06fbad 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -253,10 +253,10 @@ public class LockSettingsService extends ILockSettings.Stub { private static final String MIGRATED_FRP2 = "migrated_frp2"; private static final String MIGRATED_KEYSTORE_NS = "migrated_keystore_namespace"; - private static final String MIGRATED_SP_CE_ONLY = "migrated_all_users_to_sp_and_bound_ce"; private static final String MIGRATED_SP_FULL = "migrated_all_users_to_sp_and_bound_keys"; private static final String MIGRATED_WEAVER_DISABLED_ON_UNSECURED_USERS = "migrated_weaver_disabled_on_unsecured_users"; + // Note: some other migrated_* strings used to be used and may exist in the database already. // Duration that LockSettingsService will store the gatekeeper password for. This allows // multiple biometric enrollments without prompting the user to enter their password via @@ -347,6 +347,8 @@ public class LockSettingsService extends ILockSettings.Stub { private final StorageManagerInternal mStorageManagerInternal; + private final Object mGcWorkToken = new Object(); + // This class manages life cycle events for encrypted users on File Based Encryption (FBE) // devices. The most basic of these is to show/hide notifications about missing features until // the user unlocks the account and credential-encrypted storage is available. @@ -1183,9 +1185,7 @@ public class LockSettingsService extends ILockSettings.Stub { // If config_disableWeaverOnUnsecuredUsers=true, then the Weaver HAL may be buggy and // need multiple retries before it works here to unwrap the SP, if the SP was already - // protected by Weaver. Note that the problematic HAL can also deadlock if called with - // the ActivityManagerService lock held, but that should not be a problem here since - // that lock isn't held here, unlike unlockUserKeyIfUnsecured() where it is. + // protected by Weaver. for (int i = 0; i < 12 && sp == null; i++) { Slog.e(TAG, "Failed to unwrap synthetic password. Waiting 5 seconds to retry."); SystemClock.sleep(5000); @@ -1221,21 +1221,16 @@ public class LockSettingsService extends ILockSettings.Stub { Slog.i(TAG, "Synthetic password is already not protected by Weaver"); } } else if (sp == null) { - Slogf.wtf(TAG, "Failed to unwrap synthetic password for unsecured user %d", userId); - return; + throw new IllegalStateException( + "Failed to unwrap synthetic password for unsecured user " + userId); } // Call setCeStorageProtection(), to re-encrypt the CE key with the SP if it's currently - // encrypted by an empty secret. Skip this if it was definitely already done as part of the - // upgrade to Android 14, since while setCeStorageProtection() is idempotent it does log - // some error messages when called again. Do not skip this if - // config_disableWeaverOnUnsecuredUsers=true, since in that case we'd like to recover from - // the case where an earlier upgrade to Android 14 incorrectly skipped this step. - if (getString(MIGRATED_SP_CE_ONLY, null, 0) == null - || isWeaverDisabledOnUnsecuredUsers()) { - Slogf.i(TAG, "Encrypting CE key of user %d with synthetic password", userId); - setCeStorageProtection(userId, sp); - } + // encrypted by an empty secret. If the CE key is already encrypted by the SP, then this is + // a no-op except for some log messages. + Slogf.i(TAG, "Encrypting CE key of user %d with synthetic password", userId); + setCeStorageProtection(userId, sp); + Slogf.i(TAG, "Initializing Keystore super keys for user %d", userId); initKeystoreSuperKeys(userId, sp, /* allowExisting= */ true); } @@ -3639,11 +3634,19 @@ public class LockSettingsService extends ILockSettings.Stub { * release references to the argument. */ private void scheduleGc() { + // Cancel any existing GC request first, so that GC requests don't pile up if lockscreen + // credential operations are happening very quickly, e.g. as sometimes happens during tests. + // + // This delays the already-requested GC, but that is fine in practice where lockscreen + // operations don't happen very quickly. And the precise time that the sanitization happens + // isn't very important; doing it within a minute can be fine, for example. + mHandler.removeCallbacksAndMessages(mGcWorkToken); + mHandler.postDelayed(() -> { System.gc(); System.runFinalization(); System.gc(); - }, 2000); + }, mGcWorkToken, 2000); } private class DeviceProvisionedObserver extends ContentObserver { diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java index 89555a9f1de4..c8a87994ee16 100644 --- a/services/core/java/com/android/server/media/MediaSession2Record.java +++ b/services/core/java/com/android/server/media/MediaSession2Record.java @@ -161,6 +161,11 @@ public class MediaSession2Record extends MediaSessionRecordImpl { } @Override + public void onGlobalPrioritySessionActiveChanged(boolean isGlobalPrioritySessionActive) { + // NA as MediaSession2 doesn't support UserEngagementStates for FGS. + } + + @Override public boolean sendMediaButton(String packageName, int pid, int uid, boolean asSystemService, KeyEvent ke, int sequenceId, ResultReceiver cb) { // TODO(jaewan): Implement. diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 0a9109b3e98c..0d779af22c2d 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -234,51 +234,49 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde private final Runnable mUserEngagementTimeoutExpirationRunnable = () -> { synchronized (mLock) { - updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true); + updateUserEngagedStateIfNeededLocked( + /* isTimeoutExpired= */ true, + /* isGlobalPrioritySessionActive= */ false); } }; @GuardedBy("mLock") private @UserEngagementState int mUserEngagementState = USER_DISENGAGED; - @IntDef({USER_PERMANENTLY_ENGAGED, USER_TEMPORARY_ENGAGED, USER_DISENGAGED}) + @IntDef({USER_PERMANENTLY_ENGAGED, USER_TEMPORARILY_ENGAGED, USER_DISENGAGED}) @Retention(RetentionPolicy.SOURCE) private @interface UserEngagementState {} /** - * Indicates that the session is active and in one of the user engaged states. + * Indicates that the session is {@linkplain MediaSession#isActive() active} and in one of the + * {@linkplain PlaybackState#isActive() active states}. * * @see #updateUserEngagedStateIfNeededLocked(boolean) */ private static final int USER_PERMANENTLY_ENGAGED = 0; /** - * Indicates that the session is active and in {@link PlaybackState#STATE_PAUSED} state. + * Indicates that the session is {@linkplain MediaSession#isActive() active} and has recently + * switched to one of the {@linkplain PlaybackState#isActive() inactive states}. * * @see #updateUserEngagedStateIfNeededLocked(boolean) */ - private static final int USER_TEMPORARY_ENGAGED = 1; + private static final int USER_TEMPORARILY_ENGAGED = 1; /** - * Indicates that the session is either not active or in one of the user disengaged states + * Indicates that the session is either not {@linkplain MediaSession#isActive() active} or in + * one of the {@linkplain PlaybackState#isActive() inactive states}. * * @see #updateUserEngagedStateIfNeededLocked(boolean) */ private static final int USER_DISENGAGED = 2; /** - * Indicates the duration of the temporary engaged states, in milliseconds. + * Indicates the duration of the temporary engaged state, in milliseconds. * - * <p>Some {@link MediaSession} states like {@link PlaybackState#STATE_PAUSED} are temporarily - * engaged, meaning the corresponding session is only considered in an engaged state for the - * duration of this timeout, and only if coming from an engaged state. - * - * <p>For example, if a session is transitioning from a user-engaged state {@link - * PlaybackState#STATE_PLAYING} to a temporary user-engaged state {@link - * PlaybackState#STATE_PAUSED}, then the session will be considered in a user-engaged state for - * the duration of this timeout, starting at the transition instant. However, a temporary - * user-engaged state is not considered user-engaged when transitioning from a non-user engaged - * state {@link PlaybackState#STATE_STOPPED}. + * <p>When switching to an {@linkplain PlaybackState#isActive() inactive state}, the user is + * treated as temporarily engaged, meaning the corresponding session is only considered in an + * engaged state for the duration of this timeout, and only if coming from an engaged state. */ private static final int TEMP_USER_ENGAGED_TIMEOUT_MS = 600000; @@ -604,7 +602,8 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde mSessionCb.mCb.asBinder().unlinkToDeath(this, 0); mDestroyed = true; mPlaybackState = null; - updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ true); + updateUserEngagedStateIfNeededLocked( + /* isTimeoutExpired= */ true, /* isGlobalPrioritySessionActive= */ false); mHandler.post(MessageHandler.MSG_DESTROYED); } } @@ -621,6 +620,24 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde mHandler.post(mUserEngagementTimeoutExpirationRunnable); } + @Override + public void onGlobalPrioritySessionActiveChanged(boolean isGlobalPrioritySessionActive) { + mHandler.post( + () -> { + synchronized (mLock) { + if (isGlobalPrioritySessionActive) { + mHandler.removeCallbacks(mUserEngagementTimeoutExpirationRunnable); + } else { + if (mUserEngagementState == USER_TEMPORARILY_ENGAGED) { + mHandler.postDelayed( + mUserEngagementTimeoutExpirationRunnable, + TEMP_USER_ENGAGED_TIMEOUT_MS); + } + } + } + }); + } + /** * Sends media button. * @@ -1111,21 +1128,20 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde } @GuardedBy("mLock") - private void updateUserEngagedStateIfNeededLocked(boolean isTimeoutExpired) { + private void updateUserEngagedStateIfNeededLocked( + boolean isTimeoutExpired, boolean isGlobalPrioritySessionActive) { if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) { return; } int oldUserEngagedState = mUserEngagementState; int newUserEngagedState; - if (!isActive() || mPlaybackState == null || mDestroyed) { + if (!isActive() || mPlaybackState == null) { newUserEngagedState = USER_DISENGAGED; - } else if (isActive() && mPlaybackState.isActive()) { + } else if (mPlaybackState.isActive()) { newUserEngagedState = USER_PERMANENTLY_ENGAGED; - } else if (mPlaybackState.getState() == PlaybackState.STATE_PAUSED) { - newUserEngagedState = - oldUserEngagedState == USER_PERMANENTLY_ENGAGED || !isTimeoutExpired - ? USER_TEMPORARY_ENGAGED - : USER_DISENGAGED; + } else if (oldUserEngagedState == USER_PERMANENTLY_ENGAGED + || (oldUserEngagedState == USER_TEMPORARILY_ENGAGED && !isTimeoutExpired)) { + newUserEngagedState = USER_TEMPORARILY_ENGAGED; } else { newUserEngagedState = USER_DISENGAGED; } @@ -1134,7 +1150,7 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde } mUserEngagementState = newUserEngagedState; - if (newUserEngagedState == USER_TEMPORARY_ENGAGED) { + if (newUserEngagedState == USER_TEMPORARILY_ENGAGED && !isGlobalPrioritySessionActive) { mHandler.postDelayed( mUserEngagementTimeoutExpirationRunnable, TEMP_USER_ENGAGED_TIMEOUT_MS); } else { @@ -1189,9 +1205,11 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde .logFgsApiEnd(ActivityManager.FOREGROUND_SERVICE_API_TYPE_MEDIA_PLAYBACK, callingUid, callingPid); } + boolean isGlobalPrioritySessionActive = mService.isGlobalPrioritySessionActive(); synchronized (mLock) { mIsActive = active; - updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false); + updateUserEngagedStateIfNeededLocked( + /* isTimeoutExpired= */ false, isGlobalPrioritySessionActive); } long token = Binder.clearCallingIdentity(); try { @@ -1348,9 +1366,11 @@ public class MediaSessionRecord extends MediaSessionRecordImpl implements IBinde boolean shouldUpdatePriority = ALWAYS_PRIORITY_STATES.contains(newState) || (!TRANSITION_PRIORITY_STATES.contains(oldState) && TRANSITION_PRIORITY_STATES.contains(newState)); + boolean isGlobalPrioritySessionActive = mService.isGlobalPrioritySessionActive(); synchronized (mLock) { mPlaybackState = state; - updateUserEngagedStateIfNeededLocked(/* isTimeoutExpired= */ false); + updateUserEngagedStateIfNeededLocked( + /* isTimeoutExpired= */ false, isGlobalPrioritySessionActive); } final long token = Binder.clearCallingIdentity(); try { diff --git a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java index 15f90d4fdd0e..6c3b1234935a 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecordImpl.java +++ b/services/core/java/com/android/server/media/MediaSessionRecordImpl.java @@ -206,6 +206,10 @@ public abstract class MediaSessionRecordImpl { */ public abstract void expireTempEngaged(); + /** Notifies record that the global priority session active state changed. */ + public abstract void onGlobalPrioritySessionActiveChanged( + boolean isGlobalPrioritySessionActive); + @Override public final boolean equals(Object o) { if (this == o) return true; diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 1ebc856af2d8..2b29fbd9c5b5 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -362,6 +362,7 @@ public class MediaSessionService extends SystemService implements Monitor { + record.isActive()); } user.pushAddressedPlayerChangedLocked(); + mHandler.post(this::notifyGlobalPrioritySessionActiveChanged); } else { if (!user.mPriorityStack.contains(record)) { Log.w(TAG, "Unknown session updated. Ignoring."); @@ -394,11 +395,16 @@ public class MediaSessionService extends SystemService implements Monitor { // Currently only media1 can become global priority session. void setGlobalPrioritySession(MediaSessionRecord record) { + boolean globalPrioritySessionActiveChanged = false; synchronized (mLock) { FullUserRecord user = getFullUserRecordLocked(record.getUserId()); if (mGlobalPrioritySession != record) { Log.d(TAG, "Global priority session is changed from " + mGlobalPrioritySession + " to " + record); + globalPrioritySessionActiveChanged = + (mGlobalPrioritySession == null && record.isActive()) + || (mGlobalPrioritySession != null + && mGlobalPrioritySession.isActive() != record.isActive()); mGlobalPrioritySession = record; if (user != null && user.mPriorityStack.contains(record)) { // Handle the global priority session separately. @@ -409,6 +415,30 @@ public class MediaSessionService extends SystemService implements Monitor { } } } + if (globalPrioritySessionActiveChanged) { + mHandler.post(this::notifyGlobalPrioritySessionActiveChanged); + } + } + + /** Returns whether the global priority session is active. */ + boolean isGlobalPrioritySessionActive() { + synchronized (mLock) { + return isGlobalPriorityActiveLocked(); + } + } + + private void notifyGlobalPrioritySessionActiveChanged() { + if (!Flags.enableNotifyingActivityManagerWithMediaSessionStatusChange()) { + return; + } + synchronized (mLock) { + boolean isGlobalPriorityActive = isGlobalPriorityActiveLocked(); + for (Set<MediaSessionRecordImpl> records : mUserEngagedSessionsForFgs.values()) { + for (MediaSessionRecordImpl record : records) { + record.onGlobalPrioritySessionActiveChanged(isGlobalPriorityActive); + } + } + } } private List<MediaSessionRecord> getActiveSessionsLocked(int userId) { @@ -646,8 +676,11 @@ public class MediaSessionService extends SystemService implements Monitor { if (mGlobalPrioritySession == session) { mGlobalPrioritySession = null; - if (session.isActive() && user != null) { - user.pushAddressedPlayerChangedLocked(); + if (session.isActive()) { + if (user != null) { + user.pushAddressedPlayerChangedLocked(); + } + mHandler.post(this::notifyGlobalPrioritySessionActiveChanged); } } else { if (user != null) { diff --git a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java index 980f40ee4a9a..9b02ed0ee0bd 100644 --- a/services/core/java/com/android/server/notification/NotificationAttentionHelper.java +++ b/services/core/java/com/android/server/notification/NotificationAttentionHelper.java @@ -1597,6 +1597,9 @@ public final class NotificationAttentionHelper { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); + if (action == null) { + return; + } if (action.equals(Intent.ACTION_SCREEN_ON)) { // Keep track of screen on/off state, but do not turn off the notification light diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 69c78eb155eb..f9c103762815 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -4283,6 +4283,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService if (intent == null) { return; } + final String action = intent.getAction(); + if (action == null) { + return; + } Uri data = intent.getData(); if (data == null) { return; diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java index 881bdbd3bc6a..15fd35e15f83 100644 --- a/services/core/java/com/android/server/slice/SliceManagerService.java +++ b/services/core/java/com/android/server/slice/SliceManagerService.java @@ -604,6 +604,11 @@ public class SliceManagerService extends ISliceManager.Stub { private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (action == null) { + Slog.w(TAG, "Intent broadcast does not contain action: " + intent); + return; + } final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); if (userId == UserHandle.USER_NULL) { Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent); @@ -615,7 +620,7 @@ public class SliceManagerService extends ISliceManager.Stub { Slog.w(TAG, "Intent broadcast does not contain package name: " + intent); return; } - switch (intent.getAction()) { + switch (action) { case Intent.ACTION_PACKAGE_REMOVED: final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); diff --git a/services/core/java/com/android/server/vcn/OWNERS b/services/core/java/com/android/server/vcn/OWNERS index 2441e772468c..937699a92968 100644 --- a/services/core/java/com/android/server/vcn/OWNERS +++ b/services/core/java/com/android/server/vcn/OWNERS @@ -1,7 +1,6 @@ set noparent -benedictwong@google.com -ckesting@google.com evitayan@google.com -junyin@google.com nharold@google.com +benedictwong@google.com #{LAST_RESORT_SUGGESTION} +yangji@google.com #{LAST_RESORT_SUGGESTION} diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java index baf84cf4aa8b..3392d039109a 100644 --- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java +++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java @@ -436,6 +436,17 @@ public class TelephonySubscriptionTracker extends BroadcastReceiver { return mPrivilegedPackages.keySet(); } + /** Returns all subscription groups */ + @NonNull + public Set<ParcelUuid> getAllSubscriptionGroups() { + final Set<ParcelUuid> subGroups = new ArraySet<>(); + for (SubscriptionInfo subInfo : mSubIdToInfoMap.values()) { + subGroups.add(subInfo.getGroupUuid()); + } + + return subGroups; + } + /** Checks if the provided package is carrier privileged for the specified sub group. */ public boolean packageHasPermissionsForSubscriptionGroup( @NonNull ParcelUuid subGrp, @NonNull String packageName) { diff --git a/services/core/java/com/android/server/vcn/VcnContext.java b/services/core/java/com/android/server/vcn/VcnContext.java index a492a72933d7..6ce868540070 100644 --- a/services/core/java/com/android/server/vcn/VcnContext.java +++ b/services/core/java/com/android/server/vcn/VcnContext.java @@ -18,7 +18,6 @@ package com.android.server.vcn; import android.annotation.NonNull; import android.content.Context; -import android.net.IpSecTransformState; import android.net.vcn.FeatureFlags; import android.net.vcn.FeatureFlagsImpl; import android.os.Looper; @@ -70,19 +69,6 @@ public class VcnContext { return mIsInTestMode; } - public boolean isFlagIpSecTransformStateEnabled() { - // TODO: b/328844044: Ideally this code should gate the behavior by checking the - // android.net.platform.flags.ipsec_transform_state flag but that flag is not accessible - // right now. We should either update the code when the flag is accessible or remove the - // legacy behavior after VIC SDK finalization - try { - new IpSecTransformState.Builder(); - return true; - } catch (Exception e) { - return false; - } - } - @NonNull public FeatureFlags getFeatureFlags() { return mFeatureFlags; diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 189b6089186e..2d3bc84debff 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -1912,8 +1912,7 @@ public class VcnGatewayConnection extends StateMachine { // Transforms do not need to be persisted; the IkeSession will keep them alive mIpSecManager.applyTunnelModeTransform(tunnelIface, direction, transform); - if (direction == IpSecManager.DIRECTION_IN - && mVcnContext.isFlagIpSecTransformStateEnabled()) { + if (direction == IpSecManager.DIRECTION_IN) { mUnderlyingNetworkController.updateInboundTransform(mUnderlying, transform); } diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java index 6f1e15b5033f..16ab51e8d604 100644 --- a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java +++ b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java @@ -148,12 +148,6 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { Objects.requireNonNull(deps, "Missing deps"); - if (!vcnContext.isFlagIpSecTransformStateEnabled()) { - // Caller error - logWtf("ipsecTransformState flag disabled"); - throw new IllegalAccessException("ipsecTransformState flag disabled"); - } - mHandler = new Handler(getVcnContext().getLooper()); mPowerManager = getVcnContext().getContext().getSystemService(PowerManager.class); diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java index 0b9b677df16a..3eeeece5da46 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java @@ -204,10 +204,8 @@ public class UnderlyingNetworkController { List<NetworkCallback> oldCellCallbacks = new ArrayList<>(mCellBringupCallbacks); mCellBringupCallbacks.clear(); - if (mVcnContext.isFlagIpSecTransformStateEnabled()) { - for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { - evaluator.close(); - } + for (UnderlyingNetworkEvaluator evaluator : mUnderlyingNetworkRecords.values()) { + evaluator.close(); } mUnderlyingNetworkRecords.clear(); @@ -429,10 +427,7 @@ public class UnderlyingNetworkController { if (oldSnapshot .getAllSubIdsInGroup(mSubscriptionGroup) .equals(newSnapshot.getAllSubIdsInGroup(mSubscriptionGroup))) { - - if (mVcnContext.isFlagIpSecTransformStateEnabled()) { - reevaluateNetworks(); - } + reevaluateNetworks(); return; } registerOrUpdateNetworkRequests(); @@ -445,11 +440,6 @@ public class UnderlyingNetworkController { */ public void updateInboundTransform( @NonNull UnderlyingNetworkRecord currentNetwork, @NonNull IpSecTransform transform) { - if (!mVcnContext.isFlagIpSecTransformStateEnabled()) { - logWtf("#updateInboundTransform: unexpected call; flags missing"); - return; - } - Objects.requireNonNull(currentNetwork, "currentNetwork is null"); Objects.requireNonNull(transform, "transform is null"); @@ -572,10 +562,7 @@ public class UnderlyingNetworkController { @Override public void onLost(@NonNull Network network) { - if (mVcnContext.isFlagIpSecTransformStateEnabled()) { - mUnderlyingNetworkRecords.get(network).close(); - } - + mUnderlyingNetworkRecords.get(network).close(); mUnderlyingNetworkRecords.remove(network); reevaluateNetworks(); @@ -648,11 +635,6 @@ public class UnderlyingNetworkController { class NetworkEvaluatorCallbackImpl implements NetworkEvaluatorCallback { @Override public void onEvaluationResultChanged() { - if (!mVcnContext.isFlagIpSecTransformStateEnabled()) { - logWtf("#onEvaluationResultChanged: unexpected call; flags missing"); - return; - } - mVcnContext.ensureRunningOnLooperThread(); reevaluateNetworks(); } diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java index 448a7ebfffd8..08be11e29689 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluator.java @@ -102,17 +102,15 @@ public class UnderlyingNetworkEvaluator { updatePriorityClass( underlyingNetworkTemplates, subscriptionGroup, lastSnapshot, carrierConfig); - if (isIpSecPacketLossDetectorEnabled()) { - try { - mMetricMonitors.add( - mDependencies.newIpSecPacketLossDetector( - mVcnContext, - mNetworkRecordBuilder.getNetwork(), - carrierConfig, - new MetricMonitorCallbackImpl())); - } catch (IllegalAccessException e) { - // No action. Do not add anything to mMetricMonitors - } + try { + mMetricMonitors.add( + mDependencies.newIpSecPacketLossDetector( + mVcnContext, + mNetworkRecordBuilder.getNetwork(), + carrierConfig, + new MetricMonitorCallbackImpl())); + } catch (IllegalAccessException e) { + // No action. Do not add anything to mMetricMonitors } } @@ -188,22 +186,12 @@ public class UnderlyingNetworkEvaluator { } } - private boolean isIpSecPacketLossDetectorEnabled() { - return isIpSecPacketLossDetectorEnabled(mVcnContext); - } - - private static boolean isIpSecPacketLossDetectorEnabled(VcnContext vcnContext) { - return vcnContext.isFlagIpSecTransformStateEnabled(); - } - /** Get the comparator for UnderlyingNetworkEvaluator */ public static Comparator<UnderlyingNetworkEvaluator> getComparator(VcnContext vcnContext) { return (left, right) -> { - if (isIpSecPacketLossDetectorEnabled(vcnContext)) { - if (left.mIsPenalized != right.mIsPenalized) { - // A penalized network should have lower priority which means a larger index - return left.mIsPenalized ? 1 : -1; - } + if (left.mIsPenalized != right.mIsPenalized) { + // A penalized network should have lower priority which means a larger index + return left.mIsPenalized ? 1 : -1; } final int leftIndex = left.mPriorityClass; diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java index 043470f62850..9ee42b17c2a4 100644 --- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java +++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java @@ -84,8 +84,13 @@ public class WebViewUpdateService extends SystemService { mWebViewUpdatedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { + final String action = intent.getAction(); + if (action == null) { + return; + } + int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); - switch (intent.getAction()) { + switch (action) { case Intent.ACTION_PACKAGE_REMOVED: // When a package is replaced we will receive two intents, one // representing the removal of the old package and one representing the diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index e6d81324efa1..d2a128b74732 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -938,7 +938,6 @@ class ActivityStarter { mLastStartReason = request.reason; mLastStartActivityTimeMs = System.currentTimeMillis(); - final ActivityRecord previousStart = mLastStartActivityRecord; final IApplicationThread caller = request.caller; Intent intent = request.intent; NeededUriGrants intentGrants = request.intentGrants; diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS index 5d2c50c8aaca..431023163d0a 100644 --- a/services/core/java/com/android/server/wm/OWNERS +++ b/services/core/java/com/android/server/wm/OWNERS @@ -31,3 +31,7 @@ per-file ActivityCallerState.java = file:/core/java/android/app/COMPONENT_CALLER # Files related to tracing per-file *TransitionTracer.java = file:platform/development:/tools/winscope/OWNERS + +# Files related to activity security +per-file ActivityStarter.java = file:/ACTIVITY_SECURITY_OWNERS +per-file ActivityTaskManagerService.java = file:/ACTIVITY_SECURITY_OWNERS diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 7a710dc51004..662f916d8a69 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -55,7 +55,6 @@ cc_library_static { "com_android_server_powerstats_PowerStatsService.cpp", "com_android_server_power_stats_CpuPowerStatsCollector.cpp", "com_android_server_hint_HintManagerService.cpp", - "com_android_server_SerialService.cpp", "com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp", "com_android_server_soundtrigger_middleware_ExternalCaptureStateTracker.cpp", "com_android_server_stats_pull_StatsPullAtomService.cpp", diff --git a/services/core/jni/com_android_server_SerialService.cpp b/services/core/jni/com_android_server_SerialService.cpp deleted file mode 100644 index 6600c981b68d..000000000000 --- a/services/core/jni/com_android_server_SerialService.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -#define LOG_TAG "SerialServiceJNI" -#include "utils/Log.h" - -#include "jni.h" -#include <nativehelper/JNIPlatformHelp.h> -#include "android_runtime/AndroidRuntime.h" - -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> - -namespace android -{ - -static struct parcel_file_descriptor_offsets_t -{ - jclass mClass; - jmethodID mConstructor; -} gParcelFileDescriptorOffsets; - -static jobject android_server_SerialService_open(JNIEnv *env, jobject /* thiz */, jstring path) -{ - const char *pathStr = env->GetStringUTFChars(path, NULL); - - int fd = open(pathStr, O_RDWR | O_NOCTTY); - if (fd < 0) { - ALOGE("could not open %s", pathStr); - env->ReleaseStringUTFChars(path, pathStr); - return NULL; - } - env->ReleaseStringUTFChars(path, pathStr); - - jobject fileDescriptor = jniCreateFileDescriptor(env, fd); - if (fileDescriptor == NULL) { - close(fd); - return NULL; - } - return env->NewObject(gParcelFileDescriptorOffsets.mClass, - gParcelFileDescriptorOffsets.mConstructor, fileDescriptor); -} - - -static const JNINativeMethod method_table[] = { - { "native_open", "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;", - (void*)android_server_SerialService_open }, -}; - -int register_android_server_SerialService(JNIEnv *env) -{ - jclass clazz = env->FindClass("com/android/server/SerialService"); - if (clazz == NULL) { - ALOGE("Can't find com/android/server/SerialService"); - return -1; - } - - clazz = env->FindClass("android/os/ParcelFileDescriptor"); - LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor"); - gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); - gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V"); - LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL, - "Unable to find constructor for android.os.ParcelFileDescriptor"); - - return jniRegisterNativeMethods(env, "com/android/server/SerialService", - method_table, NELEM(method_table)); -} - -}; diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 6464081d615a..f9633e2e0b23 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -33,7 +33,6 @@ int register_android_server_PowerStatsService(JNIEnv* env); int register_android_server_power_stats_CpuPowerStatsCollector(JNIEnv* env); int register_android_server_HintManagerService(JNIEnv* env); int register_android_server_storage_AppFuse(JNIEnv* env); -int register_android_server_SerialService(JNIEnv* env); int register_android_server_SystemServer(JNIEnv* env); int register_android_server_UsbAlsaJackDetector(JNIEnv* env); int register_android_server_UsbAlsaMidiDevice(JNIEnv* env); @@ -93,7 +92,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_PowerStatsService(env); register_android_server_power_stats_CpuPowerStatsCollector(env); register_android_server_HintManagerService(env); - register_android_server_SerialService(env); register_android_server_InputManager(env); register_android_server_LightsService(env); register_android_server_UsbDeviceManager(env); diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java index 105147fe212d..4e868887ea1b 100644 --- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java @@ -16,6 +16,8 @@ package com.android.server.profcollect; +import android.Manifest; +import android.annotation.RequiresPermission; import android.app.job.JobInfo; import android.app.job.JobParameters; import android.app.job.JobScheduler; @@ -26,6 +28,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.hardware.camera2.CameraManager; +import android.hardware.usb.UsbManager; import android.os.Handler; import android.os.IBinder.DeathRecipient; import android.os.Looper; @@ -67,6 +70,8 @@ public final class ProfcollectForwardingService extends SystemService { private int mUsageSetting; private boolean mUploadEnabled; + private boolean mAdbActive; + private IProfCollectd mIProfcollect; private static ProfcollectForwardingService sSelfService; private final Handler mHandler = new ProfcollectdHandler(IoThread.getHandler().getLooper()); @@ -84,6 +89,15 @@ public final class ProfcollectForwardingService extends SystemService { Log.d(LOG_TAG, "Received broadcast to pack and upload reports"); createAndUploadReport(sSelfService); } + if (UsbManager.ACTION_USB_STATE.equals(intent.getAction())) { + boolean isADB = intent.getBooleanExtra(UsbManager.USB_FUNCTION_ADB, false); + if (isADB) { + boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false); + Log.d(LOG_TAG, "Received broadcast that ADB became " + connected + + ", was " + mAdbActive); + mAdbActive = connected; + } + } } }; @@ -108,6 +122,7 @@ public final class ProfcollectForwardingService extends SystemService { final IntentFilter filter = new IntentFilter(); filter.addAction(INTENT_UPLOAD_PROFILES); + filter.addAction(UsbManager.ACTION_USB_STATE); context.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_NOT_EXPORTED); } @@ -125,7 +140,13 @@ public final class ProfcollectForwardingService extends SystemService { } @Override + @RequiresPermission(Manifest.permission.MANAGE_USB) public void onBootPhase(int phase) { + if (phase == PHASE_SYSTEM_SERVICES_READY) { + UsbManager usbManager = getContext().getSystemService(UsbManager.class); + mAdbActive = ((usbManager.getCurrentFunctions() & UsbManager.FUNCTION_ADB) == 1); + Log.d(LOG_TAG, "ADB is " + mAdbActive + " on system startup"); + } if (phase == PHASE_BOOT_COMPLETED) { if (mIProfcollect == null) { return; @@ -281,6 +302,9 @@ public final class ProfcollectForwardingService extends SystemService { if (mIProfcollect == null) { return; } + if (mAdbActive) { + return; + } if (Utils.withFrequency("applaunch_trace_freq", 5)) { Utils.traceSystem(mIProfcollect, "applaunch"); } @@ -303,6 +327,9 @@ public final class ProfcollectForwardingService extends SystemService { if (mIProfcollect == null) { return; } + if (mAdbActive) { + return; + } if (Utils.withFrequency("dex2oat_trace_freq", 25)) { // Dex2oat could take a while before it starts. Add a short delay before start tracing. Utils.traceSystem(mIProfcollect, "dex2oat", /* delayMs */ 1000); diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt index 1d668cd3e8f2..13cf1252cadf 100644 --- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt +++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/parsing/SystemPartitionParseTest.kt @@ -17,6 +17,7 @@ package com.android.server.pm.parsing import android.content.pm.PackageManager +import android.content.pm.parsing.ApkLiteParseUtils import android.platform.test.annotations.Postsubmit import com.android.internal.pm.parsing.PackageParserException import com.android.internal.pm.pkg.parsing.ParsingPackageUtils @@ -81,8 +82,10 @@ class SystemPartitionParseTest { val exceptions = buildApks() .map { runCatching { - parser.parsePackage( + if (ApkLiteParseUtils.isApkFile(it) || it.isDirectory()) { + parser.parsePackage( it, ParsingPackageUtils.PARSE_IS_SYSTEM_DIR, false /*useCaches*/) + } } } .mapNotNull { it.exceptionOrNull() } diff --git a/services/tests/mockingservicestests/src/com/android/server/OWNERS b/services/tests/mockingservicestests/src/com/android/server/OWNERS index dc5cb8d6bdf5..0c9f70ca96b5 100644 --- a/services/tests/mockingservicestests/src/com/android/server/OWNERS +++ b/services/tests/mockingservicestests/src/com/android/server/OWNERS @@ -1,6 +1,6 @@ per-file *Alarm* = file:/apex/jobscheduler/OWNERS per-file *AppStateTracker* = file:/apex/jobscheduler/OWNERS -per-file *DeviceIdleController* = file:/apex/jobscheduler/OWNERS +per-file *DeviceIdleController* = file:/apex/jobscheduler/DEVICE_IDLE_OWNERS per-file SensitiveContentProtectionManagerService* = file:/core/java/android/permission/OWNERS per-file RescuePartyTest.java = file:/packages/CrashRecovery/OWNERS per-file *Storage* = file:/core/java/android/os/storage/OWNERS diff --git a/services/tests/mockingservicestests/src/com/android/server/am/OWNERS b/services/tests/mockingservicestests/src/com/android/server/am/OWNERS index 4fac647c4ceb..809b7bbfd3a0 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/OWNERS +++ b/services/tests/mockingservicestests/src/com/android/server/am/OWNERS @@ -2,3 +2,4 @@ include /services/core/java/com/android/server/am/OWNERS per-file ApplicationStartInfoTest.java = yforta@google.com, carmenjackson@google.com, jji@google.com per-file CachedAppOptimizerTest.java = file:/PERFORMANCE_OWNERS +per-file BaseBroadcastQueueTest.java = file:/BROADCASTS_OWNERS diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp index cedf9dbe734f..631f76ace958 100644 --- a/services/tests/powerstatstests/Android.bp +++ b/services/tests/powerstatstests/Android.bp @@ -65,6 +65,7 @@ android_ravenwood_test { "androidx.annotation_annotation", "androidx.test.rules", "androidx.test.uiautomator_uiautomator", + "androidx.test.ext.junit", "modules-utils-binary-xml", "flag-junit", ], diff --git a/services/tests/powerstatstests/TEST_MAPPING b/services/tests/powerstatstests/TEST_MAPPING index 4e209f473ff3..d3d3cf641d9a 100644 --- a/services/tests/powerstatstests/TEST_MAPPING +++ b/services/tests/powerstatstests/TEST_MAPPING @@ -9,8 +9,7 @@ "name": "PowerStatsTestsRavenwood", "host": true, "options": [ - {"include-filter": "com.android.server.power.stats"}, - {"exclude-annotation": "android.platform.test.annotations.DisabledOnRavenwood"} + {"include-filter": "com.android.server.power.stats"} ] } ], diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java deleted file mode 100644 index 723b6c5af451..000000000000 --- a/services/tests/servicestests/src/com/android/server/integrity/parser/BinaryFileOperationsTest.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.integrity.parser; - -import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS; -import static com.android.server.integrity.parser.BinaryFileOperations.getBooleanValue; -import static com.android.server.integrity.parser.BinaryFileOperations.getIntValue; -import static com.android.server.integrity.parser.BinaryFileOperations.getStringValue; -import static com.android.server.integrity.utils.TestUtils.getBits; -import static com.android.server.integrity.utils.TestUtils.getBytes; -import static com.android.server.integrity.utils.TestUtils.getValueBits; - -import static com.google.common.truth.Truth.assertThat; - -import android.content.integrity.IntegrityUtils; - -import com.android.server.integrity.model.BitInputStream; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -@RunWith(JUnit4.class) -public class BinaryFileOperationsTest { - - private static final String IS_NOT_HASHED = "0"; - private static final String IS_HASHED = "1"; - private static final String PACKAGE_NAME = "com.test.app"; - private static final String APP_CERTIFICATE = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; - - @Test - public void testGetStringValue() throws IOException { - byte[] stringBytes = - getBytes( - IS_NOT_HASHED - + getBits(PACKAGE_NAME.length(), VALUE_SIZE_BITS) - + getValueBits(PACKAGE_NAME)); - BitInputStream inputStream = new BitInputStream(new ByteArrayInputStream(stringBytes)); - - String resultString = getStringValue(inputStream); - - assertThat(resultString).isEqualTo(PACKAGE_NAME); - } - - @Test - public void testGetHashedStringValue() throws IOException { - byte[] ruleBytes = - getBytes( - IS_HASHED - + getBits(APP_CERTIFICATE.length(), VALUE_SIZE_BITS) - + getValueBits(APP_CERTIFICATE)); - BitInputStream inputStream = new BitInputStream(new ByteArrayInputStream(ruleBytes)); - - String resultString = getStringValue(inputStream); - - assertThat(resultString) - .isEqualTo(IntegrityUtils.getHexDigest( - APP_CERTIFICATE.getBytes(StandardCharsets.UTF_8))); - } - - @Test - public void testGetStringValue_withSizeAndHashingInfo() throws IOException { - byte[] ruleBytes = getBytes(getValueBits(PACKAGE_NAME)); - BitInputStream inputStream = new BitInputStream(new ByteArrayInputStream(ruleBytes)); - - String resultString = getStringValue(inputStream, - PACKAGE_NAME.length(), /* isHashedValue= */false); - - assertThat(resultString).isEqualTo(PACKAGE_NAME); - } - - @Test - public void testGetIntValue() throws IOException { - int randomValue = 15; - byte[] ruleBytes = getBytes(getBits(randomValue, /* numOfBits= */ 32)); - BitInputStream inputStream = new BitInputStream(new ByteArrayInputStream(ruleBytes)); - - assertThat(getIntValue(inputStream)).isEqualTo(randomValue); - } - - @Test - public void testGetBooleanValue_true() throws IOException { - String booleanValue = "1"; - byte[] ruleBytes = getBytes(booleanValue); - BitInputStream inputStream = new BitInputStream(new ByteArrayInputStream(ruleBytes)); - - assertThat(getBooleanValue(inputStream)).isEqualTo(true); - } - - @Test - public void testGetBooleanValue_false() throws IOException { - String booleanValue = "0"; - byte[] ruleBytes = getBytes(booleanValue); - BitInputStream inputStream = new BitInputStream(new ByteArrayInputStream(ruleBytes)); - - assertThat(getBooleanValue(inputStream)).isEqualTo(false); - } -} diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java deleted file mode 100644 index 03363a100841..000000000000 --- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java +++ /dev/null @@ -1,693 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity.parser; - -import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START; -import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END; -import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START; -import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS; -import static com.android.server.integrity.model.ComponentBitSize.DEFAULT_FORMAT_VERSION; -import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS; -import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS; -import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS; -import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS; -import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS; -import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS; -import static com.android.server.integrity.utils.TestUtils.getBits; -import static com.android.server.integrity.utils.TestUtils.getBytes; -import static com.android.server.integrity.utils.TestUtils.getValueBits; -import static com.android.server.testutils.TestUtils.assertExpectException; - -import static com.google.common.truth.Truth.assertThat; - -import android.content.integrity.AtomicFormula; -import android.content.integrity.CompoundFormula; -import android.content.integrity.IntegrityUtils; -import android.content.integrity.Rule; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -@RunWith(JUnit4.class) -public class RuleBinaryParserTest { - - private static final String COMPOUND_FORMULA_START_BITS = - getBits(COMPOUND_FORMULA_START, SEPARATOR_BITS); - private static final String COMPOUND_FORMULA_END_BITS = - getBits(COMPOUND_FORMULA_END, SEPARATOR_BITS); - private static final String ATOMIC_FORMULA_START_BITS = - getBits(ATOMIC_FORMULA_START, SEPARATOR_BITS); - private static final int INVALID_FORMULA_SEPARATOR_VALUE = (1 << SEPARATOR_BITS) - 1; - private static final String INVALID_FORMULA_SEPARATOR_BITS = - getBits(INVALID_FORMULA_SEPARATOR_VALUE, SEPARATOR_BITS); - - private static final String NOT = getBits(CompoundFormula.NOT, CONNECTOR_BITS); - private static final String AND = getBits(CompoundFormula.AND, CONNECTOR_BITS); - private static final String OR = getBits(CompoundFormula.OR, CONNECTOR_BITS); - private static final int INVALID_CONNECTOR_VALUE = 3; - private static final String INVALID_CONNECTOR = - getBits(INVALID_CONNECTOR_VALUE, CONNECTOR_BITS); - - private static final String PACKAGE_NAME = getBits(AtomicFormula.PACKAGE_NAME, KEY_BITS); - private static final String APP_CERTIFICATE = getBits(AtomicFormula.APP_CERTIFICATE, KEY_BITS); - private static final String APP_CERTIFICATE_LINEAGE = - getBits(AtomicFormula.APP_CERTIFICATE_LINEAGE, KEY_BITS); - private static final String VERSION_CODE = getBits(AtomicFormula.VERSION_CODE, KEY_BITS); - private static final String PRE_INSTALLED = getBits(AtomicFormula.PRE_INSTALLED, KEY_BITS); - private static final int INVALID_KEY_VALUE = 9; - private static final String INVALID_KEY = getBits(INVALID_KEY_VALUE, KEY_BITS); - - private static final String EQ = getBits(AtomicFormula.EQ, OPERATOR_BITS); - private static final int INVALID_OPERATOR_VALUE = 5; - private static final String INVALID_OPERATOR = getBits(INVALID_OPERATOR_VALUE, OPERATOR_BITS); - - private static final String IS_NOT_HASHED = "0"; - private static final String IS_HASHED = "1"; - - private static final String DENY = getBits(Rule.DENY, EFFECT_BITS); - private static final int INVALID_EFFECT_VALUE = 5; - private static final String INVALID_EFFECT = getBits(INVALID_EFFECT_VALUE, EFFECT_BITS); - - private static final String START_BIT = "1"; - private static final String END_BIT = "1"; - private static final String INVALID_MARKER_BIT = "0"; - - private static final byte[] DEFAULT_FORMAT_VERSION_BYTES = - getBytes(getBits(DEFAULT_FORMAT_VERSION, FORMAT_VERSION_BITS)); - - private static final List<RuleIndexRange> NO_INDEXING = Collections.emptyList(); - - @Test - public void testBinaryStream_validCompoundFormula_noIndexing() throws Exception { - String packageName = "com.test.app"; - String ruleBits = - START_BIT - + COMPOUND_FORMULA_START_BITS - + NOT - + ATOMIC_FORMULA_START_BITS - + PACKAGE_NAME - + EQ - + IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + COMPOUND_FORMULA_END_BITS - + DENY - + END_BIT; - byte[] ruleBytes = getBytes(ruleBits); - ByteBuffer rule = - ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes); - RuleParser binaryParser = new RuleBinaryParser(); - Rule expectedRule = - new Rule( - new CompoundFormula( - CompoundFormula.NOT, - Collections.singletonList( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - packageName, - /* isHashedValue= */ false))), - Rule.DENY); - - List<Rule> rules = - binaryParser.parse(RandomAccessObject.ofBytes(rule.array()), NO_INDEXING); - - assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); - } - - @Test - public void testBinaryString_validCompoundFormula_notConnector_noIndexing() throws Exception { - String packageName = "com.test.app"; - String ruleBits = - START_BIT - + COMPOUND_FORMULA_START_BITS - + NOT - + ATOMIC_FORMULA_START_BITS - + PACKAGE_NAME - + EQ - + IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + COMPOUND_FORMULA_END_BITS - + DENY - + END_BIT; - byte[] ruleBytes = getBytes(ruleBits); - ByteBuffer rule = - ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes); - RuleParser binaryParser = new RuleBinaryParser(); - Rule expectedRule = - new Rule( - new CompoundFormula( - CompoundFormula.NOT, - Collections.singletonList( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - packageName, - /* isHashedValue= */ false))), - Rule.DENY); - - List<Rule> rules = binaryParser.parse(rule.array()); - - assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); - } - - @Test - public void testBinaryString_validCompoundFormula_andConnector_noIndexing() throws Exception { - String packageName = "com.test.app"; - String appCertificate = "test_cert"; - String ruleBits = - START_BIT - + COMPOUND_FORMULA_START_BITS - + AND - + ATOMIC_FORMULA_START_BITS - + PACKAGE_NAME - + EQ - + IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + ATOMIC_FORMULA_START_BITS - + APP_CERTIFICATE - + EQ - + IS_NOT_HASHED - + getBits(appCertificate.length(), VALUE_SIZE_BITS) - + getValueBits(appCertificate) - + COMPOUND_FORMULA_END_BITS - + DENY - + END_BIT; - byte[] ruleBytes = getBytes(ruleBits); - ByteBuffer rule = - ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes); - RuleParser binaryParser = new RuleBinaryParser(); - Rule expectedRule = - new Rule( - new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - packageName, - /* isHashedValue= */ false), - new AtomicFormula.StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - appCertificate, - /* isHashedValue= */ false))), - Rule.DENY); - List<Rule> rules = binaryParser.parse(rule.array()); - - assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); - } - - @Test - public void testBinaryString_validCompoundFormula_orConnector_noIndexing() throws Exception { - String packageName = "com.test.app"; - String appCertificate = "test_cert"; - String ruleBits = - START_BIT - + COMPOUND_FORMULA_START_BITS - + OR - + ATOMIC_FORMULA_START_BITS - + PACKAGE_NAME - + EQ - + IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + ATOMIC_FORMULA_START_BITS - + APP_CERTIFICATE - + EQ - + IS_NOT_HASHED - + getBits(appCertificate.length(), VALUE_SIZE_BITS) - + getValueBits(appCertificate) - + COMPOUND_FORMULA_END_BITS - + DENY - + END_BIT; - byte[] ruleBytes = getBytes(ruleBits); - ByteBuffer rule = - ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes); - RuleParser binaryParser = new RuleBinaryParser(); - Rule expectedRule = - new Rule( - new CompoundFormula( - CompoundFormula.OR, - Arrays.asList( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - packageName, - /* isHashedValue= */ false), - new AtomicFormula.StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - appCertificate, - /* isHashedValue= */ false))), - Rule.DENY); - - List<Rule> rules = binaryParser.parse(rule.array()); - - assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); - } - - @Test - public void testBinaryString_validAtomicFormula_stringValue_noIndexing() throws Exception { - String packageName = "com.test.app"; - String ruleBits = - START_BIT - + ATOMIC_FORMULA_START_BITS - + PACKAGE_NAME - + EQ - + IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + DENY - + END_BIT; - byte[] ruleBytes = getBytes(ruleBits); - ByteBuffer rule = - ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes); - RuleParser binaryParser = new RuleBinaryParser(); - Rule expectedRule = - new Rule( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - packageName, - /* isHashedValue= */ false), - Rule.DENY); - - List<Rule> rules = binaryParser.parse(rule.array()); - - assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); - } - - @Test - public void testBinaryString_validAtomicFormula_hashedValue_noIndexing() throws Exception { - String appCertificate = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; - String ruleBits = - START_BIT - + ATOMIC_FORMULA_START_BITS - + APP_CERTIFICATE - + EQ - + IS_HASHED - + getBits(appCertificate.length(), VALUE_SIZE_BITS) - + getValueBits(appCertificate) - + DENY - + END_BIT; - byte[] ruleBytes = getBytes(ruleBits); - ByteBuffer rule = - ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes); - - RuleParser binaryParser = new RuleBinaryParser(); - Rule expectedRule = - new Rule( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - IntegrityUtils.getHexDigest( - appCertificate.getBytes(StandardCharsets.UTF_8)), - /* isHashedValue= */ true), - Rule.DENY); - - List<Rule> rules = binaryParser.parse(rule.array()); - - assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); - } - - @Test - public void testBinaryString_validAtomicFormulaWithCertificateLineage() throws Exception { - String appCertificate = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; - String ruleBits = - START_BIT - + ATOMIC_FORMULA_START_BITS - + APP_CERTIFICATE_LINEAGE - + EQ - + IS_HASHED - + getBits(appCertificate.length(), VALUE_SIZE_BITS) - + getValueBits(appCertificate) - + DENY - + END_BIT; - byte[] ruleBytes = getBytes(ruleBits); - ByteBuffer rule = - ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes); - - RuleParser binaryParser = new RuleBinaryParser(); - Rule expectedRule = - new Rule( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE_LINEAGE, - IntegrityUtils.getHexDigest( - appCertificate.getBytes(StandardCharsets.UTF_8)), - /* isHashedValue= */ true), - Rule.DENY); - - List<Rule> rules = binaryParser.parse(rule.array()); - - assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); - } - - @Test - public void testBinaryString_validAtomicFormula_integerValue_noIndexing() throws Exception { - int versionCode = 1; - String ruleBits = - START_BIT - + ATOMIC_FORMULA_START_BITS - + VERSION_CODE - + EQ - + getBits(versionCode, /* numOfBits= */ 64) - + DENY - + END_BIT; - byte[] ruleBytes = getBytes(ruleBits); - ByteBuffer rule = - ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes); - RuleParser binaryParser = new RuleBinaryParser(); - Rule expectedRule = - new Rule( - new AtomicFormula.LongAtomicFormula( - AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1), - Rule.DENY); - - List<Rule> rules = binaryParser.parse(rule.array()); - - assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); - } - - @Test - public void testBinaryString_validAtomicFormula_booleanValue_noIndexing() throws Exception { - String isPreInstalled = "1"; - String ruleBits = - START_BIT - + ATOMIC_FORMULA_START_BITS - + PRE_INSTALLED - + EQ - + isPreInstalled - + DENY - + END_BIT; - byte[] ruleBytes = getBytes(ruleBits); - ByteBuffer rule = - ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes); - RuleParser binaryParser = new RuleBinaryParser(); - Rule expectedRule = - new Rule( - new AtomicFormula.BooleanAtomicFormula( - AtomicFormula.PRE_INSTALLED, true), - Rule.DENY); - - List<Rule> rules = binaryParser.parse(rule.array()); - - assertThat(rules).isEqualTo(Collections.singletonList(expectedRule)); - } - - @Test - public void testBinaryString_invalidAtomicFormula_noIndexing() { - int versionCode = 1; - String ruleBits = - START_BIT - + ATOMIC_FORMULA_START_BITS - + VERSION_CODE - + EQ - + getBits(versionCode, /* numOfBits= */ 64) - + DENY; - byte[] ruleBytes = getBytes(ruleBits); - ByteBuffer rule = - ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes); - RuleParser binaryParser = new RuleBinaryParser(); - - assertExpectException( - RuleParseException.class, - /* expectedExceptionMessageRegex= */ "A rule must end with a '1' bit.", - () -> binaryParser.parse(rule.array())); - } - - @Test - public void testBinaryString_withNoRuleList_noIndexing() throws RuleParseException { - ByteBuffer rule = ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - RuleParser binaryParser = new RuleBinaryParser(); - - List<Rule> rules = binaryParser.parse(rule.array()); - - assertThat(rules).isEmpty(); - } - - @Test - public void testBinaryString_withEmptyRule_noIndexing() { - String ruleBits = START_BIT; - byte[] ruleBytes = getBytes(ruleBits); - ByteBuffer rule = - ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes); - RuleParser binaryParser = new RuleBinaryParser(); - - assertExpectException( - RuleParseException.class, - /* expectedExceptionMessageRegex */ "", - () -> binaryParser.parse(rule.array())); - } - - @Test - public void testBinaryString_invalidCompoundFormula_invalidNumberOfFormulas_noIndexing() { - String packageName = "com.test.app"; - String appCertificate = "test_cert"; - String ruleBits = - START_BIT - + COMPOUND_FORMULA_START_BITS - + NOT - + ATOMIC_FORMULA_START_BITS - + PACKAGE_NAME - + EQ - + IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + ATOMIC_FORMULA_START_BITS - + APP_CERTIFICATE - + EQ - + IS_NOT_HASHED - + getBits(appCertificate.length(), VALUE_SIZE_BITS) - + getValueBits(appCertificate) - + COMPOUND_FORMULA_END_BITS - + DENY - + END_BIT; - byte[] ruleBytes = getBytes(ruleBits); - ByteBuffer rule = - ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes); - RuleParser binaryParser = new RuleBinaryParser(); - - assertExpectException( - RuleParseException.class, - /* expectedExceptionMessageRegex */ "Connector NOT must have 1 formula only", - () -> binaryParser.parse(rule.array())); - } - - @Test - public void testBinaryString_invalidRule_invalidOperator_noIndexing() { - int versionCode = 1; - String ruleBits = - START_BIT - + COMPOUND_FORMULA_START_BITS - + NOT - + ATOMIC_FORMULA_START_BITS - + VERSION_CODE - + INVALID_OPERATOR - + getBits(versionCode, /* numOfBits= */ 64) - + COMPOUND_FORMULA_END_BITS - + DENY - + END_BIT; - byte[] ruleBytes = getBytes(ruleBits); - ByteBuffer rule = - ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes); - RuleParser binaryParser = new RuleBinaryParser(); - - assertExpectException( - RuleParseException.class, - /* expectedExceptionMessageRegex */ String.format( - "Unknown operator: %d", INVALID_OPERATOR_VALUE), - () -> binaryParser.parse(rule.array())); - } - - @Test - public void testBinaryString_invalidRule_invalidEffect_noIndexing() { - String packageName = "com.test.app"; - String ruleBits = - START_BIT - + COMPOUND_FORMULA_START_BITS - + NOT - + ATOMIC_FORMULA_START_BITS - + PACKAGE_NAME - + EQ - + IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + COMPOUND_FORMULA_END_BITS - + INVALID_EFFECT - + END_BIT; - byte[] ruleBytes = getBytes(ruleBits); - ByteBuffer rule = - ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes); - RuleParser binaryParser = new RuleBinaryParser(); - - assertExpectException( - RuleParseException.class, - /* expectedExceptionMessageRegex */ String.format( - "Unknown effect: %d", INVALID_EFFECT_VALUE), - () -> binaryParser.parse(rule.array())); - } - - @Test - public void testBinaryString_invalidRule_invalidConnector_noIndexing() { - String packageName = "com.test.app"; - String ruleBits = - START_BIT - + COMPOUND_FORMULA_START_BITS - + INVALID_CONNECTOR - + ATOMIC_FORMULA_START_BITS - + PACKAGE_NAME - + EQ - + IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + COMPOUND_FORMULA_END_BITS - + DENY - + END_BIT; - byte[] ruleBytes = getBytes(ruleBits); - ByteBuffer rule = - ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes); - RuleParser binaryParser = new RuleBinaryParser(); - - assertExpectException( - RuleParseException.class, - /* expectedExceptionMessageRegex */ String.format( - "Unknown connector: %d", INVALID_CONNECTOR_VALUE), - () -> binaryParser.parse(rule.array())); - } - - @Test - public void testBinaryString_invalidRule_invalidKey_noIndexing() { - String packageName = "com.test.app"; - String ruleBits = - START_BIT - + COMPOUND_FORMULA_START_BITS - + NOT - + ATOMIC_FORMULA_START_BITS - + INVALID_KEY - + EQ - + IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + COMPOUND_FORMULA_END_BITS - + DENY - + END_BIT; - byte[] ruleBytes = getBytes(ruleBits); - ByteBuffer rule = - ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes); - RuleParser binaryParser = new RuleBinaryParser(); - - assertExpectException( - RuleParseException.class, - /* expectedExceptionMessageRegex */ String.format( - "Unknown key: %d", INVALID_KEY_VALUE), - () -> binaryParser.parse(rule.array())); - } - - @Test - public void testBinaryString_invalidRule_invalidSeparator_noIndexing() { - String packageName = "com.test.app"; - String ruleBits = - START_BIT - + INVALID_FORMULA_SEPARATOR_BITS - + NOT - + ATOMIC_FORMULA_START_BITS - + PACKAGE_NAME - + EQ - + IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + COMPOUND_FORMULA_END_BITS - + DENY - + END_BIT; - byte[] ruleBytes = getBytes(ruleBits); - ByteBuffer rule = - ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes); - RuleParser binaryParser = new RuleBinaryParser(); - - assertExpectException( - RuleParseException.class, - /* expectedExceptionMessageRegex */ String.format( - "Unknown formula separator: %d", INVALID_FORMULA_SEPARATOR_VALUE), - () -> binaryParser.parse(rule.array())); - } - - @Test - public void testBinaryString_invalidRule_invalidEndMarker_noIndexing() { - String packageName = "com.test.app"; - String ruleBits = - START_BIT - + COMPOUND_FORMULA_START_BITS - + NOT - + ATOMIC_FORMULA_START_BITS - + PACKAGE_NAME - + EQ - + IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + COMPOUND_FORMULA_END_BITS - + DENY - + INVALID_MARKER_BIT; - byte[] ruleBytes = getBytes(ruleBits); - ByteBuffer rule = - ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length); - rule.put(DEFAULT_FORMAT_VERSION_BYTES); - rule.put(ruleBytes); - RuleParser binaryParser = new RuleBinaryParser(); - - assertExpectException( - RuleParseException.class, - /* expectedExceptionMessageRegex */ "A rule must end with a '1' bit", - () -> binaryParser.parse(rule.array())); - } -} diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java deleted file mode 100644 index 370bd80003bd..000000000000 --- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.integrity.parser; - -import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS; -import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY; -import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY; -import static com.android.server.integrity.utils.TestUtils.getBits; -import static com.android.server.integrity.utils.TestUtils.getBytes; -import static com.android.server.integrity.utils.TestUtils.getValueBits; - -import static com.google.common.truth.Truth.assertThat; - -import static org.testng.Assert.assertThrows; - -import android.content.integrity.AppInstallMetadata; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -@RunWith(JUnit4.class) -public class RuleIndexingControllerTest { - - @Test - public void verifyIndexRangeSearchIsCorrect() throws IOException { - InputStream inputStream = obtainDefaultIndexingMapForTest(); - - RuleIndexingController indexingController = new RuleIndexingController(inputStream); - - AppInstallMetadata appInstallMetadata = - new AppInstallMetadata.Builder() - .setPackageName("ddd") - .setAppCertificates(Collections.singletonList("777")) - .setAppCertificateLineage(Collections.singletonList("777")) - .build(); - - List<RuleIndexRange> resultingIndexes = - indexingController.identifyRulesToEvaluate(appInstallMetadata); - - assertThat(resultingIndexes) - .containsExactly( - new RuleIndexRange(200, 300), - new RuleIndexRange(700, 800), - new RuleIndexRange(900, 945)); - } - - @Test - public void verifyIndexRangeSearchIsCorrect_multipleAppCertificates() throws IOException { - InputStream inputStream = obtainDefaultIndexingMapForTest(); - - RuleIndexingController indexingController = new RuleIndexingController(inputStream); - - AppInstallMetadata appInstallMetadata = - new AppInstallMetadata.Builder() - .setPackageName("ddd") - .setAppCertificates(Arrays.asList("777", "999")) - .setAppCertificateLineage(Arrays.asList("777", "999")) - .build(); - - List<RuleIndexRange> resultingIndexes = - indexingController.identifyRulesToEvaluate(appInstallMetadata); - - assertThat(resultingIndexes) - .containsExactly( - new RuleIndexRange(200, 300), - new RuleIndexRange(700, 800), - new RuleIndexRange(800, 900), - new RuleIndexRange(900, 945)); - } - - @Test - public void verifyIndexRangeSearchIsCorrect_keysInFirstAndLastBlock() throws IOException { - InputStream inputStream = obtainDefaultIndexingMapForTest(); - - RuleIndexingController indexingController = new RuleIndexingController(inputStream); - - AppInstallMetadata appInstallMetadata = - new AppInstallMetadata.Builder() - .setPackageName("bbb") - .setAppCertificates(Collections.singletonList("999")) - .setAppCertificateLineage(Collections.singletonList("999")) - .build(); - - List<RuleIndexRange> resultingIndexes = - indexingController.identifyRulesToEvaluate(appInstallMetadata); - - assertThat(resultingIndexes) - .containsExactly( - new RuleIndexRange(100, 200), - new RuleIndexRange(800, 900), - new RuleIndexRange(900, 945)); - } - - @Test - public void verifyIndexRangeSearchIsCorrect_keysMatchWithValues() throws IOException { - InputStream inputStream = obtainDefaultIndexingMapForTest(); - - RuleIndexingController indexingController = new RuleIndexingController(inputStream); - - AppInstallMetadata appInstallMetadata = - new AppInstallMetadata.Builder() - .setPackageName("ccc") - .setAppCertificates(Collections.singletonList("444")) - .setAppCertificateLineage(Collections.singletonList("444")) - .build(); - - List<RuleIndexRange> resultingIndexes = - indexingController.identifyRulesToEvaluate(appInstallMetadata); - - assertThat(resultingIndexes) - .containsExactly( - new RuleIndexRange(200, 300), - new RuleIndexRange(700, 800), - new RuleIndexRange(900, 945)); - } - - @Test - public void verifyIndexRangeSearchIsCorrect_noIndexesAvailable() throws IOException { - byte[] stringBytes = - getBytes( - getKeyValueString(START_INDEXING_KEY, 100) - + getKeyValueString(END_INDEXING_KEY, 500) - + getKeyValueString(START_INDEXING_KEY, 500) - + getKeyValueString(END_INDEXING_KEY, 900) - + getKeyValueString(START_INDEXING_KEY, 900) - + getKeyValueString(END_INDEXING_KEY, 945)); - ByteBuffer rule = ByteBuffer.allocate(stringBytes.length); - rule.put(stringBytes); - InputStream inputStream = new ByteArrayInputStream(rule.array()); - - RuleIndexingController indexingController = new RuleIndexingController(inputStream); - - AppInstallMetadata appInstallMetadata = - new AppInstallMetadata.Builder() - .setPackageName("ccc") - .setAppCertificates(Collections.singletonList("444")) - .setAppCertificateLineage(Collections.singletonList("444")) - .build(); - - List<RuleIndexRange> resultingIndexes = - indexingController.identifyRulesToEvaluate(appInstallMetadata); - - assertThat(resultingIndexes) - .containsExactly( - new RuleIndexRange(100, 500), - new RuleIndexRange(500, 900), - new RuleIndexRange(900, 945)); - } - - @Test - public void verifyIndexingFileIsCorrupt() throws IOException { - byte[] stringBytes = - getBytes( - getKeyValueString(START_INDEXING_KEY, 100) - + getKeyValueString("ccc", 200) - + getKeyValueString(END_INDEXING_KEY, 300) - + getKeyValueString(END_INDEXING_KEY, 900)); - ByteBuffer rule = ByteBuffer.allocate(stringBytes.length); - rule.put(stringBytes); - InputStream inputStream = new ByteArrayInputStream(rule.array()); - - assertThrows(IllegalStateException.class, - () -> new RuleIndexingController(inputStream)); - } - - private static InputStream obtainDefaultIndexingMapForTest() { - byte[] stringBytes = - getBytes( - getKeyValueString(START_INDEXING_KEY, 100) - + getKeyValueString("ccc", 200) - + getKeyValueString("eee", 300) - + getKeyValueString("hhh", 400) - + getKeyValueString(END_INDEXING_KEY, 500) - + getKeyValueString(START_INDEXING_KEY, 500) - + getKeyValueString("111", 600) - + getKeyValueString("444", 700) - + getKeyValueString("888", 800) - + getKeyValueString(END_INDEXING_KEY, 900) - + getKeyValueString(START_INDEXING_KEY, 900) - + getKeyValueString(END_INDEXING_KEY, 945)); - ByteBuffer rule = ByteBuffer.allocate(stringBytes.length); - rule.put(stringBytes); - return new ByteArrayInputStream(rule.array()); - } - - private static String getKeyValueString(String key, int value) { - String isNotHashed = "0"; - return isNotHashed - + getBits(key.length(), VALUE_SIZE_BITS) - + getValueBits(key) - + getBits(value, /* numOfBits= */ 32); - } -} diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java deleted file mode 100644 index 9ed2e88bd6a2..000000000000 --- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java +++ /dev/null @@ -1,914 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity.serializer; - -import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START; -import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END; -import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START; -import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS; -import static com.android.server.integrity.model.ComponentBitSize.DEFAULT_FORMAT_VERSION; -import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS; -import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS; -import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS; -import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS; -import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS; -import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS; -import static com.android.server.integrity.model.IndexingFileConstants.END_INDEXING_KEY; -import static com.android.server.integrity.model.IndexingFileConstants.INDEXING_BLOCK_SIZE; -import static com.android.server.integrity.model.IndexingFileConstants.START_INDEXING_KEY; -import static com.android.server.integrity.serializer.RuleBinarySerializer.INDEXED_RULE_SIZE_LIMIT; -import static com.android.server.integrity.serializer.RuleBinarySerializer.NONINDEXED_RULE_SIZE_LIMIT; -import static com.android.server.integrity.utils.TestUtils.getBits; -import static com.android.server.integrity.utils.TestUtils.getBytes; -import static com.android.server.integrity.utils.TestUtils.getValueBits; -import static com.android.server.testutils.TestUtils.assertExpectException; - -import static com.google.common.truth.Truth.assertThat; - -import android.content.integrity.AppInstallMetadata; -import android.content.integrity.AtomicFormula; -import android.content.integrity.CompoundFormula; -import android.content.integrity.IntegrityFormula; -import android.content.integrity.IntegrityUtils; -import android.content.integrity.Rule; - -import androidx.annotation.NonNull; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.io.ByteArrayOutputStream; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -@RunWith(JUnit4.class) -public class RuleBinarySerializerTest { - - private static final String SAMPLE_INSTALLER_NAME = "com.test.installer"; - private static final String SAMPLE_INSTALLER_CERT = "installer_cert"; - - private static final String COMPOUND_FORMULA_START_BITS = - getBits(COMPOUND_FORMULA_START, SEPARATOR_BITS); - private static final String COMPOUND_FORMULA_END_BITS = - getBits(COMPOUND_FORMULA_END, SEPARATOR_BITS); - private static final String ATOMIC_FORMULA_START_BITS = - getBits(ATOMIC_FORMULA_START, SEPARATOR_BITS); - - private static final String NOT = getBits(CompoundFormula.NOT, CONNECTOR_BITS); - private static final String AND = getBits(CompoundFormula.AND, CONNECTOR_BITS); - private static final String OR = getBits(CompoundFormula.OR, CONNECTOR_BITS); - - private static final String PACKAGE_NAME = getBits(AtomicFormula.PACKAGE_NAME, KEY_BITS); - private static final String APP_CERTIFICATE = getBits(AtomicFormula.APP_CERTIFICATE, KEY_BITS); - private static final String INSTALLER_NAME = getBits(AtomicFormula.INSTALLER_NAME, KEY_BITS); - private static final String INSTALLER_CERTIFICATE = - getBits(AtomicFormula.INSTALLER_CERTIFICATE, KEY_BITS); - private static final String VERSION_CODE = getBits(AtomicFormula.VERSION_CODE, KEY_BITS); - private static final String PRE_INSTALLED = getBits(AtomicFormula.PRE_INSTALLED, KEY_BITS); - - private static final String EQ = getBits(AtomicFormula.EQ, OPERATOR_BITS); - - private static final String IS_NOT_HASHED = "0"; - private static final String IS_HASHED = "1"; - - private static final String DENY = getBits(Rule.DENY, EFFECT_BITS); - - private static final String START_BIT = "1"; - private static final String END_BIT = "1"; - - private static final byte[] DEFAULT_FORMAT_VERSION_BYTES = - getBytes(getBits(DEFAULT_FORMAT_VERSION, FORMAT_VERSION_BITS)); - - private static final String SERIALIZED_START_INDEXING_KEY = - IS_NOT_HASHED - + getBits(START_INDEXING_KEY.length(), VALUE_SIZE_BITS) - + getValueBits(START_INDEXING_KEY); - private static final String SERIALIZED_END_INDEXING_KEY = - IS_NOT_HASHED - + getBits(END_INDEXING_KEY.length(), VALUE_SIZE_BITS) - + getValueBits(END_INDEXING_KEY); - - @Test - public void testBinaryString_serializeNullRules() { - RuleSerializer binarySerializer = new RuleBinarySerializer(); - - assertExpectException( - RuleSerializeException.class, - /* expectedExceptionMessageRegex= */ "Null rules cannot be serialized.", - () -> binarySerializer.serialize(null, /* formatVersion= */ Optional.empty())); - } - - @Test - public void testBinaryString_emptyRules() throws Exception { - ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream(); - ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream(); - RuleSerializer binarySerializer = new RuleBinarySerializer(); - - binarySerializer.serialize( - Collections.emptyList(), - /* formatVersion= */ Optional.empty(), - ruleOutputStream, - indexingOutputStream); - - ByteArrayOutputStream expectedRuleOutputStream = new ByteArrayOutputStream(); - expectedRuleOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES); - assertThat(ruleOutputStream.toByteArray()) - .isEqualTo(expectedRuleOutputStream.toByteArray()); - - ByteArrayOutputStream expectedIndexingOutputStream = new ByteArrayOutputStream(); - String serializedIndexingBytes = - SERIALIZED_START_INDEXING_KEY - + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32) - + SERIALIZED_END_INDEXING_KEY - + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32); - byte[] expectedIndexingBytes = - getBytes( - serializedIndexingBytes - + serializedIndexingBytes - + serializedIndexingBytes); - expectedIndexingOutputStream.write(expectedIndexingBytes); - assertThat(indexingOutputStream.toByteArray()) - .isEqualTo(expectedIndexingOutputStream.toByteArray()); - } - - @Test - public void testBinaryStream_serializeValidCompoundFormula() throws Exception { - String packageName = "com.test.app"; - Rule rule = - new Rule( - new CompoundFormula( - CompoundFormula.NOT, - Collections.singletonList( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - packageName, - /* isHashedValue= */ false))), - Rule.DENY); - - ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream(); - ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream(); - RuleSerializer binarySerializer = new RuleBinarySerializer(); - binarySerializer.serialize( - Collections.singletonList(rule), - /* formatVersion= */ Optional.empty(), - ruleOutputStream, - indexingOutputStream); - - String expectedBits = - START_BIT - + COMPOUND_FORMULA_START_BITS - + NOT - + ATOMIC_FORMULA_START_BITS - + PACKAGE_NAME - + EQ - + IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + COMPOUND_FORMULA_END_BITS - + DENY - + END_BIT; - ByteArrayOutputStream expectedRuleOutputStream = new ByteArrayOutputStream(); - expectedRuleOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES); - expectedRuleOutputStream.write(getBytes(expectedBits)); - assertThat(ruleOutputStream.toByteArray()) - .isEqualTo(expectedRuleOutputStream.toByteArray()); - - ByteArrayOutputStream expectedIndexingOutputStream = new ByteArrayOutputStream(); - String expectedIndexingBitsForIndexed = - SERIALIZED_START_INDEXING_KEY - + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32) - + SERIALIZED_END_INDEXING_KEY - + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32); - String expectedIndexingBitsForUnindexed = - SERIALIZED_START_INDEXING_KEY - + getBits(DEFAULT_FORMAT_VERSION_BYTES.length, /* numOfBits= */ 32) - + SERIALIZED_END_INDEXING_KEY - + getBits( - DEFAULT_FORMAT_VERSION_BYTES.length + getBytes(expectedBits).length, - /* numOfBits= */ 32); - expectedIndexingOutputStream.write( - getBytes( - expectedIndexingBitsForIndexed - + expectedIndexingBitsForIndexed - + expectedIndexingBitsForUnindexed)); - - assertThat(indexingOutputStream.toByteArray()) - .isEqualTo(expectedIndexingOutputStream.toByteArray()); - } - - @Test - public void testBinaryString_serializeValidCompoundFormula_notConnector() throws Exception { - String packageName = "com.test.app"; - Rule rule = - new Rule( - new CompoundFormula( - CompoundFormula.NOT, - Collections.singletonList( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - packageName, - /* isHashedValue= */ false))), - Rule.DENY); - RuleSerializer binarySerializer = new RuleBinarySerializer(); - String expectedBits = - START_BIT - + COMPOUND_FORMULA_START_BITS - + NOT - + ATOMIC_FORMULA_START_BITS - + PACKAGE_NAME - + EQ - + IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + COMPOUND_FORMULA_END_BITS - + DENY - + END_BIT; - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES); - byteArrayOutputStream.write(getBytes(expectedBits)); - byte[] expectedRules = byteArrayOutputStream.toByteArray(); - - byte[] actualRules = - binarySerializer.serialize( - Collections.singletonList(rule), /* formatVersion= */ Optional.empty()); - - assertThat(actualRules).isEqualTo(expectedRules); - } - - @Test - public void testBinaryString_serializeValidCompoundFormula_andConnector() throws Exception { - String packageName = "com.test.app"; - String appCertificate = "test_cert"; - Rule rule = - new Rule( - new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - packageName, - /* isHashedValue= */ false), - new AtomicFormula.StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - appCertificate, - /* isHashedValue= */ false))), - Rule.DENY); - RuleSerializer binarySerializer = new RuleBinarySerializer(); - String expectedBits = - START_BIT - + COMPOUND_FORMULA_START_BITS - + AND - + ATOMIC_FORMULA_START_BITS - + PACKAGE_NAME - + EQ - + IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + ATOMIC_FORMULA_START_BITS - + APP_CERTIFICATE - + EQ - + IS_NOT_HASHED - + getBits(appCertificate.length(), VALUE_SIZE_BITS) - + getValueBits(appCertificate) - + COMPOUND_FORMULA_END_BITS - + DENY - + END_BIT; - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES); - byteArrayOutputStream.write(getBytes(expectedBits)); - byte[] expectedRules = byteArrayOutputStream.toByteArray(); - - byte[] actualRules = - binarySerializer.serialize( - Collections.singletonList(rule), /* formatVersion= */ Optional.empty()); - - assertThat(actualRules).isEqualTo(expectedRules); - } - - @Test - public void testBinaryString_serializeValidCompoundFormula_orConnector() throws Exception { - String packageName = "com.test.app"; - String appCertificate = "test_cert"; - Rule rule = - new Rule( - new CompoundFormula( - CompoundFormula.OR, - Arrays.asList( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - packageName, - /* isHashedValue= */ false), - new AtomicFormula.StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - appCertificate, - /* isHashedValue= */ false))), - Rule.DENY); - RuleSerializer binarySerializer = new RuleBinarySerializer(); - String expectedBits = - START_BIT - + COMPOUND_FORMULA_START_BITS - + OR - + ATOMIC_FORMULA_START_BITS - + PACKAGE_NAME - + EQ - + IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + ATOMIC_FORMULA_START_BITS - + APP_CERTIFICATE - + EQ - + IS_NOT_HASHED - + getBits(appCertificate.length(), VALUE_SIZE_BITS) - + getValueBits(appCertificate) - + COMPOUND_FORMULA_END_BITS - + DENY - + END_BIT; - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES); - byteArrayOutputStream.write(getBytes(expectedBits)); - byte[] expectedRules = byteArrayOutputStream.toByteArray(); - - byte[] actualRules = - binarySerializer.serialize( - Collections.singletonList(rule), /* formatVersion= */ Optional.empty()); - - assertThat(actualRules).isEqualTo(expectedRules); - } - - @Test - public void testBinaryString_serializeValidAtomicFormula_stringValue() throws Exception { - String packageName = "com.test.app"; - Rule rule = - new Rule( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - packageName, - /* isHashedValue= */ false), - Rule.DENY); - RuleSerializer binarySerializer = new RuleBinarySerializer(); - String expectedBits = - START_BIT - + ATOMIC_FORMULA_START_BITS - + PACKAGE_NAME - + EQ - + IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + DENY - + END_BIT; - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES); - byteArrayOutputStream.write(getBytes(expectedBits)); - byte[] expectedRules = byteArrayOutputStream.toByteArray(); - - byte[] actualRules = - binarySerializer.serialize( - Collections.singletonList(rule), /* formatVersion= */ Optional.empty()); - - assertThat(actualRules).isEqualTo(expectedRules); - } - - @Test - public void testBinaryString_serializeValidAtomicFormula_hashedValue() throws Exception { - String appCertificate = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; - Rule rule = - new Rule( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - IntegrityUtils.getHexDigest( - appCertificate.getBytes(StandardCharsets.UTF_8)), - /* isHashedValue= */ true), - Rule.DENY); - RuleSerializer binarySerializer = new RuleBinarySerializer(); - String expectedBits = - START_BIT - + ATOMIC_FORMULA_START_BITS - + APP_CERTIFICATE - + EQ - + IS_HASHED - + getBits(appCertificate.length(), VALUE_SIZE_BITS) - + getValueBits(appCertificate) - + DENY - + END_BIT; - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES); - byteArrayOutputStream.write(getBytes(expectedBits)); - byte[] expectedRules = byteArrayOutputStream.toByteArray(); - - byte[] actualRules = - binarySerializer.serialize( - Collections.singletonList(rule), /* formatVersion= */ Optional.empty()); - - assertThat(actualRules).isEqualTo(expectedRules); - } - - @Test - public void testBinaryString_serializeValidAtomicFormula_integerValue() throws Exception { - long versionCode = 1; - Rule rule = - new Rule( - new AtomicFormula.LongAtomicFormula( - AtomicFormula.VERSION_CODE, AtomicFormula.EQ, versionCode), - Rule.DENY); - RuleSerializer binarySerializer = new RuleBinarySerializer(); - String expectedBits = - START_BIT - + ATOMIC_FORMULA_START_BITS - + VERSION_CODE - + EQ - + getBits(versionCode, /* numOfBits= */ 64) - + DENY - + END_BIT; - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES); - byteArrayOutputStream.write(getBytes(expectedBits)); - byte[] expectedRules = byteArrayOutputStream.toByteArray(); - - byte[] actualRules = - binarySerializer.serialize( - Collections.singletonList(rule), /* formatVersion= */ Optional.empty()); - - assertThat(actualRules).isEqualTo(expectedRules); - } - - @Test - public void testBinaryString_serializeValidAtomicFormula_booleanValue() throws Exception { - String preInstalled = "1"; - Rule rule = - new Rule( - new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true), - Rule.DENY); - RuleSerializer binarySerializer = new RuleBinarySerializer(); - String expectedBits = - START_BIT - + ATOMIC_FORMULA_START_BITS - + PRE_INSTALLED - + EQ - + preInstalled - + DENY - + END_BIT; - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES); - byteArrayOutputStream.write(getBytes(expectedBits)); - byte[] expectedRules = byteArrayOutputStream.toByteArray(); - - byte[] actualRules = - binarySerializer.serialize( - Collections.singletonList(rule), /* formatVersion= */ Optional.empty()); - - assertThat(actualRules).isEqualTo(expectedRules); - } - - @Test - public void testBinaryString_serializeInvalidFormulaType() throws Exception { - IntegrityFormula invalidFormula = getInvalidFormula(); - Rule rule = new Rule(invalidFormula, Rule.DENY); - RuleSerializer binarySerializer = new RuleBinarySerializer(); - - assertExpectException( - RuleSerializeException.class, - /* expectedExceptionMessageRegex= */ "Malformed rule identified.", - () -> - binarySerializer.serialize( - Collections.singletonList(rule), - /* formatVersion= */ Optional.empty())); - } - - @Test - public void testBinaryString_serializeFormatVersion() throws Exception { - int formatVersion = 1; - RuleSerializer binarySerializer = new RuleBinarySerializer(); - String expectedBits = getBits(formatVersion, FORMAT_VERSION_BITS); - byte[] expectedRules = getBytes(expectedBits); - - byte[] actualRules = - binarySerializer.serialize( - Collections.emptyList(), /* formatVersion= */ Optional.of(formatVersion)); - - assertThat(actualRules).isEqualTo(expectedRules); - } - - @Test - public void testBinaryString_verifyManyRulesAreIndexedCorrectly() throws Exception { - int ruleCount = 225; - String packagePrefix = "package.name."; - String appCertificatePrefix = "app.cert."; - String installerNamePrefix = "installer."; - - // Create the rule set with 225 package name based rules, 225 app certificate indexed rules, - // and 225 non-indexed rules.. - List<Rule> ruleList = new ArrayList(); - for (int count = 0; count < ruleCount; count++) { - ruleList.add( - getRuleWithPackageNameAndSampleInstallerName( - String.format("%s%04d", packagePrefix, count))); - } - for (int count = 0; count < ruleCount; count++) { - ruleList.add( - getRuleWithAppCertificateAndSampleInstallerName( - String.format("%s%04d", appCertificatePrefix, count))); - } - for (int count = 0; count < ruleCount; count++) { - ruleList.add( - getNonIndexedRuleWithInstallerName( - String.format("%s%04d", installerNamePrefix, count))); - } - - // Serialize the rules. - ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream(); - ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream(); - RuleSerializer binarySerializer = new RuleBinarySerializer(); - binarySerializer.serialize( - ruleList, - /* formatVersion= */ Optional.empty(), - ruleOutputStream, - indexingOutputStream); - - // Verify the rules file and index files. - ByteArrayOutputStream expectedOrderedRuleOutputStream = new ByteArrayOutputStream(); - ByteArrayOutputStream expectedIndexingOutputStream = new ByteArrayOutputStream(); - - expectedOrderedRuleOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES); - int totalBytesWritten = DEFAULT_FORMAT_VERSION_BYTES.length; - - String expectedIndexingBytesForPackageNameIndexed = - SERIALIZED_START_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32); - for (int count = 0; count < ruleCount; count++) { - String packageName = String.format("%s%04d", packagePrefix, count); - if (count > 0 && count % INDEXING_BLOCK_SIZE == 0) { - expectedIndexingBytesForPackageNameIndexed += - IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + getBits(totalBytesWritten, /* numOfBits= */ 32); - } - - byte[] bytesForPackage = - getBytes( - getSerializedCompoundRuleWithPackageNameAndSampleInstallerName( - packageName)); - expectedOrderedRuleOutputStream.write(bytesForPackage); - totalBytesWritten += bytesForPackage.length; - } - expectedIndexingBytesForPackageNameIndexed += - SERIALIZED_END_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32); - - String expectedIndexingBytesForAppCertificateIndexed = - SERIALIZED_START_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32); - for (int count = 0; count < ruleCount; count++) { - String appCertificate = String.format("%s%04d", appCertificatePrefix, count); - if (count > 0 && count % INDEXING_BLOCK_SIZE == 0) { - expectedIndexingBytesForAppCertificateIndexed += - IS_NOT_HASHED - + getBits(appCertificate.length(), VALUE_SIZE_BITS) - + getValueBits(appCertificate) - + getBits(totalBytesWritten, /* numOfBits= */ 32); - } - - byte[] bytesForPackage = - getBytes( - getSerializedCompoundRuleWithCertificateNameAndSampleInstallerName( - appCertificate)); - expectedOrderedRuleOutputStream.write(bytesForPackage); - totalBytesWritten += bytesForPackage.length; - } - expectedIndexingBytesForAppCertificateIndexed += - SERIALIZED_END_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32); - - String expectedIndexingBytesForUnindexed = - SERIALIZED_START_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32); - for (int count = 0; count < ruleCount; count++) { - byte[] bytesForPackage = - getBytes( - getSerializedCompoundRuleWithInstallerNameAndInstallerCert( - String.format("%s%04d", installerNamePrefix, count))); - expectedOrderedRuleOutputStream.write(bytesForPackage); - totalBytesWritten += bytesForPackage.length; - } - expectedIndexingBytesForUnindexed += - SERIALIZED_END_INDEXING_KEY + getBits(totalBytesWritten, /* numOfBits= */ 32); - expectedIndexingOutputStream.write( - getBytes( - expectedIndexingBytesForPackageNameIndexed - + expectedIndexingBytesForAppCertificateIndexed - + expectedIndexingBytesForUnindexed)); - - assertThat(ruleOutputStream.toByteArray()) - .isEqualTo(expectedOrderedRuleOutputStream.toByteArray()); - assertThat(indexingOutputStream.toByteArray()) - .isEqualTo(expectedIndexingOutputStream.toByteArray()); - } - - @Test - public void testBinaryString_totalRuleSizeLimitReached() { - int ruleCount = INDEXED_RULE_SIZE_LIMIT - 1; - String packagePrefix = "package.name."; - String appCertificatePrefix = "app.cert."; - String installerNamePrefix = "installer."; - - // Create the rule set with more rules than the system can handle in total. - List<Rule> ruleList = new ArrayList(); - for (int count = 0; count < ruleCount; count++) { - ruleList.add( - getRuleWithPackageNameAndSampleInstallerName( - String.format("%s%04d", packagePrefix, count))); - } - for (int count = 0; count < ruleCount; count++) { - ruleList.add( - getRuleWithAppCertificateAndSampleInstallerName( - String.format("%s%04d", appCertificatePrefix, count))); - } - for (int count = 0; count < NONINDEXED_RULE_SIZE_LIMIT - 1; count++) { - ruleList.add( - getNonIndexedRuleWithInstallerName( - String.format("%s%04d", installerNamePrefix, count))); - } - - // Serialize the rules. - ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream(); - ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream(); - RuleSerializer binarySerializer = new RuleBinarySerializer(); - - assertExpectException( - RuleSerializeException.class, - "Too many rules provided", - () -> - binarySerializer.serialize( - ruleList, - /* formatVersion= */ Optional.empty(), - ruleOutputStream, - indexingOutputStream)); - } - - @Test - public void testBinaryString_tooManyPackageNameIndexedRules() { - String packagePrefix = "package.name."; - - // Create a rule set with too many package name indexed rules. - List<Rule> ruleList = new ArrayList(); - for (int count = 0; count < INDEXED_RULE_SIZE_LIMIT + 1; count++) { - ruleList.add( - getRuleWithPackageNameAndSampleInstallerName( - String.format("%s%04d", packagePrefix, count))); - } - - // Serialize the rules. - ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream(); - ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream(); - RuleSerializer binarySerializer = new RuleBinarySerializer(); - - assertExpectException( - RuleSerializeException.class, - "Too many rules provided in the indexing group.", - () -> - binarySerializer.serialize( - ruleList, - /* formatVersion= */ Optional.empty(), - ruleOutputStream, - indexingOutputStream)); - } - - @Test - public void testBinaryString_tooManyAppCertificateIndexedRules() { - String appCertificatePrefix = "app.cert."; - - // Create a rule set with too many app certificate indexed rules. - List<Rule> ruleList = new ArrayList(); - for (int count = 0; count < INDEXED_RULE_SIZE_LIMIT + 1; count++) { - ruleList.add( - getRuleWithAppCertificateAndSampleInstallerName( - String.format("%s%04d", appCertificatePrefix, count))); - } - - // Serialize the rules. - ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream(); - ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream(); - RuleSerializer binarySerializer = new RuleBinarySerializer(); - - assertExpectException( - RuleSerializeException.class, - "Too many rules provided in the indexing group.", - () -> - binarySerializer.serialize( - ruleList, - /* formatVersion= */ Optional.empty(), - ruleOutputStream, - indexingOutputStream)); - } - - @Test - public void testBinaryString_tooManyNonIndexedRules() { - String installerNamePrefix = "installer."; - - // Create a rule set with too many unindexed rules. - List<Rule> ruleList = new ArrayList(); - for (int count = 0; count < NONINDEXED_RULE_SIZE_LIMIT + 1; count++) { - ruleList.add( - getNonIndexedRuleWithInstallerName( - String.format("%s%04d", installerNamePrefix, count))); - } - - // Serialize the rules. - ByteArrayOutputStream ruleOutputStream = new ByteArrayOutputStream(); - ByteArrayOutputStream indexingOutputStream = new ByteArrayOutputStream(); - RuleSerializer binarySerializer = new RuleBinarySerializer(); - - assertExpectException( - RuleSerializeException.class, - "Too many rules provided in the indexing group.", - () -> - binarySerializer.serialize( - ruleList, - /* formatVersion= */ Optional.empty(), - ruleOutputStream, - indexingOutputStream)); - } - - private Rule getRuleWithPackageNameAndSampleInstallerName(String packageName) { - return new Rule( - new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - packageName, - /* isHashedValue= */ false), - new AtomicFormula.StringAtomicFormula( - AtomicFormula.INSTALLER_NAME, - SAMPLE_INSTALLER_NAME, - /* isHashedValue= */ false))), - Rule.DENY); - } - - private String getSerializedCompoundRuleWithPackageNameAndSampleInstallerName( - String packageName) { - return START_BIT - + COMPOUND_FORMULA_START_BITS - + AND - + ATOMIC_FORMULA_START_BITS - + PACKAGE_NAME - + EQ - + IS_NOT_HASHED - + getBits(packageName.length(), VALUE_SIZE_BITS) - + getValueBits(packageName) - + ATOMIC_FORMULA_START_BITS - + INSTALLER_NAME - + EQ - + IS_NOT_HASHED - + getBits(SAMPLE_INSTALLER_NAME.length(), VALUE_SIZE_BITS) - + getValueBits(SAMPLE_INSTALLER_NAME) - + COMPOUND_FORMULA_END_BITS - + DENY - + END_BIT; - } - - private Rule getRuleWithAppCertificateAndSampleInstallerName(String certificate) { - return new Rule( - new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - certificate, - /* isHashedValue= */ false), - new AtomicFormula.StringAtomicFormula( - AtomicFormula.INSTALLER_NAME, - SAMPLE_INSTALLER_NAME, - /* isHashedValue= */ false))), - Rule.DENY); - } - - private String getSerializedCompoundRuleWithCertificateNameAndSampleInstallerName( - String appCertificate) { - return START_BIT - + COMPOUND_FORMULA_START_BITS - + AND - + ATOMIC_FORMULA_START_BITS - + APP_CERTIFICATE - + EQ - + IS_NOT_HASHED - + getBits(appCertificate.length(), VALUE_SIZE_BITS) - + getValueBits(appCertificate) - + ATOMIC_FORMULA_START_BITS - + INSTALLER_NAME - + EQ - + IS_NOT_HASHED - + getBits(SAMPLE_INSTALLER_NAME.length(), VALUE_SIZE_BITS) - + getValueBits(SAMPLE_INSTALLER_NAME) - + COMPOUND_FORMULA_END_BITS - + DENY - + END_BIT; - } - - private Rule getNonIndexedRuleWithInstallerName(String installerName) { - return new Rule( - new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.INSTALLER_NAME, - installerName, - /* isHashedValue= */ false), - new AtomicFormula.StringAtomicFormula( - AtomicFormula.INSTALLER_CERTIFICATE, - SAMPLE_INSTALLER_CERT, - /* isHashedValue= */ false))), - Rule.DENY); - } - - private String getSerializedCompoundRuleWithInstallerNameAndInstallerCert( - String installerName) { - return START_BIT - + COMPOUND_FORMULA_START_BITS - + AND - + ATOMIC_FORMULA_START_BITS - + INSTALLER_NAME - + EQ - + IS_NOT_HASHED - + getBits(installerName.length(), VALUE_SIZE_BITS) - + getValueBits(installerName) - + ATOMIC_FORMULA_START_BITS - + INSTALLER_CERTIFICATE - + EQ - + IS_NOT_HASHED - + getBits(SAMPLE_INSTALLER_CERT.length(), VALUE_SIZE_BITS) - + getValueBits(SAMPLE_INSTALLER_CERT) - + COMPOUND_FORMULA_END_BITS - + DENY - + END_BIT; - } - - private static IntegrityFormula getInvalidFormula() { - return new AtomicFormula(0) { - @Override - public int getTag() { - return 0; - } - - @Override - public boolean matches(AppInstallMetadata appInstallMetadata) { - return false; - } - - @Override - public boolean isAppCertificateFormula() { - return false; - } - - @Override - public boolean isAppCertificateLineageFormula() { - return false; - } - - @Override - public boolean isInstallerFormula() { - return false; - } - - @Override - public int hashCode() { - return super.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } - - @NonNull - @Override - protected Object clone() throws CloneNotSupportedException { - return super.clone(); - } - - @Override - public String toString() { - return super.toString(); - } - - @Override - protected void finalize() throws Throwable { - super.finalize(); - } - }; - } -} diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java deleted file mode 100644 index 6dccdf51af02..000000000000 --- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Copyright (C) 2019 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.integrity.serializer; - -import static com.android.server.integrity.serializer.RuleIndexingDetails.APP_CERTIFICATE_INDEXED; -import static com.android.server.integrity.serializer.RuleIndexingDetails.NOT_INDEXED; -import static com.android.server.integrity.serializer.RuleIndexingDetails.PACKAGE_NAME_INDEXED; -import static com.android.server.integrity.serializer.RuleIndexingDetailsIdentifier.splitRulesIntoIndexBuckets; -import static com.android.server.testutils.TestUtils.assertExpectException; - -import static com.google.common.truth.Truth.assertThat; - -import android.content.integrity.AppInstallMetadata; -import android.content.integrity.AtomicFormula; -import android.content.integrity.CompoundFormula; -import android.content.integrity.IntegrityFormula; -import android.content.integrity.Rule; - -import androidx.annotation.NonNull; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -/** Unit tests for {@link RuleIndexingDetailsIdentifier}. */ -@RunWith(JUnit4.class) -public class RuleIndexingDetailsIdentifierTest { - - private static final String SAMPLE_APP_CERTIFICATE = "testcert"; - private static final String SAMPLE_INSTALLER_NAME = "com.test.installer"; - private static final String SAMPLE_INSTALLER_CERTIFICATE = "installercert"; - private static final String SAMPLE_PACKAGE_NAME = "com.test.package"; - - private static final AtomicFormula ATOMIC_FORMULA_WITH_PACKAGE_NAME = - new AtomicFormula.StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - SAMPLE_PACKAGE_NAME, - /* isHashedValue= */ false); - private static final AtomicFormula ATOMIC_FORMULA_WITH_APP_CERTIFICATE = - new AtomicFormula.StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - SAMPLE_APP_CERTIFICATE, - /* isHashedValue= */ false); - private static final AtomicFormula ATOMIC_FORMULA_WITH_INSTALLER_NAME = - new AtomicFormula.StringAtomicFormula( - AtomicFormula.INSTALLER_NAME, - SAMPLE_INSTALLER_NAME, - /* isHashedValue= */ false); - private static final AtomicFormula ATOMIC_FORMULA_WITH_INSTALLER_CERTIFICATE = - new AtomicFormula.StringAtomicFormula( - AtomicFormula.INSTALLER_CERTIFICATE, - SAMPLE_INSTALLER_CERTIFICATE, - /* isHashedValue= */ false); - private static final AtomicFormula ATOMIC_FORMULA_WITH_VERSION_CODE = - new AtomicFormula.LongAtomicFormula(AtomicFormula.VERSION_CODE, - AtomicFormula.EQ, 12); - private static final AtomicFormula ATOMIC_FORMULA_WITH_ISPREINSTALLED = - new AtomicFormula.BooleanAtomicFormula( - AtomicFormula.PRE_INSTALLED, /* booleanValue= */ - true); - - - private static final Rule RULE_WITH_PACKAGE_NAME = - new Rule( - new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - ATOMIC_FORMULA_WITH_PACKAGE_NAME, - ATOMIC_FORMULA_WITH_INSTALLER_NAME)), - Rule.DENY); - private static final Rule RULE_WITH_APP_CERTIFICATE = - new Rule( - new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - ATOMIC_FORMULA_WITH_APP_CERTIFICATE, - ATOMIC_FORMULA_WITH_INSTALLER_NAME)), - Rule.DENY); - private static final Rule RULE_WITH_INSTALLER_RESTRICTIONS = - new Rule( - new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - ATOMIC_FORMULA_WITH_INSTALLER_NAME, - ATOMIC_FORMULA_WITH_INSTALLER_CERTIFICATE)), - Rule.DENY); - - private static final Rule RULE_WITH_NONSTRING_RESTRICTIONS = - new Rule( - new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - ATOMIC_FORMULA_WITH_VERSION_CODE, - ATOMIC_FORMULA_WITH_ISPREINSTALLED)), - Rule.DENY); - public static final int INVALID_FORMULA_TAG = -1; - - @Test - public void getIndexType_nullRule() { - List<Rule> ruleList = null; - - assertExpectException( - IllegalArgumentException.class, - /* expectedExceptionMessageRegex= */ - "Index buckets cannot be created for null rule list.", - () -> splitRulesIntoIndexBuckets(ruleList)); - } - - @Test - public void getIndexType_invalidFormula() { - List<Rule> ruleList = new ArrayList(); - ruleList.add(new Rule(getInvalidFormula(), Rule.DENY)); - - assertExpectException( - IllegalArgumentException.class, - /* expectedExceptionMessageRegex= */ "Malformed rule identified.", - () -> splitRulesIntoIndexBuckets(ruleList)); - } - - @Test - public void getIndexType_ruleContainingPackageNameFormula() { - List<Rule> ruleList = new ArrayList(); - ruleList.add(RULE_WITH_PACKAGE_NAME); - - Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList); - - // Verify the resulting map content. - assertThat(result.keySet()) - .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED); - assertThat(result.get(NOT_INDEXED)).isEmpty(); - assertThat(result.get(APP_CERTIFICATE_INDEXED)).isEmpty(); - assertThat(result.get(PACKAGE_NAME_INDEXED).keySet()).containsExactly(SAMPLE_PACKAGE_NAME); - assertThat(result.get(PACKAGE_NAME_INDEXED).get(SAMPLE_PACKAGE_NAME)) - .containsExactly(RULE_WITH_PACKAGE_NAME); - } - - @Test - public void getIndexType_ruleContainingAppCertificateFormula() { - List<Rule> ruleList = new ArrayList(); - ruleList.add(RULE_WITH_APP_CERTIFICATE); - - Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList); - - assertThat(result.keySet()) - .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED); - assertThat(result.get(NOT_INDEXED)).isEmpty(); - assertThat(result.get(PACKAGE_NAME_INDEXED)).isEmpty(); - assertThat(result.get(APP_CERTIFICATE_INDEXED).keySet()) - .containsExactly(SAMPLE_APP_CERTIFICATE); - assertThat(result.get(APP_CERTIFICATE_INDEXED).get(SAMPLE_APP_CERTIFICATE)) - .containsExactly(RULE_WITH_APP_CERTIFICATE); - } - - @Test - public void getIndexType_ruleWithUnindexedCompoundFormula() { - List<Rule> ruleList = new ArrayList(); - ruleList.add(RULE_WITH_INSTALLER_RESTRICTIONS); - - Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList); - - assertThat(result.keySet()) - .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED); - assertThat(result.get(PACKAGE_NAME_INDEXED)).isEmpty(); - assertThat(result.get(APP_CERTIFICATE_INDEXED)).isEmpty(); - assertThat(result.get(NOT_INDEXED).get("N/A")) - .containsExactly(RULE_WITH_INSTALLER_RESTRICTIONS); - } - - @Test - public void getIndexType_ruleContainingCompoundFormulaWithIntAndBoolean() { - List<Rule> ruleList = new ArrayList(); - ruleList.add(RULE_WITH_NONSTRING_RESTRICTIONS); - - Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList); - - assertThat(result.keySet()) - .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED); - assertThat(result.get(PACKAGE_NAME_INDEXED)).isEmpty(); - assertThat(result.get(APP_CERTIFICATE_INDEXED)).isEmpty(); - assertThat(result.get(NOT_INDEXED).get("N/A")) - .containsExactly(RULE_WITH_NONSTRING_RESTRICTIONS); - } - - @Test - public void getIndexType_negatedRuleContainingPackageNameFormula() { - Rule negatedRule = - new Rule( - new CompoundFormula( - CompoundFormula.NOT, - Arrays.asList( - new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - ATOMIC_FORMULA_WITH_PACKAGE_NAME, - ATOMIC_FORMULA_WITH_APP_CERTIFICATE)))), - Rule.DENY); - List<Rule> ruleList = new ArrayList(); - ruleList.add(negatedRule); - - Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList); - - assertThat(result.keySet()) - .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED); - assertThat(result.get(PACKAGE_NAME_INDEXED)).isEmpty(); - assertThat(result.get(APP_CERTIFICATE_INDEXED)).isEmpty(); - assertThat(result.get(NOT_INDEXED).get("N/A")).containsExactly(negatedRule); - } - - @Test - public void getIndexType_allRulesTogetherSplitCorrectly() { - Rule packageNameRuleA = getRuleWithPackageName("aaa"); - Rule packageNameRuleB = getRuleWithPackageName("bbb"); - Rule packageNameRuleC = getRuleWithPackageName("ccc"); - Rule certificateRule1 = getRuleWithAppCertificate("cert1"); - Rule certificateRule2 = getRuleWithAppCertificate("cert2"); - Rule certificateRule3 = getRuleWithAppCertificate("cert3"); - - List<Rule> ruleList = new ArrayList(); - ruleList.add(packageNameRuleB); - ruleList.add(packageNameRuleC); - ruleList.add(packageNameRuleA); - ruleList.add(certificateRule3); - ruleList.add(certificateRule2); - ruleList.add(certificateRule1); - ruleList.add(RULE_WITH_INSTALLER_RESTRICTIONS); - ruleList.add(RULE_WITH_NONSTRING_RESTRICTIONS); - - Map<Integer, Map<String, List<Rule>>> result = splitRulesIntoIndexBuckets(ruleList); - - assertThat(result.keySet()) - .containsExactly(NOT_INDEXED, PACKAGE_NAME_INDEXED, APP_CERTIFICATE_INDEXED); - - // We check asserts this way to ensure ordering based on package name. - assertThat(result.get(PACKAGE_NAME_INDEXED).keySet()).containsExactly("aaa", "bbb", "ccc"); - - // We check asserts this way to ensure ordering based on app certificate. - assertThat(result.get(APP_CERTIFICATE_INDEXED).keySet()).containsExactly("cert1", "cert2", - "cert3"); - - assertThat(result.get(NOT_INDEXED).get("N/A")) - .containsExactly(RULE_WITH_INSTALLER_RESTRICTIONS, - RULE_WITH_NONSTRING_RESTRICTIONS); - } - - private Rule getRuleWithPackageName(String packageName) { - return new Rule( - new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.PACKAGE_NAME, - packageName, - /* isHashedValue= */ false), - ATOMIC_FORMULA_WITH_INSTALLER_NAME)), - Rule.DENY); - } - - private Rule getRuleWithAppCertificate(String certificate) { - return new Rule( - new CompoundFormula( - CompoundFormula.AND, - Arrays.asList( - new AtomicFormula.StringAtomicFormula( - AtomicFormula.APP_CERTIFICATE, - certificate, - /* isHashedValue= */ false), - ATOMIC_FORMULA_WITH_INSTALLER_NAME)), - Rule.DENY); - } - - private IntegrityFormula getInvalidFormula() { - return new AtomicFormula(0) { - @Override - public int getTag() { - return INVALID_FORMULA_TAG; - } - - @Override - public boolean matches(AppInstallMetadata appInstallMetadata) { - return false; - } - - @Override - public boolean isAppCertificateFormula() { - return false; - } - - @Override - public boolean isAppCertificateLineageFormula() { - return false; - } - - @Override - public boolean isInstallerFormula() { - return false; - } - - @Override - public int hashCode() { - return super.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } - - @NonNull - @Override - protected Object clone() throws CloneNotSupportedException { - return super.clone(); - } - - @Override - public String toString() { - return super.toString(); - } - - @Override - protected void finalize() throws Throwable { - super.finalize(); - } - }; - } -} diff --git a/services/usb/OWNERS b/services/usb/OWNERS index d35dbb56437b..2dff392d4e34 100644 --- a/services/usb/OWNERS +++ b/services/usb/OWNERS @@ -1,9 +1,9 @@ -aprasath@google.com -kumarashishg@google.com -sarup@google.com anothermark@google.com +febinthattil@google.com +aprasath@google.com badhri@google.com elaurent@google.com albertccwang@google.com jameswei@google.com -howardyen@google.com
\ No newline at end of file +howardyen@google.com +kumarashishg@google.com
\ No newline at end of file diff --git a/tests/vcn/OWNERS b/tests/vcn/OWNERS index 2441e772468c..937699a92968 100644 --- a/tests/vcn/OWNERS +++ b/tests/vcn/OWNERS @@ -1,7 +1,6 @@ set noparent -benedictwong@google.com -ckesting@google.com evitayan@google.com -junyin@google.com nharold@google.com +benedictwong@google.com #{LAST_RESORT_SUGGESTION} +yangji@google.com #{LAST_RESORT_SUGGESTION} diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index 7e0bbc4b3e50..3828a71d7b28 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -70,6 +70,7 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.Uri; +import android.net.vcn.Flags; import android.net.vcn.IVcnStatusCallback; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; @@ -84,6 +85,7 @@ import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.os.test.TestLooper; +import android.platform.test.flag.junit.SetFlagsRule; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; @@ -102,6 +104,7 @@ import com.android.server.vcn.util.PersistableBundleUtils; import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -119,6 +122,8 @@ import java.util.UUID; @RunWith(AndroidJUnit4.class) @SmallTest public class VcnManagementServiceTest { + @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private static final String CONTEXT_ATTRIBUTION_TAG = "VCN"; private static final String TEST_PACKAGE_NAME = VcnManagementServiceTest.class.getPackage().getName(); @@ -288,6 +293,8 @@ public class VcnManagementServiceTest { doReturn(Collections.singleton(TRANSPORT_WIFI)) .when(mMockDeps) .getRestrictedTransports(any(), any(), any()); + + mSetFlagsRule.enableFlags(Flags.FLAG_FIX_CONFIG_GARBAGE_COLLECTION); } @@ -438,6 +445,14 @@ public class VcnManagementServiceTest { return subIds; }).when(snapshot).getAllSubIdsInGroup(any()); + doAnswer(invocation -> { + final Set<ParcelUuid> subGroups = new ArraySet<>(); + for (Entry<Integer, ParcelUuid> entry : subIdToGroupMap.entrySet()) { + subGroups.add(entry.getValue()); + } + return subGroups; + }).when(snapshot).getAllSubscriptionGroups(); + return snapshot; } @@ -1483,6 +1498,28 @@ public class VcnManagementServiceTest { } @Test + public void testGarbageCollectionKeepConfigUntilNewSnapshot() throws Exception { + setupActiveSubscription(TEST_UUID_2); + startAndGetVcnInstance(TEST_UUID_2); + + // Report loss of subscription from mSubMgr + doReturn(Collections.emptyList()).when(mSubMgr).getSubscriptionsInGroup(any()); + triggerSubscriptionTrackerCbAndGetSnapshot( + TEST_UUID_2, + Collections.singleton(TEST_UUID_2), + Collections.singletonMap(TEST_SUBSCRIPTION_ID, TEST_UUID_2)); + + assertTrue(mVcnMgmtSvc.getConfigs().containsKey(TEST_UUID_2)); + + // Report loss of subscription from snapshot + triggerSubscriptionTrackerCbAndGetSnapshot(null, Collections.emptySet()); + + mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS); + mTestLooper.dispatchAll(); + assertFalse(mVcnMgmtSvc.getConfigs().containsKey(TEST_UUID_2)); + } + + @Test public void testVcnCarrierConfigChangeUpdatesPolicyListener() throws Exception { setupActiveSubscription(TEST_UUID_2); diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java index e045f100e549..4c7b25aaa7c3 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -223,7 +223,6 @@ public class VcnGatewayConnectionTestBase { doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider(); doReturn(mFeatureFlags).when(mVcnContext).getFeatureFlags(); doReturn(true).when(mVcnContext).isFlagSafeModeTimeoutConfigEnabled(); - doReturn(true).when(mVcnContext).isFlagIpSecTransformStateEnabled(); doReturn(mUnderlyingNetworkController) .when(mDeps) diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java index bc7ff47d9a01..441b78035703 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java @@ -20,7 +20,6 @@ import static com.android.server.vcn.VcnTestUtils.setupSystemService; import static com.android.server.vcn.routeselection.UnderlyingNetworkControllerTest.getLinkPropertiesWithName; import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -127,8 +126,6 @@ public abstract class NetworkEvaluationTestBase { false /* isInTestMode */)); doNothing().when(mVcnContext).ensureRunningOnLooperThread(); - doReturn(true).when(mVcnContext).isFlagIpSecTransformStateEnabled(); - setupSystemService( mContext, mConnectivityManager, diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java index 6f31d8db070f..e540932d0e1f 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java @@ -226,7 +226,6 @@ public class UnderlyingNetworkControllerTest { private void resetVcnContext(VcnContext vcnContext) { reset(vcnContext); doNothing().when(vcnContext).ensureRunningOnLooperThread(); - doReturn(true).when(vcnContext).isFlagIpSecTransformStateEnabled(); } // Package private for use in NetworkPriorityClassifierTest diff --git a/tools/systemfeatures/Android.bp b/tools/systemfeatures/Android.bp index 590f7190881a..e6d0a3d4149f 100644 --- a/tools/systemfeatures/Android.bp +++ b/tools/systemfeatures/Android.bp @@ -58,6 +58,7 @@ java_test_host { "junit", "objenesis", "mockito", + "systemfeatures-gen-lib", "truth", ], } diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt index 196b5e7c02ab..1abe77fd3ceb 100644 --- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt +++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt @@ -71,7 +71,7 @@ object SystemFeaturesGenerator { println("Usage: SystemFeaturesGenerator <outputClassName> [options]") println(" Options:") println(" --readonly=true|false Whether to encode features as build-time constants") - println(" --feature=\$NAME:\$VER A feature+version pair, where \$VER can be:") + println(" --feature=\$NAME:\$VER A feature+version pair, where \$VER can be:") println(" * blank/empty == undefined (variable API)") println(" * valid int == enabled (constant API)") println(" * UNAVAILABLE == disabled (constant API)") @@ -89,6 +89,17 @@ object SystemFeaturesGenerator { /** Main entrypoint for build-time system feature codegen. */ @JvmStatic fun main(args: Array<String>) { + generate(args, System.out) + } + + /** + * Simple API entrypoint for build-time system feature codegen. + * + * Note: Typically this would be implemented in terms of a proper Builder-type input argument, + * but it's primarily used for testing as opposed to direct production usage. + */ + @JvmStatic + fun generate(args: Array<String>, output: Appendable) { if (args.size < 1) { usage() return @@ -155,7 +166,7 @@ object SystemFeaturesGenerator { .addFileComment("This file is auto-generated. DO NOT MODIFY.\n") .addFileComment("Args: ${args.joinToString(" \\\n ")}") .build() - .writeTo(System.out) + .writeTo(output) } /* @@ -171,12 +182,27 @@ object SystemFeaturesGenerator { return when (featureArgs.getOrNull(1)) { null, "" -> FeatureInfo(name, null, readonly = false) "UNAVAILABLE" -> FeatureInfo(name, null, readonly = true) - else -> FeatureInfo(name, featureArgs[1].toIntOrNull(), readonly = true) + else -> { + val featureVersion = + featureArgs[1].toIntOrNull() + ?: throw IllegalArgumentException( + "Invalid feature version input for $name: ${featureArgs[1]}" + ) + FeatureInfo(name, featureArgs[1].toInt(), readonly = true) + } } } private fun parseFeatureName(name: String): String = - if (name.startsWith("FEATURE_")) name else "FEATURE_$name" + when { + name.startsWith("android") -> + throw IllegalArgumentException( + "Invalid feature name input: \"android\"-namespaced features must be " + + "provided as PackageManager.FEATURE_* suffixes, not raw feature strings." + ) + name.startsWith("FEATURE_") -> name + else -> "FEATURE_$name" + } /* * Adds per-feature query methods to the class with the form: diff --git a/tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java new file mode 100644 index 000000000000..f8c585d60ef7 --- /dev/null +++ b/tools/systemfeatures/tests/src/SystemFeaturesGeneratorApiTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemfeatures; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.io.IOException; + +// Note: This is a very simple argument test to validate certain behaviors for +// invalid arguments. Correctness and validity is largely exercised by +// SystemFeaturesGeneratorTest. +@RunWith(JUnit4.class) +public class SystemFeaturesGeneratorApiTest { + + @Rule public final MockitoRule mockito = MockitoJUnit.rule(); + + @Mock private Appendable mOut; + + @Test + public void testEmpty() throws IOException { + final String[] args = new String[] {}; + // This should just print the commandline and return. + SystemFeaturesGenerator.generate(args, mOut); + verify(mOut, never()).append(any()); + } + + @Test + public void testBasic() throws IOException { + final String[] args = new String[] { + "com.foo.Features", + "--feature=TELEVISION:0", + }; + SystemFeaturesGenerator.generate(args, mOut); + verify(mOut, atLeastOnce()).append(any()); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidFeatureVersion() throws IOException { + final String[] args = new String[] { + "com.foo.Features", + "--feature=TELEVISION:blarg", + }; + SystemFeaturesGenerator.generate(args, mOut); + verify(mOut, never()).append(any()); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidFeatureNameFromAndroidNamespace() throws IOException { + final String[] args = new String[] { + "com.foo.Features", + "--feature=android.hardware.doesntexist:0", + }; + SystemFeaturesGenerator.generate(args, mOut); + verify(mOut, never()).append(any()); + } +} |