Revert "Revert "Revert "Revert "Introduce initOrder for apex-system-services.""""

This reverts commit 00029af7e01f7911648b819af154369b04f1c3c2.

Reason for revert: broken test is unrelated and is still red

Change-Id: I1784b76b3c5afc922881334370e0df95e51dc280
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 3a2fb6e..cb40e86 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2839,6 +2839,14 @@
         <attr name="path" />
         <attr name="minSdkVersion" />
         <attr name="maxSdkVersion" />
+        <!-- The order in which the apex system services are initiated. When there are dependencies
+        among apex system services, setting this attribute for each of them ensures that they are
+        created in the order required by those dependencies. The apex-system-services that are
+        started manually within SystemServer ignore the initOrder and are not considered for
+        automatic starting of the other services.
+        The value is a simple integer, with higher number being initialized first. If not specified,
+        the default order is 0. -->
+        <attr name="initOrder" format="integer" />
     </declare-styleable>
 
     <!-- The <code>receiver</code> tag declares an
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 2e9ad50..2d87099 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -32,9 +32,6 @@
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.SigningDetails;
-import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.pkg.component.ParsedApexSystemService;
 import android.content.pm.parsing.result.ParseResult;
 import android.content.pm.parsing.result.ParseTypeImpl;
 import android.os.Binder;
@@ -59,6 +56,9 @@
 import com.android.server.pm.parsing.PackageParser2;
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.pkg.component.ParsedApexSystemService;
+import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
 import com.android.server.utils.TimingsTraceAndSlog;
 
 import com.google.android.collect.Lists;
@@ -414,9 +414,11 @@
             throws PackageManagerException;
 
     /**
-     * Get a map of system services defined in an apex mapped to the jar files they reside in.
+     * Get a list of apex system services implemented in an apex.
+     *
+     * <p>The list is sorted by initOrder for consistency.
      */
-    public abstract Map<String, String> getApexSystemServices();
+    public abstract List<ApexSystemServiceInfo> getApexSystemServices();
 
     /**
      * Dumps various state information to the provided {@link PrintWriter} object.
@@ -449,7 +451,7 @@
          * Map of all apex system services to the jar files they are contained in.
          */
         @GuardedBy("mLock")
-        private Map<String, String> mApexSystemServices = new ArrayMap<>();
+        private List<ApexSystemServiceInfo> mApexSystemServices = new ArrayList<>();
 
         /**
          * Contains the list of {@code packageName}s of apks-in-apex for given
@@ -605,14 +607,19 @@
                         }
 
                         String name = service.getName();
-                        if (mApexSystemServices.containsKey(name)) {
-                            throw new IllegalStateException(String.format(
-                                    "Duplicate apex-system-service %s from %s, %s",
-                                    name, mApexSystemServices.get(name), service.getJarPath()));
+                        for (ApexSystemServiceInfo info : mApexSystemServices) {
+                            if (info.getName().equals(name)) {
+                                throw new IllegalStateException(String.format(
+                                        "Duplicate apex-system-service %s from %s, %s",
+                                        name, info.mJarPath, service.getJarPath()));
+                            }
                         }
 
-                        mApexSystemServices.put(name, service.getJarPath());
+                        ApexSystemServiceInfo info = new ApexSystemServiceInfo(
+                                service.getName(), service.getJarPath(), service.getInitOrder());
+                        mApexSystemServices.add(info);
                     }
+                    Collections.sort(mApexSystemServices);
                     mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName);
                     if (ai.isActive) {
                         if (activePackagesSet.contains(packageInfo.packageName)) {
@@ -1133,7 +1140,7 @@
         }
 
         @Override
-        public Map<String, String> getApexSystemServices() {
+        public List<ApexSystemServiceInfo> getApexSystemServices() {
             synchronized (mLock) {
                 Preconditions.checkState(mApexSystemServices != null,
                         "APEX packages have not been scanned");
@@ -1423,10 +1430,10 @@
         }
 
         @Override
-        public Map<String, String> getApexSystemServices() {
+        public List<ApexSystemServiceInfo> getApexSystemServices() {
             // TODO(satayev): we can't really support flattened apex use case, and need to migrate
             // the manifest entries into system's manifest asap.
-            return Collections.emptyMap();
+            return Collections.emptyList();
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/ApexSystemServiceInfo.java b/services/core/java/com/android/server/pm/ApexSystemServiceInfo.java
new file mode 100644
index 0000000..f75ba6d
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ApexSystemServiceInfo.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 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.pm;
+
+import android.annotation.Nullable;
+
+/**
+ * A helper class that contains information about apex-system-service to be used within system
+ * server process.
+ */
+public final class ApexSystemServiceInfo implements Comparable<ApexSystemServiceInfo> {
+
+    final String mName;
+    @Nullable
+    final String mJarPath;
+    final int mInitOrder;
+
+    public ApexSystemServiceInfo(String name, String jarPath, int initOrder) {
+        this.mName = name;
+        this.mJarPath = jarPath;
+        this.mInitOrder = initOrder;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public String getJarPath() {
+        return mJarPath;
+    }
+
+    public int getInitOrder() {
+        return mInitOrder;
+    }
+
+    @Override
+    public int compareTo(ApexSystemServiceInfo other) {
+        if (mInitOrder == other.mInitOrder) {
+            return mName.compareTo(other.mName);
+        }
+        // higher initOrder values take precedence
+        return -Integer.compare(mInitOrder, other.mInitOrder);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java
index 586d2c4..cf478b1 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java
@@ -34,4 +34,7 @@
 
     @Nullable
     String getMaxSdkVersion();
+
+    int getInitOrder();
+
 }
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
index 1e427d0..167aba3 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
@@ -48,18 +48,18 @@
     @Nullable
     private String maxSdkVersion;
 
+    private int initOrder;
+
     public ParsedApexSystemServiceImpl() {
     }
 
-
-
     // Code below generated by codegen v1.0.23.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
     //
     // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
     //   Settings > Editor > Code Style > Formatter Control
@@ -71,13 +71,15 @@
             @NonNull String name,
             @Nullable String jarPath,
             @Nullable String minSdkVersion,
-            @Nullable String maxSdkVersion) {
+            @Nullable String maxSdkVersion,
+            int initOrder) {
         this.name = name;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, name);
         this.jarPath = jarPath;
         this.minSdkVersion = minSdkVersion;
         this.maxSdkVersion = maxSdkVersion;
+        this.initOrder = initOrder;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -103,6 +105,11 @@
     }
 
     @DataClass.Generated.Member
+    public int getInitOrder() {
+        return initOrder;
+    }
+
+    @DataClass.Generated.Member
     public @NonNull ParsedApexSystemServiceImpl setName(@NonNull String value) {
         name = value;
         com.android.internal.util.AnnotationValidations.validate(
@@ -129,6 +136,12 @@
     }
 
     @DataClass.Generated.Member
+    public @NonNull ParsedApexSystemServiceImpl setInitOrder( int value) {
+        initOrder = value;
+        return this;
+    }
+
+    @DataClass.Generated.Member
     static Parcelling<String> sParcellingForName =
             Parcelling.Cache.get(
                     Parcelling.BuiltIn.ForInternedString.class);
@@ -187,6 +200,7 @@
         sParcellingForJarPath.parcel(jarPath, dest, flags);
         sParcellingForMinSdkVersion.parcel(minSdkVersion, dest, flags);
         sParcellingForMaxSdkVersion.parcel(maxSdkVersion, dest, flags);
+        dest.writeInt(initOrder);
     }
 
     @Override
@@ -205,6 +219,7 @@
         String _jarPath = sParcellingForJarPath.unparcel(in);
         String _minSdkVersion = sParcellingForMinSdkVersion.unparcel(in);
         String _maxSdkVersion = sParcellingForMaxSdkVersion.unparcel(in);
+        int _initOrder = in.readInt();
 
         this.name = _name;
         com.android.internal.util.AnnotationValidations.validate(
@@ -212,6 +227,7 @@
         this.jarPath = _jarPath;
         this.minSdkVersion = _minSdkVersion;
         this.maxSdkVersion = _maxSdkVersion;
+        this.initOrder = _initOrder;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -231,10 +247,10 @@
     };
 
     @DataClass.Generated(
-            time = 1641431950080L,
+            time = 1643723578605L,
             codegenVersion = "1.0.23",
-            sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java",
-            inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedApexSystemService, android.os.Parcelable]\[email protected](genGetters=true, genAidl=false, genSetters=true, genParcelable=true)")
+            sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java",
+            inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nprivate  int initOrder\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [com.android.server.pm.pkg.component.ParsedApexSystemService, android.os.Parcelable]\[email protected](genGetters=true, genAidl=false, genSetters=true, genParcelable=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java
index 38a6f5a35..ed9aa2e 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java
@@ -53,10 +53,13 @@
                     R.styleable.AndroidManifestApexSystemService_minSdkVersion);
             String maxSdkVersion = sa.getString(
                     R.styleable.AndroidManifestApexSystemService_maxSdkVersion);
+            int initOrder = sa.getInt(R.styleable.AndroidManifestApexSystemService_initOrder, 0);
 
             systemService.setName(className)
                     .setMinSdkVersion(minSdkVersion)
-                    .setMaxSdkVersion(maxSdkVersion);
+                    .setMaxSdkVersion(maxSdkVersion)
+                    .setInitOrder(initOrder);
+
             if (!TextUtils.isEmpty(jarPath)) {
                 systemService.setJarPath(jarPath);
             }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c9aeabd..9be481a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -154,6 +154,7 @@
 import com.android.server.os.SchedulingPolicyService;
 import com.android.server.people.PeopleService;
 import com.android.server.pm.ApexManager;
+import com.android.server.pm.ApexSystemServiceInfo;
 import com.android.server.pm.CrossProfileAppsService;
 import com.android.server.pm.DataLoaderManagerService;
 import com.android.server.pm.DynamicCodeLoggingService;
@@ -224,8 +225,8 @@
 import java.util.Arrays;
 import java.util.Date;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.Locale;
-import java.util.Map;
 import java.util.Timer;
 import java.util.TreeSet;
 import java.util.concurrent.CountDownLatch;
@@ -1470,7 +1471,7 @@
             // TelecomLoader hooks into classes with defined HFP logic,
             // so check for either telephony or microphone.
             if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE) ||
-                mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+                    mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
                 t.traceBegin("StartTelecomLoaderService");
                 mSystemServiceManager.startService(TelecomLoaderService.class);
                 t.traceEnd();
@@ -1478,7 +1479,7 @@
 
             t.traceBegin("StartTelephonyRegistry");
             telephonyRegistry = new TelephonyRegistry(
-                context, new TelephonyRegistry.ConfigurationProvider());
+                    context, new TelephonyRegistry.ConfigurationProvider());
             ServiceManager.addService("telephony.registry", telephonyRegistry);
             t.traceEnd();
 
@@ -3008,7 +3009,9 @@
             t.traceEnd();
             t.traceBegin("MakeTelephonyRegistryReady");
             try {
-                if (telephonyRegistryF != null) telephonyRegistryF.systemRunning();
+                if (telephonyRegistryF != null) {
+                    telephonyRegistryF.systemRunning();
+                }
             } catch (Throwable e) {
                 reportWtf("Notifying TelephonyRegistry running", e);
             }
@@ -3073,10 +3076,12 @@
      */
     private void startApexServices(@NonNull TimingsTraceAndSlog t) {
         t.traceBegin("startApexServices");
-        Map<String, String> services = ApexManager.getInstance().getApexSystemServices();
-        // TODO(satayev): introduce android:order for services coming the same apexes
-        for (String name : new TreeSet<>(services.keySet())) {
-            String jarPath = services.get(name);
+        // TODO(b/192880996): get the list from "android" package, once the manifest entries
+        // are migrated to system manifest.
+        List<ApexSystemServiceInfo> services = ApexManager.getInstance().getApexSystemServices();
+        for (ApexSystemServiceInfo info : services) {
+            String name = info.getName();
+            String jarPath = info.getJarPath();
             t.traceBegin("starting " + name);
             if (TextUtils.isEmpty(jarPath)) {
                 mSystemServiceManager.startService(name);
diff --git a/services/tests/apexsystemservices/apexes/test_com.android.server/Android.bp b/services/tests/apexsystemservices/apexes/test_com.android.server/Android.bp
index 16d6241..0a9b7b1 100644
--- a/services/tests/apexsystemservices/apexes/test_com.android.server/Android.bp
+++ b/services/tests/apexsystemservices/apexes/test_com.android.server/Android.bp
@@ -32,7 +32,7 @@
     name: "test_com.android.server",
     manifest: "manifest.json",
     androidManifest: "AndroidManifest.xml",
-    java_libs: ["FakeApexSystemService"],
+    java_libs: ["FakeApexSystemServices"],
     file_contexts: ":apex.test-file_contexts",
     key: "test_com.android.server.key",
     updatable: false,
diff --git a/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml b/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml
index eb741ca..6bec284 100644
--- a/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml
+++ b/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml
@@ -21,21 +21,29 @@
     <application android:hasCode="false" android:testOnly="true">
         <apex-system-service
             android:name="com.android.server.testing.FakeApexSystemService"
-            android:path="/apex/test_com.android.server/javalib/FakeApexSystemService.jar"
-            android:minSdkVersion="30"/>
+            android:path="/apex/test_com.android.server/javalib/FakeApexSystemServices.jar"
+            android:minSdkVersion="30"
+        />
+
+        <apex-system-service
+            android:name="com.android.server.testing.FakeApexSystemService2"
+            android:path="/apex/test_com.android.server/javalib/FakeApexSystemServices.jar"
+            android:minSdkVersion="30"
+            android:initOrder="1"
+        />
 
         <!-- Always inactive system service, since maxSdkVersion is low -->
         <apex-system-service
-            android:name="com.android.apex.test.OldApexSystemService"
-            android:path="/apex/com.android.apex.test/javalib/fake.jar"
+            android:name="com.android.server.testing.OldApexSystemService"
+            android:path="/apex/test_com.android.server/javalib/fake.jar"
             android:minSdkVersion="1"
             android:maxSdkVersion="1"
         />
 
         <!-- Always inactive system service, since minSdkVersion is high -->
         <apex-system-service
-            android:name="com.android.apex.test.NewApexSystemService"
-            android:path="/apex/com.android.apex.test/javalib/fake.jar"
+            android:name="com.android.server.testing.NewApexSystemService"
+            android:path="/apex/test_com.android.server/javalib/fake.jar"
             android:minSdkVersion="999999"
         />
     </application>
diff --git a/services/tests/apexsystemservices/service/Android.bp b/services/tests/apexsystemservices/services/Android.bp
similarity index 94%
rename from services/tests/apexsystemservices/service/Android.bp
rename to services/tests/apexsystemservices/services/Android.bp
index 9d04f39..477ea4c 100644
--- a/services/tests/apexsystemservices/service/Android.bp
+++ b/services/tests/apexsystemservices/services/Android.bp
@@ -8,7 +8,7 @@
 }
 
 java_library {
-    name: "FakeApexSystemService",
+    name: "FakeApexSystemServices",
     srcs: ["**/*.java"],
     sdk_version: "system_server_current",
     libs: [
diff --git a/services/tests/apexsystemservices/service/src/com/android/server/testing/FakeApexSystemService.java b/services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService.java
similarity index 100%
rename from services/tests/apexsystemservices/service/src/com/android/server/testing/FakeApexSystemService.java
rename to services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService.java
diff --git a/services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService2.java b/services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService2.java
new file mode 100644
index 0000000..e83343b
--- /dev/null
+++ b/services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService2.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 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.testing;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.server.SystemService;
+
+/**
+ * A fake system service that just logs when it is started.
+ */
+public class FakeApexSystemService2 extends SystemService {
+
+    private static final String TAG = "FakeApexSystemService";
+
+    public FakeApexSystemService2(@NonNull Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onStart() {
+        Log.d(TAG, "FakeApexSystemService2 onStart");
+    }
+}
diff --git a/services/tests/apexsystemservices/src/com/android/server/ApexSystemServicesTestCases.java b/services/tests/apexsystemservices/src/com/android/server/ApexSystemServicesTestCases.java
index 2b453a9..10635a1 100644
--- a/services/tests/apexsystemservices/src/com/android/server/ApexSystemServicesTestCases.java
+++ b/services/tests/apexsystemservices/src/com/android/server/ApexSystemServicesTestCases.java
@@ -37,9 +37,15 @@
 import org.junit.rules.TemporaryFolder;
 import org.junit.runner.RunWith;
 
+import java.util.Objects;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
 @RunWith(DeviceJUnit4ClassRunner.class)
 public class ApexSystemServicesTestCases extends BaseHostJUnit4Test {
 
+    private static final int REBOOT_TIMEOUT = 1 * 60 * 1000;
+
     private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this);
     private final TemporaryFolder mTemporaryFolder = new TemporaryFolder();
     private final SystemPreparer mPreparer = new SystemPreparer(mTemporaryFolder, this::getDevice);
@@ -67,7 +73,7 @@
     }
 
     @Test
-    public void noApexSystemServerStartsWithoutApex() throws Exception {
+    public void testNoApexSystemServiceStartsWithoutApex() throws Exception {
         mPreparer.reboot();
 
         assertThat(getFakeApexSystemServiceLogcat())
@@ -75,20 +81,55 @@
     }
 
     @Test
-    public void apexSystemServerStarts() throws Exception {
+    public void testApexSystemServiceStarts() throws Exception {
         // Pre-install the apex
         String apex = "test_com.android.server.apex";
         mPreparer.pushResourceFile(apex, "/system/apex/" + apex);
         // Reboot activates the apex
         mPreparer.reboot();
 
+        mDevice.waitForBootComplete(REBOOT_TIMEOUT);
+
         assertThat(getFakeApexSystemServiceLogcat())
                 .contains("FakeApexSystemService onStart");
     }
 
+    @Test
+    public void testInitOrder() throws Exception {
+        // Pre-install the apex
+        String apex = "test_com.android.server.apex";
+        mPreparer.pushResourceFile(apex, "/system/apex/" + apex);
+        // Reboot activates the apex
+        mPreparer.reboot();
+
+        mDevice.waitForBootComplete(REBOOT_TIMEOUT);
+
+        assertThat(getFakeApexSystemServiceLogcat().lines()
+                .map(ApexSystemServicesTestCases::getDebugMessage)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList()))
+                .containsExactly(
+                        // Second service has a higher initOrder and must be started first
+                        "FakeApexSystemService2 onStart",
+                        "FakeApexSystemService onStart"
+                )
+                .inOrder();
+    }
+
     private String getFakeApexSystemServiceLogcat() throws DeviceNotAvailableException {
         return mDevice.executeAdbCommand("logcat", "-v", "brief", "-d", "FakeApexSystemService:D",
                 "*:S");
     }
 
+    private static final Pattern DEBUG_MESSAGE =
+            Pattern.compile("(FakeApexSystemService[0-9]* onStart)");
+
+    private static String getDebugMessage(String logcatLine) {
+        return DEBUG_MESSAGE.matcher(logcatLine)
+                .results()
+                .map(m -> m.group(1))
+                .findFirst()
+                .orElse(null);
+    }
+
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
index 7f7c716..2f5993d1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -61,7 +61,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.util.Map;
+import java.util.List;
 
 @SmallTest
 @Presubmit
@@ -136,9 +136,10 @@
         mApexManager.scanApexPackagesTraced(mPackageParser2,
                 ParallelPackageParser.makeExecutorService());
 
-        Map<String, String> services = mApexManager.getApexSystemServices();
+        List<ApexSystemServiceInfo> services = mApexManager.getApexSystemServices();
         assertThat(services).hasSize(1);
-        assertThat(services).containsKey("com.android.apex.test.ApexSystemService");
+        assertThat(services.stream().map(ApexSystemServiceInfo::getName).findFirst().orElse(null))
+                .matches("com.android.apex.test.ApexSystemService");
     }
 
     @Test