Merge "Use Tremor for sim-eng, and Tremolo for other targets."
diff --git a/api/current.xml b/api/current.xml
index c54fc98..77298ed 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -29824,6 +29824,8 @@
  deprecated="not deprecated"
  visibility="public"
 >
+<implements name="java.lang.Cloneable">
+</implements>
 <implements name="java.lang.Comparable">
 </implements>
 <implements name="android.os.Parcelable">
@@ -29874,6 +29876,17 @@
 <parameter name="in" type="android.os.Parcel">
 </parameter>
 </constructor>
+<method name="clone"
+ return="android.content.ComponentName"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="compareTo"
  return="int"
  abstract="false"
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 5f89496..45d7546 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2166,7 +2166,7 @@
                             filter, null, null, null);
                     // Register for events related to sdcard installation.
                     IntentFilter sdFilter = new IntentFilter();
-                    sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+                    sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
                     mContext.registerReceiverInternal(sPackageRemovedReceiver,
                             sdFilter, null, null, null);
                 }
@@ -2189,7 +2189,7 @@
                 String pkgList[] = null;
                 String action = intent.getAction();
                 boolean immediateGc = false;
-                if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+                if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                     immediateGc = true;
                 } else {
diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java
index 0455202..c4ba05d 100644
--- a/core/java/android/content/ComponentName.java
+++ b/core/java/android/content/ComponentName.java
@@ -30,7 +30,7 @@
  * name inside of that package.
  * 
  */
-public final class ComponentName implements Parcelable, Comparable<ComponentName> {
+public final class ComponentName implements Parcelable, Cloneable, Comparable<ComponentName> {
     private final String mPackage;
     private final String mClass;
 
@@ -76,6 +76,10 @@
         mClass = cls.getName();
     }
 
+    public ComponentName clone() {
+        return new ComponentName(mPackage, mClass);
+    }
+
     /**
      * Return the package name of this component.
      */
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e957e20..c32999f 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1381,8 +1381,8 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_MEDIA_RESOURCES_AVAILABLE =
-        "android.intent.action.MEDIA_RESOURCES_AVAILABILE";
+    public static final String ACTION_EXTERNAL_APPLICATIONS_AVAILABLE =
+        "android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE";
 
     /**
      * Broadcast Action: Resources for a set of packages are currently
@@ -1406,8 +1406,8 @@
      * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_MEDIA_RESOURCES_UNAVAILABLE =
-        "android.intent.action.MEDIA_RESOURCES_UNAVAILABILE";
+    public static final String ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE =
+        "android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABILE";
 
     /**
      * Broadcast Action:  The current system wallpaper has changed.  See
@@ -2198,8 +2198,8 @@
 
     /**
      * This field is part of
-     * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_AVAILABLE},
-     * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_UNAVAILABLE}
+     * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_AVAILABLE},
+     * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE}
      * and contains a string array of all of the components that have changed.
      * @hide
      */
@@ -2208,8 +2208,8 @@
 
     /**
      * This field is part of
-     * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_AVAILABLE},
-     * {@link android.content.Intent#ACTION_MEDIA_RESOURCES_UNAVAILABLE}
+     * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_AVAILABLE},
+     * {@link android.content.Intent#ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE}
      * and contains an integer array of uids of all of the components
      * that have changed.
      * @hide
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 745628a..e3b1694 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -437,6 +437,15 @@
     public static final int INSTALL_FAILED_CONTAINER_ERROR = -18;
 
     /**
+     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
+     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
+     * the new package couldn't be installed in the specified install
+     * location.
+     * @hide
+     */
+    public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19;
+
+    /**
      * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
      * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
      * if the parser was given a path that is not a file, or does not end with the expected
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 7362394..a885820 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -117,8 +117,8 @@
         mContext.registerReceiver(receiver, intentFilter);
         // Register for events related to sdcard installation.
         IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(receiver, sdFilter);
     }
 
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index b0c3909..cecacaa 100755
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -620,7 +620,10 @@
         if (mBuffer == null || mKeyboardChanged) {
             if (mBuffer == null || mKeyboardChanged &&
                     (mBuffer.getWidth() != getWidth() || mBuffer.getHeight() != getHeight())) {
-                mBuffer = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
+                // Make sure our bitmap is at least 1x1
+                final int width = Math.max(1, getWidth());
+                final int height = Math.max(1, getHeight());
+                mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
                 mCanvas = new Canvas(mBuffer);
             }
             invalidateAllKeys();
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index 324fbaa..fbc4a81 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -75,8 +75,8 @@
         mContext.registerReceiver(mPackageChangedReceiver, packageFilter);
         // Register for events related to sdcard installation.
         IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(mPackageChangedReceiver, sdFilter);
     }
 
@@ -96,8 +96,8 @@
             if (Intent.ACTION_PACKAGE_ADDED.equals(action) ||
                     Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
                     Intent.ACTION_PACKAGE_CHANGED.equals(action) ||
-                    Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action) ||
-                    Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+                    Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action) ||
+                    Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                 if (DBG) Log.d(TAG, "Got " + action);
                 // Update list of searchable activities
                 getSearchables().buildSearchableList();
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 6627973..554c8c0 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -26,6 +26,7 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Interpolator;
 import android.graphics.Picture;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -2972,8 +2973,16 @@
         if (mTitleBar != null) {
             canvas.translate(0, (int) mTitleBar.getHeight());
         }
-        if (mDragTrackerHandler == null || !mDragTrackerHandler.draw(canvas)) {
+        if (mDragTrackerHandler == null) {
             drawContent(canvas);
+        } else {
+            if (!mDragTrackerHandler.draw(canvas)) {
+                // sometimes the tracker doesn't draw, even though its active
+                drawContent(canvas);
+            }
+            if (mDragTrackerHandler.isFinished()) {
+                mDragTrackerHandler = null;
+            }
         }
         canvas.restoreToCount(saveCount);
 
@@ -4066,6 +4075,14 @@
         private final float mMaxDY, mMaxDX;
         private float mCurrStretchY, mCurrStretchX;
         private int mSX, mSY;
+        private Interpolator mInterp;
+        private float[] mXY = new float[2];
+
+        // inner (non-state) classes can't have enums :(
+        private static final int DRAGGING_STATE = 0;
+        private static final int ANIMATING_STATE = 1;
+        private static final int FINISHED_STATE = 2;
+        private int mState;
 
         public DragTrackerHandler(float x, float y, DragTracker proxy) {
             mProxy = proxy;
@@ -4090,6 +4107,7 @@
             mMinDX = -viewLeft;
             mMaxDX = docRight - viewRight;
 
+            mState = DRAGGING_STATE;
             mProxy.onStartDrag(x, y);
 
             // ensure we buildBitmap at least once
@@ -4112,6 +4130,12 @@
             float sy = computeStretch(mStartY - y, mMinDY, mMaxDY);
             float sx = computeStretch(mStartX - x, mMinDX, mMaxDX);
 
+            if ((mSnapScrollMode & SNAP_X) != 0) {
+                sy = 0;
+            } else if ((mSnapScrollMode & SNAP_Y) != 0) {
+                sx = 0;
+            }
+
             if (mCurrStretchX != sx || mCurrStretchY != sy) {
                 mCurrStretchX = sx;
                 mCurrStretchY = sy;
@@ -4126,10 +4150,26 @@
         }
 
         public void stopDrag() {
+            final int DURATION = 200;
+            int now = (int)SystemClock.uptimeMillis();
+            mInterp = new Interpolator(2);
+            mXY[0] = mCurrStretchX;
+            mXY[1] = mCurrStretchY;
+         //   float[] blend = new float[] { 0.5f, 0, 0.75f, 1 };
+            float[] blend = new float[] { 0, 0.5f, 0.75f, 1 };
+            mInterp.setKeyFrame(0, now, mXY, blend);
+            float[] zerozero = new float[] { 0, 0 };
+            mInterp.setKeyFrame(1, now + DURATION, zerozero, null);
+            mState = ANIMATING_STATE;
+
             if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
-                Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "----- stopDrag");
+                Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "----- stopDrag, starting animation");
             }
-            mProxy.onStopDrag();
+        }
+
+        // Call this after each draw. If it ruturns null, the tracker is done
+        public boolean isFinished() {
+            return mState == FINISHED_STATE;
         }
 
         private int hiddenHeightOfTitleBar() {
@@ -4150,13 +4190,23 @@
             if (mCurrStretchX != 0 || mCurrStretchY != 0) {
                 int sx = getScrollX();
                 int sy = getScrollY() - hiddenHeightOfTitleBar();
-
                 if (mSX != sx || mSY != sy) {
                     buildBitmap(sx, sy);
                     mSX = sx;
                     mSY = sy;
                 }
 
+                if (mState == ANIMATING_STATE) {
+                    Interpolator.Result result = mInterp.timeToValues(mXY);
+                    if (result == Interpolator.Result.FREEZE_END) {
+                        mState = FINISHED_STATE;
+                        return false;
+                    } else {
+                        mProxy.onStretchChange(mXY[0], mXY[1]);
+                        invalidate();
+                        // fall through to the draw
+                    }
+                }
                 int count = canvas.save(Canvas.MATRIX_SAVE_FLAG);
                 canvas.translate(sx, sy);
                 mProxy.onDraw(canvas);
@@ -4600,7 +4650,6 @@
             case MotionEvent.ACTION_UP: {
                 if (mDragTrackerHandler != null) {
                     mDragTrackerHandler.stopDrag();
-                    mDragTrackerHandler = null;
                 }
                 mLastTouchUpTime = eventTime;
                 switch (mTouchMode) {
@@ -4712,7 +4761,6 @@
     private void cancelTouch() {
         if (mDragTrackerHandler != null) {
             mDragTrackerHandler.stopDrag();
-            mDragTrackerHandler = null;
         }
         // we also use mVelocityTracker == null to tell us that we are
         // not "moving around", so we can take the slower/prettier
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index fd6af05..2f96aef 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1953,6 +1953,7 @@
                 }
             }
         }
+        mLastTouchMode = isInTouchMode ? TOUCH_MODE_ON : TOUCH_MODE_OFF;
     }
 
     @Override
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 8d7e187..6bc6f2e 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -74,6 +74,10 @@
     <uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_TRANSITION_TYPES" />
     <uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_NOTIFICATION_TYPES" />
 
+    <!-- package manager test permissions -->
+    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+    <uses-permission android:name="android.permission.DELETE_PACKAGES" />
+
     <application android:theme="@style/Theme">
         <uses-library android:name="android.test.runner" />
         <activity android:name="StubTestBrowserActivity" android:label="Stubbed Test Browser">
diff --git a/include/media/mediametadataretriever.h b/include/media/mediametadataretriever.h
index 113c452..022b849 100644
--- a/include/media/mediametadataretriever.h
+++ b/include/media/mediametadataretriever.h
@@ -54,6 +54,7 @@
     METADATA_KEY_VIDEO_WIDTH     = 20,
     METADATA_KEY_WRITER          = 21,
     METADATA_KEY_MIMETYPE        = 22,
+    METADATA_KEY_DISC_NUMBER     = 23,
     // Add more here...
 };
 
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 3b21468..8f423f7 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -59,6 +59,7 @@
     kKeyAlbumArtMIME      = 'alAM',  // cstring
     kKeyAuthor            = 'auth',  // cstring
     kKeyCDTrackNumber     = 'cdtr',  // cstring
+    kKeyDiscNumber        = 'dnum',  // cstring
     kKeyDate              = 'date',  // cstring
     kKeyWriter            = 'writ',  // cstring
 };
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index accd94d..79b7674 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -737,6 +737,7 @@
         { kKeyYear, "TYE", "TYER" },
         { kKeyAuthor, "TXT", "TEXT" },
         { kKeyCDTrackNumber, "TRK", "TRCK" },
+        { kKeyDiscNumber, "TPA", "TPOS" },
     };
     static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);
 
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index 3451383..22f701c 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -191,6 +191,7 @@
         };
         static const KeyMap kKeyMap[] = {
             { "tracknumber", METADATA_KEY_CD_TRACK_NUMBER },
+            { "discnumber", METADATA_KEY_DISC_NUMBER },
             { "album", METADATA_KEY_ALBUM },
             { "artist", METADATA_KEY_ARTIST },
             { "composer", METADATA_KEY_COMPOSER },
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index dfba74f..f617fe8 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -305,6 +305,7 @@
     static const Map kMap[] = {
         { kKeyMIMEType, METADATA_KEY_MIMETYPE },
         { kKeyCDTrackNumber, METADATA_KEY_CD_TRACK_NUMBER },
+        { kKeyDiscNumber, METADATA_KEY_DISC_NUMBER },
         { kKeyAlbum, METADATA_KEY_ALBUM },
         { kKeyArtist, METADATA_KEY_ARTIST },
         { kKeyAuthor, METADATA_KEY_AUTHOR },
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
index b7eea2e..bca736a2e 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -133,6 +133,7 @@
     private HashMap<String, ITtsCallback> mCallbacksMap;
 
     private Boolean mIsSpeaking;
+    private Boolean mSynthBusy;
     private ArrayList<SpeechItem> mSpeechQueue;
     private HashMap<String, SoundResource> mEarcons;
     private HashMap<String, SoundResource> mUtterances;
@@ -168,6 +169,7 @@
 
         mSelf = this;
         mIsSpeaking = false;
+        mSynthBusy = false;
 
         mEarcons = new HashMap<String, SoundResource>();
         mUtterances = new HashMap<String, SoundResource>();
@@ -733,10 +735,11 @@
                 try {
                     synthAvailable = synthesizerLock.tryLock();
                     if (!synthAvailable) {
+                        mSynthBusy = true;
                         Thread.sleep(100);
                         Thread synth = (new Thread(new SynthThread()));
-                        //synth.setPriority(Thread.MIN_PRIORITY);
                         synth.start();
+                        mSynthBusy = false;
                         return;
                     }
                     int streamType = DEFAULT_STREAM_TYPE;
@@ -821,10 +824,11 @@
                 try {
                     synthAvailable = synthesizerLock.tryLock();
                     if (!synthAvailable) {
+                        mSynthBusy = true;
                         Thread.sleep(100);
                         Thread synth = (new Thread(new SynthThread()));
-                        //synth.setPriority(Thread.MIN_PRIORITY);
                         synth.start();
+                        mSynthBusy = false;
                         return;
                     }
                     String language = "";
@@ -959,6 +963,12 @@
 
     private void processSpeechQueue() {
         boolean speechQueueAvailable = false;
+        synchronized (this) {
+            if (mSynthBusy){
+                // There is already a synth thread waiting to run.
+                return;
+            }
+        }
         try {
             speechQueueAvailable =
                     speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT, TimeUnit.MILLISECONDS);
diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/AccessibilityManagerService.java
index b56b13b..407983d 100644
--- a/services/java/com/android/server/AccessibilityManagerService.java
+++ b/services/java/com/android/server/AccessibilityManagerService.java
@@ -176,8 +176,8 @@
         context.registerReceiver(broadcastReceiver, packageFilter);
         // Register for events related to sdcard installation.
         IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(broadcastReceiver, sdFilter);
 
         // boot completed
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index c28cf44..44cc0bb 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -782,7 +782,7 @@
             mContext.registerReceiver(this, filter);
              // Register for events related to sdcard installation.
             IntentFilter sdFilter = new IntentFilter();
-            sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+            sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
             mContext.registerReceiver(this, sdFilter);
         }
         
@@ -791,7 +791,7 @@
             synchronized (mLock) {
                 String action = intent.getAction();
                 String pkgList[] = null;
-                if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+                if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                 } else {
                     Uri data = intent.getData();
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 684f117..f336d1f 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -147,8 +147,8 @@
         mContext.registerReceiver(mBroadcastReceiver, filter);
         // Register for events related to sdcard installation.
         IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(mBroadcastReceiver, sdFilter);
     }
 
@@ -1077,10 +1077,10 @@
             } else {
                 boolean added = false;
                 String pkgList[] = null;
-                if (Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action)) {
+                if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                     added = true;
-                } if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+                } if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                     added = false;
                 } else  {
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 2f845e1..24187a5 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -565,8 +565,8 @@
         mContext.registerReceiver(mBroadcastReceiver, filter);
         // Register for events related to sdcard installation.
         IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(mBroadcastReceiver, sdFilter);
     }
 
@@ -724,10 +724,10 @@
                 }
                 added = Intent.ACTION_PACKAGE_ADDED.equals(action);
                 replacing = extras.getBoolean(Intent.EXTRA_REPLACING, false);
-            } else if (Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action)) {
+            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
                 added = true;
                 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
-            } else if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                 added = false;
                 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
             }
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index dc942a2..5e96a2f 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -344,8 +344,8 @@
                 if (pkg != null) {
                     pkgList = new String[] { pkg };
                 }
-            } else if (Intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(action) ||
-                    Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(action)) {
+            } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action) ||
+                    Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
                 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
             }
             if (pkgList == null || pkgList.length == 0) {
@@ -448,8 +448,8 @@
         mContext.registerReceiver(mBroadcastReceiver, packageFilt);
         // Register for events related to sdcard installation.
         IntentFilter sdFilter = new IntentFilter();
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_AVAILABLE);
-        sdFilter.addAction(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(mBroadcastReceiver, sdFilter);
 
         IntentFilter screenOnOffFilt = new IntentFilter();
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 3a42b37..990a77a 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -484,7 +484,7 @@
         intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
         mContext.registerReceiver(mBroadcastReceiver, intentFilter);
-        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(mBroadcastReceiver, sdFilter);
 
         // listen for settings changes
@@ -1552,10 +1552,10 @@
 
             if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
-                    || action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) {
+                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
                 synchronized (mLock) {
                     int uidList[] = null;
-                    if (action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) {
+                    if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
                         uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
                     } else {
                         uidList = new int[]{intent.getIntExtra(Intent.EXTRA_UID, -1)};
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 6382646..51abfc3 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -670,7 +670,6 @@
      */
     public int mountVolume(String path) {
         validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
-
         int rc = MountServiceResultCode.OperationSucceeded;
 
         try {
@@ -721,10 +720,21 @@
     public int unmountVolume(String path) {
         validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
 
+        // Check if media has been mounted
+        String oldState = mLegacyState;
+        if (!oldState.equals(Environment.MEDIA_MOUNTED)) {
+            return VoldResponseCode.OpFailedVolNotMounted;
+        }
+        // Notify PackageManager of potential media removal and deal with
+        // return code later on. The caller of this api should be aware or have been
+        // notified that the applications installed on the media will be killed.
+        mPms.updateExternalMediaStatus(false);
         try {
             mConnector.doCommand(String.format("volume unmount %s", path));
             return MountServiceResultCode.OperationSucceeded;
         } catch (NativeDaemonConnectorException e) {
+            // Don't worry about mismatch in PackageManager since the
+            // call back will handle the status changes any way.
             int code = e.getCode();
             if (code == VoldResponseCode.OpFailedVolNotMounted) {
                 return MountServiceResultCode.OperationFailedVolumeNotMounted;
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 02a0401..d989b6b 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -330,9 +330,9 @@
                 updateAdbNotification();
             } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
-                    || action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) {
+                    || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
                 String pkgList[] = null;
-                if (action.equals(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE)) {
+                if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                 } else {
                     Uri uri = intent.getData();
@@ -440,7 +440,7 @@
         filter.addAction(Intent.ACTION_SCREEN_ON);
         filter.addAction(Intent.ACTION_SCREEN_OFF);
         mContext.registerReceiver(mIntentReceiver, filter);
-        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+        IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
         mContext.registerReceiver(mIntentReceiver, sdFilter);
 
         SettingsObserver observer = new SettingsObserver(mHandler);
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 387f139..f7e3cea 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -4959,69 +4959,82 @@
         File tmpPackageFile = new File(args.getCodePath());
         boolean forwardLocked = ((pFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
         boolean onSd = ((pFlags & PackageManager.INSTALL_ON_SDCARD) != 0);
-        boolean replacingExistingPackage = false;
+        boolean replace = false;
         int scanMode = SCAN_MONITOR | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE
                 | (newInstall ? SCAN_NEW_INSTALL : 0);
         // Result object to be returned
         res.returnCode = PackageManager.INSTALL_SUCCEEDED;
 
-        main_flow: try {
-            // Retrieve PackageSettings and parse package
-            int parseFlags = PackageParser.PARSE_CHATTY |
-                    (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) |
-                    (onSd ? PackageParser.PARSE_ON_SDCARD : 0);
-            parseFlags |= mDefParseFlags;
-            PackageParser pp = new PackageParser(tmpPackageFile.getPath());
-            pp.setSeparateProcesses(mSeparateProcesses);
-            final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile,
-                    null, mMetrics, parseFlags);
-            if (pkg == null) {
-                res.returnCode = pp.getParseError();
-                break main_flow;
+        // Retrieve PackageSettings and parse package
+        int parseFlags = PackageParser.PARSE_CHATTY |
+        (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0) |
+        (onSd ? PackageParser.PARSE_ON_SDCARD : 0);
+        parseFlags |= mDefParseFlags;
+        PackageParser pp = new PackageParser(tmpPackageFile.getPath());
+        pp.setSeparateProcesses(mSeparateProcesses);
+        final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile,
+                null, mMetrics, parseFlags);
+        if (pkg == null) {
+            res.returnCode = pp.getParseError();
+            return;
+        }
+        String pkgName = res.name = pkg.packageName;
+        if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
+            if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) {
+                res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY;
+                return;
             }
-            String pkgName = res.name = pkg.packageName;
-            if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
-                if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) {
-                    res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY;
-                    break main_flow;
+        }
+        if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) {
+            res.returnCode = pp.getParseError();
+            return;
+        }
+        // Some preinstall checks
+        if (forwardLocked && onSd) {
+            // Make sure forward locked apps can only be installed
+            // on internal storage
+            Log.w(TAG, "Cannot install protected apps on sdcard");
+            res.returnCode = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
+            return;
+        }
+        // Get rid of all references to package scan path via parser.
+        pp = null;
+        String oldCodePath = null;
+        boolean systemApp = false;
+        synchronized (mPackages) {
+            // Check if installing already existing package
+            if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0
+                    && mPackages.containsKey(pkgName)) {
+                replace = true;
+            }
+            PackageSetting ps = mSettings.mPackages.get(pkgName);
+            if (ps != null) {
+                oldCodePath = mSettings.mPackages.get(pkgName).codePathString;
+                if (ps.pkg != null && ps.pkg.applicationInfo != null) {
+                    systemApp = (ps.pkg.applicationInfo.flags &
+                            ApplicationInfo.FLAG_SYSTEM) != 0;
                 }
             }
-            if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) {
-                res.returnCode = pp.getParseError();
-                break main_flow;
-            }
+        }
 
-            // Get rid of all references to package scan path via parser.
-            pp = null;
-            String oldCodePath = null;
-            synchronized (mPackages) {
-                //check if installing already existing package
-                if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0
-                        && mPackages.containsKey(pkgName)) {
-                    replacingExistingPackage = true;
-                }
-                PackageSetting ps = mSettings.mPackages.get(pkgName);
-                if (ps != null) {
-                    oldCodePath = mSettings.mPackages.get(pkgName).codePathString;
-                }
-            }
-
-            if (!args.doRename(res.returnCode, pkgName, oldCodePath)) {
-                res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
-                break main_flow;
-            }
-            // Set application objects path explicitly after the rename
-            setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());
-            if(replacingExistingPackage) {
-                replacePackageLI(pkg, parseFlags, scanMode,
-                        installerPackageName, res);
-            } else {
-                installNewPackageLI(pkg, parseFlags, scanMode,
-                        installerPackageName,res);
-            }
-        } finally {
-            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
-            }
+        if (systemApp && onSd) {
+            // Disable updates to system apps on sdcard
+            Log.w(TAG, "Cannot install updates to system apps on sdcard");
+            res.returnCode = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
+            return;
+        }
+        if (!args.doRename(res.returnCode, pkgName, oldCodePath)) {
+            res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
+            return;
+        }
+        // Set application objects path explicitly after the rename
+        setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());
+        if(replace) {
+            replacePackageLI(pkg, parseFlags, scanMode,
+                    installerPackageName, res);
+        } else {
+            installNewPackageLI(pkg, parseFlags, scanMode,
+                    installerPackageName,res);
         }
     }
 
