Implement issue #3094621 and #3094609 - wipe sd card

3094621: add "wipe sd card" option to factory data reset
3094609: collapse unmount/format into one command

Implements requested UI changes.  Also some final tweaks to
Manage Applications.

Change-Id: I0219195dd0c74d8c003ef1c3f6e09714859d7f89
diff --git a/src/com/android/settings/MasterClear.java b/src/com/android/settings/MasterClear.java
index 4de0e44..e653d90 100644
--- a/src/com/android/settings/MasterClear.java
+++ b/src/com/android/settings/MasterClear.java
@@ -16,20 +16,16 @@
 
 package com.android.settings;
 
+import com.android.internal.os.storage.ExternalStorageFormatter;
 import com.android.internal.widget.LockPatternUtils;
 
 import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
-import android.os.ServiceManager;
-import android.os.SystemProperties;
-import android.text.TextUtils;
-import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.Button;
+import android.widget.CheckBox;
 
 /**
  * Confirm and execute a reset of the device to a clean "just out of the box"
@@ -48,6 +44,8 @@
 
     private View mInitialView;
     private Button mInitiateButton;
+    private View mExternalStorageContainer;
+    private CheckBox mExternalStorage;
 
     private View mFinalView;
     private Button mFinalButton;
@@ -63,8 +61,14 @@
                     return;
                 }
 
-                sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
-                // Intent handling is asynchronous -- assume it will happen soon.
+                if (mExternalStorage.isChecked()) {
+                    Intent intent = new Intent(ExternalStorageFormatter.FORMAT_AND_FACTORY_RESET);
+                    intent.setComponent(ExternalStorageFormatter.COMPONENT_NAME);
+                    startService(intent);
+                } else {
+                    sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
+                    // Intent handling is asynchronous -- assume it will happen soon.
+                }
             }
         };
 
@@ -145,6 +149,16 @@
             mInitiateButton =
                     (Button) mInitialView.findViewById(R.id.initiate_master_clear);
             mInitiateButton.setOnClickListener(mInitiateListener);
+            mExternalStorageContainer =
+                mInitialView.findViewById(R.id.erase_external_container);
+            mExternalStorage =
+                    (CheckBox) mInitialView.findViewById(R.id.erase_external);
+            mExternalStorageContainer.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    mExternalStorage.toggle();
+                }
+            });
         }
 
         setContentView(mInitialView);
@@ -170,7 +184,8 @@
     public void onPause() {
         super.onPause();
 
-        establishInitialState();
+        if (!isFinishing()) {
+            establishInitialState();
+        }
     }
-
 }
diff --git a/src/com/android/settings/MediaFormat.java b/src/com/android/settings/MediaFormat.java
index b78ff62..d8d57e4 100644
--- a/src/com/android/settings/MediaFormat.java
+++ b/src/com/android/settings/MediaFormat.java
@@ -16,23 +16,15 @@
 
 package com.android.settings;
 
-import com.android.internal.widget.LockPatternUtils;
-
 import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
-import android.os.storage.IMountService;
-import android.os.ServiceManager;
-import android.os.SystemProperties;
-import android.os.Environment;
-import android.text.TextUtils;
-import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.Button;
 
+import com.android.internal.os.storage.ExternalStorageFormatter;
+
 /**
  * Confirm and execute a format of the sdcard.
  * Multiple confirmations are required: first, a general "are you sure
@@ -46,7 +38,6 @@
     private static final int KEYGUARD_REQUEST = 55;
 
     private LayoutInflater mInflater;
-    private LockPatternUtils mLockUtils;
 
     private View mInitialView;
     private Button mInitiateButton;
@@ -64,23 +55,10 @@
                 if (Utils.isMonkeyRunning()) {
                     return;
                 }
-                final IMountService service =
-                        IMountService.Stub.asInterface(ServiceManager.getService("mount"));
-                if (service != null) {
-                    new Thread() {
-                        public void run() {
-                            try {
-                                service.formatVolume(Environment.getExternalStorageDirectory().toString());
-                            } catch (Exception e) {
-                                // Intentionally blank - there's nothing we can do here
-                                Log.w("MediaFormat", "Unable to invoke IMountService.formatMedia()");
-                            }
-                        }
-                    }.start();
-                } else {
-                    Log.w("MediaFormat", "Unable to locate IMountService");
-                }
-            finish();
+                Intent intent = new Intent(ExternalStorageFormatter.FORMAT_ONLY);
+                intent.setComponent(ExternalStorageFormatter.COMPONENT_NAME);
+                startService(intent);
+                finish();
             }
         };
 
@@ -171,7 +149,6 @@
         mInitialView = null;
         mFinalView = null;
         mInflater = LayoutInflater.from(this);
-        mLockUtils = new LockPatternUtils(this);
 
         establishInitialState();
     }
@@ -184,7 +161,8 @@
     public void onPause() {
         super.onPause();
 
-        establishInitialState();
+        if (!isFinishing()) {
+            establishInitialState();
+        }
     }
-
 }
diff --git a/src/com/android/settings/applications/LinearColorBar.java b/src/com/android/settings/applications/LinearColorBar.java
new file mode 100644
index 0000000..74164c4
--- /dev/null
+++ b/src/com/android/settings/applications/LinearColorBar.java
@@ -0,0 +1,163 @@
+/**
+ *
+ */
+package com.android.settings.applications;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.Shader;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.widget.LinearLayout;
+
+public class LinearColorBar extends LinearLayout {
+    static final int LEFT_COLOR = 0xffa0a0a0;
+    static final int MIDDLE_COLOR = 0xff7070ff;
+    static final int RIGHT_COLOR = 0xffa0c0a0;
+
+    private float mRedRatio;
+    private float mYellowRatio;
+    private float mGreenRatio;
+
+    private boolean mShowingGreen;
+
+    final Rect mRect = new Rect();
+    final Paint mPaint = new Paint();
+
+    int mLastInterestingLeft, mLastInterestingRight;
+    int mLineWidth;
+
+    final Path mColorPath = new Path();
+    final Path mEdgePath = new Path();
+    final Paint mColorGradientPaint = new Paint();
+    final Paint mEdgeGradientPaint = new Paint();
+
+    public LinearColorBar(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setWillNotDraw(false);
+        mPaint.setStyle(Paint.Style.FILL);
+        mColorGradientPaint.setStyle(Paint.Style.FILL);
+        mColorGradientPaint.setAntiAlias(true);
+        mEdgeGradientPaint.setStyle(Paint.Style.STROKE);
+        mLineWidth = getResources().getDisplayMetrics().densityDpi >= DisplayMetrics.DENSITY_HIGH
+                ? 2 : 1;
+        mEdgeGradientPaint.setStrokeWidth(mLineWidth);
+        mEdgeGradientPaint.setAntiAlias(true);
+    }
+
+    public void setRatios(float red, float yellow, float green) {
+        mRedRatio = red;
+        mYellowRatio = yellow;
+        mGreenRatio = green;
+        invalidate();
+    }
+
+    public void setShowingGreen(boolean showingGreen) {
+        if (mShowingGreen != showingGreen) {
+            mShowingGreen = showingGreen;
+            updateIndicator();
+            invalidate();
+        }
+    }
+
+    private void updateIndicator() {
+        int off = getPaddingTop() - getPaddingBottom();
+        if (off < 0) off = 0;
+        mRect.top = off;
+        mRect.bottom = getHeight();
+        if (mShowingGreen) {
+            mColorGradientPaint.setShader(new LinearGradient(
+                    0, 0, 0, off-2, RIGHT_COLOR&0xffffff, RIGHT_COLOR, Shader.TileMode.CLAMP));
+        } else {
+            mColorGradientPaint.setShader(new LinearGradient(
+                    0, 0, 0, off-2, MIDDLE_COLOR&0xffffff, MIDDLE_COLOR, Shader.TileMode.CLAMP));
+        }
+        mEdgeGradientPaint.setShader(new LinearGradient(
+                0, 0, 0, off/2, 0x00a0a0a0, 0xffa0a0a0, Shader.TileMode.CLAMP));
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        updateIndicator();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        int width = getWidth();
+
+        int left = 0;
+
+        int right = left + (int)(width*mRedRatio);
+        int right2 = right + (int)(width*mYellowRatio);
+        int right3 = right2 + (int)(width*mGreenRatio);
+
+        int indicatorLeft, indicatorRight;
+        if (mShowingGreen) {
+            indicatorLeft = right2;
+            indicatorRight = right3;
+        } else {
+            indicatorLeft = right;
+            indicatorRight = right2;
+        }
+
+        if (mLastInterestingLeft != indicatorLeft || mLastInterestingRight != indicatorRight) {
+            mColorPath.reset();
+            mEdgePath.reset();
+            if (indicatorLeft < indicatorRight) {
+                mColorPath.moveTo(indicatorLeft, mRect.top);
+                mColorPath.lineTo(-1, 0);
+                mColorPath.lineTo(width, 0);
+                mColorPath.lineTo(indicatorRight, mRect.top);
+                mColorPath.close();
+                float lineOffset = mLineWidth+.5f;
+                mEdgePath.moveTo(indicatorLeft+lineOffset, mRect.top);
+                mEdgePath.lineTo(-1+lineOffset, 0);
+                mEdgePath.moveTo(indicatorRight-lineOffset, mRect.top);
+                mEdgePath.lineTo(width-lineOffset, 0);
+            }
+            mLastInterestingLeft = indicatorLeft;
+            mLastInterestingRight = indicatorRight;
+        }
+
+        if (!mColorPath.isEmpty()) {
+            canvas.drawPath(mEdgePath, mEdgeGradientPaint);
+            canvas.drawPath(mColorPath, mColorGradientPaint);
+        }
+
+        if (left < right) {
+            mRect.left = left;
+            mRect.right = right;
+            mPaint.setColor(LEFT_COLOR);
+            canvas.drawRect(mRect, mPaint);
+            width -= (right-left);
+            left = right;
+        }
+
+        right = right2;
+
+        if (left < right) {
+            mRect.left = left;
+            mRect.right = right;
+            mPaint.setColor(MIDDLE_COLOR);
+            canvas.drawRect(mRect, mPaint);
+            width -= (right-left);
+            left = right;
+        }
+
+
+        right = left + width;
+        if (left < right) {
+            mRect.left = left;
+            mRect.right = right;
+            mPaint.setColor(RIGHT_COLOR);
+            canvas.drawRect(mRect, mPaint);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index 1053d3b..b4cd26ef 100644
--- a/src/com/android/settings/applications/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -29,9 +29,12 @@
 import android.content.pm.PackageInfo;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Environment;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.StatFs;
 import android.provider.Settings;
+import android.text.format.Formatter;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.Menu;
@@ -126,6 +129,8 @@
 
     public static final int SORT_ORDER_ALPHA = MENU_OPTIONS_BASE + 4;
     public static final int SORT_ORDER_SIZE = MENU_OPTIONS_BASE + 5;
+    public static final int SHOW_RUNNING_SERVICES = MENU_OPTIONS_BASE + 6;
+    public static final int SHOW_BACKGROUND_PROCESSES = MENU_OPTIONS_BASE + 7;
     // sort order
     private int mSortOrder = SORT_ORDER_ALPHA;
     // Filter value
@@ -152,6 +157,11 @@
     // Custom view used to display running processes
     private RunningProcessesView mRunningProcessesView;
     
+    LinearColorBar mColorBar;
+    TextView mStorageChartLabel;
+    TextView mUsedStorageText;
+    TextView mFreeStorageText;
+
     // These are for keeping track of activity and tab switch state.
     private int mCurView;
     private boolean mCreatedRunning;
@@ -160,6 +170,11 @@
     private boolean mActivityResumed;
     private Object mNonConfigInstance;
     
+    private StatFs mDataFileStats;
+    private StatFs mSDCardFileStats;
+    private boolean mLastShowedInternalStorage = true;
+    private long mLastUsedStorage, mLastAppStorage, mLastFreeStorage;
+
     final Runnable mRunningProcessesAvail = new Runnable() {
         public void run() {
             handleRunningProcessesAvail();
@@ -222,6 +237,7 @@
                 mCurFilterPrefix = constraint;
                 mEntries = (ArrayList<ApplicationsState.AppEntry>)results.values;
                 notifyDataSetChanged();
+                updateStorageUsage();
             }
         };
 
@@ -294,6 +310,7 @@
                 mEntries = null;
             }
             notifyDataSetChanged();
+            updateStorageUsage();
 
             if (entries == null) {
                 mWaitingForData = true;
@@ -338,6 +355,7 @@
             mBaseEntries = apps;
             mEntries = applyPrefixFilter(mCurFilterPrefix, mBaseEntries);
             notifyDataSetChanged();
+            updateStorageUsage();
         }
 
         @Override
@@ -367,6 +385,7 @@
                         // the list with the new size to reflect it to the user.
                         rebuild(false);
                     }
+                    updateStorageUsage();
                     return;
                 }
             }
