summaryrefslogtreecommitdiff
path: root/android/app/SystemServiceRegistry.java
diff options
context:
space:
mode:
Diffstat (limited to 'android/app/SystemServiceRegistry.java')
-rw-r--r--android/app/SystemServiceRegistry.java152
1 files changed, 71 insertions, 81 deletions
diff --git a/android/app/SystemServiceRegistry.java b/android/app/SystemServiceRegistry.java
index 1776eace..246d4a37 100644
--- a/android/app/SystemServiceRegistry.java
+++ b/android/app/SystemServiceRegistry.java
@@ -18,6 +18,7 @@ package android.app;
import android.accounts.AccountManager;
import android.accounts.IAccountManager;
+import android.app.ContextImpl.ServiceInitializationState;
import android.app.admin.DevicePolicyManager;
import android.app.admin.IDevicePolicyManager;
import android.app.job.IJobScheduler;
@@ -104,10 +105,12 @@ import android.nfc.NfcManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Build;
+import android.os.DeviceIdleManager;
import android.os.DropBoxManager;
import android.os.HardwarePropertiesManager;
import android.os.IBatteryPropertiesRegistrar;
import android.os.IBinder;
+import android.os.IDeviceIdleController;
import android.os.IHardwarePropertiesManager;
import android.os.IPowerManager;
import android.os.IRecoverySystem;
@@ -160,7 +163,6 @@ import com.android.internal.os.IDropBoxManagerService;
import com.android.internal.policy.PhoneLayoutInflater;
import java.util.HashMap;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* Manages all of the system services that can be returned by {@link Context#getSystemService}.
@@ -279,12 +281,12 @@ final class SystemServiceRegistry {
}});
registerService(Context.IPSEC_SERVICE, IpSecManager.class,
- new StaticServiceFetcher<IpSecManager>() {
+ new CachedServiceFetcher<IpSecManager>() {
@Override
- public IpSecManager createService() {
+ public IpSecManager createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder b = ServiceManager.getService(Context.IPSEC_SERVICE);
IIpSecService service = IIpSecService.Stub.asInterface(b);
- return new IpSecManager(service);
+ return new IpSecManager(ctx, service);
}});
registerService(Context.COUNTRY_DETECTOR, CountryDetector.class,
@@ -984,6 +986,17 @@ final class SystemServiceRegistry {
ctx.mMainThread.getHandler());
}
});
+
+ registerService(Context.DEVICE_IDLE_CONTROLLER, DeviceIdleManager.class,
+ new CachedServiceFetcher<DeviceIdleManager>() {
+ @Override
+ public DeviceIdleManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IDeviceIdleController service = IDeviceIdleController.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(
+ Context.DEVICE_IDLE_CONTROLLER));
+ return new DeviceIdleManager(ctx.getOuterContext(), service);
+ }});
}
/**
@@ -993,10 +1006,6 @@ final class SystemServiceRegistry {
return new Object[sServiceCacheSize];
}
- public static AtomicInteger[] createServiceInitializationStateArray() {
- return new AtomicInteger[sServiceCacheSize];
- }
-
/**
* Gets a system service from a given context.
*/
@@ -1037,7 +1046,10 @@ final class SystemServiceRegistry {
static abstract class CachedServiceFetcher<T> implements ServiceFetcher<T> {
private final int mCacheIndex;
- public CachedServiceFetcher() {
+ CachedServiceFetcher() {
+ // Note this class must be instantiated only by the static initializer of the
+ // outer class (SystemServiceRegistry), which already does the synchronization,
+ // so bare access to sServiceCacheSize is okay here.
mCacheIndex = sServiceCacheSize++;
}
@@ -1045,95 +1057,73 @@ final class SystemServiceRegistry {
@SuppressWarnings("unchecked")
public final T getService(ContextImpl ctx) {
final Object[] cache = ctx.mServiceCache;
+ final int[] gates = ctx.mServiceInitializationStateArray;
+
+ for (;;) {
+ boolean doInitialize = false;
+ synchronized (cache) {
+ // Return it if we already have a cached instance.
+ T service = (T) cache[mCacheIndex];
+ if (service != null || gates[mCacheIndex] == ContextImpl.STATE_NOT_FOUND) {
+ return service;
+ }
- // Fast path. If it's already cached, just return it.
- Object service = cache[mCacheIndex];
- if (service != null) {
- return (T) service;
- }
+ // If we get here, there's no cached instance.
- // Slow path.
- final AtomicInteger[] gates = ctx.mServiceInitializationStateArray;
- final AtomicInteger gate;
+ // Grr... if gate is STATE_READY, then this means we initialized the service
+ // once but someone cleared it.
+ // We start over from STATE_UNINITIALIZED.
+ if (gates[mCacheIndex] == ContextImpl.STATE_READY) {
+ gates[mCacheIndex] = ContextImpl.STATE_UNINITIALIZED;
+ }
- synchronized (cache) {
- // See if it's cached or not again, with the lock held this time.
- service = cache[mCacheIndex];
- if (service != null) {
- return (T) service;
- }
+ // It's possible for multiple threads to get here at the same time, so
+ // use the "gate" to make sure only the first thread will call createService().
- // Not initialized yet. Create an atomic boolean to control which thread should
- // instantiate the service.
- if (gates[mCacheIndex] != null) {
- gate = gates[mCacheIndex];
- } else {
- gate = new AtomicInteger(ContextImpl.STATE_UNINITIALIZED);
- gates[mCacheIndex] = gate;
+ // At this point, the gate must be either UNINITIALIZED or INITIALIZING.
+ if (gates[mCacheIndex] == ContextImpl.STATE_UNINITIALIZED) {
+ doInitialize = true;
+ gates[mCacheIndex] = ContextImpl.STATE_INITIALIZING;
+ }
}
- }
- // Not cached yet.
- //
- // Note multiple threads can reach here for the same service on the same context
- // concurrently.
- //
- // Now we're going to instantiate the service, but do so without the cache held;
- // otherwise it could deadlock. (b/71882178)
- //
- // However we still don't want to instantiate the same service multiple times, so
- // use the atomic integer to ensure only one thread will call createService().
-
- if (gate.compareAndSet(
- ContextImpl.STATE_UNINITIALIZED, ContextImpl.STATE_INITIALIZING)) {
- try {
- // This thread is the first one to get here. Instantiate the service
- // *without* the cache lock held.
+ if (doInitialize) {
+ // Only the first thread gets here.
+
+ T service = null;
+ @ServiceInitializationState int newState = ContextImpl.STATE_NOT_FOUND;
try {
+ // This thread is the first one to get here. Instantiate the service
+ // *without* the cache lock held.
service = createService(ctx);
+ newState = ContextImpl.STATE_READY;
+
+ } catch (ServiceNotFoundException e) {
+ onServiceNotFound(e);
+ } finally {
synchronized (cache) {
cache[mCacheIndex] = service;
+ gates[mCacheIndex] = newState;
+ cache.notifyAll();
}
- } catch (ServiceNotFoundException e) {
- onServiceNotFound(e);
- }
- } finally {
- // Tell the all other threads that the cache is ready now.
- // (But it's still be null in case of ServiceNotFoundException.)
- synchronized (gate) {
- gate.set(ContextImpl.STATE_READY);
- gate.notifyAll();
}
+ return service;
}
- return (T) service;
- }
- // Other threads will wait on the gate lock.
- synchronized (gate) {
- boolean interrupted = false;
-
- // Note: We check whether "state == STATE_READY", not
- // "cache[mCacheIndex] != null", because "cache[mCacheIndex] == null"
- // is still a valid outcome in the ServiceNotFoundException case.
- while (gate.get() != ContextImpl.STATE_READY) {
- try {
- gate.wait();
- } catch (InterruptedException e) {
- Log.w(TAG, "getService() interrupted");
- interrupted = true;
+ // The other threads will wait for the first thread to call notifyAll(),
+ // and go back to the top and retry.
+ synchronized (cache) {
+ while (gates[mCacheIndex] < ContextImpl.STATE_READY) {
+ try {
+ cache.wait();
+ } catch (InterruptedException e) {
+ Log.w(TAG, "getService() interrupted");
+ Thread.currentThread().interrupt();
+ return null;
+ }
}
}
- if (interrupted) {
- Thread.currentThread().interrupt();
- }
- }
- // Now the first thread has initialized it.
- // It may still be null if ServiceNotFoundException was thrown, but that shouldn't
- // happen, so we'll just return null here in that case.
- synchronized (cache) {
- service = cache[mCacheIndex];
}
- return (T) service;
}
public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;