@@ -8409,8 +8422,8 @@
    }
 
    public void updateExternalMediaStatus(final boolean mediaStatus) {
-       final boolean DEBUG = true;
-       if (DEBUG) Log.i(TAG, "updateExterMediaStatus::");
+       if (DEBUG_SD_INSTALL) Log.i(TAG, "updateExternalMediaStatus:: mediaStatus=" +
+               mediaStatus+", mMediaMounted=" + mMediaMounted);
        if (mediaStatus == mMediaMounted) {
            return;
        }
@@ -8432,9 +8445,6 @@
        HashMap<SdInstallArgs, String> processCids = new HashMap<SdInstallArgs, String>();
        int uidList[] = new int[list.length];
        int num = 0;
-       for (int i = 0; i < uidList.length; i++) {
-           uidList[i] = Process.LAST_APPLICATION_UID;
-       }
        synchronized (mPackages) {
            Set<String> appList = mSettings.findPackagesWithFlag(ApplicationInfo.FLAG_ON_SDCARD);
            for (String cid : list) {
@@ -8456,24 +8466,34 @@
                processCids.put(args, ps.codePathString);
                int uid = ps.userId;
                if (uid != -1) {
-                   int idx = Arrays.binarySearch(uidList, uid);
-                   if (idx < 0) {
-                       uidList[-idx] = uid;
-                       num++;
-                   }
+                   uidList[num++] = uid;
                }
            }
        }
-       int uidArr[] = uidList;
-       if ((num > 0) && (num < uidList.length)) {
+       int uidArr[] = null;
+       if (num > 0) {
+           // Sort uid list
+           Arrays.sort(uidList, 0, num);
+           // Throw away duplicates
            uidArr = new int[num];
-           for (int i = 0; i < num; i++) {
-               uidArr[i] = uidList[i];
+           uidArr[0] = uidList[0];
+           int di = 0;
+           for (int i = 1; i < num; i++) {
+               if (uidList[i-1] != uidList[i]) {
+                   uidArr[di++] = uidList[i];
+               }
+           }
+           if (true) {
+               for (int j = 0; j < num; j++) {
+                   Log.i(TAG, "uidArr[" + j + "]=" + uidArr[j]);
+               }
            }
        }
        if (mediaStatus) {
+           if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages");
            loadMediaPackages(processCids, uidArr);
        } else {
+           if (DEBUG_SD_INSTALL) Log.i(TAG, "Unloading packages");
            unloadMediaPackages(processCids, uidArr);
        }
    }
@@ -8486,9 +8506,11 @@
            Bundle extras = new Bundle();
            extras.putStringArray(Intent.EXTRA_CHANGED_PACKAGE_LIST,
                    pkgList.toArray(new String[size]));
-           extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr);
-           String action = mediaStatus ? Intent.ACTION_MEDIA_RESOURCES_AVAILABLE
-                   : Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE;
+           if (uidArr != null) {
+               extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr);
+           }
+           String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
+                   : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
            sendPackageBroadcast(action, null, extras);
        }
    }