@@ -502,6 +521,9 @@
         
         mNonConfigInstance = getLastNonConfigurationInstance();
         
+        mDataFileStats = new StatFs("/data");
+        mSDCardFileStats = new StatFs(Environment.getExternalStorageDirectory().toString());
+
         // initialize some window features
         requestWindowFeature(Window.FEATURE_RIGHT_ICON);
         requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
@@ -526,6 +548,10 @@
         mListView = lv;
         lv.setRecyclerListener(mApplicationsAdapter);
         mListView.setAdapter(mApplicationsAdapter);
+        mColorBar = (LinearColorBar)mListContainer.findViewById(R.id.storage_color_bar);
+        mStorageChartLabel = (TextView)mListContainer.findViewById(R.id.storageChartLabel);
+        mUsedStorageText = (TextView)mListContainer.findViewById(R.id.usedStorageText);
+        mFreeStorageText = (TextView)mListContainer.findViewById(R.id.freeStorageText);
         mRunningProcessesView = (RunningProcessesView)mRootView.findViewById(
                 R.id.running_processes);
 
@@ -609,6 +635,8 @@
                 .setIcon(android.R.drawable.ic_menu_sort_alphabetically);
         menu.add(0, SORT_ORDER_SIZE, 2, R.string.sort_order_size)
                 .setIcon(android.R.drawable.ic_menu_sort_by_size); 
