Support per-process useEmbeddedDex opt-in

Extend the useEmbeddedDex attribute to allow opt-in at the <process>
level. Note that if the parent application has opted in to
useEmbeddedDex, that will override any value set at the process level.

This configuration is useful for lighter weight processes that don't
rely heavily on the usual set of dex compilation optimizations critical
for the usual set of app CUJs (e.g., app startup).

Bug: 295870718
Test: atest android.appsecurity.cts.UseEmbeddedDexTest
Change-Id: Ic50ae2e1c568006cb3199013889a91fe38afc9d0
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index e6e835b..1f76d3b 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -68,6 +68,7 @@
     ":com.android.hardware.input-aconfig-java{.generated_srcjars}",
     ":com.android.input.flags-aconfig-java{.generated_srcjars}",
     ":com.android.internal.foldables.flags-aconfig-java{.generated_srcjars}",
+    ":com.android.internal.pm.pkg.component.flags-aconfig-java{.generated_srcjars}",
     ":com.android.media.flags.bettertogether-aconfig-java{.generated_srcjars}",
     ":com.android.media.flags.editing-aconfig-java{.generated_srcjars}",
     ":com.android.net.thread.flags-aconfig-java{.generated_srcjars}",
@@ -1137,3 +1138,22 @@
     aconfig_declarations: "android.app.wearable.flags-aconfig",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