@@ -8497,10 +8519,11 @@
        ArrayList<String> pkgList = new ArrayList<String>();
        Set<SdInstallArgs> keys = processCids.keySet();
        for (SdInstallArgs args : keys) {
-           String cid = args.cid;
            String codePath = processCids.get(args);
+           if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to install pkg : "
+                   + args.cid + " from " + args.cachePath);
            if (args.doPreInstall(PackageManager.INSTALL_SUCCEEDED) != PackageManager.INSTALL_SUCCEEDED) {
-               Log.i(TAG, "Failed to install package: " + codePath + " from sdcard");
+               Log.e(TAG, "Failed to install package: " + codePath + " from sdcard");
                continue;
            }
            // Parse package
@@ -8511,7 +8534,8 @@
            final PackageParser.Package pkg = pp.parsePackage(new File(codePath),
                    codePath, mMetrics, parseFlags);
            if (pkg == null) {
-               Log.w(TAG, "Failed to install package : " + cid + " from sd card");
+               Log.e(TAG, "Trying to install pkg : "
+                       + args.cid + " from " + args.cachePath);
                continue;
            }
            setApplicationInfoPaths(pkg, codePath, codePath);
@@ -8532,22 +8556,24 @@
                }
            }
            args.doPostInstall(retCode);
-           pkgList.add(pkg.packageName);
        }
        // Send broadcasts first
