Merge "Clean up system service registration. (2nd try)"
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManagerFrameworkInitializer.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManagerFrameworkInitializer.java
index 6e6d6ae..56c419a 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManagerFrameworkInitializer.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManagerFrameworkInitializer.java
@@ -26,7 +26,7 @@
 public class BlobStoreManagerFrameworkInitializer {
     /** Register the BlobStoreManager wrapper class */
     public static void initialize() {
-        SystemServiceRegistry.registerCachedService(
+        SystemServiceRegistry.registerContextAwareService(
                 Context.BLOB_STORE_SERVICE, BlobStoreManager.class,
                 (context, service) ->
                         new BlobStoreManager(context, IBlobStoreManager.Stub.asInterface(service)));
diff --git a/apex/jobscheduler/framework/java/android/app/DeviceIdleFrameworkInitializer.java b/apex/jobscheduler/framework/java/android/app/DeviceIdleFrameworkInitializer.java
deleted file mode 100644
index c264531..0000000
--- a/apex/jobscheduler/framework/java/android/app/DeviceIdleFrameworkInitializer.java
+++ /dev/null
@@ -1,35 +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 android.app;
-
-import android.content.Context;
-import android.os.DeviceIdleManager;
-import android.os.IDeviceIdleController;
-
-/**
- * @hide
- */
-public class DeviceIdleFrameworkInitializer {
-    private static IDeviceIdleController sIDeviceIdleController;
-
-    public static void initialize() {
-        SystemServiceRegistry.registerCachedService(
-                Context.DEVICE_IDLE_CONTROLLER, DeviceIdleManager.class,
-                (context, b) -> new DeviceIdleManager(
-                        context, IDeviceIdleController.Stub.asInterface(b)));
-    }
-}
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java b/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java
index 175e5f2..f3ec5e5 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobSchedulerFrameworkInitializer.java
@@ -19,14 +19,34 @@
 import android.app.JobSchedulerImpl;
 import android.app.SystemServiceRegistry;
 import android.content.Context;
+import android.os.DeviceIdleManager;
+import android.os.IDeviceIdleController;
 
 /**
+ * Class holding initialization code for the job scheduler module.
+ *
  * @hide
  */
 public class JobSchedulerFrameworkInitializer {
-    public static void initialize() {
+    private JobSchedulerFrameworkInitializer() {
+    }
+
+    /**
+     * Called by {@link SystemServiceRegistry}'s static initializer and registers
+     * {@link JobScheduler} and other services to {@link Context}, so
+     * {@link Context#getSystemService} can return them.
+     *
+     * <p>If this is called from other places, it throws a {@link IllegalStateException).
+     *
+     * TODO Make it a system API
+     */
+    public static void registerServiceWrappers() {
         SystemServiceRegistry.registerStaticService(
                 Context.JOB_SCHEDULER_SERVICE, JobScheduler.class,
                 (b) -> new JobSchedulerImpl(IJobScheduler.Stub.asInterface(b)));
+        SystemServiceRegistry.registerContextAwareService(
+                Context.DEVICE_IDLE_CONTROLLER, DeviceIdleManager.class,
+                (context, b) -> new DeviceIdleManager(
+                        context, IDeviceIdleController.Stub.asInterface(b)));
     }
 }
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 4fb2196..33ea32b 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -18,6 +18,7 @@
 
 import android.accounts.AccountManager;
 import android.accounts.IAccountManager;
+import android.annotation.NonNull;
 import android.app.ContextImpl.ServiceInitializationState;
 import android.app.admin.DevicePolicyManager;
 import android.app.admin.IDevicePolicyManager;
@@ -194,10 +195,9 @@
 import com.android.internal.net.INetworkWatchlistManager;
 import com.android.internal.os.IDropBoxManagerService;
 import com.android.internal.policy.PhoneLayoutInflater;
+import com.android.internal.util.Preconditions;
 
 import java.util.Map;
-import java.util.function.BiFunction;
-import java.util.function.Function;
 
 /**
  * Manages all of the system services that can be returned by {@link Context#getSystemService}.
@@ -216,6 +216,8 @@
             new ArrayMap<String, ServiceFetcher<?>>();
     private static int sServiceCacheSize;
 
+    private static volatile boolean sInitializing;
+
     // Not instantiable.
     private SystemServiceRegistry() { }
 
@@ -1292,14 +1294,28 @@
                     }});
         //CHECKSTYLE:ON IndentationCheck
 
-        JobSchedulerFrameworkInitializer.initialize();
-        DeviceIdleFrameworkInitializer.initialize();
+        sInitializing = true;
+        try {
+            // Note: the following functions need to be @SystemApis, once they become mainline
+            // modules.
 
-        BlobStoreManagerFrameworkInitializer.initialize();
+            JobSchedulerFrameworkInitializer.registerServiceWrappers();
+            BlobStoreManagerFrameworkInitializer.initialize();
+        } finally {
+            // If any of the above code throws, we're in a pretty bad shape and the process
+            // will likely crash, but we'll reset it just in case there's an exception handler...
+            sInitializing = false;
+        }
     }
 
+    /** Throws {@link IllegalStateException} if not during a static initialization. */
+    private static void ensureInitializing(String methodName) {
+        Preconditions.checkState(sInitializing, "Internal error: " + methodName
+                + " can only be called during class initialization.");
+    }
     /**
      * Creates an array which is used to cache per-Context service instances.
+     * @hide
      */
     public static Object[] createServiceCache() {
         return new Object[sServiceCacheSize];
@@ -1307,6 +1323,7 @@
 
     /**
      * Gets a system service from a given context.
+     * @hide
      */
     public static Object getSystemService(ContextImpl ctx, String name) {
         ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
@@ -1315,6 +1332,7 @@
 
     /**
      * Gets the name of the system-level service that is represented by the specified class.
+     * @hide
      */
     public static String getSystemServiceName(Class<?> serviceClass) {
         return SYSTEM_SERVICE_NAMES.get(serviceClass);
@@ -1324,41 +1342,204 @@
      * Statically registers a system service with the context.
      * This method must be called during static initialization only.
      */
-    private static <T> void registerService(String serviceName, Class<T> serviceClass,
-            ServiceFetcher<T> serviceFetcher) {
+    private static <T> void registerService(@NonNull String serviceName,
+            @NonNull Class<T> serviceClass, @NonNull ServiceFetcher<T> serviceFetcher) {
         SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
         SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
     }
 
     /**
-     * APEX modules will use it to register their service wrapper.
+     * Callback interface used as a parameter to {@link #registerStaticService(
+     * String, Class, StaticServiceProducerNoBinder)}, which generates a service wrapper instance
+     * that's not tied to any context and does not take a service binder object in the constructor.
+     *
+     * @param <TServiceClass> type of the service wrapper class.
      *
      * @hide
      */
-    public static <T> void registerStaticService(String serviceName, Class<T> serviceWrapperClass,
-            Function<IBinder, T> serviceFetcher) {
+    //@SystemApi TODO Make it a system API.
+    public interface StaticServiceProducerNoBinder<TServiceClass> {
+        /**
+         * Return a new service wrapper of type {@code TServiceClass}.
+         */
+        TServiceClass createService();
+    }
+
+    /**
+     * Callback interface used as a parameter to {@link #registerStaticService(
+     * String, Class, StaticServiceProducerWithBinder)}, which generates a service wrapper instance
+     * that's not tied to any context and takes a service binder object in the constructor.
+     *
+     * @param <TServiceClass> type of the service wrapper class.
+     *
+     * @hide
+     */
+    //@SystemApi TODO Make it a system API.
+    public interface StaticServiceProducerWithBinder<TServiceClass> {
+        /**
+         * Return a new service wrapper of type {@code TServiceClass} backed by a given
+         * service binder object.
+         */
+        TServiceClass createService(IBinder serviceBinder);
+    }
+
+    /**
+     * Callback interface used as a parameter to {@link #registerContextAwareService(
+     * String, Class, ContextAwareServiceProducerNoBinder)},
+     * which generates a service wrapper instance
+     * that's tied to a specific context and does not take a service binder object in the
+     * constructor.
+     *
+     * @param <TServiceClass> type of the service wrapper class.
+     *
+     * @hide
+     */
+    //@SystemApi TODO Make it a system API.
+    public interface ContextAwareServiceProducerNoBinder<TServiceClass> {
+        /**
+         * Return a new service wrapper of type {@code TServiceClass} tied to a given
+         * {@code context}.
+         *
+         * TODO Do we need to pass the "base context" too?
+         */
+        TServiceClass createService(Context context);
+    }
+
+    /**
+     * Callback interface used as a parameter to {@link #registerContextAwareService(
+     * String, Class, ContextAwareServiceProducerWithBinder)},
+     * which generates a service wrapper instance
+     * that's tied to a specific context and takes a service binder object in the constructor.
+     *
+     * @param <TServiceClass> type of the service wrapper class.
+     *
+     * @hide
+     */
+    //@SystemApi TODO Make it a system API.
+    public interface ContextAwareServiceProducerWithBinder<TServiceClass> {
+        /**
+         * Return a new service wrapper of type {@code TServiceClass} backed by a given
+         * service binder object that's tied to a given {@code context}.
+         *
+         * TODO Do we need to pass the "base context" too?
+         */
+        TServiceClass createService(Context context, IBinder serviceBinder);
+    }
+
+    /**
+     * Used by apex modules to register a "service wrapper" that is not tied to any {@link Context}.
+     *
+     * <p>This can only be called from the methods called by the static initializer of
+     * {@link SystemServiceRegistry}. (Otherwise it throws a {@link IllegalStateException}.)
+     *
+     * @param serviceName the name of the binder object, such as
+     *     {@link Context#JOB_SCHEDULER_SERVICE}.
+     * @param serviceWrapperClass the wrapper class, such as the class of
+     *     {@link android.app.job.JobScheduler}.
+     * @param serviceProducer Callback that takes the service binder object with the name
+     *     {@code serviceName} and returns an actual service wrapper instance.
+     *
+     * @hide
+     */
+    //@SystemApi TODO Make it a system API.
+    public static <TServiceClass> void registerStaticService(
+            @NonNull String serviceName, @NonNull Class<TServiceClass> serviceWrapperClass,
+            @NonNull StaticServiceProducerWithBinder<TServiceClass> serviceProducer) {
+        ensureInitializing("registerStaticService");
+        Preconditions.checkStringNotEmpty(serviceName);
+        Preconditions.checkNotNull(serviceWrapperClass);
+        Preconditions.checkNotNull(serviceProducer);
+
         registerService(serviceName, serviceWrapperClass,
-                new StaticServiceFetcher<T>() {
+                new StaticServiceFetcher<TServiceClass>() {
                     @Override
-                    public T createService() throws ServiceNotFoundException {
-                        IBinder b = ServiceManager.getServiceOrThrow(serviceName);
-                        return serviceFetcher.apply(b);
+                    public TServiceClass createService() throws ServiceNotFoundException {
+                        return serviceProducer.createService(
+                                ServiceManager.getServiceOrThrow(serviceName));
                     }});
     }
 
     /**
-     * APEX modules will use it to register their service wrapper.
+     * Similar to {@link #registerStaticService(String, Class, StaticServiceProducerWithBinder)},
+     * but used for a "service wrapper" that doesn't take a service binder in its constructor.
      *
      * @hide
      */
-    public static <T> void registerCachedService(String serviceName, Class<T> serviceWrapperClass,
-            BiFunction<Context, IBinder, T> serviceFetcher) {
+    //@SystemApi TODO Make it a system API.
+    public static <TServiceClass> void registerStaticService(
+            @NonNull String serviceName, @NonNull Class<TServiceClass> serviceWrapperClass,
+            @NonNull StaticServiceProducerNoBinder<TServiceClass> serviceProducer) {
+        ensureInitializing("registerStaticService");
+        Preconditions.checkStringNotEmpty(serviceName);
+        Preconditions.checkNotNull(serviceWrapperClass);
+        Preconditions.checkNotNull(serviceProducer);
+
         registerService(serviceName, serviceWrapperClass,
-                new CachedServiceFetcher<T>() {
+                new StaticServiceFetcher<TServiceClass>() {
                     @Override
-                    public T createService(ContextImpl ctx) throws ServiceNotFoundException {
-                        IBinder b = ServiceManager.getServiceOrThrow(serviceName);
-                        return serviceFetcher.apply(ctx.getOuterContext(), b);
+                    public TServiceClass createService() {
+                        return serviceProducer.createService();
+                    }});
+    }
+
+    /**
+     * Used by apex modules to register a "service wrapper" that is tied to a specific
+     * {@link Context}.
+     *
+     * <p>This can only be called from the methods called by the static initializer of
+     * {@link SystemServiceRegistry}. (Otherwise it throws a {@link IllegalStateException}.)
+     *
+     * @param serviceName the name of the binder object, such as
+     *     {@link Context#JOB_SCHEDULER_SERVICE}.
+     * @param serviceWrapperClass the wrapper class, such as the class of
+     *     {@link android.app.job.JobScheduler}.
+     * @param serviceProducer lambda that takes the service binder object with the name
+     *     {@code serviceName}, a {@link Context} and returns an actual service wrapper instance.
+     *
+     * @hide
+     */
+    //@SystemApi TODO Make it a system API.
+    public static <TServiceClass> void registerContextAwareService(
+            @NonNull String serviceName, @NonNull Class<TServiceClass> serviceWrapperClass,
+            @NonNull ContextAwareServiceProducerWithBinder<TServiceClass> serviceProducer) {
+        ensureInitializing("registerContextAwareService");
+        Preconditions.checkStringNotEmpty(serviceName);
+        Preconditions.checkNotNull(serviceWrapperClass);
+        Preconditions.checkNotNull(serviceProducer);
+
+        registerService(serviceName, serviceWrapperClass,
+                new CachedServiceFetcher<TServiceClass>() {
+                    @Override
+                    public TServiceClass createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
+                        return serviceProducer.createService(
+                                ctx.getOuterContext(),
+                                ServiceManager.getServiceOrThrow(serviceName));
+                    }});
+    }
+
+
+    /**
+     * Similar to {@link #registerContextAwareService(String, Class,
+     * ContextAwareServiceProducerWithBinder)},
+     * but used for a "service wrapper" that doesn't take a service binder in its constructor.
+     *
+     * @hide
+     */
+    //@SystemApi TODO Make it a system API.
+    public static <TServiceClass> void registerContextAwareService(
+            @NonNull String serviceName, @NonNull Class<TServiceClass> serviceWrapperClass,
+            @NonNull ContextAwareServiceProducerNoBinder<TServiceClass> serviceProducer) {
+        ensureInitializing("registerContextAwareService");
+        Preconditions.checkStringNotEmpty(serviceName);
+        Preconditions.checkNotNull(serviceWrapperClass);
+        Preconditions.checkNotNull(serviceProducer);
+
+        registerService(serviceName, serviceWrapperClass,
+                new CachedServiceFetcher<TServiceClass>() {
+                    @Override
+                    public TServiceClass createService(ContextImpl ctx) {
+                        return serviceProducer.createService(ctx.getOuterContext());
                     }});
     }
 
@@ -1517,6 +1698,7 @@
         public abstract T createService(Context applicationContext) throws ServiceNotFoundException;
     }
 
+    /** @hide */
     public static void onServiceNotFound(ServiceNotFoundException e) {
         // We're mostly interested in tracking down long-lived core system
         // components that might stumble if they obtain bad references; just