+        menu.add(0, SHOW_RUNNING_SERVICES, 3, R.string.show_running_services);
+        menu.add(0, SHOW_BACKGROUND_PROCESSES, 3, R.string.show_background_processes);
         return true;
     }
     
@@ -619,11 +647,17 @@
          * so bringing up this menu in that case doesn't make any sense.
          */
         if (mCurView == VIEW_RUNNING) {
-            return false;
+            boolean showingBackground = mRunningProcessesView.mAdapter.getShowBackground();
+            menu.findItem(SORT_ORDER_ALPHA).setVisible(false);
+            menu.findItem(SORT_ORDER_SIZE).setVisible(false);
+            menu.findItem(SHOW_RUNNING_SERVICES).setVisible(showingBackground);
+            menu.findItem(SHOW_BACKGROUND_PROCESSES).setVisible(!showingBackground);
+        } else {
+            menu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA);
+            menu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder != SORT_ORDER_SIZE);
+            menu.findItem(SHOW_RUNNING_SERVICES).setVisible(false);
+            menu.findItem(SHOW_BACKGROUND_PROCESSES).setVisible(false);
         }
-
-        menu.findItem(SORT_ORDER_ALPHA).setVisible(mSortOrder != SORT_ORDER_ALPHA);
-        menu.findItem(SORT_ORDER_SIZE).setVisible(mSortOrder != SORT_ORDER_SIZE);
         return true;
     }
 
@@ -632,7 +666,13 @@
         int menuId = item.getItemId();
         if ((menuId == SORT_ORDER_ALPHA) || (menuId == SORT_ORDER_SIZE)) {
             mSortOrder = menuId;
-            mApplicationsAdapter.rebuild(mFilterApps, mSortOrder);
+            if (mCurView != VIEW_RUNNING) {
+                mApplicationsAdapter.rebuild(mFilterApps, mSortOrder);
+            }
+        } else if (menuId == SHOW_RUNNING_SERVICES) {
+            mRunningProcessesView.mAdapter.setShowBackground(false);
+        } else if (menuId == SHOW_BACKGROUND_PROCESSES) {
+            mRunningProcessesView.mAdapter.setShowBackground(true);
         }
         return true;
     }
@@ -657,6 +697,82 @@
     static final int VIEW_LIST = 1;
     static final int VIEW_RUNNING = 2;
 