-       sendResourcesChangedBroadcast(true, pkgList, uidArr);
-       Runtime.getRuntime().gc();
-       // If something failed do we clean up here or next install?
+       if (pkgList.size() > 0) {
+           sendResourcesChangedBroadcast(true, pkgList, uidArr);
+           Runtime.getRuntime().gc();
+           // If something failed do we clean up here or next install?
+       }
    }
 
    void unloadMediaPackages(HashMap<SdInstallArgs, String> processCids, int uidArr[]) {
+       if (DEBUG_SD_INSTALL) Log.i(TAG, "unloading media packages");
        ArrayList<String> pkgList = new ArrayList<String>();
+       ArrayList<SdInstallArgs> failedList = new ArrayList<SdInstallArgs>();
        Set<SdInstallArgs> keys = processCids.keySet();
        for (SdInstallArgs args : keys) {
            String cid = args.cid;
            String pkgName = args.getPackageName();
-           // STOPSHIP Send broadcast to apps to remove references
-           // STOPSHIP Unmount package
+           if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to unload pkg : " + pkgName);
            // Delete package internally
            PackageRemovedInfo outInfo = new PackageRemovedInfo();
            synchronized (mInstallLock) {
@@ -8557,14 +8583,17 @@
                    pkgList.add(pkgName);
                } else {
                    Log.e(TAG, "Failed to delete pkg  from sdcard : " + pkgName);
+                   failedList.add(args);
                }
            }
        }
        // Send broadcasts