+
+aconfig_declarations {
+    name: "com.android.internal.pm.pkg.component.flags-aconfig",
+    package: "com.android.internal.pm.pkg.component.flags",
+    srcs: ["core/java/com/android/internal/pm/pkg/component/flags/flags.aconfig"],
+}
+
+java_aconfig_library {
+    name: "com.android.internal.pm.pkg.component.flags-aconfig-java",
+    aconfig_declarations: "com.android.internal.pm.pkg.component.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
+java_aconfig_library {
+    name: "com.android.internal.pm.pkg.component.flags-aconfig-java-host",
+    aconfig_declarations: "com.android.internal.pm.pkg.component.flags-aconfig",
+    host_supported: true,
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/core/java/android/content/pm/ProcessInfo.java b/core/java/android/content/pm/ProcessInfo.java
index 632c0f5..f84b46d 100644
--- a/core/java/android/content/pm/ProcessInfo.java
+++ b/core/java/android/content/pm/ProcessInfo.java
@@ -18,10 +18,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.pm.ApplicationInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.text.TextUtils;
 import android.util.ArraySet;
 
 import com.android.internal.util.DataClass;
@@ -64,6 +62,12 @@
      */
     public @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized;
 
+    /**
+     * Enable use of embedded dex in the APK, rather than extracted or locally compiled variants.
+     * If false (default), the parent app's configuration determines behavior.
+     */
+    public boolean useEmbeddedDex;
+
     @Deprecated
     public ProcessInfo(@NonNull ProcessInfo orig) {
         this.name = orig.name;
@@ -71,11 +75,12 @@
         this.gwpAsanMode = orig.gwpAsanMode;
         this.memtagMode = orig.memtagMode;
         this.nativeHeapZeroInitialized = orig.nativeHeapZeroInitialized;
+        this.useEmbeddedDex = orig.useEmbeddedDex;
     }
 
 
 
-    // Code below generated by codegen v1.0.22.
+    // Code below generated by codegen v1.0.23.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -102,6 +107,9 @@
      *   disabled, or left unspecified.
      * @param nativeHeapZeroInitialized
      *   Enable automatic zero-initialization of native heap memory allocations.
+     * @param useEmbeddedDex
+     *   Enable use of embedded dex in the APK, rather than extracted or locally compiled variants.
+     *   If false (default), the parent app's configuration determines behavior.
      */
     @DataClass.Generated.Member
     public ProcessInfo(
@@ -109,7 +117,8 @@
             @Nullable ArraySet<String> deniedPermissions,
             @ApplicationInfo.GwpAsanMode int gwpAsanMode,
             @ApplicationInfo.MemtagMode int memtagMode,
-            @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized) {
+            @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized,
+            boolean useEmbeddedDex) {
         this.name = name;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, name);
@@ -123,6 +132,7 @@
         this.nativeHeapZeroInitialized = nativeHeapZeroInitialized;
         com.android.internal.util.AnnotationValidations.validate(
                 ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
+        this.useEmbeddedDex = useEmbeddedDex;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -145,6 +155,7 @@
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         byte flg = 0;
+        if (useEmbeddedDex) flg |= 0x20;
         if (deniedPermissions != null) flg |= 0x2;
         dest.writeByte(flg);
         dest.writeString(name);
@@ -166,6 +177,7 @@
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
         byte flg = in.readByte();
+        boolean _useEmbeddedDex = (flg & 0x20) != 0;
         String _name = in.readString();
         ArraySet<String> _deniedPermissions = sParcellingForDeniedPermissions.unparcel(in);
         int _gwpAsanMode = in.readInt();
@@ -185,6 +197,7 @@
         this.nativeHeapZeroInitialized = _nativeHeapZeroInitialized;
         com.android.internal.util.AnnotationValidations.validate(
                 ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
+        this.useEmbeddedDex = _useEmbeddedDex;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -204,10 +217,10 @@
     };
 
     @DataClass.Generated(
-            time = 1615850184524L,
-            codegenVersion = "1.0.22",
+            time = 1706177470784L,
+            codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/content/pm/ProcessInfo.java",
-            inputSignatures = "public @android.annotation.NonNull java.lang.String name\npublic @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringArraySet.class) android.util.ArraySet<java.lang.String> deniedPermissions\npublic @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\npublic @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\npublic @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\nclass ProcessInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
+            inputSignatures = "public @android.annotation.NonNull java.lang.String name\npublic @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringArraySet.class) android.util.ArraySet<java.lang.String> deniedPermissions\npublic @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\npublic @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\npublic @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic  boolean useEmbeddedDex\nclass ProcessInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=false, genParcelable=true, genAidl=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 4626679..69f9a7d 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -39,6 +39,7 @@
 import android.util.Pair;
 import android.util.Slog;
 
+import com.android.internal.pm.pkg.component.flags.Flags;
 import com.android.internal.util.ArrayUtils;
 
 import libcore.io.IoUtils;
@@ -89,6 +90,8 @@
     private static final String TAG_SDK_LIBRARY = "sdk-library";
     private static final int SDK_VERSION = Build.VERSION.SDK_INT;
     private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
+    private static final String TAG_PROCESSES = "processes";
+    private static final String TAG_PROCESS = "process";
 
     /**
      * Parse only lightweight details about the package at the given location.
@@ -518,6 +521,28 @@
                         case TAG_SDK_LIBRARY:
                             isSdkLibrary = true;
                             break;
+                        case TAG_PROCESSES:
+                            final int processesDepth = parser.getDepth();
+                            int processesType;
+                            while ((processesType = parser.next()) != XmlPullParser.END_DOCUMENT
+                                    && (processesType != XmlPullParser.END_TAG
+                                    || parser.getDepth() > processesDepth)) {
+                                if (processesType == XmlPullParser.END_TAG
+                                        || processesType == XmlPullParser.TEXT) {
+                                    continue;
+                                }
+
+                                if (parser.getDepth() != processesDepth + 1) {
+                                    // Search only under <processes>.
+                                    continue;
+                                }
+
+                                if (parser.getName().equals(TAG_PROCESS)
+                                        && Flags.enablePerProcessUseEmbeddedDexAttr()) {
+                                    useEmbeddedDex |= parser.getAttributeBooleanValue(
+                                            ANDROID_RES_NAMESPACE, "useEmbeddedDex", false);
+                                }
+                            }
                     }
                 }
             } else if (TAG_OVERLAY.equals(parser.getName())) {
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedProcess.java b/core/java/com/android/internal/pm/pkg/component/ParsedProcess.java
index e5247f9..852ed1c 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedProcess.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedProcess.java
@@ -51,4 +51,6 @@
 
     @ApplicationInfo.NativeHeapZeroInitialized
     int getNativeHeapZeroInitialized();
+
+    boolean isUseEmbeddedDex();
 }
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedProcessImpl.java b/core/java/com/android/internal/pm/pkg/component/ParsedProcessImpl.java
index 212fb86..ff9b11a 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedProcessImpl.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedProcessImpl.java
@@ -54,6 +54,8 @@
     @ApplicationInfo.NativeHeapZeroInitialized
     private int nativeHeapZeroInitialized = ApplicationInfo.ZEROINIT_DEFAULT;
 
+    private boolean useEmbeddedDex;
+
     public ParsedProcessImpl() {
     }
 
@@ -65,6 +67,7 @@
         gwpAsanMode = other.getGwpAsanMode();
         memtagMode = other.getMemtagMode();
         nativeHeapZeroInitialized = other.getNativeHeapZeroInitialized();
+        useEmbeddedDex = other.isUseEmbeddedDex();
     }
 
     public void addStateFrom(@NonNull ParsedProcess other) {
@@ -72,6 +75,7 @@
         gwpAsanMode = other.getGwpAsanMode();
         memtagMode = other.getMemtagMode();
         nativeHeapZeroInitialized = other.getNativeHeapZeroInitialized();
+        useEmbeddedDex = other.isUseEmbeddedDex();
 
         final ArrayMap<String, String> oacn = other.getAppClassNamesByPackage();
         for (int i = 0; i < oacn.size(); i++) {
@@ -115,7 +119,8 @@
             @NonNull Set<String> deniedPermissions,
             @ApplicationInfo.GwpAsanMode int gwpAsanMode,
             @ApplicationInfo.MemtagMode int memtagMode,
-            @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized) {
+            @ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized,
+            boolean useEmbeddedDex) {
         this.name = name;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, name);
@@ -134,6 +139,7 @@
         this.nativeHeapZeroInitialized = nativeHeapZeroInitialized;
         com.android.internal.util.AnnotationValidations.validate(
                 ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
+        this.useEmbeddedDex = useEmbeddedDex;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -172,6 +178,11 @@
     }
 
     @DataClass.Generated.Member
+    public boolean isUseEmbeddedDex() {
+        return useEmbeddedDex;
+    }
+
+    @DataClass.Generated.Member
     public @NonNull ParsedProcessImpl setName(@NonNull String value) {
         name = value;
         com.android.internal.util.AnnotationValidations.validate(
@@ -223,6 +234,12 @@
     }
 
     @DataClass.Generated.Member
+    public @NonNull ParsedProcessImpl setUseEmbeddedDex( boolean value) {
+        useEmbeddedDex = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
     static Parcelling<Set<String>> sParcellingForDeniedPermissions =
             Parcelling.Cache.get(
                     Parcelling.BuiltIn.ForInternedStringSet.class);
@@ -239,6 +256,9 @@
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
+        byte flg = 0;
+        if (useEmbeddedDex) flg |= 0x40;
+        dest.writeByte(flg);
         dest.writeString(name);
         dest.writeMap(appClassNamesByPackage);
         sParcellingForDeniedPermissions.parcel(deniedPermissions, dest, flags);
@@ -258,6 +278,8 @@
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
+        byte flg = in.readByte();
+        boolean _useEmbeddedDex = (flg & 0x40) != 0;
         String _name = in.readString();
         ArrayMap<String,String> _appClassNamesByPackage = new ArrayMap();
         in.readMap(_appClassNamesByPackage, String.class.getClassLoader());
@@ -284,6 +306,7 @@
         this.nativeHeapZeroInitialized = _nativeHeapZeroInitialized;
         com.android.internal.util.AnnotationValidations.validate(
                 ApplicationInfo.NativeHeapZeroInitialized.class, null, nativeHeapZeroInitialized);
+        this.useEmbeddedDex = _useEmbeddedDex;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -303,10 +326,10 @@
     };
 
     @DataClass.Generated(
-            time = 1701445656489L,
+            time = 1706177189475L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/com/android/internal/pm/pkg/component/ParsedProcessImpl.java",
-            inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.String> appClassNamesByPackage\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\npublic  void addStateFrom(com.android.internal.pm.pkg.component.ParsedProcess)\npublic  void putAppClassNameForPackage(java.lang.String,java.lang.String)\nclass ParsedProcessImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedProcess, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)")
+            inputSignatures = "private @android.annotation.NonNull java.lang.String name\nprivate @android.annotation.NonNull android.util.ArrayMap<java.lang.String,java.lang.String> appClassNamesByPackage\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedStringSet.class) java.util.Set<java.lang.String> deniedPermissions\nprivate @android.content.pm.ApplicationInfo.GwpAsanMode int gwpAsanMode\nprivate @android.content.pm.ApplicationInfo.MemtagMode int memtagMode\nprivate @android.content.pm.ApplicationInfo.NativeHeapZeroInitialized int nativeHeapZeroInitialized\nprivate  boolean useEmbeddedDex\npublic  void addStateFrom(com.android.internal.pm.pkg.component.ParsedProcess)\npublic  void putAppClassNameForPackage(java.lang.String,java.lang.String)\nclass ParsedProcessImpl extends java.lang.Object implements [com.android.internal.pm.pkg.component.ParsedProcess, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genParcelable=true, genAidl=false, genBuilder=false)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/com/android/internal/pm/pkg/component/ParsedProcessUtils.java b/core/java/com/android/internal/pm/pkg/component/ParsedProcessUtils.java
index 3b2056e..dd58815f 100644
--- a/core/java/com/android/internal/pm/pkg/component/ParsedProcessUtils.java
+++ b/core/java/com/android/internal/pm/pkg/component/ParsedProcessUtils.java
@@ -27,6 +27,7 @@
 import android.util.ArraySet;
 
 import com.android.internal.R;
+import com.android.internal.pm.pkg.component.flags.Flags;
 import com.android.internal.pm.pkg.parsing.ParsingPackage;
 import com.android.internal.pm.pkg.parsing.ParsingUtils;
 import com.android.internal.util.CollectionUtils;
@@ -111,6 +112,12 @@
                 proc.setNativeHeapZeroInitialized(
                         v ? ApplicationInfo.ZEROINIT_ENABLED : ApplicationInfo.ZEROINIT_DISABLED);
             }
+            if (Flags.enablePerProcessUseEmbeddedDexAttr()) {
+                proc.setUseEmbeddedDex(
+                        sa.getBoolean(R.styleable.AndroidManifestProcess_useEmbeddedDex, false));
+            } else {
+                proc.setUseEmbeddedDex(false);
+            }
         } finally {
             sa.recycle();
         }
diff --git a/core/java/com/android/internal/pm/pkg/component/flags/flags.aconfig b/core/java/com/android/internal/pm/pkg/component/flags/flags.aconfig
new file mode 100644
index 0000000..ea9abdb
--- /dev/null
+++ b/core/java/com/android/internal/pm/pkg/component/flags/flags.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.internal.pm.pkg.component.flags"
+
+flag {
+    name: "enable_per_process_use_embedded_dex_attr"
+    namespace: "machine_learning"
+    description: "This flag enables support for parsing per-process useEmbeddedDex attribute."
+    is_fixed_read_only: true
+    bug: "295870718"
+}
\ No newline at end of file
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 6884fc0..65c4d9f 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1273,11 +1273,17 @@
          null to indicate no split types are offered. -->
     <attr name="splitTypes" format="string" />
 
-    <!-- Flag to specify if this app wants to run the dex within its APK but not extracted or
-         locally compiled variants. This keeps the dex code protected by the APK signature. Such
-         apps will always run in JIT mode (same when they are first installed), and the system will
-         never generate ahead-of-time compiled code for them. Depending on the app's workload,
-         there may be some run time performance change, noteably the cold start time. -->
+    <!-- Flag to specify if this app (or process) wants to run the dex within its APK but not
+         extracted or locally compiled variants. This keeps the dex code protected by the APK
+         signature. Such apps (or processes) will always run in JIT mode (same when they are first
+         installed). If enabled at the app level, the system will never generate ahead-of-time
+         compiled code for the app. Depending on the app's workload, there may be some run time
+         performance change, noteably the cold start time.
+
+         <p>This attribute can be applied to either
+         {@link android.R.styleable#AndroidManifestProcess process} or
+         {@link android.R.styleable#AndroidManifestApplication application} tags. If enabled at the
+         app level, any process level attribute is effectively ignored.  -->
     <attr name="useEmbeddedDex" format="boolean" />
 
     <!-- Extra options for an activity's UI. Applies to either the {@code <activity>} or
@@ -2793,6 +2799,7 @@
         <attr name="gwpAsanMode" />
         <attr name="memtagMode" />
         <attr name="nativeHeapZeroInitialized" />
+        <attr name="useEmbeddedDex" />
     </declare-styleable>
 
     <!-- The <code>deny-permission</code> tag specifies that a permission is to be denied
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 10cd6e5..d110349 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1950,7 +1950,8 @@
                 mService.mNativeDebuggingApp = null;
             }
 
-            if (app.info.isEmbeddedDexUsed()) {
+            if (app.info.isEmbeddedDexUsed()
+                    || (app.processInfo != null && app.processInfo.useEmbeddedDex)) {
                 runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
             }
 
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 6ed2d31..a9e5a54 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -831,7 +831,7 @@
             retProcs.put(proc.getName(),
                     new ProcessInfo(proc.getName(), new ArraySet<>(proc.getDeniedPermissions()),
                             proc.getGwpAsanMode(), proc.getMemtagMode(),
-                            proc.getNativeHeapZeroInitialized()));
+                            proc.getNativeHeapZeroInitialized(), proc.isUseEmbeddedDex()));
         }
         return retProcs;
     }
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
index 93bdeae..d538f25 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedProcessTest.kt
@@ -40,6 +40,7 @@
         ParsedProcess::getGwpAsanMode,
         ParsedProcess::getMemtagMode,
         ParsedProcess::getNativeHeapZeroInitialized,
+        ParsedProcess::isUseEmbeddedDex,
     )
 
     override fun extraParams() = listOf(