Phase 1 of refactoring SystemServer.

SystemServer is currently a monolithic class that brings up key system
services. This change is the first phase of refactoring it to be more
configurable. Specifically, it adds a set of on/off switches used to control
startup of individual services. Future plans include finer grained controls
and a more explicit and consistent startup sequence for these services.

Change-Id: I7299f5ce7d7b74a34eb56dffb788366fbc058532
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index d9846ec..41e4958 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -19,6 +19,7 @@
 import android.os.storage.IMountService;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageVolume;
+import android.os.SystemProperties;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -58,6 +59,10 @@
     private static volatile StorageVolume sPrimaryVolume;
 
     private static StorageVolume getPrimaryVolume() {
+        if (SystemProperties.getBoolean("config.disable_storage", false)) {
+            return null;
+        }
+
         if (sPrimaryVolume == null) {
             synchronized (sLock) {
                 if (sPrimaryVolume == null) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9387624..efbca0b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -116,6 +116,7 @@
      * at 60 Hz. This can be used to measure the potential framerate.
      */
     private static final String PROPERTY_PROFILE_RENDERING = "viewancestor.profile_rendering";
+    private static final String PROPERTY_MEDIA_DISABLED = "config.disable_media";
     
     private static final boolean MEASURE_LATENCY = false;
     private static LatencyTimer lt;
@@ -316,6 +317,8 @@
     private Choreographer.FrameCallback mRenderProfiler;
     private boolean mRenderProfilingEnabled;
 
+    private boolean mMediaDisabled;
+
     // Variables to track frames per second, enabled via DEBUG_FPS flag
     private long mFpsStartTime = -1;
     private long mFpsPrevTime = -1;
@@ -4130,6 +4133,10 @@
     public void playSoundEffect(int effectId) {
         checkThread();
 
+        if (mMediaDisabled) {
+            return;
+        }
+
         try {
             final AudioManager audioManager = getAudioManager();
 
@@ -4275,6 +4282,9 @@
                 mProfileRendering = SystemProperties.getBoolean(PROPERTY_PROFILE_RENDERING, false);
                 profileRendering(mAttachInfo.mHasWindowFocus);
 
+                // Media (used by sound effects)
+                mMediaDisabled = SystemProperties.getBoolean(PROPERTY_MEDIA_DISABLED, false);
+
                 // Hardware rendering
                 if (mAttachInfo.mHardwareRenderer != null) {
                     if (mAttachInfo.mHardwareRenderer.loadSystemProperties(mHolder.getSurface())) {
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index bef28aa..822ac6f 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -29,6 +29,7 @@
 import android.os.Process;
 import android.util.Log;
 import android.view.Surface;
+import android.os.SystemProperties;
 
 
 
@@ -56,20 +57,22 @@
      * We use a class initializer to allow the native code to cache some
      * field offsets.
      */
-    @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
+    @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) // TODO: now used locally; remove?
     static boolean sInitialized;
     native static void _nInit();
 
 
     static {
         sInitialized = false;
-        try {
-            System.loadLibrary("rs_jni");
-            _nInit();
-            sInitialized = true;
-        } catch (UnsatisfiedLinkError e) {
-            Log.e(LOG_TAG, "Error loading RS jni library: " + e);
-            throw new RSRuntimeException("Error loading RS jni library: " + e);
+        if (!SystemProperties.getBoolean("config.disable_renderscript", false)) {
+            try {
+                System.loadLibrary("rs_jni");
+                _nInit();
+                sInitialized = true;
+            } catch (UnsatisfiedLinkError e) {
+                Log.e(LOG_TAG, "Error loading RS jni library: " + e);
+                throw new RSRuntimeException("Error loading RS jni library: " + e);
+            }
         }
     }
 
@@ -97,6 +100,11 @@
      * @param cacheDir A directory the current process can write to
      */
     public static void setupDiskCache(File cacheDir) {
+        if (!sInitialized) {
+            Log.e(LOG_TAG, "RenderScript.setupDiskCache() called when disabled");
+            return;
+        }
+
         File f = new File(cacheDir, CACHE_PATH);
         mCachePath = f.getAbsolutePath();
         f.mkdirs();
@@ -1036,6 +1044,11 @@
      * @return RenderScript
      */
     public static RenderScript create(Context ctx, int sdkVersion, ContextType ct) {
+        if (!sInitialized) {
+            Log.e(LOG_TAG, "RenderScript.create() called when disabled; someone is likely to crash");
+            return null;
+        }
+
         RenderScript rs = new RenderScript(ctx);
 
         rs.mDev = rs.nDeviceCreate();
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 06e658d..2eef91b 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -51,17 +51,23 @@
 		external/skia/include/images \
 		external/skia/src/core \
 		external/skia/src/ports \
-		external/skia/include/utils \
-		$(intermediates) \
-		frameworks/rs/cpp \
-		frameworks/rs
+		external/skia/include/utils
 
 	LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DGL_GLEXT_PROTOTYPES
 	LOCAL_MODULE_CLASS := SHARED_LIBRARIES
-	LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia libui libRS libRScpp
+	LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia libui
 	LOCAL_MODULE := libhwui
 	LOCAL_MODULE_TAGS := optional
 
+        ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
+            LOCAL_CFLAGS += -DANDROID_ENABLE_RENDERSCRIPT
+            LOCAL_SHARED_LIBRARIES += libRS libRScpp
+            LOCAL_C_INCLUDES += \
+		$(intermediates) \
+		frameworks/rs/cpp \
+		frameworks/rs
+        endif
+
 	ifndef HWUI_COMPILE_SYMBOLS
 		LOCAL_CFLAGS += -fvisibility=hidden
 	endif
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 26c7e5d..354bd77 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -24,7 +24,9 @@
 #include <utils/Functor.h>
 #include <utils/Log.h>
 
+#ifdef ANDROID_ENABLE_RENDERSCRIPT
 #include <RenderScript.h>
+#endif
 
 #include "utils/Blur.h"
 #include "utils/Timing.h"
@@ -532,13 +534,18 @@
     uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
     uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
 
+#ifdef ANDROID_ENABLE_RENDERSCRIPT
     // Align buffers for renderscript usage
     if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
         paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
     }
-
     int size = paddedWidth * paddedHeight;
     uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
+#else
+    int size = paddedWidth * paddedHeight;
+    uint8_t* dataBuffer = (uint8_t*) malloc(size);
+#endif
+
     memset(dataBuffer, 0, size);
 
     int penX = radius - bounds.left;
@@ -624,43 +631,46 @@
 }
 
 void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
-    if (width * height * radius < RS_MIN_INPUT_CUTOFF) {
-        float *gaussian = new float[2 * radius + 1];
-        Blur::generateGaussianWeights(gaussian, radius);
+#ifdef ANDROID_ENABLE_RENDERSCRIPT
+    if (width * height * radius >= RS_MIN_INPUT_CUTOFF) {
+        uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
 
-        uint8_t* scratch = new uint8_t[width * height];
-        Blur::horizontal(gaussian, radius, *image, scratch, width, height);
-        Blur::vertical(gaussian, radius, scratch, *image, width, height);
+        if (mRs.get() == 0) {
+            mRs = new RSC::RS();
+            if (!mRs->init(true, true)) {
+                ALOGE("blur RS failed to init");
+            }
 
-        delete[] gaussian;
-        delete[] scratch;
-        return;
-    }
-
-    uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
-
-    if (mRs.get() == 0) {
-        mRs = new RSC::RS();
-        if (!mRs->init(true, true)) {
-            ALOGE("blur RS failed to init");
+            mRsElement = RSC::Element::A_8(mRs);
+            mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
         }
 
-        mRsElement = RSC::Element::A_8(mRs);
-        mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
+        sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
+        sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
+                RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
+        sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
+                RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
+
+        mRsScript->setRadius(radius);
+        mRsScript->blur(ain, aout);
+
+        // replace the original image's pointer, avoiding a copy back to the original buffer
+        free(*image);
+        *image = outImage;
+
+        return;
     }
+#endif
 
-    sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
-    sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
-            RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
-    sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
-            RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
+    float *gaussian = new float[2 * radius + 1];
+    Blur::generateGaussianWeights(gaussian, radius);
 
-    mRsScript->setRadius(radius);
-    mRsScript->blur(ain, aout);
+    uint8_t* scratch = new uint8_t[width * height];
+    Blur::horizontal(gaussian, radius, *image, scratch, width, height);
+    Blur::vertical(gaussian, radius, scratch, *image, width, height);
 
-    // replace the original image's pointer, avoiding a copy back to the original buffer
-    free(*image);
-    *image = outImage;
+    delete[] gaussian;
+    delete[] scratch;
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 1da3b6c..382e2d5 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -32,11 +32,13 @@
 #include "Matrix.h"
 #include "Properties.h"
 
+#ifdef ANDROID_ENABLE_RENDERSCRIPT
 namespace RSC {
     class Element;
     class RS;
     class ScriptIntrinsicBlur;
 }
+#endif
 
 class Functor;
 
@@ -174,10 +176,12 @@
 
     bool mLinearFiltering;
 
+#ifdef ANDROID_ENABLE_RENDERSCRIPT
     // RS constructs
     sp<RSC::RS> mRs;
     sp<const RSC::Element> mRsElement;
     sp<RSC::ScriptIntrinsicBlur> mRsScript;
+#endif
 
     static void computeGaussianWeights(float* weights, int32_t radius);
     static void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 587af47..5127479 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -16,19 +16,21 @@
 
 package android.media;
 
-import android.util.AndroidRuntimeException;
-import android.util.Log;
 import java.io.File;
 import java.io.FileDescriptor;
-import android.os.ParcelFileDescriptor;
+import java.io.IOException;
 import java.lang.ref.WeakReference;
+
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
-import java.io.IOException;
-
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemProperties;
+import android.util.AndroidRuntimeException;
+import android.util.Log;
+
 
 /**
  * The SoundPool class manages and plays audio resources for applications.
@@ -102,24 +104,8 @@
  * another level, a new SoundPool is created, sounds are loaded, and play
  * resumes.</p>
  */
-public class SoundPool
-{
-    static { System.loadLibrary("soundpool"); }
-
-    private final static String TAG = "SoundPool";
-    private final static boolean DEBUG = false;
-
-    private int mNativeContext; // accessed by native methods
-
-    private EventHandler mEventHandler;
-    private OnLoadCompleteListener mOnLoadCompleteListener;
-
-    private final Object mLock;
-
-    // SoundPool messages
-    //
-    // must match SoundPool.h
-    private static final int SAMPLE_LOADED = 1;
+public class SoundPool {
+    private final SoundPoolDelegate mImpl;
 
     /**
      * Constructor. Constructs a SoundPool object with the following
@@ -135,12 +121,11 @@
      * @return a SoundPool object, or null if creation failed
      */
     public SoundPool(int maxStreams, int streamType, int srcQuality) {
-
-        // do native setup
-        if (native_setup(new WeakReference(this), maxStreams, streamType, srcQuality) != 0) {
-            throw new RuntimeException("Native setup failed");
+        if (SystemProperties.getBoolean("config.disable_media", false)) {
+            mImpl = new SoundPoolStub();
+        } else {
+            mImpl = new SoundPoolImpl(this, maxStreams, streamType, srcQuality);
         }
-        mLock = new Object();
     }
 
     /**
@@ -151,25 +136,8 @@
      *                 a value of 1 for future compatibility.
      * @return a sound ID. This value can be used to play or unload the sound.
      */
-    public int load(String path, int priority)
-    {
-        // pass network streams to player
-        if (path.startsWith("http:"))
-            return _load(path, priority);
-
-        // try local path
-        int id = 0;
-        try {
-            File f = new File(path);
-            ParcelFileDescriptor fd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
-            if (fd != null) {
-                id = _load(fd.getFileDescriptor(), 0, f.length(), priority);
-                fd.close();
-            }
-        } catch (java.io.IOException e) {
-            Log.e(TAG, "error loading " + path);
-        }
-        return id;
+    public int load(String path, int priority) {
+        return mImpl.load(path, priority);
     }
 
     /**
@@ -188,17 +156,7 @@
      * @return a sound ID. This value can be used to play or unload the sound.
      */
     public int load(Context context, int resId, int priority) {
-        AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId);
-        int id = 0;
-        if (afd != null) {
-            id = _load(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength(), priority);
-            try {
-                afd.close();
-            } catch (java.io.IOException ex) {
-                //Log.d(TAG, "close failed:", ex);
-            }
-        }
-        return id;
+        return mImpl.load(context, resId, priority);
     }
 
     /**
@@ -210,15 +168,7 @@
      * @return a sound ID. This value can be used to play or unload the sound.
      */
     public int load(AssetFileDescriptor afd, int priority) {
-        if (afd != null) {
-            long len = afd.getLength();
-            if (len < 0) {
-                throw new AndroidRuntimeException("no length for fd");
-            }
-            return _load(afd.getFileDescriptor(), afd.getStartOffset(), len, priority);
-        } else {
-            return 0;
-        }
+        return mImpl.load(afd, priority);
     }
 
     /**
@@ -236,13 +186,9 @@
      * @return a sound ID. This value can be used to play or unload the sound.
      */
     public int load(FileDescriptor fd, long offset, long length, int priority) {
-        return _load(fd, offset, length, priority);
+        return mImpl.load(fd, offset, length, priority);
     }
 
-    private native final int _load(String uri, int priority);
-
-    private native final int _load(FileDescriptor fd, long offset, long length, int priority);
-
     /**
      * Unload a sound from a sound ID.
      *
@@ -253,7 +199,9 @@
      * @param soundID a soundID returned by the load() function
      * @return true if just unloaded, false if previously unloaded
      */
-    public native final boolean unload(int soundID);
+    public final boolean unload(int soundID) {
+        return mImpl.unload(soundID);
+    }
 
     /**
      * Play a sound from a sound ID.
@@ -279,8 +227,11 @@
      * @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0)
      * @return non-zero streamID if successful, zero if failed
      */
-    public native final int play(int soundID, float leftVolume, float rightVolume,
-            int priority, int loop, float rate);
+    public final int play(int soundID, float leftVolume, float rightVolume,
+            int priority, int loop, float rate) {
+        return mImpl.play(
+            soundID, leftVolume, rightVolume, priority, loop, rate);
+    }
 
     /**
      * Pause a playback stream.
@@ -293,7 +244,9 @@
      *
      * @param streamID a streamID returned by the play() function
      */
-    public native final void pause(int streamID);
+    public final void pause(int streamID) {
+        mImpl.pause(streamID);
+    }
 
     /**
      * Resume a playback stream.
@@ -305,7 +258,9 @@
      *
      * @param streamID a streamID returned by the play() function
      */
-    public native final void resume(int streamID);
+    public final void resume(int streamID) {
+        mImpl.resume(streamID);
+    }
 
     /**
      * Pause all active streams.
@@ -315,7 +270,9 @@
      * are playing. It also sets a flag so that any streams that
      * are playing can be resumed by calling autoResume().
      */
-    public native final void autoPause();
+    public final void autoPause() {
+        mImpl.autoPause();
+    }
 
     /**
      * Resume all previously active streams.
@@ -323,7 +280,9 @@
      * Automatically resumes all streams that were paused in previous
      * calls to autoPause().
      */
-    public native final void autoResume();
+    public final void autoResume() {
+        mImpl.autoResume();
+    }
 
     /**
      * Stop a playback stream.
@@ -336,7 +295,9 @@
      *
      * @param streamID a streamID returned by the play() function
      */
-    public native final void stop(int streamID);
+    public final void stop(int streamID) {
+        mImpl.stop(streamID);
+    }
 
     /**
      * Set stream volume.
@@ -350,8 +311,10 @@
      * @param leftVolume left volume value (range = 0.0 to 1.0)
      * @param rightVolume right volume value (range = 0.0 to 1.0)
      */
-    public native final void setVolume(int streamID,
-            float leftVolume, float rightVolume);
+    public final void setVolume(int streamID,
+            float leftVolume, float rightVolume) {
+        mImpl.setVolume(streamID, leftVolume, rightVolume);
+    }
 
     /**
      * Similar, except set volume of all channels to same value.
@@ -371,7 +334,9 @@
      *
      * @param streamID a streamID returned by the play() function
      */
-    public native final void setPriority(int streamID, int priority);
+    public final void setPriority(int streamID, int priority) {
+        mImpl.setPriority(streamID, priority);
+    }
 
     /**
      * Set loop mode.
@@ -384,7 +349,9 @@
      * @param streamID a streamID returned by the play() function
      * @param loop loop mode (0 = no loop, -1 = loop forever)
      */
-    public native final void setLoop(int streamID, int loop);
+    public final void setLoop(int streamID, int loop) {
+        mImpl.setLoop(streamID, loop);
+    }
 
     /**
      * Change playback rate.
@@ -398,14 +365,11 @@
      * @param streamID a streamID returned by the play() function
      * @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0)
      */
-    public native final void setRate(int streamID, float rate);
+    public final void setRate(int streamID, float rate) {
+        mImpl.setRate(streamID, rate);
+    }
 
-    /**
-     * Interface definition for a callback to be invoked when all the
-     * sounds are loaded.
-     */
-    public interface OnLoadCompleteListener
-    {
+    public interface OnLoadCompleteListener {
         /**
          * Called when a sound has completed loading.
          *
@@ -419,64 +383,8 @@
     /**
      * Sets the callback hook for the OnLoadCompleteListener.
      */
-    public void setOnLoadCompleteListener(OnLoadCompleteListener listener)
-    {
-        synchronized(mLock) {
-            if (listener != null) {
-                // setup message handler
-                Looper looper;
-                if ((looper = Looper.myLooper()) != null) {
-                    mEventHandler = new EventHandler(this, looper);
-                } else if ((looper = Looper.getMainLooper()) != null) {
-                    mEventHandler = new EventHandler(this, looper);
-                } else {
-                    mEventHandler = null;
-                }
-            } else {
-                mEventHandler = null;
-            }
-            mOnLoadCompleteListener = listener;
-        }
-    }
-
-    private class EventHandler extends Handler
-    {
-        private SoundPool mSoundPool;
-
-        public EventHandler(SoundPool soundPool, Looper looper) {
-            super(looper);
-            mSoundPool = soundPool;
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch(msg.what) {
-            case SAMPLE_LOADED:
-                if (DEBUG) Log.d(TAG, "Sample " + msg.arg1 + " loaded");
-                synchronized(mLock) {
-                    if (mOnLoadCompleteListener != null) {
-                        mOnLoadCompleteListener.onLoadComplete(mSoundPool, msg.arg1, msg.arg2);
-                    }
-                }
-                break;
-            default:
-                Log.e(TAG, "Unknown message type " + msg.what);
-                return;
-            }
-        }
-    }
-
-    // post event from native code to message handler
-    private static void postEventFromNative(Object weakRef, int msg, int arg1, int arg2, Object obj)
-    {
-        SoundPool soundPool = (SoundPool)((WeakReference)weakRef).get();
-        if (soundPool == null)
-            return;
-
-        if (soundPool.mEventHandler != null) {
-            Message m = soundPool.mEventHandler.obtainMessage(msg, arg1, arg2, obj);
-            soundPool.mEventHandler.sendMessage(m);
-        }
+    public void setOnLoadCompleteListener(OnLoadCompleteListener listener) {
+        mImpl.setOnLoadCompleteListener(listener);
     }
 
     /**
@@ -486,9 +394,286 @@
      * object. The SoundPool can no longer be used and the reference
      * should be set to null.
      */
-    public native final void release();
+    public final void release() {
+        mImpl.release();
+    }
 
-    private native final int native_setup(Object weakRef, int maxStreams, int streamType, int srcQuality);
+    /**
+     * Interface for SoundPool implementations.
+     * SoundPool is statically referenced and unconditionally called from all
+     * over the framework, so we can't simply omit the class or make it throw
+     * runtime exceptions, as doing so would break the framework. Instead we
+     * now select either a real or no-op impl object based on whether media is
+     * enabled.
+     *
+     * @hide
+     */
+    /* package */ interface SoundPoolDelegate {
+        public int load(String path, int priority);
+        public int load(Context context, int resId, int priority);
+        public int load(AssetFileDescriptor afd, int priority);
+        public int load(
+                FileDescriptor fd, long offset, long length, int priority);
+        public boolean unload(int soundID);
+        public int play(
+                int soundID, float leftVolume, float rightVolume,
+                int priority, int loop, float rate);
+        public void pause(int streamID);
+        public void resume(int streamID);
+        public void autoPause();
+        public void autoResume();
+        public void stop(int streamID);
+        public void setVolume(int streamID, float leftVolume, float rightVolume);
+        public void setVolume(int streamID, float volume);
+        public void setPriority(int streamID, int priority);
+        public void setLoop(int streamID, int loop);
+        public void setRate(int streamID, float rate);
+        public void setOnLoadCompleteListener(OnLoadCompleteListener listener);
+        public void release();
+    }
 
-    protected void finalize() { release(); }
+
+    /**
+     * Real implementation of the delegate interface. This was formerly the
+     * body of SoundPool itself.
+     */
+    /* package */ static class SoundPoolImpl implements SoundPoolDelegate {
+        static { System.loadLibrary("soundpool"); }
+
+        private final static String TAG = "SoundPool";
+        private final static boolean DEBUG = false;
+
+        private int mNativeContext; // accessed by native methods
+
+        private EventHandler mEventHandler;
+        private SoundPool.OnLoadCompleteListener mOnLoadCompleteListener;
+        private SoundPool mProxy;
+
+        private final Object mLock;
+
+        // SoundPool messages
+        //
+        // must match SoundPool.h
+        private static final int SAMPLE_LOADED = 1;
+
+        public SoundPoolImpl(SoundPool proxy, int maxStreams, int streamType, int srcQuality) {
+
+            // do native setup
+            if (native_setup(new WeakReference(this), maxStreams, streamType, srcQuality) != 0) {
+                throw new RuntimeException("Native setup failed");
+            }
+            mLock = new Object();
+            mProxy = proxy;
+        }
+
+        public int load(String path, int priority)
+        {
+            // pass network streams to player
+            if (path.startsWith("http:"))
+                return _load(path, priority);
+
+            // try local path
+            int id = 0;
+            try {
+                File f = new File(path);
+                ParcelFileDescriptor fd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
+                if (fd != null) {
+                    id = _load(fd.getFileDescriptor(), 0, f.length(), priority);
+                    fd.close();
+                }
+            } catch (java.io.IOException e) {
+                Log.e(TAG, "error loading " + path);
+            }
+            return id;
+        }
+
+        public int load(Context context, int resId, int priority) {
+            AssetFileDescriptor afd = context.getResources().openRawResourceFd(resId);
+            int id = 0;
+            if (afd != null) {
+                id = _load(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength(), priority);
+                try {
+                    afd.close();
+                } catch (java.io.IOException ex) {
+                    //Log.d(TAG, "close failed:", ex);
+                }
+            }
+            return id;
+        }
+
+        public int load(AssetFileDescriptor afd, int priority) {
+            if (afd != null) {
+                long len = afd.getLength();
+                if (len < 0) {
+                    throw new AndroidRuntimeException("no length for fd");
+                }
+                return _load(afd.getFileDescriptor(), afd.getStartOffset(), len, priority);
+            } else {
+                return 0;
+            }
+        }
+
+        public int load(FileDescriptor fd, long offset, long length, int priority) {
+            return _load(fd, offset, length, priority);
+        }
+
+        private native final int _load(String uri, int priority);
+
+        private native final int _load(FileDescriptor fd, long offset, long length, int priority);
+
+        public native final boolean unload(int soundID);
+
+        public native final int play(int soundID, float leftVolume, float rightVolume,
+                int priority, int loop, float rate);
+
+        public native final void pause(int streamID);
+
+        public native final void resume(int streamID);
+
+        public native final void autoPause();
+
+        public native final void autoResume();
+
+        public native final void stop(int streamID);
+
+        public native final void setVolume(int streamID,
+                float leftVolume, float rightVolume);
+
+        public void setVolume(int streamID, float volume) {
+            setVolume(streamID, volume, volume);
+        }
+
+        public native final void setPriority(int streamID, int priority);
+
+        public native final void setLoop(int streamID, int loop);
+
+        public native final void setRate(int streamID, float rate);
+
+        public void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener)
+        {
+            synchronized(mLock) {
+                if (listener != null) {
+                    // setup message handler
+                    Looper looper;
+                    if ((looper = Looper.myLooper()) != null) {
+                        mEventHandler = new EventHandler(mProxy, looper);
+                    } else if ((looper = Looper.getMainLooper()) != null) {
+                        mEventHandler = new EventHandler(mProxy, looper);
+                    } else {
+                        mEventHandler = null;
+                    }
+                } else {
+                    mEventHandler = null;
+                }
+                mOnLoadCompleteListener = listener;
+            }
+        }
+
+        private class EventHandler extends Handler
+        {
+            private SoundPool mSoundPool;
+
+            public EventHandler(SoundPool soundPool, Looper looper) {
+                super(looper);
+                mSoundPool = soundPool;
+            }
+
+            @Override
+            public void handleMessage(Message msg) {
+                switch(msg.what) {
+                case SAMPLE_LOADED:
+                    if (DEBUG) Log.d(TAG, "Sample " + msg.arg1 + " loaded");
+                    synchronized(mLock) {
+                        if (mOnLoadCompleteListener != null) {
+                            mOnLoadCompleteListener.onLoadComplete(mSoundPool, msg.arg1, msg.arg2);
+                        }
+                    }
+                    break;
+                default:
+                    Log.e(TAG, "Unknown message type " + msg.what);
+                    return;
+                }
+            }
+        }
+
+        // post event from native code to message handler
+        private static void postEventFromNative(Object weakRef, int msg, int arg1, int arg2, Object obj)
+        {
+            SoundPoolImpl soundPoolImpl = (SoundPoolImpl)((WeakReference)weakRef).get();
+            if (soundPoolImpl == null)
+                return;
+
+            if (soundPoolImpl.mEventHandler != null) {
+                Message m = soundPoolImpl.mEventHandler.obtainMessage(msg, arg1, arg2, obj);
+                soundPoolImpl.mEventHandler.sendMessage(m);
+            }
+        }
+
+        public native final void release();
+
+        private native final int native_setup(Object weakRef, int maxStreams, int streamType, int srcQuality);
+
+        protected void finalize() { release(); }
+    }
+
+    /**
+     * No-op implementation of SoundPool.
+     * Used when media is disabled by the system.
+     * @hide
+     */
+    /* package */ static class SoundPoolStub implements SoundPoolDelegate {
+        public SoundPoolStub() { }
+
+        public int load(String path, int priority) {
+            return 0;
+        }
+
+        public int load(Context context, int resId, int priority) {
+            return 0;
+        }
+
+        public int load(AssetFileDescriptor afd, int priority) {
+            return 0;
+        }
+
+        public int load(FileDescriptor fd, long offset, long length, int priority) {
+            return 0;
+        }
+
+        public final boolean unload(int soundID) {
+            return true;
+        }
+
+        public final int play(int soundID, float leftVolume, float rightVolume,
+                int priority, int loop, float rate) {
+            return 0;
+        }
+
+        public final void pause(int streamID) { }
+
+        public final void resume(int streamID) { }
+
+        public final void autoPause() { }
+
+        public final void autoResume() { }
+
+        public final void stop(int streamID) { }
+
+        public final void setVolume(int streamID,
+                float leftVolume, float rightVolume) { }
+
+        public void setVolume(int streamID, float volume) {
+        }
+
+        public final void setPriority(int streamID, int priority) { }
+
+        public final void setLoop(int streamID, int loop) { }
+
+        public final void setRate(int streamID, float rate) { }
+
+        public void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener) {
+        }
+
+        public final void release() { }
+    }
 }
diff --git a/media/jni/soundpool/Android.mk b/media/jni/soundpool/Android.mk
index 9b11bfa..1286482 100644
--- a/media/jni/soundpool/Android.mk
+++ b/media/jni/soundpool/Android.mk
@@ -2,7 +2,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
-	android_media_SoundPool.cpp
+	android_media_SoundPool_SoundPoolImpl.cpp
 
 LOCAL_SHARED_LIBRARIES := \
 	libcutils \
diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
similarity index 66%
rename from media/jni/soundpool/android_media_SoundPool.cpp
rename to media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
index 9658856..2604850 100644
--- a/media/jni/soundpool/android_media_SoundPool.cpp
+++ b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
@@ -39,9 +39,9 @@
 
 // ----------------------------------------------------------------------------
 static int
-android_media_SoundPool_load_URL(JNIEnv *env, jobject thiz, jstring path, jint priority)
+android_media_SoundPool_SoundPoolImpl_load_URL(JNIEnv *env, jobject thiz, jstring path, jint priority)
 {
-    ALOGV("android_media_SoundPool_load_URL");
+    ALOGV("android_media_SoundPool_SoundPoolImpl_load_URL");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (path == NULL) {
         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
@@ -54,10 +54,10 @@
 }
 
 static int
-android_media_SoundPool_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor,
+android_media_SoundPool_SoundPoolImpl_load_FD(JNIEnv *env, jobject thiz, jobject fileDescriptor,
         jlong offset, jlong length, jint priority)
 {
-    ALOGV("android_media_SoundPool_load_FD");
+    ALOGV("android_media_SoundPool_SoundPoolImpl_load_FD");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return 0;
     return ap->load(jniGetFDFromFileDescriptor(env, fileDescriptor),
@@ -65,104 +65,104 @@
 }
 
 static bool
-android_media_SoundPool_unload(JNIEnv *env, jobject thiz, jint sampleID) {
-    ALOGV("android_media_SoundPool_unload\n");
+android_media_SoundPool_SoundPoolImpl_unload(JNIEnv *env, jobject thiz, jint sampleID) {
+    ALOGV("android_media_SoundPool_SoundPoolImpl_unload\n");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return 0;
     return ap->unload(sampleID);
 }
 
 static int
-android_media_SoundPool_play(JNIEnv *env, jobject thiz, jint sampleID,
+android_media_SoundPool_SoundPoolImpl_play(JNIEnv *env, jobject thiz, jint sampleID,
         jfloat leftVolume, jfloat rightVolume, jint priority, jint loop,
         jfloat rate)
 {
-    ALOGV("android_media_SoundPool_play\n");
+    ALOGV("android_media_SoundPool_SoundPoolImpl_play\n");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return 0;
     return ap->play(sampleID, leftVolume, rightVolume, priority, loop, rate);
 }
 
 static void
-android_media_SoundPool_pause(JNIEnv *env, jobject thiz, jint channelID)
+android_media_SoundPool_SoundPoolImpl_pause(JNIEnv *env, jobject thiz, jint channelID)
 {
-    ALOGV("android_media_SoundPool_pause");
+    ALOGV("android_media_SoundPool_SoundPoolImpl_pause");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return;
     ap->pause(channelID);
 }
 
 static void
-android_media_SoundPool_resume(JNIEnv *env, jobject thiz, jint channelID)
+android_media_SoundPool_SoundPoolImpl_resume(JNIEnv *env, jobject thiz, jint channelID)
 {
-    ALOGV("android_media_SoundPool_resume");
+    ALOGV("android_media_SoundPool_SoundPoolImpl_resume");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return;
     ap->resume(channelID);
 }
 
 static void
-android_media_SoundPool_autoPause(JNIEnv *env, jobject thiz)
+android_media_SoundPool_SoundPoolImpl_autoPause(JNIEnv *env, jobject thiz)
 {
-    ALOGV("android_media_SoundPool_autoPause");
+    ALOGV("android_media_SoundPool_SoundPoolImpl_autoPause");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return;
     ap->autoPause();
 }
 
 static void
-android_media_SoundPool_autoResume(JNIEnv *env, jobject thiz)
+android_media_SoundPool_SoundPoolImpl_autoResume(JNIEnv *env, jobject thiz)
 {
-    ALOGV("android_media_SoundPool_autoResume");
+    ALOGV("android_media_SoundPool_SoundPoolImpl_autoResume");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return;
     ap->autoResume();
 }
 
 static void
-android_media_SoundPool_stop(JNIEnv *env, jobject thiz, jint channelID)
+android_media_SoundPool_SoundPoolImpl_stop(JNIEnv *env, jobject thiz, jint channelID)
 {
-    ALOGV("android_media_SoundPool_stop");
+    ALOGV("android_media_SoundPool_SoundPoolImpl_stop");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return;
     ap->stop(channelID);
 }
 
 static void
-android_media_SoundPool_setVolume(JNIEnv *env, jobject thiz, jint channelID,
+android_media_SoundPool_SoundPoolImpl_setVolume(JNIEnv *env, jobject thiz, jint channelID,
         float leftVolume, float rightVolume)
 {
-    ALOGV("android_media_SoundPool_setVolume");
+    ALOGV("android_media_SoundPool_SoundPoolImpl_setVolume");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return;
     ap->setVolume(channelID, leftVolume, rightVolume);
 }
 
 static void
-android_media_SoundPool_setPriority(JNIEnv *env, jobject thiz, jint channelID,
+android_media_SoundPool_SoundPoolImpl_setPriority(JNIEnv *env, jobject thiz, jint channelID,
         int priority)
 {
-    ALOGV("android_media_SoundPool_setPriority");
+    ALOGV("android_media_SoundPool_SoundPoolImpl_setPriority");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return;
     ap->setPriority(channelID, priority);
 }
 
 static void
-android_media_SoundPool_setLoop(JNIEnv *env, jobject thiz, jint channelID,
+android_media_SoundPool_SoundPoolImpl_setLoop(JNIEnv *env, jobject thiz, jint channelID,
         int loop)
 {
-    ALOGV("android_media_SoundPool_setLoop");
+    ALOGV("android_media_SoundPool_SoundPoolImpl_setLoop");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return;
     ap->setLoop(channelID, loop);
 }
 
 static void
-android_media_SoundPool_setRate(JNIEnv *env, jobject thiz, jint channelID,
+android_media_SoundPool_SoundPoolImpl_setRate(JNIEnv *env, jobject thiz, jint channelID,
         float rate)
 {
-    ALOGV("android_media_SoundPool_setRate");
+    ALOGV("android_media_SoundPool_SoundPoolImpl_setRate");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap == NULL) return;
     ap->setRate(channelID, rate);
@@ -176,9 +176,9 @@
 }
 
 static jint
-android_media_SoundPool_native_setup(JNIEnv *env, jobject thiz, jobject weakRef, jint maxChannels, jint streamType, jint srcQuality)
+android_media_SoundPool_SoundPoolImpl_native_setup(JNIEnv *env, jobject thiz, jobject weakRef, jint maxChannels, jint streamType, jint srcQuality)
 {
-    ALOGV("android_media_SoundPool_native_setup");
+    ALOGV("android_media_SoundPool_SoundPoolImpl_native_setup");
     SoundPool *ap = new SoundPool(maxChannels, (audio_stream_type_t) streamType, srcQuality);
     if (ap == NULL) {
         return -1;
@@ -194,9 +194,9 @@
 }
 
 static void
-android_media_SoundPool_release(JNIEnv *env, jobject thiz)
+android_media_SoundPool_SoundPoolImpl_release(JNIEnv *env, jobject thiz)
 {
-    ALOGV("android_media_SoundPool_release");
+    ALOGV("android_media_SoundPool_SoundPoolImpl_release");
     SoundPool *ap = MusterSoundPool(env, thiz);
     if (ap != NULL) {
 
@@ -219,67 +219,67 @@
 static JNINativeMethod gMethods[] = {
     {   "_load",
         "(Ljava/lang/String;I)I",
-        (void *)android_media_SoundPool_load_URL
+        (void *)android_media_SoundPool_SoundPoolImpl_load_URL
     },
     {   "_load",
         "(Ljava/io/FileDescriptor;JJI)I",
-        (void *)android_media_SoundPool_load_FD
+        (void *)android_media_SoundPool_SoundPoolImpl_load_FD
     },
     {   "unload",
         "(I)Z",
-        (void *)android_media_SoundPool_unload
+        (void *)android_media_SoundPool_SoundPoolImpl_unload
     },
     {   "play",
         "(IFFIIF)I",
-        (void *)android_media_SoundPool_play
+        (void *)android_media_SoundPool_SoundPoolImpl_play
     },
     {   "pause",
         "(I)V",
-        (void *)android_media_SoundPool_pause
+        (void *)android_media_SoundPool_SoundPoolImpl_pause
     },
     {   "resume",
         "(I)V",
-        (void *)android_media_SoundPool_resume
+        (void *)android_media_SoundPool_SoundPoolImpl_resume
     },
     {   "autoPause",
         "()V",
-        (void *)android_media_SoundPool_autoPause
+        (void *)android_media_SoundPool_SoundPoolImpl_autoPause
     },
     {   "autoResume",
         "()V",
-        (void *)android_media_SoundPool_autoResume
+        (void *)android_media_SoundPool_SoundPoolImpl_autoResume
     },
     {   "stop",
         "(I)V",
-        (void *)android_media_SoundPool_stop
+        (void *)android_media_SoundPool_SoundPoolImpl_stop
     },
     {   "setVolume",
         "(IFF)V",
-        (void *)android_media_SoundPool_setVolume
+        (void *)android_media_SoundPool_SoundPoolImpl_setVolume
     },
     {   "setPriority",
         "(II)V",
-        (void *)android_media_SoundPool_setPriority
+        (void *)android_media_SoundPool_SoundPoolImpl_setPriority
     },
     {   "setLoop",
         "(II)V",
-        (void *)android_media_SoundPool_setLoop
+        (void *)android_media_SoundPool_SoundPoolImpl_setLoop
     },
     {   "setRate",
         "(IF)V",
-        (void *)android_media_SoundPool_setRate
+        (void *)android_media_SoundPool_SoundPoolImpl_setRate
     },
     {   "native_setup",
         "(Ljava/lang/Object;III)I",
-        (void*)android_media_SoundPool_native_setup
+        (void*)android_media_SoundPool_SoundPoolImpl_native_setup
     },
     {   "release",
         "()V",
-        (void*)android_media_SoundPool_release
+        (void*)android_media_SoundPool_SoundPoolImpl_release
     }
 };
 
-static const char* const kClassPathName = "android/media/SoundPool";
+static const char* const kClassPathName = "android/media/SoundPool$SoundPoolImpl";
 
 jint JNI_OnLoad(JavaVM* vm, void* reserved)
 {
@@ -301,14 +301,14 @@
 
     fields.mNativeContext = env->GetFieldID(clazz, "mNativeContext", "I");
     if (fields.mNativeContext == NULL) {
-        ALOGE("Can't find SoundPool.mNativeContext");
+        ALOGE("Can't find SoundPoolImpl.mNativeContext");
         goto bail;
     }
 
     fields.mPostEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
                                                "(Ljava/lang/Object;IIILjava/lang/Object;)V");
     if (fields.mPostEvent == NULL) {
-        ALOGE("Can't find android/media/SoundPool.postEventFromNative");
+        ALOGE("Can't find android/media/SoundPoolImpl.postEventFromNative");
         goto bail;
     }
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 6cd67d6..def65a8 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -42,6 +42,7 @@
 import android.util.Slog;
 import android.view.WindowManager;
 
+import com.android.internal.R;
 import com.android.internal.os.BinderInternal;
 import com.android.internal.os.SamplingProfilerIntegration;
 import com.android.server.accessibility.AccessibilityManagerService;
@@ -196,8 +197,9 @@
             }
         });
 
-        // Critical services...
+        // bootstrap services
         boolean onlyCore = false;
+        boolean firstBoot = false;
         try {
             // Wait for installd to finished starting up so that it has a chance to
             // create critical directories such as /data/user with the appropriate
@@ -212,7 +214,21 @@
 
             Slog.i(TAG, "Activity Manager");
             context = ActivityManagerService.main(factoryTest);
+        } catch (RuntimeException e) {
+            Slog.e("System", "******************************************");
+            Slog.e("System", "************ Failure starting bootstrap service", e);
+        }
 
+        boolean disableStorage = SystemProperties.getBoolean("config.disable_storage", false);
+        boolean disableMedia = SystemProperties.getBoolean("config.disable_media", false);
+        boolean disableBluetooth = SystemProperties.getBoolean("config.disable_bluetooth", false);
+        boolean disableTelephony = SystemProperties.getBoolean("config.disable_telephony", false);
+        boolean disableLocation = SystemProperties.getBoolean("config.disable_location", false);
+        boolean disableSystemUI = SystemProperties.getBoolean("config.disable_systemui", false);
+        boolean disableNonCoreServices = SystemProperties.getBoolean("config.disable_noncore", false);
+        boolean disableNetwork = SystemProperties.getBoolean("config.disable_network", false);
+
+        try {
             Slog.i(TAG, "Display Manager");
             display = new DisplayManagerService(context, wmHandler, uiHandler);
             ServiceManager.addService(Context.DISPLAY_SERVICE, display, true);
@@ -246,7 +262,6 @@
             pm = PackageManagerService.main(context, installer,
                     factoryTest != SystemServer.FACTORY_TEST_OFF,
                     onlyCore);
-            boolean firstBoot = false;
             try {
                 firstBoot = pm.isFirstBoot();
             } catch (RemoteException e) {
@@ -265,6 +280,7 @@
 
             // The AccountManager must come before the ContentService
             try {
+                // TODO: seems like this should be disable-able, but req'd by ContentService
                 Slog.i(TAG, "Account Manager");
                 accountManager = new AccountManagerService(context);
                 ServiceManager.addService(Context.ACCOUNT_SERVICE, accountManager);
@@ -329,12 +345,13 @@
                 Slog.i(TAG, "No Bluetooh Service (emulator)");
             } else if (factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL) {
                 Slog.i(TAG, "No Bluetooth Service (factory test)");
+            } else if (disableBluetooth) {
+                Slog.i(TAG, "Bluetooth Service disabled by config");
             } else {
                 Slog.i(TAG, "Bluetooth Manager Service");
                 bluetooth = new BluetoothManagerService(context);
                 ServiceManager.addService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE, bluetooth);
             }
-
         } catch (RuntimeException e) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting core service", e);
@@ -354,20 +371,23 @@
 
         // Bring up services needed for UI.
         if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
-            try {
-                Slog.i(TAG, "Input Method Service");
-                imm = new InputMethodManagerService(context, wm);
-                ServiceManager.addService(Context.INPUT_METHOD_SERVICE, imm);
-            } catch (Throwable e) {
-                reportWtf("starting Input Manager Service", e);
-            }
+            //if (!disableNonCoreServices) { // TODO: View depends on these; mock them?
+            if (true) {
+                try {
+                    Slog.i(TAG, "Input Method Service");
+                    imm = new InputMethodManagerService(context, wm);
+                    ServiceManager.addService(Context.INPUT_METHOD_SERVICE, imm);
+                } catch (Throwable e) {
+                    reportWtf("starting Input Manager Service", e);
+                }
 
-            try {
-                Slog.i(TAG, "Accessibility Manager");
-                ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,
-                        new AccessibilityManagerService(context));
-            } catch (Throwable e) {
-                reportWtf("starting Accessibility Manager", e);
+                try {
+                    Slog.i(TAG, "Accessibility Manager");
+                    ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,
+                            new AccessibilityManagerService(context));
+                } catch (Throwable e) {
+                    reportWtf("starting Accessibility Manager", e);
+                }
             }
         }
 
@@ -392,7 +412,8 @@
         }
 
         if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
-            if (!"0".equals(SystemProperties.get("system_init.startmountservice"))) {
+            if (!disableStorage &&
+                !"0".equals(SystemProperties.get("system_init.startmountservice"))) {
                 try {
                     /*
                      * NotificationManagerService is dependant on MountService,
@@ -406,116 +427,130 @@
                 }
             }
 
-            try {
-                Slog.i(TAG,  "LockSettingsService");
-                lockSettings = new LockSettingsService(context);
-                ServiceManager.addService("lock_settings", lockSettings);
-            } catch (Throwable e) {
-                reportWtf("starting LockSettingsService service", e);
+            if (!disableNonCoreServices) {
+                try {
+                    Slog.i(TAG,  "LockSettingsService");
+                    lockSettings = new LockSettingsService(context);
+                    ServiceManager.addService("lock_settings", lockSettings);
+                } catch (Throwable e) {
+                    reportWtf("starting LockSettingsService service", e);
+                }
+
+                try {
+                    Slog.i(TAG, "Device Policy");
+                    devicePolicy = new DevicePolicyManagerService(context);
+                    ServiceManager.addService(Context.DEVICE_POLICY_SERVICE, devicePolicy);
+                } catch (Throwable e) {
+                    reportWtf("starting DevicePolicyService", e);
+                }
             }
 
-            try {
-                Slog.i(TAG, "Device Policy");
-                devicePolicy = new DevicePolicyManagerService(context);
-                ServiceManager.addService(Context.DEVICE_POLICY_SERVICE, devicePolicy);
-            } catch (Throwable e) {
-                reportWtf("starting DevicePolicyService", e);
+            if (!disableSystemUI) {
+                try {
+                    Slog.i(TAG, "Status Bar");
+                    statusBar = new StatusBarManagerService(context, wm);
+                    ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
+                } catch (Throwable e) {
+                    reportWtf("starting StatusBarManagerService", e);
+                }
             }
 
-            try {
-                Slog.i(TAG, "Status Bar");
-                statusBar = new StatusBarManagerService(context, wm);
-                ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
-            } catch (Throwable e) {
-                reportWtf("starting StatusBarManagerService", e);
+            if (!disableNonCoreServices) {
+                try {
+                    Slog.i(TAG, "Clipboard Service");
+                    ServiceManager.addService(Context.CLIPBOARD_SERVICE,
+                            new ClipboardService(context));
+                } catch (Throwable e) {
+                    reportWtf("starting Clipboard Service", e);
+                }
             }
 
-            try {
-                Slog.i(TAG, "Clipboard Service");
-                ServiceManager.addService(Context.CLIPBOARD_SERVICE,
-                        new ClipboardService(context));
-            } catch (Throwable e) {
-                reportWtf("starting Clipboard Service", e);
+            if (!disableNetwork) {
+                try {
+                    Slog.i(TAG, "NetworkManagement Service");
+                    networkManagement = NetworkManagementService.create(context);
+                    ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
+                } catch (Throwable e) {
+                    reportWtf("starting NetworkManagement Service", e);
+                }
             }
 
-            try {
-                Slog.i(TAG, "NetworkManagement Service");
-                networkManagement = NetworkManagementService.create(context);
-                ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE, networkManagement);
-            } catch (Throwable e) {
-                reportWtf("starting NetworkManagement Service", e);
+            if (!disableNonCoreServices) {
+                try {
+                    Slog.i(TAG, "Text Service Manager Service");
+                    tsms = new TextServicesManagerService(context);
+                    ServiceManager.addService(Context.TEXT_SERVICES_MANAGER_SERVICE, tsms);
+                } catch (Throwable e) {
+                    reportWtf("starting Text Service Manager Service", e);
+                }
             }
 
-            try {
-                Slog.i(TAG, "Text Service Manager Service");
-                tsms = new TextServicesManagerService(context);
-                ServiceManager.addService(Context.TEXT_SERVICES_MANAGER_SERVICE, tsms);
-            } catch (Throwable e) {
-                reportWtf("starting Text Service Manager Service", e);
+            if (!disableNetwork) {
+                try {
+                    Slog.i(TAG, "NetworkStats Service");
+                    networkStats = new NetworkStatsService(context, networkManagement, alarm);
+                    ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
+                } catch (Throwable e) {
+                    reportWtf("starting NetworkStats Service", e);
+                }
+
+                try {
+                    Slog.i(TAG, "NetworkPolicy Service");
+                    networkPolicy = new NetworkPolicyManagerService(
+                            context, ActivityManagerService.self(), power,
+                            networkStats, networkManagement);
+                    ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy);
+                } catch (Throwable e) {
+                    reportWtf("starting NetworkPolicy Service", e);
+                }
+
+               try {
+                    Slog.i(TAG, "Wi-Fi P2pService");
+                    wifiP2p = new WifiP2pService(context);
+                    ServiceManager.addService(Context.WIFI_P2P_SERVICE, wifiP2p);
+                } catch (Throwable e) {
+                    reportWtf("starting Wi-Fi P2pService", e);
+                }
+
+               try {
+                    Slog.i(TAG, "Wi-Fi Service");
+                    wifi = new WifiService(context);
+                    ServiceManager.addService(Context.WIFI_SERVICE, wifi);
+                } catch (Throwable e) {
+                    reportWtf("starting Wi-Fi Service", e);
+                }
+
+                try {
+                    Slog.i(TAG, "Connectivity Service");
+                    connectivity = new ConnectivityService(
+                            context, networkManagement, networkStats, networkPolicy);
+                    ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
+                    networkStats.bindConnectivityManager(connectivity);
+                    networkPolicy.bindConnectivityManager(connectivity);
+                    wifi.checkAndStartWifi();
+                    wifiP2p.connectivityServiceReady();
+                } catch (Throwable e) {
+                    reportWtf("starting Connectivity Service", e);
+                }
+
+                try {
+                    Slog.i(TAG, "Network Service Discovery Service");
+                    serviceDiscovery = NsdService.create(context);
+                    ServiceManager.addService(
+                            Context.NSD_SERVICE, serviceDiscovery);
+                } catch (Throwable e) {
+                    reportWtf("starting Service Discovery Service", e);
+                }
             }
 
-            try {
-                Slog.i(TAG, "NetworkStats Service");
-                networkStats = new NetworkStatsService(context, networkManagement, alarm);
-                ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
-            } catch (Throwable e) {
-                reportWtf("starting NetworkStats Service", e);
-            }
-
-            try {
-                Slog.i(TAG, "NetworkPolicy Service");
-                networkPolicy = new NetworkPolicyManagerService(
-                        context, ActivityManagerService.self(), power,
-                        networkStats, networkManagement);
-                ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy);
-            } catch (Throwable e) {
-                reportWtf("starting NetworkPolicy Service", e);
-            }
-
-           try {
-                Slog.i(TAG, "Wi-Fi P2pService");
-                wifiP2p = new WifiP2pService(context);
-                ServiceManager.addService(Context.WIFI_P2P_SERVICE, wifiP2p);
-            } catch (Throwable e) {
-                reportWtf("starting Wi-Fi P2pService", e);
-            }
-
-           try {
-                Slog.i(TAG, "Wi-Fi Service");
-                wifi = new WifiService(context);
-                ServiceManager.addService(Context.WIFI_SERVICE, wifi);
-            } catch (Throwable e) {
-                reportWtf("starting Wi-Fi Service", e);
-            }
-
-            try {
-                Slog.i(TAG, "Connectivity Service");
-                connectivity = new ConnectivityService(
-                        context, networkManagement, networkStats, networkPolicy);
-                ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
-                networkStats.bindConnectivityManager(connectivity);
-                networkPolicy.bindConnectivityManager(connectivity);
-                wifi.checkAndStartWifi();
-                wifiP2p.connectivityServiceReady();
-            } catch (Throwable e) {
-                reportWtf("starting Connectivity Service", e);
-            }
-
-            try {
-                Slog.i(TAG, "Network Service Discovery Service");
-                serviceDiscovery = NsdService.create(context);
-                ServiceManager.addService(
-                        Context.NSD_SERVICE, serviceDiscovery);
-            } catch (Throwable e) {
-                reportWtf("starting Service Discovery Service", e);
-            }
-
-            try {
-                Slog.i(TAG, "UpdateLock Service");
-                ServiceManager.addService(Context.UPDATE_LOCK_SERVICE,
-                        new UpdateLockService(context));
-            } catch (Throwable e) {
-                reportWtf("starting UpdateLockService", e);
+            if (!disableNonCoreServices) {
+                try {
+                    Slog.i(TAG, "UpdateLock Service");
+                    ServiceManager.addService(Context.UPDATE_LOCK_SERVICE,
+                            new UpdateLockService(context));
+                } catch (Throwable e) {
+                    reportWtf("starting UpdateLockService", e);
+                }
             }
 
             /*
@@ -558,28 +593,32 @@
                 reportWtf("starting DeviceStorageMonitor service", e);
             }
 
-            try {
-                Slog.i(TAG, "Location Manager");
-                location = new LocationManagerService(context);
-                ServiceManager.addService(Context.LOCATION_SERVICE, location);
-            } catch (Throwable e) {
-                reportWtf("starting Location Manager", e);
+            if (!disableLocation) {
+                try {
+                    Slog.i(TAG, "Location Manager");
+                    location = new LocationManagerService(context);
+                    ServiceManager.addService(Context.LOCATION_SERVICE, location);
+                } catch (Throwable e) {
+                    reportWtf("starting Location Manager", e);
+                }
+
+                try {
+                    Slog.i(TAG, "Country Detector");
+                    countryDetector = new CountryDetectorService(context);
+                    ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector);
+                } catch (Throwable e) {
+                    reportWtf("starting Country Detector", e);
+                }
             }
 
-            try {
-                Slog.i(TAG, "Country Detector");
-                countryDetector = new CountryDetectorService(context);
-                ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector);
-            } catch (Throwable e) {
-                reportWtf("starting Country Detector", e);
-            }
-
-            try {
-                Slog.i(TAG, "Search Service");
-                ServiceManager.addService(Context.SEARCH_SERVICE,
-                        new SearchManagerService(context));
-            } catch (Throwable e) {
-                reportWtf("starting Search Service", e);
+            if (!disableNonCoreServices) {
+                try {
+                    Slog.i(TAG, "Search Service");
+                    ServiceManager.addService(Context.SEARCH_SERVICE,
+                            new SearchManagerService(context));
+                } catch (Throwable e) {
+                    reportWtf("starting Search Service", e);
+                }
             }
 
             try {
@@ -590,8 +629,8 @@
                 reportWtf("starting DropBoxManagerService", e);
             }
 
-            if (context.getResources().getBoolean(
-                        com.android.internal.R.bool.config_enableWallpaperService)) {
+            if (!disableNonCoreServices && context.getResources().getBoolean(
+                        R.bool.config_enableWallpaperService)) {
                 try {
                     Slog.i(TAG, "Wallpaper Service");
                     if (!headless) {
@@ -603,7 +642,7 @@
                 }
             }
 
-            if (!"0".equals(SystemProperties.get("system_init.startaudioservice"))) {
+            if (!disableMedia && !"0".equals(SystemProperties.get("system_init.startaudioservice"))) {
                 try {
                     Slog.i(TAG, "Audio Service");
                     ServiceManager.addService(Context.AUDIO_SERVICE, new AudioService(context));
@@ -612,39 +651,45 @@
                 }
             }
 
-            try {
-                Slog.i(TAG, "Dock Observer");
-                // Listen for dock station changes
-                dock = new DockObserver(context);
-            } catch (Throwable e) {
-                reportWtf("starting DockObserver", e);
+            if (!disableNonCoreServices) {
+                try {
+                    Slog.i(TAG, "Dock Observer");
+                    // Listen for dock station changes
+                    dock = new DockObserver(context);
+                } catch (Throwable e) {
+                    reportWtf("starting DockObserver", e);
+                }
             }
 
-            try {
-                Slog.i(TAG, "Wired Accessory Manager");
-                // Listen for wired headset changes
-                inputManager.setWiredAccessoryCallbacks(
-                        new WiredAccessoryManager(context, inputManager));
-            } catch (Throwable e) {
-                reportWtf("starting WiredAccessoryManager", e);
+            if (!disableMedia) {
+                try {
+                    Slog.i(TAG, "Wired Accessory Manager");
+                    // Listen for wired headset changes
+                    inputManager.setWiredAccessoryCallbacks(
+                            new WiredAccessoryManager(context, inputManager));
+                } catch (Throwable e) {
+                    reportWtf("starting WiredAccessoryManager", e);
+                }
             }
 
-            try {
-                Slog.i(TAG, "USB Service");
-                // Manage USB host and device support
-                usb = new UsbService(context);
-                ServiceManager.addService(Context.USB_SERVICE, usb);
-            } catch (Throwable e) {
-                reportWtf("starting UsbService", e);
-            }
+            if (!disableNonCoreServices) {
+                try {
+                    Slog.i(TAG, "USB Service");
+                    // Manage USB host and device support
+                    usb = new UsbService(context);
+                    ServiceManager.addService(Context.USB_SERVICE, usb);
+                } catch (Throwable e) {
+                    reportWtf("starting UsbService", e);
+                }
 
-            try {
-                Slog.i(TAG, "Serial Service");
-                // Serial port support
-                serial = new SerialService(context);
-                ServiceManager.addService(Context.SERIAL_SERVICE, serial);
-            } catch (Throwable e) {
-                Slog.e(TAG, "Failure starting SerialService", e);
+                try {
+                    Slog.i(TAG, "Serial Service");
+                    // Serial port support
+                    serial = new SerialService(context);
+                    ServiceManager.addService(Context.SERIAL_SERVICE, serial);
+                } catch (Throwable e) {
+                    Slog.e(TAG, "Failure starting SerialService", e);
+                }
             }
 
             try {
@@ -662,27 +707,29 @@
                 reportWtf("starting UiModeManagerService", e);
             }
 
-            try {
-                Slog.i(TAG, "Backup Service");
-                ServiceManager.addService(Context.BACKUP_SERVICE,
-                        new BackupManagerService(context));
-            } catch (Throwable e) {
-                Slog.e(TAG, "Failure starting Backup Service", e);
-            }
+            if (!disableNonCoreServices) {
+                try {
+                    Slog.i(TAG, "Backup Service");
+                    ServiceManager.addService(Context.BACKUP_SERVICE,
+                            new BackupManagerService(context));
+                } catch (Throwable e) {
+                    Slog.e(TAG, "Failure starting Backup Service", e);
+                }
 
-            try {
-                Slog.i(TAG, "AppWidget Service");
-                appWidget = new AppWidgetService(context);
-                ServiceManager.addService(Context.APPWIDGET_SERVICE, appWidget);
-            } catch (Throwable e) {
-                reportWtf("starting AppWidget Service", e);
-            }
+                try {
+                    Slog.i(TAG, "AppWidget Service");
+                    appWidget = new AppWidgetService(context);
+                    ServiceManager.addService(Context.APPWIDGET_SERVICE, appWidget);
+                } catch (Throwable e) {
+                    reportWtf("starting AppWidget Service", e);
+                }
 
-            try {
-                Slog.i(TAG, "Recognition Service");
-                recognition = new RecognitionManagerService(context);
-            } catch (Throwable e) {
-                reportWtf("starting Recognition Service", e);
+                try {
+                    Slog.i(TAG, "Recognition Service");
+                    recognition = new RecognitionManagerService(context);
+                } catch (Throwable e) {
+                    reportWtf("starting Recognition Service", e);
+                }
             }
 
             try {
@@ -704,30 +751,36 @@
                 reportWtf("starting SamplingProfiler Service", e);
             }
 
-            try {
-                Slog.i(TAG, "NetworkTimeUpdateService");
-                networkTimeUpdater = new NetworkTimeUpdateService(context);
-            } catch (Throwable e) {
-                reportWtf("starting NetworkTimeUpdate service", e);
+            if (!disableNetwork) {
+                try {
+                    Slog.i(TAG, "NetworkTimeUpdateService");
+                    networkTimeUpdater = new NetworkTimeUpdateService(context);
+                } catch (Throwable e) {
+                    reportWtf("starting NetworkTimeUpdate service", e);
+                }
             }
 
-            try {
-                Slog.i(TAG, "CommonTimeManagementService");
-                commonTimeMgmtService = new CommonTimeManagementService(context);
-                ServiceManager.addService("commontime_management", commonTimeMgmtService);
-            } catch (Throwable e) {
-                reportWtf("starting CommonTimeManagementService service", e);
+            if (!disableMedia) {
+                try {
+                    Slog.i(TAG, "CommonTimeManagementService");
+                    commonTimeMgmtService = new CommonTimeManagementService(context);
+                    ServiceManager.addService("commontime_management", commonTimeMgmtService);
+                } catch (Throwable e) {
+                    reportWtf("starting CommonTimeManagementService service", e);
+                }
             }
 
-            try {
-                Slog.i(TAG, "CertBlacklister");
-                CertBlacklister blacklister = new CertBlacklister(context);
-            } catch (Throwable e) {
-                reportWtf("starting CertBlacklister", e);
+            if (!disableNetwork) {
+                try {
+                    Slog.i(TAG, "CertBlacklister");
+                    CertBlacklister blacklister = new CertBlacklister(context);
+                } catch (Throwable e) {
+                    reportWtf("starting CertBlacklister", e);
+                }
             }
 
-            if (context.getResources().getBoolean(
-                    com.android.internal.R.bool.config_dreamsSupported)) {
+            if (!disableNonCoreServices && 
+                context.getResources().getBoolean(R.bool.config_dreamsSupported)) {
                 try {
                     Slog.i(TAG, "Dreams Service");
                     // Dreams (interactive idle-time views, a/k/a screen savers)
@@ -768,10 +821,12 @@
             reportWtf("making Vibrator Service ready", e);
         }
 
-        try {
-            lockSettings.systemReady();
-        } catch (Throwable e) {
-            reportWtf("making Lock Settings Service ready", e);
+        if (lockSettings != null) {
+            try {
+                lockSettings.systemReady();
+            } catch (Throwable e) {
+                reportWtf("making Lock Settings Service ready", e);
+            }
         }
 
         if (devicePolicy != null) {
@@ -981,6 +1036,7 @@
                 } catch (Throwable e) {
                     reportWtf("making InputManagerService ready", e);
                 }
+
                 try {
                     if (telephonyRegistryF != null) telephonyRegistryF.systemReady();
                 } catch (Throwable e) {
diff --git a/services/java/com/android/server/content/ContentService.java b/services/java/com/android/server/content/ContentService.java
index 68cf5fc..30c3219 100644
--- a/services/java/com/android/server/content/ContentService.java
+++ b/services/java/com/android/server/content/ContentService.java
@@ -36,6 +36,7 @@
 import android.os.Parcel;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.util.Log;
 import android.util.Slog;
@@ -61,6 +62,10 @@
     private final Object mSyncManagerLock = new Object();
 
     private SyncManager getSyncManager() {
+        if (SystemProperties.getBoolean("config.disable_network", false)) {
+            return null;
+        }
+
         synchronized(mSyncManagerLock) {
             try {
                 // Try to create the SyncManager, return null if it fails (e.g. the disk is full).