-       sendResourcesChangedBroadcast(false, pkgList, uidArr);
-       Runtime.getRuntime().gc();
+       if (pkgList.size() > 0) {
+           sendResourcesChangedBroadcast(false, pkgList, uidArr);
+           Runtime.getRuntime().gc();
+       }
        // Do clean up. Just unmount
-       for (SdInstallArgs args : keys) {
+       for (SdInstallArgs args : failedList) {
            synchronized (mInstallLock) {
                args.doPostDeleteLI(false);
            }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 5e6881c..98ded37 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -11941,7 +11941,7 @@
                 intent.getAction());
         if (intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())
                 || intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())
-                || Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(intent.getAction())
+                || Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())
                 || uidRemoved) {
             if (checkComponentPermission(
                     android.Manifest.permission.BROADCAST_PACKAGE_REMOVED,
@@ -11960,7 +11960,7 @@
                 } else {
                     // If resources are unvailble just force stop all
                     // those packages and flush the attribute cache as well.
-                    if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(intent.getAction())) {
+                    if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) {
                         String list[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                         if (list != null && (list.length > 0)) {
                             for (String pkg : list) {
@@ -12146,7 +12146,7 @@
                         skipPackages = new String[] { pkgName };
                     }
                 }
-            } else if (intent.ACTION_MEDIA_RESOURCES_AVAILABLE.equals(intent.getAction())) {
+            } else if (intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) {
                 skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
             }
             if (skipPackages != null && (skipPackages.length > 0)) {
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
index 55041fb..c7c3335 100644
--- a/services/java/com/android/server/status/StatusBarService.java
+++ b/services/java/com/android/server/status/StatusBarService.java
@@ -1813,14 +1813,14 @@
             filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
             filter.addDataScheme("package");
             mContext.registerReceiver(this, filter);
-            IntentFilter sdFilter = new IntentFilter(Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE);
+            IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
             mContext.registerReceiver(this, sdFilter);
         }
         
         @Override
         public void onReceive(Context context, Intent intent) {
             String pkgList[] = null;
-            if (Intent.ACTION_MEDIA_RESOURCES_UNAVAILABLE.equals(intent.getAction())) {
+            if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) {
                 pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
             } else {
                 Uri data = intent.getData();
diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml
index 70d0e78..2b6b81e 100644
--- a/tests/AndroidTests/AndroidManifest.xml
+++ b/tests/AndroidTests/AndroidManifest.xml
@@ -28,6 +28,7 @@
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
     <uses-permission android:name="android.permission.DELETE_PACKAGES" />
+    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
     <uses-permission android:name="com.android.unit_tests.permission.TEST_GRANTED" />
     <uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" />
     <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
index 163ddd5..a1370ea 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -16,6 +16,8 @@
 
 package com.android.unit_tests;
 
+import android.os.IMountService.Stub;
+
 import android.net.Uri;
 import android.os.FileUtils;
 
@@ -34,7 +36,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageInstallObserver;
-import android.content.pm.IPackageStatsObserver;
+import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageStats;
@@ -51,15 +53,27 @@
 import android.util.Log;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.IBinder;
+import android.os.IMountService;
+import android.os.MountServiceResultCode;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.StatFs;
 
 public class PackageManagerTests extends AndroidTestCase {
-    private static final boolean localLOGV = false;
+    private static final boolean localLOGV = true;
     public static final String TAG="PackageManagerTests";
-    public final long MAX_WAIT_TIME=60*1000;
-    public final long WAIT_TIME_INCR=10*1000;
+    public final long MAX_WAIT_TIME=120*1000;
+    public final long WAIT_TIME_INCR=20*1000;
+
+    void failStr(String errMsg) {
+        Log.w(TAG, "errMsg="+errMsg);
+        fail(errMsg);
+    }
+    void failStr(Exception e) {
+        Log.w(TAG, "e.getMessage="+e.getMessage());
+        Log.w(TAG, "e="+e);
+    }
 
     @Override
     protected void setUp() throws Exception {
@@ -146,6 +160,7 @@
         try {
             // Wait on observer
             synchronized(observer) {
+                synchronized (receiver) {
                     getPm().installPackage(packageURI, observer, flags, null);
                     long waitTime = 0;
                     while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
@@ -158,7 +173,6 @@
                     if (observer.returnCode != PackageManager.INSTALL_SUCCEEDED) {
                         return false;
                     }
-                    synchronized (receiver) {
                     // Verify we received the broadcast
                     waitTime = 0;
                     while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
@@ -262,6 +276,7 @@
         PackageParser.Package pkg = parsePackage(packageURI);
         assertNotNull(pkg);
         InstallReceiver receiver = new InstallReceiver(pkg.packageName);
+        InstallParams ip = null;
         try {
             try {
                 assertTrue(invokeInstallPackage(packageURI, flags,
@@ -271,13 +286,11 @@
             }
             // Verify installed information
             assertInstall(pkg.packageName, flags);
-            return new InstallParams(pkg, outFileName, packageURI);
+            ip = new InstallParams(pkg, outFileName, packageURI);
+            return ip;
         } finally {
             if (cleanUp) {
-                getPm().deletePackage(pkg.packageName, null, 0);
-                if (outFile != null && outFile.exists()) {
-                    outFile.delete();
-                }
+                cleanUpInstall(ip);
             }
         }
     }
@@ -300,28 +313,52 @@
     /* ------------------------- Test replacing packages --------------*/
     class ReplaceReceiver extends GenericReceiver {
         String pkgName;
-        boolean removed = false;
+        final static int INVALID = -1;
+        final static int REMOVED = 1;
+        final static int ADDED = 2;
+        final static int REPLACED = 3;
+        int removed = INVALID;
+        // for updated system apps only
+        boolean update = false;
 
         ReplaceReceiver(String pkgName) {
             this.pkgName = pkgName;
             filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
-            filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+            filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+            if (update) {
+                filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+            }
+            filter.addDataScheme("package");
             super.setFilter(filter);
         }
 
         public boolean notifyNow(Intent intent) {
             String action = intent.getAction();
-            if (Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
-                    Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
-                Uri data = intent.getData();
-                String installedPkg = data.getEncodedSchemeSpecificPart();
-                if (pkgName.equals(installedPkg)) {
-                    if (removed) {
-                        return true;
-                    } else {
-                        removed = true;
-                    }
+            Uri data = intent.getData();
+            String installedPkg = data.getEncodedSchemeSpecificPart();
+            if (pkgName == null || !pkgName.equals(installedPkg)) {
+                return false;
+            }
+            if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                removed = REMOVED;
+            } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+                if (removed != REMOVED) {
+                    return false;
                 }
+                boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+                if (!replacing) {
+                    return false;
+                }
+                removed = ADDED;
+                if (!update) {
+                    return true;
+                }
+            } else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
+                if (removed != ADDED) {
+                    return false;
+                }
+                removed = REPLACED;
+                return true;
             }
             return false;
         }
@@ -335,29 +372,26 @@
      */
     public void replaceFromRawResource(int flags) {
         InstallParams ip = installFromRawResource(flags, false);
-        boolean result = ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0);
+        boolean replace = ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0);
         GenericReceiver receiver;
-        if (result) {
+        if (replace) {
             receiver = new ReplaceReceiver(ip.pkg.packageName);
+            Log.i(TAG, "Creating replaceReceiver");
         } else {
             receiver = new InstallReceiver(ip.pkg.packageName);
         }
         try {
             try {
                 assertEquals(invokeInstallPackage(ip.packageURI, flags,
-                        ip.pkg.packageName, receiver), result);
-                if (result) {
+                        ip.pkg.packageName, receiver), replace);
+                if (replace) {
                     assertInstall(ip.pkg.packageName, flags);
                 }
             } catch (Exception e) {
                 failStr("Failed with exception : " + e);
             }
         } finally {
-            getPm().deletePackage(ip.pkg.packageName, null, 0);
-            File outFile = new File(ip.outFileName);
-            if (outFile != null && outFile.exists()) {
-                outFile.delete();
-            }
+            cleanUpInstall(ip);
         }
     }
 
@@ -376,12 +410,362 @@
         replaceFromRawResource(PackageManager.INSTALL_ON_SDCARD);
     }
 
-    void failStr(String errMsg) {
-        Log.w(TAG, "errMsg="+errMsg);
-        fail(errMsg);
+    @MediumTest
+    public void testReplaceNormalInternal() {
+        replaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING);
     }
-    void failStr(Exception e) {
-        Log.w(TAG, "e.getMessage="+e.getMessage());
-        Log.w(TAG, "e="+e);
+
+    @MediumTest
+    public void testReplaceFwdLockedInternal() {
+        replaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING |
+                PackageManager.INSTALL_FORWARD_LOCK);
     }
+
+    @MediumTest
+    public void testReplaceSdcard() {
+        replaceFromRawResource(PackageManager.INSTALL_REPLACE_EXISTING |
+                PackageManager.INSTALL_ON_SDCARD);
+    }
+
+    /* -------------- Delete tests ---*/
+    class DeleteObserver extends IPackageDeleteObserver.Stub {
+
+        public boolean succeeded;
+        private boolean doneFlag = false;
+
+        public boolean isDone() {
+            return doneFlag;
+        }
+
+        public void packageDeleted(boolean succeeded) throws RemoteException {
+            synchronized(this) {
+                this.succeeded = succeeded;
+                doneFlag = true;
+                notifyAll();
+            }
+        }
+    }
+
+    class DeleteReceiver extends GenericReceiver {
+        String pkgName;
+
+        DeleteReceiver(String pkgName) {
+            this.pkgName = pkgName;
+            IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
+            filter.addDataScheme("package");
+            super.setFilter(filter);
+        }
+
+        public boolean notifyNow(Intent intent) {
+            String action = intent.getAction();
+            if (!Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+                return false;
+            }
+            Uri data = intent.getData();
+            String installedPkg = data.getEncodedSchemeSpecificPart();
+            if (pkgName.equals(installedPkg)) {
+                return true;
+            }
+            return false;
+        }
+    }
+
+    public boolean invokeDeletePackage(Uri packageURI, int flags,
+            final String pkgName, GenericReceiver receiver) throws Exception {
+        DeleteObserver observer = new DeleteObserver();
+        final boolean received = false;
+        mContext.registerReceiver(receiver, receiver.filter);
+        try {
+            // Wait on observer
+            synchronized(observer) {
+                synchronized (receiver) {
+                    getPm().deletePackage(pkgName, observer, flags);
+                    long waitTime = 0;
+                    while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+                        observer.wait(WAIT_TIME_INCR);
+                        waitTime += WAIT_TIME_INCR;
+                    }
+                    if(!observer.isDone()) {
+                        throw new Exception("Timed out waiting for packageInstalled callback");
+                    }
+                    // Verify we received the broadcast
+                    waitTime = 0;
+                    while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+                        receiver.wait(WAIT_TIME_INCR);
+                        waitTime += WAIT_TIME_INCR;
+                    }
+                    if(!receiver.isDone()) {
+                        throw new Exception("Timed out waiting for PACKAGE_ADDED notification");
+                    }
+                    return receiver.received;
+                }
+            }
+        } finally {
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    public void deleteFromRawResource(int iFlags, int dFlags) {
+        InstallParams ip = installFromRawResource(iFlags, false);
+        boolean retainData = ((dFlags & PackageManager.DONT_DELETE_DATA) != 0);
+        GenericReceiver receiver = new DeleteReceiver(ip.pkg.packageName);
+        DeleteObserver observer = new DeleteObserver();
+        try {
+            assertTrue(invokeDeletePackage(ip.packageURI, dFlags,
+                    ip.pkg.packageName, receiver));
+            ApplicationInfo info = null;
+            Log.i(TAG, "okay4");
+            try {
+            info = getPm().getApplicationInfo(ip.pkg.packageName,
+                    PackageManager.GET_UNINSTALLED_PACKAGES);
+            } catch (NameNotFoundException e) {
+                info = null;
+            }
+            if (retainData) {
+                assertNotNull(info);
+                assertEquals(info.packageName, ip.pkg.packageName);
+                File file = new File(info.dataDir);
+                assertTrue(file.exists());
+            } else {
+                assertNull(info);
+            }
+        } catch (Exception e) {
+            failStr(e);
+        } finally {
+            cleanUpInstall(ip);
+        }
+    }
+
+    @MediumTest
+    public void testDeleteNormalInternal() {
+        deleteFromRawResource(0, 0);
+    }
+
+    @MediumTest
+    public void testDeleteFwdLockedInternal() {
+        deleteFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, 0);
+    }
+
+    @MediumTest
+    public void testDeleteSdcard() {
+        deleteFromRawResource(PackageManager.INSTALL_ON_SDCARD, 0);
+    }
+
+    @MediumTest
+    public void testDeleteNormalInternalRetainData() {
+        deleteFromRawResource(0, PackageManager.DONT_DELETE_DATA);
+    }
+
+    @MediumTest
+    public void testDeleteFwdLockedInternalRetainData() {
+        deleteFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, PackageManager.DONT_DELETE_DATA);
+    }
+
+    @MediumTest
+    public void testDeleteSdcardRetainData() {
+        deleteFromRawResource(PackageManager.INSTALL_ON_SDCARD, PackageManager.DONT_DELETE_DATA);
+    }
+
+    /* sdcard mount/unmount tests ******/
+
+    class SdMountReceiver extends GenericReceiver {
+        String pkgNames[];
+        boolean status = true;
+
+        SdMountReceiver(String[] pkgNames) {
+            this.pkgNames = pkgNames;
+            IntentFilter filter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+            super.setFilter(filter);
+        }
+
+        public boolean notifyNow(Intent intent) {
+            Log.i(TAG, "okay 1");
+            String action = intent.getAction();
+            if (!Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+                return false;
+            }
+            String rpkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            for (String pkg : pkgNames) {
+                boolean found = false;
+                for (String rpkg : rpkgList) {
+                    if (rpkg.equals(pkg)) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    status = false;
+                    return true;
+                }
+            }
+            return true;
+        }
+    }
+
+    class SdUnMountReceiver extends GenericReceiver {
+        String pkgNames[];
+        boolean status = true;
+
+        SdUnMountReceiver(String[] pkgNames) {
+            this.pkgNames = pkgNames;
+            IntentFilter filter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+            super.setFilter(filter);
+        }
+
+        public boolean notifyNow(Intent intent) {
+            String action = intent.getAction();
+            if (!Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+                return false;
+            }
+            String rpkgList[] = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+            for (String pkg : pkgNames) {
+                boolean found = false;
+                for (String rpkg : rpkgList) {
+                    if (rpkg.equals(pkg)) {
+                        found = true;
+                        break;
+                    }
+                }
+                if (!found) {
+                    status = false;
+                    return true;
+                }
+            }
+            return true;
+        }
+    }
+
+    IMountService getMs() {
+        IBinder service = ServiceManager.getService("mount");
+        if (service != null) {
+            return IMountService.Stub.asInterface(service);
+        } else {
+            Log.e(TAG, "Can't get mount service");
+        }
+        return null;
+    }
+
+    boolean getMediaState() {
+        try {
+        String mPath = Environment.getExternalStorageDirectory().toString();
+        String state = getMs().getVolumeState(mPath);
+        return Environment.MEDIA_MOUNTED.equals(state);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    boolean mountMedia() {
+        if (getMediaState()) {
+            return true;
+        }
+        try {
+        String mPath = Environment.getExternalStorageDirectory().toString();
+        int ret = getMs().mountVolume(mPath);
+        return ret == MountServiceResultCode.OperationSucceeded;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    private boolean unmountMedia() {
+        if (!getMediaState()) {
+            return true;
+        }
+        try {
+        String mPath = Environment.getExternalStorageDirectory().toString();
+        int ret = getMs().unmountVolume(mPath);
+        return ret == MountServiceResultCode.OperationSucceeded;
+        } catch (RemoteException e) {
+            return true;
+        }
+    }
+
+    /*
+     * Install package on sdcard. Unmount and then mount the media.
+     * (Use PackageManagerService private api for now)
+     * Make sure the installed package is available.
+     * STOPSHIP will uncomment when MountService api's to mount/unmount
+     * are made asynchronous.
+     */
+    public void xxxtestMountSdNormalInternal() {
+        assertTrue(mountFromRawResource());
+    }
+
+    private boolean mountFromRawResource() {
+        // Install pkg on sdcard
+        InstallParams ip = installFromRawResource(PackageManager.INSTALL_ON_SDCARD |
+                PackageManager.INSTALL_REPLACE_EXISTING, false);
+        if (localLOGV) Log.i(TAG, "Installed pkg on sdcard");
+        boolean origState = getMediaState();
+        SdMountReceiver receiver = new SdMountReceiver(new String[]{ip.pkg.packageName});
+        try {
+            if (localLOGV) Log.i(TAG, "Unmounting media");
+            // Unmount media
+            assertTrue(unmountMedia());
+            if (localLOGV) Log.i(TAG, "Unmounted media");
+            try {
+                if (localLOGV) Log.i(TAG, "Sleeping for 10 second");
+                Thread.sleep(10*1000);
+            } catch (InterruptedException e) {
+                failStr(e);
+            }
+            // Register receiver here
+            PackageManager pm = getPm();
+            mContext.registerReceiver(receiver, receiver.filter);
+
+            // Wait on receiver
+            synchronized (receiver) {
+                if (localLOGV) Log.i(TAG, "Mounting media");
+                // Mount media again
+                assertTrue(mountMedia());
+                if (localLOGV) Log.i(TAG, "Mounted media");
+                if (localLOGV) Log.i(TAG, "Waiting for notification");
+                long waitTime = 0;
+                // Verify we received the broadcast
+                waitTime = 0;
+                while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+                    receiver.wait(WAIT_TIME_INCR);
+                    waitTime += WAIT_TIME_INCR;
+                }
+                if(!receiver.isDone()) {
+                    failStr("Timed out waiting for EXTERNAL_APPLICATIONS notification");
+                }
+                return receiver.received;
+            }
+        } catch (InterruptedException e) {
+            failStr(e);
+            return false;
+        } finally {
+            mContext.unregisterReceiver(receiver);
+            // Restore original media state
+            if (origState) {
+                mountMedia();
+            } else {
+                unmountMedia();
+            }
+            if (localLOGV) Log.i(TAG, "Cleaning up install");
+            cleanUpInstall(ip);
+        }
+    }
+
+    void cleanUpInstall(InstallParams ip) {
+        if (ip == null) {
+            return;
+        }
+        Runtime.getRuntime().gc();
+        Log.i(TAG, "Deleting package : " + ip.pkg.packageName);
+        getPm().deletePackage(ip.pkg.packageName, null, 0);
+        File outFile = new File(ip.outFileName);
+        if (outFile != null && outFile.exists()) {
+            outFile.delete();
+        }
+    }
+    /*
+     * TODO's
+     * check version numbers for upgrades
+     * check permissions of installed packages
+     * how to do tests on updated system apps?
+     * verify updates to system apps cannot be installed on the sdcard.
+     */
 }