+    void updateStorageUsage() {
+        if (mCurView == VIEW_RUNNING) {
+            return;
+        }
+
+        long freeStorage = 0;
+        long appStorage = 0;
+        long totalStorage = 0;
+        CharSequence newLabel = null;
+
+        if (mFilterApps == FILTER_APPS_SDCARD) {
+            if (mLastShowedInternalStorage) {
+                mLastShowedInternalStorage = false;
+            }
+            newLabel = this.getText(R.string.sd_card_storage);
+            mSDCardFileStats.restat(Environment.getExternalStorageDirectory().toString());
+            try {
+                totalStorage = (long)mSDCardFileStats.getBlockCount() *
+                        mSDCardFileStats.getBlockSize();
+                freeStorage = (long) mSDCardFileStats.getAvailableBlocks() *
+                mSDCardFileStats.getBlockSize();
+            } catch (IllegalArgumentException e) {
+                // use the old value of mFreeMem
+            }
+        } else {
+            if (!mLastShowedInternalStorage) {
+                mLastShowedInternalStorage = true;
+            }
+            newLabel = this.getText(R.string.internal_storage);
+            mDataFileStats.restat("/data");
+            try {
+                totalStorage = (long)mDataFileStats.getBlockCount() *
+                        mDataFileStats.getBlockSize();
+                freeStorage = (long) mDataFileStats.getAvailableBlocks() *
+                    mDataFileStats.getBlockSize();
+            } catch (IllegalArgumentException e) {
+            }
+            final int N = mApplicationsAdapter.getCount();
+            for (int i=0; i<N; i++) {
+                ApplicationsState.AppEntry ae = mApplicationsAdapter.getAppEntry(i);
+                appStorage += ae.codeSize + ae.dataSize;
+                freeStorage += ae.cacheSize;
+            }
+        }
+        if (newLabel != null) {
+            mStorageChartLabel.setText(newLabel);
+        }
+        if (totalStorage > 0) {
+            mColorBar.setRatios((totalStorage-freeStorage-appStorage)/(float)totalStorage,
+                    appStorage/(float)totalStorage, freeStorage/(float)totalStorage);
+            long usedStorage = totalStorage - freeStorage;
+            if (mLastUsedStorage != usedStorage) {
+                mLastUsedStorage = usedStorage;
+                String sizeStr = Formatter.formatShortFileSize(this, usedStorage);
+                mUsedStorageText.setText(getResources().getString(
+                        R.string.service_foreground_processes, sizeStr));
+            }
+            if (mLastFreeStorage != freeStorage) {
+                mLastFreeStorage = freeStorage;
+                String sizeStr = Formatter.formatShortFileSize(this, freeStorage);
+                mFreeStorageText.setText(getResources().getString(
+                        R.string.service_background_processes, sizeStr));
+            }
+        } else {
+            mColorBar.setRatios(0, 0, 0);
+            if (mLastUsedStorage != -1) {
+                mLastUsedStorage = -1;
+                mUsedStorageText.setText("");
+            }
+            if (mLastFreeStorage != -1) {
+                mLastFreeStorage = -1;
+                mFreeStorageText.setText("");
+            }
+        }
+    }
+
     private void selectView(int which) {
         if (which == VIEW_LIST) {
             if (mResumedRunning) {
@@ -724,6 +840,7 @@
         
         mFilterApps = newOption;
         selectView(VIEW_LIST);
+        updateStorageUsage();
     }
 
     public void onTabChanged(String tabId) {
diff --git a/src/com/android/settings/applications/RunningProcessesView.java b/src/com/android/settings/applications/RunningProcessesView.java
index 1de67f7..86457bf 100644
--- a/src/com/android/settings/applications/RunningProcessesView.java
+++ b/src/com/android/settings/applications/RunningProcessesView.java
@@ -22,9 +22,7 @@
 import android.app.Dialog;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.os.SystemProperties;
@@ -39,7 +37,6 @@
 import android.widget.BaseAdapter;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.AbsListView.RecyclerListener;
@@ -71,6 +68,7 @@
     RunningState.BaseItem mCurSelected;
     
     ListView mListView;
+    ServiceListAdapter mAdapter;
     LinearColorBar mColorBar;
     TextView mBackgroundProcessText;
     TextView mForegroundProcessText;
@@ -93,6 +91,7 @@
         ActivityManager.RunningServiceInfo mService;
         ViewHolder mHolder;
         long mFirstRunTime;
+        boolean mSetBackground;
         
         void updateTime(Context context, StringBuilder builder) {
             TextView uptimeView = null;
@@ -109,14 +108,21 @@
                     mHolder.size.setText(size);
                 }
                 
-                if (mItem instanceof RunningState.MergedItem) {
-                    // This item represents both services and proceses,
+                if (mItem.mBackground) {
+                    // This is a background process; no uptime.
+                    if (!mSetBackground) {
+                        mSetBackground = true;
+                        mHolder.uptime.setText("");
+                    }
+                } else if (mItem instanceof RunningState.MergedItem) {
+                    // This item represents both services and processes,
                     // so show the service uptime below.
                     uptimeView = mHolder.uptime;
                 }
             }
             
             if (uptimeView != null) {
+                mSetBackground = false;
                 if (mFirstRunTime >= 0) {
                     //Log.i("foo", "Time for " + mItem.mDisplayLabel
                     //        + ": " + (SystemClock.uptimeMillis()-mFirstRunTime));
@@ -159,16 +165,29 @@
         public ActiveItem bind(RunningState state, RunningState.BaseItem item,
                 StringBuilder builder) {
             synchronized (state.mLock) {
+                PackageManager pm = rootView.getContext().getPackageManager();
+                if (item.mPackageInfo == null && item instanceof RunningState.MergedItem) {
+                    // Items for background processes don't normally load
+                    // their labels for performance reasons.  Do it now.
+                    ((RunningState.MergedItem)item).mProcess.ensureLabel(pm);
+                    item.mPackageInfo = ((RunningState.MergedItem)item).mProcess.mPackageInfo;
+                    item.mDisplayLabel = ((RunningState.MergedItem)item).mProcess.mDisplayLabel;
+                }
                 name.setText(item.mDisplayLabel);
                 ActiveItem ai = new ActiveItem();
                 ai.mRootView = rootView;
                 ai.mItem = item;
                 ai.mHolder = this;
                 ai.mFirstRunTime = item.mActiveSince;
-                description.setText(item.mDescription);
+                if (item.mBackground) {
+                    description.setText(rootView.getContext().getText(R.string.cached));
+                } else {
+                    description.setText(item.mDescription);
+                }
                 item.mCurSizeStr = null;
-                icon.setImageDrawable(item.mPackageInfo.loadIcon(
-                        rootView.getContext().getPackageManager()));
+                if (item.mPackageInfo != null) {
+                    icon.setImageDrawable(item.mPackageInfo.loadIcon(pm));
+                }
                 icon.setVisibility(View.VISIBLE);
                 ai.updateTime(rootView.getContext(), builder);
                 return ai;
@@ -185,6 +204,7 @@
     class ServiceListAdapter extends BaseAdapter {
         final RunningState mState;
         final LayoutInflater mInflater;
+        boolean mShowBackground;
         ArrayList<RunningState.MergedItem> mItems;
         
         ServiceListAdapter(RunningState state) {
@@ -194,8 +214,24 @@
             refreshItems();
         }
 
+        void setShowBackground(boolean showBackground) {
+            if (mShowBackground != showBackground) {
+                mShowBackground = showBackground;
+                mState.setWatchingBackgroundItems(showBackground);
+                refreshItems();
+                notifyDataSetChanged();
+                mColorBar.setShowingGreen(mShowBackground);
+            }
+        }
+
+        boolean getShowBackground() {
+            return mShowBackground;
+        }
+
         void refreshItems() {
-            ArrayList<RunningState.MergedItem> newItems = mState.getCurrentMergedItems();
+            ArrayList<RunningState.MergedItem> newItems =
+                mShowBackground ? mState.getCurrentBackgroundItems()
+                        : mState.getCurrentMergedItems();
             if (mItems != newItems) {
                 mItems = newItems;
             }
@@ -266,67 +302,6 @@
         }
     }
     
-    public static class LinearColorBar extends LinearLayout {
-        private float mRedRatio;
-        private float mYellowRatio;
-        private float mGreenRatio;
-        
-        final Rect mRect = new Rect();
-        final Paint mPaint = new Paint();
-        
-        public LinearColorBar(Context context, AttributeSet attrs) {
-            super(context, attrs);
-            setWillNotDraw(false);
-            mPaint.setStyle(Paint.Style.FILL);
-        }
-
-        public void setRatios(float red, float yellow, float green) {
-            mRedRatio = red;
-            mYellowRatio = yellow;
-            mGreenRatio = green;
-            invalidate();
-        }
-        
-        @Override
-        protected void onDraw(Canvas canvas) {
-            super.onDraw(canvas);
-            
-            int width = getWidth();
-            mRect.top = 0;
-            mRect.bottom = getHeight();
-            
-            int left = 0;
-            
-            int right = left + (int)(width*mRedRatio);
-            if (left < right) {
-                mRect.left = left;
-                mRect.right = right;
-                mPaint.setColor(0xffff8080);
-                canvas.drawRect(mRect, mPaint);
-                width -= (right-left);
-                left = right;
-            }
-            
-            right = left + (int)(width*mYellowRatio);
-            if (left < right) {
-                mRect.left = left;
-                mRect.right = right;
-                mPaint.setColor(0xffffff00);
-                canvas.drawRect(mRect, mPaint);
-                width -= (right-left);
-                left = right;
-            }
-            
-            right = left + width;
-            if (left < right) {
-                mRect.left = left;
-                mRect.right = right;
-                mPaint.setColor(0xff80ff80);
-                canvas.drawRect(mRect, mPaint);
-            }
-        }
-    }
-    
     private boolean matchText(byte[] buffer, int index, String text) {
         int N = text.length();
         if ((index+N) >= buffer.length) {
@@ -434,7 +409,7 @@
                     + mLastForegroundProcessMemory + mLastServiceProcessMemory;
             mColorBar.setRatios(mLastForegroundProcessMemory/totalMem,
                     mLastServiceProcessMemory/totalMem,
-                    (availMem+mLastBackgroundProcessMemory)/totalMem);
+                    mLastBackgroundProcessMemory/totalMem);
         }
     }
     
@@ -445,6 +420,7 @@
         Intent intent = new Intent();
         intent.putExtra(RunningServiceDetails.KEY_UID, mi.mProcess.mUid);
         intent.putExtra(RunningServiceDetails.KEY_PROCESS, mi.mProcess.mProcessName);
+        intent.putExtra(RunningServiceDetails.KEY_BACKGROUND, mAdapter.mShowBackground);
         intent.setClass(getContext(), RunningServiceDetails.class);
         getContext().startActivity(intent);
     }
@@ -470,10 +446,23 @@
         }
         mListView.setOnItemClickListener(this);
         mListView.setRecyclerListener(this);
-        mListView.setAdapter(new ServiceListAdapter(mState));
+        mAdapter = new ServiceListAdapter(mState);
+        mListView.setAdapter(mAdapter);
         mColorBar = (LinearColorBar)findViewById(R.id.color_bar);
         mBackgroundProcessText = (TextView)findViewById(R.id.backgroundText);
+        mBackgroundProcessText.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mAdapter.setShowBackground(true);
+            }
+        });
         mForegroundProcessText = (TextView)findViewById(R.id.foregroundText);
+        mForegroundProcessText.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mAdapter.setShowBackground(false);
+            }
+        });
         
         // Magic!  Implementation detail!  Don't count on this!
         SECONDARY_SERVER_MEM =
