Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bd3ef4a7 authored by Artur Satayev's avatar Artur Satayev Committed by Android (Google) Code Review
Browse files

Merge "Ignore requests to start an already started system service."

parents f4d2b4d4 722533f2
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.os.Environment;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -47,6 +48,7 @@ import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@@ -94,6 +96,7 @@ public final class SystemServiceManager implements Dumpable {

    // Services that should receive lifecycle events.
    private List<SystemService> mServices;
    private Set<String> mServiceClassnames;

    private int mCurrentPhase = -1;

@@ -116,6 +119,7 @@ public final class SystemServiceManager implements Dumpable {
    SystemServiceManager(Context context) {
        mContext = context;
        mServices = new ArrayList<>();
        mServiceClassnames = new ArraySet<>();
        // Disable using the thread pool for low ram devices
        sUseLifecycleThreadPool = sUseLifecycleThreadPool
                && !ActivityManager.isLowRamDeviceStatic();
@@ -210,8 +214,17 @@ public final class SystemServiceManager implements Dumpable {
    }

    public void startService(@NonNull final SystemService service) {
        // Check if already started
        String className = service.getClass().getName();
        if (mServiceClassnames.contains(className)) {
            Slog.i(TAG, "Not starting an already started service " + className);
            return;
        }
        mServiceClassnames.add(className);

        // Register it.
        mServices.add(service);

        // Start it.
        long time = SystemClock.elapsedRealtime();
        try {
@@ -225,6 +238,7 @@ public final class SystemServiceManager implements Dumpable {

    /** Disallow starting new services after this call. */
    void sealStartedServices() {
        mServiceClassnames = Collections.emptySet();
        mServices = Collections.unmodifiableList(mServices);
    }

+0 −1
Original line number Diff line number Diff line
@@ -3053,7 +3053,6 @@ public final class SystemServer implements Dumpable {
    private void startApexServices(@NonNull TimingsTraceAndSlog t) {
        t.traceBegin("startApexServices");
        Map<String, String> services = ApexManager.getInstance().getApexSystemServices();
        // TODO(satayev): filter out already started services
        // TODO(satayev): introduce android:order for services coming the same apexes
        for (String name : new TreeSet<>(services.keySet())) {
            String jarPath = services.get(name);
+34 −5
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.test.AndroidTestCase;
import org.junit.Test;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;


/**
@@ -32,24 +33,52 @@ public class SystemServiceManagerTest extends AndroidTestCase {

    private static final String TAG = "SystemServiceManagerTest";

    private final SystemServiceManager mSystemServiceManager =
            new SystemServiceManager(getContext());

    @Test
    public void testSealStartedServices() throws Exception {
        SystemServiceManager manager = new SystemServiceManager(getContext());
        // must be effectively final, since it's changed from inner class below
        AtomicBoolean serviceStarted = new AtomicBoolean(false);
        SystemService service = new SystemService(getContext()) {
        SystemService service1 = new SystemService(getContext()) {
            @Override
            public void onStart() {
                serviceStarted.set(true);
            }
        };
        SystemService service2 = new SystemService(getContext()) {
            @Override
            public void onStart() {
                throw new IllegalStateException("Second service must not be called");
            }
        };

        // started services have their #onStart methods called
        manager.startService(service);
        mSystemServiceManager.startService(service1);
        assertTrue(serviceStarted.get());

        // however, after locking started services, it is not possible to start a new service
        manager.sealStartedServices();
        assertThrows(UnsupportedOperationException.class, () -> manager.startService(service));
        mSystemServiceManager.sealStartedServices();
        assertThrows(UnsupportedOperationException.class,
                () -> mSystemServiceManager.startService(service2));
    }

    @Test
    public void testDuplicateServices() throws Exception {
        AtomicInteger counter = new AtomicInteger(0);
        SystemService service = new SystemService(getContext()) {
            @Override
            public void onStart() {
                counter.incrementAndGet();
            }
        };

        mSystemServiceManager.startService(service);
        assertEquals(1, counter.get());

        // manager does not start the same service twice
        mSystemServiceManager.startService(service);
        assertEquals(1, counter.get());
    }

}