diff options
14 files changed, 234 insertions, 43 deletions
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 3a2fb6e70ba8..cb40e86f1535 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 2e9ad50f23f6..2d870997bed7 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.PackageInfo; 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.modules.utils.build.UnboundedSdkLevel; 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 @@ public abstract class ApexManager { 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 @@ public abstract class ApexManager { * 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 @@ public abstract class ApexManager { } 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 @@ public abstract class ApexManager { } @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 @@ public abstract class ApexManager { } @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 000000000000..f75ba6d165c9 --- /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 586d2c48d27d..cf478b1da2e4 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 @@ public interface ParsedApexSystemService { @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 1e427d015cc1..167aba301f35 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 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par @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 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par @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 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par } @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 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par } @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 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par sParcellingForJarPath.parcel(jarPath, dest, flags); sParcellingForMinSdkVersion.parcel(minSdkVersion, dest, flags); sParcellingForMaxSdkVersion.parcel(maxSdkVersion, dest, flags); + dest.writeInt(initOrder); } @Override @@ -205,6 +219,7 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par 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 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par 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 @@ public class ParsedApexSystemServiceImpl implements ParsedApexSystemService, Par }; @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]\n@com.android.internal.util.DataClass(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]\n@com.android.internal.util.DataClass(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 38a6f5a356e7..ed9aa2e6860a 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 @@ public class ParsedApexSystemServiceUtils { 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 d0c861fa912e..f4fae2f81718 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.NativeTombstoneManagerService; 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.text.SimpleDateFormat; 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; @@ -1472,7 +1473,7 @@ public final class SystemServer implements Dumpable { // 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(); @@ -1480,7 +1481,7 @@ public final class SystemServer implements Dumpable { t.traceBegin("StartTelephonyRegistry"); telephonyRegistry = new TelephonyRegistry( - context, new TelephonyRegistry.ConfigurationProvider()); + context, new TelephonyRegistry.ConfigurationProvider()); ServiceManager.addService("telephony.registry", telephonyRegistry); t.traceEnd(); @@ -3017,7 +3018,9 @@ public final class SystemServer implements Dumpable { t.traceEnd(); t.traceBegin("MakeTelephonyRegistryReady"); try { - if (telephonyRegistryF != null) telephonyRegistryF.systemRunning(); + if (telephonyRegistryF != null) { + telephonyRegistryF.systemRunning(); + } } catch (Throwable e) { reportWtf("Notifying TelephonyRegistry running", e); } @@ -3082,10 +3085,12 @@ public final class SystemServer implements Dumpable { */ 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 16d624199d5a..0a9b7b1302cc 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 @@ apex_test { 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 eb741cad4ad9..6bec28463dc3 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 index 9d04f39f2237..477ea4cdad37 100644 --- a/services/tests/apexsystemservices/service/Android.bp +++ b/services/tests/apexsystemservices/services/Android.bp @@ -8,7 +8,7 @@ package { } 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 index 4947c3455cc7..4947c3455cc7 100644 --- a/services/tests/apexsystemservices/service/src/com/android/server/testing/FakeApexSystemService.java +++ b/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 000000000000..e83343b9c996 --- /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 2b453a9265bb..10635a138eb9 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.RuleChain; 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 @@ public class ApexSystemServicesTestCases extends BaseHostJUnit4Test { } @Test - public void noApexSystemServerStartsWithoutApex() throws Exception { + public void testNoApexSystemServiceStartsWithoutApex() throws Exception { mPreparer.reboot(); assertThat(getFakeApexSystemServiceLogcat()) @@ -75,20 +81,55 @@ public class ApexSystemServicesTestCases extends BaseHostJUnit4Test { } @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 7f7c716bc1f0..2f5993d1d989 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.FileOutputStream; 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 @@ public class ApexManagerTest { 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 |