diff options
Diffstat (limited to 'android/app/SystemServiceRegistry.java')
-rw-r--r-- | android/app/SystemServiceRegistry.java | 152 |
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; |