diff --git a/src/com/android/settings/applications/RunningServiceDetails.java b/src/com/android/settings/applications/RunningServiceDetails.java
index 399c89f..aa89baf 100644
--- a/src/com/android/settings/applications/RunningServiceDetails.java
+++ b/src/com/android/settings/applications/RunningServiceDetails.java
@@ -43,6 +43,7 @@
     
     static final String KEY_UID = "uid";
     static final String KEY_PROCESS = "process";
+    static final String KEY_BACKGROUND = "background";
     
     static final int DIALOG_CONFIRM_STOP = 1;
     
@@ -54,6 +55,7 @@
     
     int mUid;
     String mProcessName;
+    boolean mShowBackground;
     
     RunningState.MergedItem mMergedItem;
     
@@ -90,9 +92,14 @@
                 }
             }
             stopService(new Intent().setComponent(si.mRunningService.service));
-            if (mMergedItem == null || mMergedItem.mServices.size() <= 1) {
+            if (mMergedItem == null) {
+                // If this is gone, we are gone.
+                mState.updateNow();
+                finish();
+            } else if (!mShowBackground && mMergedItem.mServices.size() <= 1) {
                 // If there was only one service, we are finishing it,
                 // so no reason for the UI to stick around.
+                mState.updateNow();
                 finish();
             } else {
                 mState.updateNow();
@@ -166,6 +173,10 @@
                 }
             } else if (mServiceItem != null) {
                 stopActiveService(false);
+            } else if (mActiveItem.mItem.mBackground) {
+                // Background process.  Just kill it.
+                mAm.killBackgroundProcesses(mActiveItem.mItem.mPackageInfo.packageName);
+                finish();
             } else {
                 // Heavy-weight process.  We'll do a force-stop on it.
                 mAm.forceStopPackage(mActiveItem.mItem.mPackageInfo.packageName);
@@ -178,7 +189,8 @@
     
     boolean findMergedItem() {
         RunningState.MergedItem item = null;
-        ArrayList<RunningState.MergedItem> newItems = mState.getCurrentMergedItems();
+        ArrayList<RunningState.MergedItem> newItems = mShowBackground
+                ? mState.getCurrentBackgroundItems() : mState.getCurrentMergedItems();
         if (newItems != null) {
             for (int i=0; i<newItems.size(); i++) {
                 RunningState.MergedItem mi = newItems.get(i);
@@ -189,6 +201,7 @@
                 }
             }
         }
+
         if (mMergedItem != item) {
             mMergedItem = item;
             return true;
@@ -227,7 +240,9 @@
                     si.mServiceInfo.packageName, si.mServiceInfo.descriptionRes,
                     si.mServiceInfo.applicationInfo));
         } else {
-            if (detail.mManageIntent != null) {
+            if (mi.mBackground) {
+                description.setText(R.string.background_process_stop_description);
+            } else if (detail.mManageIntent != null) {
                 try {
                     Resources clientr = getPackageManager().getResourcesForApplication(
                             si.mRunningService.clientPackage);
@@ -254,7 +269,7 @@
         // check if error reporting is enabled in secure settings
         int enabled = Settings.Secure.getInt(getContentResolver(),
                 Settings.Secure.SEND_ACTION_APP_ERROR, 0);
-        if (enabled != 0) {
+        if (enabled != 0 && si != null) {
             detail.mInstaller = ApplicationErrorReport.getErrorReportReceiver(
                     this, si.mServiceInfo.packageName, si.mServiceInfo.applicationInfo.flags);
             detail.mReportButton.setEnabled(detail.mInstaller != null);
@@ -351,7 +366,7 @@
             
             if (mMergedItem.mServices.size() <= 0) {
                 // This item does not have any services, so it must be
-                // a heavy-weight process...  we will put a fake service
+                // another interesting process...  we will put a fake service
                 // entry for it, to allow the user to "stop" it.
                 addServiceDetailsView(null, mMergedItem);
             }
@@ -396,6 +411,7 @@
         
         mUid = getIntent().getIntExtra(KEY_UID, 0);
         mProcessName = getIntent().getStringExtra(KEY_PROCESS);
+        mShowBackground = getIntent().getBooleanExtra(KEY_BACKGROUND, false);
         
         mAm = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
         mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
diff --git a/src/com/android/settings/applications/RunningState.java b/src/com/android/settings/applications/RunningState.java
index 6d08c57..dbe4a64 100644
--- a/src/com/android/settings/applications/RunningState.java
+++ b/src/com/android/settings/applications/RunningState.java
@@ -85,9 +85,9 @@
     final ServiceProcessComparator mServiceProcessComparator
             = new ServiceProcessComparator();
     
-    // Additional heavy-weight processes to be shown to the user, even if
+    // Additional interesting processes to be shown to the user, even if
     // there is no service running in them.
-    final ArrayList<ProcessItem> mHeavyProcesses = new ArrayList<ProcessItem>();
+    final ArrayList<ProcessItem> mInterestingProcesses = new ArrayList<ProcessItem>();
     
     // All currently running processes, for finding dependencies etc.
     final SparseArray<ProcessItem> mRunningProcesses
@@ -109,9 +109,11 @@
     
     boolean mResumed;
     boolean mHaveData;
+    boolean mWatchingBackgroundItems;
 
     ArrayList<BaseItem> mItems = new ArrayList<BaseItem>();
     ArrayList<MergedItem> mMergedItems = new ArrayList<MergedItem>();
+    ArrayList<MergedItem> mBackgroundItems = new ArrayList<MergedItem>();
     
     int mNumBackgroundProcesses;
     long mBackgroundProcessMemory;
@@ -207,6 +209,7 @@
         String mSizeStr;
         String mCurSizeStr;
         boolean mNeedDivider;
+        boolean mBackground;
         
         public BaseItem(boolean isProcess) {
             mIsProcess = isProcess;
@@ -437,26 +440,36 @@
         final ArrayList<ProcessItem> mOtherProcesses = new ArrayList<ProcessItem>();
         final ArrayList<ServiceItem> mServices = new ArrayList<ServiceItem>();
         
+        private int mLastNumProcesses = -1, mLastNumServices = -1;
+
         MergedItem() {
             super(false);
         }
         
-        boolean update(Context context) {
+        boolean update(Context context, boolean background) {
             mPackageInfo = mProcess.mPackageInfo;
             mDisplayLabel = mProcess.mDisplayLabel;
             mLabel = mProcess.mLabel;
+            mBackground = background;
             
-            int numProcesses = (mProcess.mPid > 0 ? 1 : 0) + mOtherProcesses.size();
-            int numServices = mServices.size();
-            int resid = R.string.running_processes_item_description_s_s;
-            if (numProcesses != 1) {
-                resid = numServices != 1
-                        ? R.string.running_processes_item_description_p_p
-                        : R.string.running_processes_item_description_p_s;
-            } else if (numServices != 1) {
-                resid = R.string.running_processes_item_description_s_p;
+            if (!mBackground) {
+                int numProcesses = (mProcess.mPid > 0 ? 1 : 0) + mOtherProcesses.size();
+                int numServices = mServices.size();
+                if (mLastNumProcesses != numProcesses || mLastNumServices != numServices) {
+                    mLastNumProcesses = numProcesses;
+                    mLastNumServices = numServices;
+                    int resid = R.string.running_processes_item_description_s_s;
+                    if (numProcesses != 1) {
+                        resid = numServices != 1
+                                ? R.string.running_processes_item_description_p_p
+                                : R.string.running_processes_item_description_p_s;
+                    } else if (numServices != 1) {
+                        resid = R.string.running_processes_item_description_s_p;
+                    }
+                    mDescription = context.getResources().getString(resid, numProcesses,
+                            numServices);
+                }
             }
-            mDescription = context.getResources().getString(resid, numProcesses, numServices);
             
             mActiveSince = -1;
             for (int i=0; i<mServices.size(); i++) {
@@ -587,6 +600,19 @@
         }
     }
 
+    private boolean isInterestingProcess(ActivityManager.RunningAppProcessInfo pi) {
+        if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE) != 0) {
+            return true;
+        }
+        if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_PERSISTENT) == 0
+                && pi.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
+                && pi.importanceReasonCode
+                        == ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN) {
+            return true;
+        }
+        return false;
+    }
+
     private boolean update(Context context, ActivityManager am) {
         final PackageManager pm = context.getPackageManager();
         
@@ -666,10 +692,10 @@
                 proc.mDependentProcesses.clear();
             }
             
-            if ((pi.flags&ActivityManager.RunningAppProcessInfo.FLAG_CANT_SAVE_STATE) != 0) {
-                if (!mHeavyProcesses.contains(proc)) {
+            if (isInterestingProcess(pi)) {
+                if (!mInterestingProcesses.contains(proc)) {
                     changed = true;
-                    mHeavyProcesses.add(proc);
+                    mInterestingProcesses.add(proc);
                 }
                 proc.mCurSeq = mSequence;
                 proc.ensureLabel(pm);
@@ -706,13 +732,13 @@
             }
         }
         
-        // Remove any old heavy processes.
-        int NHP = mHeavyProcesses.size();
+        // Remove any old interesting processes.
+        int NHP = mInterestingProcesses.size();
         for (int i=0; i<NHP; i++) {
-            ProcessItem proc = mHeavyProcesses.get(i);
+            ProcessItem proc = mInterestingProcesses.get(i);
             if (mRunningProcesses.get(proc.mPid) == null) {
                 changed = true;
-                mHeavyProcesses.remove(i);
+                mInterestingProcesses.remove(i);
                 i--;
                 NHP--;
             }
@@ -841,21 +867,21 @@
                     }
                 }
                 
-                mergedItem.update(context);
+                mergedItem.update(context, false);
                 newMergedItems.add(mergedItem);
             }
             
-            // Finally, heavy-weight processes need to be shown and will
+            // Finally, interesting processes need to be shown and will
             // go at the top.
-            NHP = mHeavyProcesses.size();
+            NHP = mInterestingProcesses.size();
             for (int i=0; i<NHP; i++) {
-                ProcessItem proc = mHeavyProcesses.get(i);
+                ProcessItem proc = mInterestingProcesses.get(i);
                 if (proc.mClient == null && proc.mServices.size() <= 0) {
                     if (proc.mMergedItem == null) {
                         proc.mMergedItem = new MergedItem();
                         proc.mMergedItem.mProcess = proc;
                     }
-                    proc.mMergedItem.update(context);
+                    proc.mMergedItem.update(context, false);
                     newMergedItems.add(0, proc.mMergedItem);
                     mProcessItems.add(proc);
                 }
@@ -900,6 +926,7 @@
         long backgroundProcessMemory = 0;
         long foregroundProcessMemory = 0;
         long serviceProcessMemory = 0;
+        ArrayList<MergedItem> newBackgroundItems = null;
         try {
             final int numProc = mAllProcessItems.size();
             int[] pids = new int[numProc];
@@ -908,7 +935,8 @@
             }
             Debug.MemoryInfo[] mem = ActivityManagerNative.getDefault()
                     .getProcessMemoryInfo(pids);
-            for (int i=pids.length-1; i>=0; i--) {
+            int bgIndex = 0;
+            for (int i=0; i<pids.length; i++) {
                 ProcessItem proc = mAllProcessItems.get(i);
                 changed |= proc.updateSize(context, mem[i], mSequence);
                 if (proc.mCurSeq == mSequence) {
@@ -916,6 +944,28 @@
                 } else if (proc.mRunningProcessInfo.importance >=
                         ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
                     backgroundProcessMemory += proc.mSize;
+                    MergedItem mergedItem;
+                    if (newBackgroundItems != null) {
+                        mergedItem = proc.mMergedItem = new MergedItem();
+                        proc.mMergedItem.mProcess = proc;
+                        newBackgroundItems.add(mergedItem);
+                    } else {
+                        if (bgIndex >= mBackgroundItems.size()
+                                || mBackgroundItems.get(bgIndex).mProcess != proc) {
+                            newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses);
+                            for (int bgi=0; bgi<bgIndex; bgi++) {
+                                newBackgroundItems.add(mBackgroundItems.get(bgi));
+                            }
+                            mergedItem = proc.mMergedItem = new MergedItem();
+                            proc.mMergedItem.mProcess = proc;
+                            newBackgroundItems.add(mergedItem);
+                        } else {
+                            mergedItem = mBackgroundItems.get(bgIndex);
+                        }
+                    }
+                    mergedItem.update(context, true);
+                    mergedItem.updateSize(context);
+                    bgIndex++;
                 } else if (proc.mRunningProcessInfo.importance <=
                         ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE) {
                     foregroundProcessMemory += proc.mSize;
@@ -924,6 +974,16 @@
         } catch (RemoteException e) {
         }
         
+        if (newBackgroundItems == null) {
+            // One or more at the bottom may no longer exit.
+            if (mBackgroundItems.size() > numBackgroundProcesses) {
+                newBackgroundItems = new ArrayList<MergedItem>(numBackgroundProcesses);
+                for (int bgi=0; bgi<numBackgroundProcesses; bgi++) {
+                    newBackgroundItems.add(mBackgroundItems.get(bgi));
+                }
+            }
+        }
+
         for (int i=0; i<mMergedItems.size(); i++) {
             mMergedItems.get(i).updateSize(context);
         }
@@ -935,6 +995,12 @@
             mBackgroundProcessMemory = backgroundProcessMemory;
             mForegroundProcessMemory = foregroundProcessMemory;
             mServiceProcessMemory = serviceProcessMemory;
+            if (newBackgroundItems != null) {
+                mBackgroundItems = newBackgroundItems;
+                if (mWatchingBackgroundItems) {
+                    changed = true;
+                }
+            }
             if (!mHaveData) {
                 mHaveData = true;
                 mLock.notifyAll();
@@ -950,9 +1016,21 @@
         }
     }
     
+    void setWatchingBackgroundItems(boolean watching) {
+        synchronized (mLock) {
+            mWatchingBackgroundItems = watching;
+        }
+    }
+
     ArrayList<MergedItem> getCurrentMergedItems() {
         synchronized (mLock) {
             return mMergedItems;
         }
     }
+
+    ArrayList<MergedItem> getCurrentBackgroundItems() {
+        synchronized (mLock) {
+            return mBackgroundItems;
+        }
+    }
 }
diff --git a/src/com/android/settings/deviceinfo/Memory.java b/src/com/android/settings/deviceinfo/Memory.java
index b574849..0d00528 100644
--- a/src/com/android/settings/deviceinfo/Memory.java
+++ b/src/com/android/settings/deviceinfo/Memory.java
@@ -26,13 +26,9 @@
 import android.content.IntentFilter;
 import android.content.DialogInterface.OnCancelListener;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.IBinder;
-import android.os.Message;
 import android.os.RemoteException;
 import android.os.Environment;
 import android.os.storage.IMountService;
@@ -42,6 +38,7 @@
 import android.os.storage.StorageEventListener;
 import android.preference.Preference;
 import android.preference.PreferenceActivity;
+import android.preference.PreferenceGroup;
 import android.preference.PreferenceScreen;
 import android.text.format.Formatter;
 import android.util.Log;
@@ -50,9 +47,7 @@
 import com.android.settings.R;
 
 import java.io.File;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
 public class Memory extends PreferenceActivity implements OnCancelListener {
     private static final String TAG = "Memory";
@@ -66,6 +61,8 @@
 
     private static final String MEMORY_SD_FORMAT = "memory_sd_format";
 
+    private static final String MEMORY_SD_GROUP = "memory_sd";
+
     private static final int DLG_CONFIRM_UNMOUNT = 1;
     private static final int DLG_ERROR_UNMOUNT = 2;
 
@@ -75,6 +72,9 @@
     private Preference mSdAvail;
     private Preference mSdMountToggle;
     private Preference mSdFormat;
+    private PreferenceGroup mSdMountPreferenceGroup;
+
+    boolean mSdMountToggleAdded = true;
     
     // Access using getMountService()
     private IMountService mMountService = null;
@@ -97,6 +97,8 @@
         mSdAvail = findPreference(MEMORY_SD_AVAIL);
         mSdMountToggle = findPreference(MEMORY_SD_MOUNT_TOGGLE);
         mSdFormat = findPreference(MEMORY_SD_FORMAT);
+
+        mSdMountPreferenceGroup = (PreferenceGroup)findPreference(MEMORY_SD_GROUP);
     }
     
     @Override
@@ -225,7 +227,6 @@
     private boolean hasAppsAccessingStorage() throws RemoteException {
         String extStoragePath = Environment.getExternalStorageDirectory().toString();
         IMountService mountService = getMountService();
-        boolean showPidDialog = false;
         int stUsers[] = mountService.getStorageUsers(extStoragePath);
         if (stUsers != null && stUsers.length > 0) {
             return true;
@@ -275,9 +276,15 @@
             readOnly = mRes.getString(R.string.read_only);
         }
  
-        mSdFormat.setEnabled(false);
-
         if (status.equals(Environment.MEDIA_MOUNTED)) {
+            if (!Environment.isExternalStorageRemovable()) {
+                // This device has built-in storage that is not removable.
+                // There is no reason for the user to unmount it.
+                if (mSdMountToggleAdded) {
+                    mSdMountPreferenceGroup.removePreference(mSdMountToggle);
+                    mSdMountToggleAdded = false;
+                }
+            }
             try {
                 File path = Environment.getExternalStorageDirectory();
                 StatFs stat = new StatFs(path.getPath());
@@ -303,10 +310,18 @@
             mSdAvail.setSummary(mRes.getString(R.string.sd_unavailable));
 
 
+            if (!Environment.isExternalStorageRemovable()) {
+                if (status.equals(Environment.MEDIA_UNMOUNTED)) {
+                    if (!mSdMountToggleAdded) {
+                        mSdMountPreferenceGroup.addPreference(mSdMountToggle);
+                        mSdMountToggleAdded = true;
+                    }
+                }
+            }
+
             if (status.equals(Environment.MEDIA_UNMOUNTED) ||
                 status.equals(Environment.MEDIA_NOFS) ||
                 status.equals(Environment.MEDIA_UNMOUNTABLE) ) {
-                mSdFormat.setEnabled(true);
                 mSdMountToggle.setEnabled(true);
                 mSdMountToggle.setTitle(mRes.getString(R.string.sd_mount));
                 mSdMountToggle.setSummary(mRes.getString(R.string.sd_mount_summary));