Merge "Transparent system ui flags -> Translucent WM flags." into klp-dev
diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java
index eeb6cba..4af0f51 100644
--- a/core/java/android/transition/TransitionInflater.java
+++ b/core/java/android/transition/TransitionInflater.java
@@ -169,7 +169,7 @@
             } else if ("recolor".equals(name)) {
                 transition = new Recolor();
                 newTransition = true;
-            } else if ("set".equals(name)) {
+            } else if ("transitionSet".equals(name)) {
                 transition = new TransitionSet();
                 TypedArray a = mContext.obtainStyledAttributes(attrs,
                         com.android.internal.R.styleable.TransitionSet);
diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java
index 79cd8b6..4545e3b 100644
--- a/core/java/android/transition/TransitionSet.java
+++ b/core/java/android/transition/TransitionSet.java
@@ -30,6 +30,22 @@
  * others play {@link #ORDERING_SEQUENTIAL}. For example, {@link AutoTransition}
  * uses a TransitionSet to sequentially play a Fade(Fade.OUT), followed by
  * a {@link ChangeBounds}, followed by a Fade(Fade.OUT) transition.
+ *
+ * <p>A TransitionSet can be described in a resource file by using the
+ * tag <code>transitionSet</code>, along with the standard
+ * attributes of {@link android.R.styleable#TransitionSet} and
+ * {@link android.R.styleable#Transition}. Child transitions of the
+ * TransitionSet object can be loaded by adding those child tags inside the
+ * enclosing <code>transitionSet</code> tag. For example, the following xml
+ * describes a TransitionSet that plays a Fade and then a ChangeBounds
+ * transition on the affected view targets:</p>
+ * <pre>
+ *     &lt;transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
+ *             android:ordering="sequential"&gt;
+ *         &lt;fade/&gt;
+ *         &lt;changeBounds/&gt;
+ *     &lt;/transitionSet&gt;
+ * </pre>
  */
 public class TransitionSet extends Transition {
 
diff --git a/packages/PrintSpooler/res/layout/select_printer_activity.xml b/packages/PrintSpooler/res/layout/select_printer_activity.xml
index 2792dcf..6fc77df 100644
--- a/packages/PrintSpooler/res/layout/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/layout/select_printer_activity.xml
@@ -48,6 +48,7 @@
             </ImageView>
 
             <TextView
+                android:id="@+id/title"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:textAppearance="?android:attr/textAppearanceLarge"
@@ -56,6 +57,7 @@
             </TextView>
 
             <ProgressBar
+                android:id="@+id/progress_bar"
                 android:layout_width="fill_parent"
                 android:layout_height="wrap_content"
                 android:indeterminate="true"
diff --git a/packages/PrintSpooler/res/menu/select_printer_activity.xml b/packages/PrintSpooler/res/menu/select_printer_activity.xml
index d4ce1cf..ee62f9f 100644
--- a/packages/PrintSpooler/res/menu/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/menu/select_printer_activity.xml
@@ -21,14 +21,14 @@
         android:title="@string/search"
         android:icon="@*android:drawable/ic_menu_search_holo_light"
         android:actionViewClass="android.widget.SearchView"
-        android:showAsAction="ifRoom"
+        android:showAsAction="ifRoom|collapseActionView"
         android:alphabeticShortcut="f"
         android:imeOptions="actionSearch">
     </item>
 
     <item
         android:id="@+id/action_add_printer"
-        android:title="@null"
+        android:title="@string/print_add_printer"
         android:icon="@*android:drawable/create_contact"
         android:showAsAction="ifRoom"
         android:alphabeticShortcut="a">
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 3a888a8..c82a20e 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -67,6 +67,9 @@
     <!-- Title for the open all printers UI option in the printer list. [CHAR LIMIT=30] -->
     <string name="all_printers">All printers&#8230;</string>
 
+    <!-- Title for the print dialog announced to the user for accessibility. Not shown in the UI. [CHAR LIMIT=none] -->
+    <string name="print_dialog">Print dialog</string>
+
     <!-- Select printer activity -->
 
     <!-- Title for the share action bar menu item. [CHAR LIMIT=20] -->
@@ -78,14 +81,32 @@
     <!-- Title of the button to install a print service. [CHAR LIMIT=25] -->
     <string name="add_print_service_label">Add service</string>
 
+    <!-- Utterance to announce that the search box is shown. This is spoken to a blind user. [CHAR LIMIT=none] -->
+    <string name="print_search_box_shown_utterance">Search box shown</string>
+
+    <!-- Utterance to announce that the search box is hidden. This is spoken to a blind user. [CHAR LIMIT=none] -->
+    <string name="print_search_box_hidden_utterance">Search box hidden</string>
+
+    <!-- Title of the action bar button to got to add a printer. [CHAR LIMIT=25] -->
+    <string name="print_add_printer">Add printer</string>
+
+    <!-- Utterance to announce a change in the number of matches during a search. This is spoken to a blind user. [CHAR LIMIT=none] -->
+    <plurals name="print_search_result_count_utterance">
+        <item quantity="one"><xliff:g id="count" example="1">%1$s</xliff:g> printer found</item>
+        <item quantity="other"><xliff:g id="count" example="2">%1$s</xliff:g> printers found</item>
+    </plurals>
+
     <!-- Add printer dialog  -->
 
     <!-- Title for the alert dialog for selecting a print service. [CHAR LIMIT=50] -->
     <string name="choose_print_service">Choose print service</string>
 
-    <!-- Title for the prompt shown as a placeholder if no printers are found while searching. [CHAR LIMIT=50] -->
+    <!-- Title for the prompt shown as a placeholder if no printers are found while not searching. [CHAR LIMIT=50] -->
     <string name="print_searching_for_printers">Searching for printers</string>
 
+    <!-- Title for the prompt shown as a placeholder if there are no printers while searching. [CHAR LIMIT=50] -->
+    <string name="print_no_printers">No printers found</string>
+
     <!-- Notifications -->
 
     <!-- Template for the notificaiton label for a printing print job. [CHAR LIMIT=25] -->
diff --git a/packages/PrintSpooler/res/values/styles.xml b/packages/PrintSpooler/res/values/styles.xml
index f6db6be..d64380a 100644
--- a/packages/PrintSpooler/res/values/styles.xml
+++ b/packages/PrintSpooler/res/values/styles.xml
@@ -30,7 +30,6 @@
     </style>
 
     <style name="PrintOptionEditTextStyle">
-         <item name="android:selectAllOnFocus">true</item>
          <item name="android:minHeight">?android:attr/listPreferredItemHeightSmall</item>
          <item name="android:singleLine">true</item>
          <item name="android:ellipsize">end</item>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
index 65af830..8aa290c 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java
@@ -63,7 +63,6 @@
     private static final boolean DEBUG = false;
 
     private static final double WEIGHT_DECAY_COEFFICIENT = 0.95f;
-
     private static final int MAX_HISTORY_LENGTH = 50;
 
     private static final int MAX_FAVORITE_PRINTER_COUNT = 4;
@@ -388,25 +387,25 @@
                             + FusedPrintersProvider.this.hashCode());
                 }
 
-                // Ignore printer records whose target services are not installed.
+                // Ignore printer records whose target services are not enabled.
                 PrintManager printManager = (PrintManager) getContext()
                         .getSystemService(Context.PRINT_SERVICE);
                 List<PrintServiceInfo> services = printManager
-                        .getInstalledPrintServices();
+                        .getEnabledPrintServices();
 
-                Set<ComponentName> installedComponents = new ArraySet<ComponentName>();
+                Set<ComponentName> enabledComponents = new ArraySet<ComponentName>();
                 final int installedServiceCount = services.size();
                 for (int i = 0; i < installedServiceCount; i++) {
                     ServiceInfo serviceInfo = services.get(i).getResolveInfo().serviceInfo;
                     ComponentName componentName = new ComponentName(
                             serviceInfo.packageName, serviceInfo.name);
-                    installedComponents.add(componentName);
+                    enabledComponents.add(componentName);
                 }
 
                 final int printerCount = printers.size();
                 for (int i = printerCount - 1; i >= 0; i--) {
                     ComponentName printerServiceName = printers.get(i).getId().getServiceName();
-                    if (!installedComponents.contains(printerServiceName.getPackageName())) {
+                    if (!enabledComponents.contains(printerServiceName)) {
                         printers.remove(i);
                     }
                 }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index f1678ff..7f9be6c 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -178,6 +178,8 @@
     protected void onCreate(Bundle bundle) {
         super.onCreate(bundle);
 
+        setTitle(R.string.print_dialog);
+
         Bundle extras = getIntent().getExtras();
 
         PrintJobInfo printJob = extras.getParcelable(EXTRA_PRINT_JOB);
@@ -1214,7 +1216,7 @@
 
                                 if (capabilitiesChanged || statusChanged) {
                                     // If something changed during update...
-                                    if (updateUi()) {
+                                    if (updateUi() || !mController.hasPerformedLayout()) {
                                         // Update the document.
                                         mController.update();
                                     }
@@ -1269,10 +1271,6 @@
 
             showUi(UI_EDITING_PRINT_JOB, null);
             bindUi();
-
-            mCurrentPrinter = mDestinationSpinnerAdapter.mFakePdfPrinter;
-            updatePrintAttributes(mCurrentPrinter.getCapabilities());
-
             updateUi();
         }
 
@@ -2001,11 +1999,10 @@
                 implements LoaderManager.LoaderCallbacks<List<PrinterInfo>>{
             private final List<PrinterInfo> mPrinters = new ArrayList<PrinterInfo>();
 
-            private final PrinterInfo mFakePdfPrinter;
+            private PrinterInfo mFakePdfPrinter;
 
             public DestinationAdapter() {
                 getLoaderManager().initLoader(LOADER_ID_PRINTERS_LOADER, null, this);
-                mFakePdfPrinter = createFakePdfPrinter();
             }
 
             public int getPrinterIndex(PrinterId printerId) {
@@ -2039,7 +2036,9 @@
 
             @Override
             public int getCount() {
-                return Math.min(mPrinters.size() + 2, DEST_ADAPTER_MAX_ITEM_COUNT);
+                final int additionalItemCount = (mFakePdfPrinter != null) ? 2 : 1;
+                return Math.min(mPrinters.size() + additionalItemCount,
+                        DEST_ADAPTER_MAX_ITEM_COUNT);
             }
 
             @Override
@@ -2055,14 +2054,14 @@
             @Override
             public Object getItem(int position) {
                 if (mPrinters.isEmpty()) {
-                    if (position == 0) {
+                    if (position == 0 && mFakePdfPrinter != null) {
                         return mFakePdfPrinter;
                     }
                 } else {
                     if (position < 1) {
                         return mPrinters.get(position);
                     }
-                    if (position == 1) {
+                    if (position == 1 && mFakePdfPrinter != null) {
                         return mFakePdfPrinter;
                     }
                     if (position < getCount() - 1) {
@@ -2075,14 +2074,14 @@
             @Override
             public long getItemId(int position) {
                 if (mPrinters.isEmpty()) {
-                    if (position == 0) {
+                    if (position == 0 && mFakePdfPrinter != null) {
                         return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF;
                     }
                     if (position == 1) {
                         return DEST_ADAPTER_ITEM_ID_ALL_PRINTERS;
                     }
                 } else {
-                    if (position == 1) {
+                    if (position == 1 && mFakePdfPrinter != null) {
                         return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF;
                     }
                     if (position == getCount() - 1) {
@@ -2114,14 +2113,14 @@
                 Drawable icon = null;
 
                 if (mPrinters.isEmpty()) {
-                    if (position == 0) {
+                    if (position == 0 && mFakePdfPrinter != null) {
                         PrinterInfo printer = (PrinterInfo) getItem(position);
                         title = printer.getName();
                     } else if (position == 1) {
                         title = getString(R.string.all_printers);
                     }
                 } else {
-                    if (position == 1) {
+                    if (position == 1 && mFakePdfPrinter != null) {
                         PrinterInfo printer = (PrinterInfo) getItem(position);
                         title = printer.getName();
                     } else if (position == getCount() - 1) {
@@ -2174,6 +2173,16 @@
             @Override
             public void onLoadFinished(Loader<List<PrinterInfo>> loader,
                     List<PrinterInfo> printers) {
+                // If this is the first load, create the fake PDF printer.
+                // We do this to avoid flicker where the PDF printer is the
+                // only one and as soon as the loader loads the favorites
+                // it gets switched. Not a great user experience.
+                if (mFakePdfPrinter == null) {
+                    mCurrentPrinter = mFakePdfPrinter = createFakePdfPrinter();
+                    updatePrintAttributes(mCurrentPrinter.getCapabilities());
+                    updateUi();
+                }
+
                 // We rearrange the printers if the user selects a printer
                 // not shown in the initial short list. Therefore, we have
                 // to keep the printer order.
diff --git a/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java b/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
index 114c151..b8a9417 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/SelectPrinterFragment.java
@@ -36,6 +36,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.database.DataSetObserver;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
@@ -43,6 +44,7 @@
 import android.print.PrinterId;
 import android.print.PrinterInfo;
 import android.printservice.PrintServiceInfo;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Menu;
@@ -80,6 +82,8 @@
     private final ArrayList<PrintServiceInfo> mAddPrinterServices =
             new ArrayList<PrintServiceInfo>();
 
+    private AnnounceFilterResult mAnnounceFilterResult;
+
     public static interface OnPrinterSelectedListener {
         public void onPrinterSelected(PrinterId printerId);
     }
@@ -93,9 +97,23 @@
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         super.onActivityCreated(savedInstanceState);
-        setListAdapter(new DestinationAdapter());
-        View emptyView = getActivity().findViewById(R.id.empty_print_state);
-        getListView().setEmptyView(emptyView);
+        final DestinationAdapter adapter = new DestinationAdapter();
+        adapter.registerDataSetObserver(new DataSetObserver() {
+            @Override
+            public void onChanged() {
+                if (!getActivity().isFinishing() && adapter.getCount() <= 0) {
+                    updateEmptyView(adapter);
+                }
+            }
+
+            @Override
+            public void onInvalidated() {
+                if (!getActivity().isFinishing()) {
+                    updateEmptyView(adapter);
+                }
+            }
+        });
+        setListAdapter(adapter);
     }
 
     @Override
@@ -117,6 +135,18 @@
                 return true;
             }
         });
+        searchView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+            @Override
+            public void onViewAttachedToWindow(View view) {
+                view.announceForAccessibility(getString(
+                        R.string.print_search_box_shown_utterance));
+            }
+            @Override
+            public void onViewDetachedFromWindow(View view) {
+                view.announceForAccessibility(getString(
+                        R.string.print_search_box_hidden_utterance));
+            }
+        });
 
         if (mAddPrinterServices.isEmpty()) {
             menu.removeItem(R.id.action_add_printer);
@@ -213,10 +243,32 @@
         transaction.commit();
     }
 
+    public void updateEmptyView(DestinationAdapter adapter) {
+        if (getListView().getEmptyView() == null) {
+            View emptyView = getActivity().findViewById(R.id.empty_print_state);
+            getListView().setEmptyView(emptyView);
+        }
+        TextView titleView = (TextView) getActivity().findViewById(R.id.title);
+        View progressBar = getActivity().findViewById(R.id.progress_bar);
+        if (adapter.getUnfilteredCount() <= 0) {
+            titleView.setText(R.string.print_searching_for_printers);
+            progressBar.setVisibility(View.VISIBLE);
+        } else {
+            titleView.setText(R.string.print_no_printers);
+            progressBar.setVisibility(View.GONE);
+        }
+    }
+
+    private void announceSearchResult() {
+        if (mAnnounceFilterResult == null) {
+            mAnnounceFilterResult = new AnnounceFilterResult();
+        }
+        mAnnounceFilterResult.post();
+    }
+
     public static class AddPrinterAlertDialogFragment extends DialogFragment {
 
-        private static final String DEFAULT_MARKET_QUERY_STRING =
-                "market://search?q=print";
+        private String mAddPrintServiceItem;
 
         @Override
         @SuppressWarnings("unchecked")
@@ -227,47 +279,56 @@
             final List<PrintServiceInfo> printServices = (List<PrintServiceInfo>) (List<?>)
                     getArguments().getParcelableArrayList(FRAGMRNT_ARGUMENT_PRINT_SERVICE_INFOS);
 
-            ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(),
-                    android.R.layout.simple_list_item_1);
+            final ArrayAdapter<String> adapter = new ArrayAdapter<String>(
+                    getActivity(), android.R.layout.simple_list_item_1);
             final int printServiceCount = printServices.size();
             for (int i = 0; i < printServiceCount; i++) {
                 PrintServiceInfo printService = printServices.get(i);
                 adapter.add(printService.getResolveInfo().loadLabel(
                         getActivity().getPackageManager()).toString());
             }
+            final String searchUri = Settings.Secure.getString(getActivity().getContentResolver(),
+                    Settings.Secure.PRINT_SERVICE_SEARCH_URI);
+            final Intent marketIntent;
+            if (!TextUtils.isEmpty(searchUri)) {
+                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri));
+                if (getActivity().getPackageManager().resolveActivity(intent, 0) != null) {
+                    marketIntent = intent;
+                    mAddPrintServiceItem = getString(R.string.add_print_service_label);
+                    adapter.add(mAddPrintServiceItem);
+                } else {
+                    marketIntent = null;
+                }
+            } else {
+                marketIntent = null;
+            }
 
             builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
                 @Override
                 public void onClick(DialogInterface dialog, int which) {
-                    PrintServiceInfo printService = printServices.get(which);
-                    ComponentName componentName = new ComponentName(
-                            printService.getResolveInfo().serviceInfo.packageName,
-                            printService.getAddPrintersActivityName());
-                    Intent intent = new Intent(Intent.ACTION_MAIN);
-                    intent.setComponent(componentName);
-                    try {
-                        startActivity(intent);
-                    } catch (ActivityNotFoundException anfe) {
-                        Log.w(LOG_TAG, "Couldn't start settings activity", anfe);
+                    String item = adapter.getItem(which);
+                    if (item == mAddPrintServiceItem) {
+                        try {
+                          startActivity(marketIntent);
+                      } catch (ActivityNotFoundException anfe) {
+                          Log.w(LOG_TAG, "Couldn't start add printer activity", anfe);
+                      }
+                    } else {
+                        PrintServiceInfo printService = printServices.get(which);
+                        ComponentName componentName = new ComponentName(
+                                printService.getResolveInfo().serviceInfo.packageName,
+                                printService.getAddPrintersActivityName());
+                        Intent intent = new Intent(Intent.ACTION_MAIN);
+                        intent.setComponent(componentName);
+                        try {
+                            startActivity(intent);
+                        } catch (ActivityNotFoundException anfe) {
+                            Log.w(LOG_TAG, "Couldn't start settings activity", anfe);
+                        }
                     }
                 }
             });
 
-            Uri marketUri = Uri.parse(DEFAULT_MARKET_QUERY_STRING);
-            final Intent marketIntent = new Intent(Intent.ACTION_VIEW, marketUri);
-            if (getActivity().getPackageManager().resolveActivity(marketIntent, 0) != null) {
-                builder.setPositiveButton(R.string.add_print_service_label,
-                    new DialogInterface.OnClickListener() {
-                        public void onClick(DialogInterface dialog, int whichButton) {
-                            try {
-                                startActivity(marketIntent);
-                            } catch (ActivityNotFoundException anfe) {
-                                Log.w(LOG_TAG, "Couldn't start add printer activity", anfe);
-                            }
-                        }
-                    });
-            }
-
             return builder.create();
         }
     }
@@ -315,7 +376,9 @@
                 @Override
                 @SuppressWarnings("unchecked")
                 protected void publishResults(CharSequence constraint, FilterResults results) {
+                    final boolean resultCountChanged;
                     synchronized (mLock) {
+                        final int oldPrinterCount = mFilteredPrinters.size();
                         mLastSearchString = constraint;
                         mFilteredPrinters.clear();
                         if (results == null) {
@@ -324,12 +387,22 @@
                             List<PrinterInfo> printers = (List<PrinterInfo>) results.values;
                             mFilteredPrinters.addAll(printers);
                         }
+                        resultCountChanged = (oldPrinterCount != mFilteredPrinters.size());
+                    }
+                    if (resultCountChanged) {
+                        announceSearchResult();
                     }
                     notifyDataSetChanged();
                 }
             };
         }
 
+        public int getUnfilteredCount() {
+            synchronized (mLock) {
+                return mPrinters.size();
+            }
+        }
+
         @Override
         public int getCount() {
             synchronized (mLock) {
@@ -434,4 +507,30 @@
             notifyDataSetInvalidated();
         }
     }
+
+    private final class AnnounceFilterResult implements Runnable {
+        private static final int SEARCH_RESULT_ANNOUNCEMENT_DELAY = 1000; // 1 sec
+
+        public void post() {
+            remove();
+            getListView().postDelayed(this, SEARCH_RESULT_ANNOUNCEMENT_DELAY);
+        }
+
+        public void remove() {
+            getListView().removeCallbacks(this);
+        }
+
+        @Override
+        public void run() {
+            final int count = getListView().getAdapter().getCount();
+            final String text;
+            if (count <= 0) {
+                text = getString(R.string.print_no_printers);
+            } else {
+                text = getActivity().getResources().getQuantityString(
+                    R.plurals.print_search_result_count_utterance, count, count);
+            }
+            getListView().announceForAccessibility(text);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 04885f0..6e53363 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -391,7 +391,7 @@
 
         final AccessibilityManager accessibilityManager =
                 (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
-        if (accessibilityManager.isEnabled()) {
+        if (accessibilityManager.isEnabled() && accessibilityManager.isTouchExplorationEnabled()) {
             // In accessibility mode, we add a simple click handler since swipe is tough to
             // trigger near screen edges.
             View camera = getCameraButton();
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index f23bcba..fe91b6c 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -3193,6 +3193,7 @@
             if (task.userId == tr.userId
                     && ((task.affinity != null && task.affinity.equals(tr.affinity))
                     || (task.intent != null && task.intent.filterEquals(tr.intent)))) {
+                tr.disposeThumbnail();
                 mRecentTasks.remove(i);
                 i--;
                 N--;
@@ -3204,7 +3205,7 @@
             }
         }
         if (N >= MAX_RECENT_TASKS) {
-            mRecentTasks.remove(N-1);
+            mRecentTasks.remove(N-1).disposeThumbnail();
         }
         mRecentTasks.add(0, task);
     }
@@ -3464,7 +3465,8 @@
             clearProfilerLocked();
         }
 
-        mStackSupervisor.handleAppDiedLocked(app, restarting);
+        // Remove this application's activities from active lists.
+        boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(app, restarting);
 
         app.activities.clear();
 
@@ -3475,6 +3477,19 @@
             info.putString("shortMsg", "Process crashed.");
             finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info);
         }
+
+        if (!restarting) {
+            if (!mStackSupervisor.resumeTopActivitiesLocked()) {
+                // If there was nothing to resume, and we are not already
+                // restarting this process, but there is a visible activity that
+                // is hosted by the process...  then make sure all visible
+                // activities are running, taking care of restarting this
+                // process.
+                if (hasVisibleActivities) {
+                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+                }
+            }
+        }
     }
 
     private final int getLRURecordIndexForAppLocked(IApplicationThread thread) {
@@ -6738,6 +6753,7 @@
     }
 
     private void cleanUpRemovedTaskLocked(TaskRecord tr, int flags) {
+        tr.disposeThumbnail();
         mRecentTasks.remove(tr);
         mStackSupervisor.removeTask(tr);
         final boolean killProcesses = (flags&ActivityManager.REMOVE_TASK_KILL_PROCESS) != 0;
@@ -15818,9 +15834,11 @@
                     }
                 }
 
-                boolean haveActivities = mStackSupervisor.switchUserLocked(userId, uss);
-                if (!haveActivities) {
+                boolean homeInFront = mStackSupervisor.switchUserLocked(userId, uss);
+                if (homeInFront) {
                     startHomeActivityLocked(userId);
+                } else {
+                    mStackSupervisor.resumeTopActivitiesLocked();
                 }
 
                 EventLogTags.writeAmSwitchUser(userId);
@@ -16146,6 +16164,8 @@
             } catch (RemoteException e) {
             }
         }
+
+        mStackSupervisor.removeUserLocked(userId);
     }
 
     @Override
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 2c0b83b..cf686672 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -572,8 +572,8 @@
     }
 
     boolean isRootActivity() {
-        ArrayList<ActivityRecord> activities = task.mActivities;
-        return activities.size() == 0 || this == task.mActivities.get(0);
+        final ArrayList<ActivityRecord> activities = task.mActivities;
+        return activities.size() == 0 || this == activities.get(0);
     }
 
     UriPermissionOwner getUriPermissionsLocked() {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 45b30f1..fc83f05 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -549,31 +549,26 @@
 
     /*
      * Move the activities around in the stack to bring a user to the foreground.
-     * @return whether there are any activities for the specified user.
      */
-    final boolean switchUserLocked(int userId) {
+    final void switchUserLocked(int userId) {
         if (VALIDATE_TOKENS) {
             validateAppTokensLocked();
         }
         if (mCurrentUser == userId) {
-            return true;
+            return;
         }
         mCurrentUser = userId;
 
         // Move userId's tasks to the top.
-        boolean haveActivities = false;
         int index = mTaskHistory.size();
         for (int i = 0; i < index; ++i) {
             TaskRecord task = mTaskHistory.get(i);
             if (task.userId == userId) {
-                haveActivities = true;
                 mTaskHistory.remove(i);
                 mTaskHistory.add(task);
                 --index;
             }
         }
-
-        return haveActivities;
     }
 
     void minimalResumeActivityLocked(ActivityRecord r) {
@@ -678,6 +673,14 @@
             return null;
         }
 
+        TaskRecord tr = who.task;
+        if (tr.intent != null && (tr.intent.getFlags()
+                &Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0) {
+            // If this task is being excluded from recents, we don't want to take
+            // the expense of capturing a thumbnail, since we will never show it.
+            return null;
+        }
+
         Resources res = mService.mContext.getResources();
         int w = mThumbnailWidth;
         int h = mThumbnailHeight;
@@ -690,6 +693,7 @@
 
         if (w > 0) {
             if (who != mLastScreenshotActivity || mLastScreenshotBitmap == null
+                    || mLastScreenshotActivity.state == ActivityState.RESUMED
                     || mLastScreenshotBitmap.getWidth() != w
                     || mLastScreenshotBitmap.getHeight() != h) {
                 mLastScreenshotActivity = who;
@@ -1092,7 +1096,7 @@
                         int rIndex = task.mActivities.indexOf(r);
                         for ( --rIndex; rIndex >= 0; --rIndex) {
                             final ActivityRecord blocker = task.mActivities.get(rIndex);
-                            if (!blocker.finishing && blocker.visible) {
+                            if (!blocker.finishing) {
                                 if (DEBUG_VISBILITY) Slog.v(TAG, "Home visibility for " +
                                         r + " blocked by " + blocker);
                                 break;
@@ -1623,6 +1627,12 @@
     }
 
     private void insertTaskAtTop(TaskRecord task) {
+        // If this is being moved to the top by another activity or being launched from the home
+        // activity, set mOnTopOfHome accordingly.
+        final boolean fromHome = mStackSupervisor.getLastStack().isHomeStack();
+        if (!isHomeStack() && (fromHome || topTask() != task)) {
+            task.mOnTopOfHome = fromHome;
+        }
         mTaskHistory.remove(task);
         // Now put task at top.
         int stackNdx = mTaskHistory.size();
@@ -3421,13 +3431,9 @@
     /**
      * Reset local parameters because an app's activity died.
      * @param app The app of the activity that died.
-     * @return true if home should be launched next.
+     * @return result from removeHistoryRecordsForAppLocked.
      */
     boolean handleAppDiedLocked(ProcessRecord app) {
-        if (!containsApp(app)) {
-            return false;
-        }
-
         if (mPausingActivity != null && mPausingActivity.app == app) {
             if (DEBUG_PAUSE || DEBUG_CLEANUP) Slog.v(TAG,
                     "App died while pausing: " + mPausingActivity);
@@ -3438,30 +3444,7 @@
             mLastNoHistoryActivity = null;
         }
 
-        // Determine if the top task is exiting and should return to home. Do this before it gets
-        // removed in removeHistoryRecordsForAppsLocked.
-        boolean launchHomeNext = false;
-        TaskRecord topTask = mTaskHistory.get(mTaskHistory.size() - 1);
-        ArrayList<ActivityRecord> activities = topTask.mActivities;
-        int activityNdx;
-        for (activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-            ActivityRecord r = activities.get(activityNdx);
-            if (r.finishing) {
-                continue;
-            }
-            if (r.app != app) {
-                // This is the dying activity.
-                break;
-            }
-        }
-        if (activityNdx < 0) {
-            // All activities in task belong to app. Set launchHomeNext to task's value.
-            launchHomeNext = topTask.mOnTopOfHome;
-        }
-
-        removeHistoryRecordsForAppLocked(app);
-
-        return launchHomeNext;
+        return removeHistoryRecordsForAppLocked(app);
     }
 
     void handleAppCrashLocked(ProcessRecord app) {
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index 2b69a4e..b4de258 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -70,6 +70,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import android.util.SparseBooleanArray;
 import com.android.internal.app.HeavyWeightSwitcherActivity;
 import com.android.internal.os.TransferPipe;
 import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
@@ -203,6 +204,9 @@
      */
     final PowerManager.WakeLock mGoingToSleep;
 
+    /** State of the stacks when user switched, indexed by userId. */
+    SparseBooleanArray mUserHomeInFront = new SparseBooleanArray(2);
+
     public ActivityStackSupervisor(ActivityManagerService service, Context context,
             Looper looper) {
         mService = service;
@@ -288,22 +292,6 @@
         return mService.startHomeActivityLocked(mCurrentUser);
     }
 
-    final void setLaunchHomeTaskNextFlag(ActivityRecord sourceRecord, ActivityRecord r,
-            ActivityStack stack) {
-        if (stack == mHomeStack) {
-            return;
-        }
-        if ((sourceRecord == null && getLastStack() == mHomeStack) ||
-                (sourceRecord != null && sourceRecord.isHomeActivity())) {
-            if (r == null) {
-                r = stack.topRunningActivityLocked(null);
-            }
-            if (r != null && !r.isHomeActivity() && r.isRootActivity()) {
-                r.task.mOnTopOfHome = true;
-            }
-        }
-    }
-
     void setDismissKeyguard(boolean dismiss) {
         if (ActivityManagerService.DEBUG_LOCKSCREEN) mService.logLockScreen(" dismiss=" + dismiss);
         mDismissKeyguardOnNextActivity = dismiss;
@@ -1479,7 +1467,6 @@
                         // is the case, so this is it!  And for paranoia, make
                         // sure we have correctly resumed the top activity.
                         if (doResume) {
-                            setLaunchHomeTaskNextFlag(sourceRecord, null, targetStack);
                             resumeTopActivitiesLocked(targetStack, null, options);
                         } else {
                             ActivityOptions.abort(options);
@@ -1575,9 +1562,6 @@
                         // don't use that intent!)  And for paranoia, make
                         // sure we have correctly resumed the top activity.
                         if (doResume) {
-                            // Reset flag so it gets correctly reevaluated.
-                            intentActivity.task.mOnTopOfHome = false;
-                            setLaunchHomeTaskNextFlag(sourceRecord, intentActivity, targetStack);
                             targetStack.resumeTopActivityLocked(null, options);
                         } else {
                             ActivityOptions.abort(options);
@@ -1615,7 +1599,6 @@
                             // resumed the top activity.
                             topStack.mLastPausedActivity = null;
                             if (doResume) {
-                                setLaunchHomeTaskNextFlag(sourceRecord, null, topStack);
                                 resumeTopActivitiesLocked();
                             }
                             ActivityOptions.abort(options);
@@ -1694,7 +1677,6 @@
                     // resumed the top activity.
                     targetStack.mLastPausedActivity = null;
                     if (doResume) {
-                        setLaunchHomeTaskNextFlag(sourceRecord, null, targetStack);
                         targetStack.resumeTopActivityLocked(null);
                     }
                     ActivityOptions.abort(options);
@@ -1717,7 +1699,6 @@
                     top.deliverNewIntentLocked(callingUid, r.intent);
                     targetStack.mLastPausedActivity = null;
                     if (doResume) {
-                        setLaunchHomeTaskNextFlag(sourceRecord, null, targetStack);
                         targetStack.resumeTopActivityLocked(null);
                     }
                     return ActivityManager.START_DELIVERED_TO_TOP;
@@ -1751,7 +1732,6 @@
             EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId);
         }
         ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
-        setLaunchHomeTaskNextFlag(sourceRecord, r, targetStack);
         targetStack.mLastPausedActivity = null;
         targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
         mService.setFocusedActivityLocked(r);
@@ -1926,30 +1906,12 @@
         return r;
     }
 
-    void handleAppDiedLocked(ProcessRecord app, boolean restarting) {
-        boolean launchHomeTaskNext = false;
-        final ActivityStack focusedStack = getFocusedStack();
-        final int numStacks = mStacks.size();
-        for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-            final ActivityStack stack = mStacks.get(stackNdx);
-            // Only update launchHomeTaskNext for the focused stack.
-            launchHomeTaskNext |= (stack.handleAppDiedLocked(app) && stack == focusedStack);
+    boolean handleAppDiedLocked(ProcessRecord app, boolean restarting) {
+        boolean hasVisibleActivities = false;
+        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+            hasVisibleActivities |= mStacks.get(stackNdx).handleAppDiedLocked(app);
         }
-
-        if (!restarting) {
-            if (launchHomeTaskNext) {
-                resumeHomeActivity(null);
-            } else {
-                if (!resumeTopActivitiesLocked(focusedStack, null, null)) {
-                    // If there was nothing to resume, and we are not already
-                    // restarting this process, but there is a visible activity that
-                    // is hosted by the process...  then make sure all visible
-                    // activities are running, taking care of restarting this
-                    // process.
-                    ensureActivitiesVisibleLocked(null, 0);
-                }
-            }
-        }
+        return hasVisibleActivities;
     }
 
     void closeSystemDialogsLocked() {
@@ -1960,6 +1922,10 @@
         }
     }
 
+    void removeUserLocked(int userId) {
+        mUserHomeInFront.delete(userId);
+    }
+
     /**
      * @return true if some activity was finished (or would have finished if doit were true).
      */
@@ -2278,17 +2244,17 @@
     }
 
     boolean switchUserLocked(int userId, UserStartedState uss) {
+        mUserHomeInFront.put(mCurrentUser, isFrontStack(mHomeStack));
+        final boolean homeInFront = mUserHomeInFront.get(userId, true);
         mCurrentUser = userId;
 
         mStartingUsers.add(uss);
-        boolean haveActivities = false;
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            haveActivities |= mStacks.get(stackNdx).switchUserLocked(userId);
+            mStacks.get(stackNdx).switchUserLocked(userId);
         }
 
-        resumeTopActivitiesLocked();
-
-        return haveActivities;
+        moveHomeStack(homeInFront);
+        return homeInFront;
     }
 
     final ArrayList<ActivityRecord> processStoppingActivitiesLocked(boolean remove) {
@@ -2381,6 +2347,7 @@
         pw.print(prefix); pw.print("mStackState="); pw.println(stackStateToString(mStackState));
         pw.print(prefix); pw.println("mSleepTimeout: " + mSleepTimeout);
         pw.print(prefix); pw.println("mCurTaskId: " + mCurTaskId);
+        pw.print(prefix); pw.println("mUserHomeInFront: " + mUserHomeInFront);
     }
 
     ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index 385253e..3d568ff 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -128,6 +128,16 @@
         }
     }
 
+    void disposeThumbnail() {
+        super.disposeThumbnail();
+        for (int i=mActivities.size()-1; i>=0; i--) {
+            ThumbnailHolder thumb = mActivities.get(i).thumbHolder;
+            if (thumb != this) {
+                thumb.disposeThumbnail();
+            }
+        }
+    }
+
     ActivityRecord getTopActivity() {
         for (int i = mActivities.size() - 1; i >= 0; --i) {
             final ActivityRecord r = mActivities.get(i);
diff --git a/services/java/com/android/server/am/ThumbnailHolder.java b/services/java/com/android/server/am/ThumbnailHolder.java
index 02f4fcb..a6974f5 100644
--- a/services/java/com/android/server/am/ThumbnailHolder.java
+++ b/services/java/com/android/server/am/ThumbnailHolder.java
@@ -21,4 +21,9 @@
 public class ThumbnailHolder {
     Bitmap lastThumbnail;         // Last thumbnail captured for this item.
     CharSequence lastDescription; // Last description captured for this item.
+
+    void disposeThumbnail() {
+        lastThumbnail = null;
+        lastDescription = null;
+    }
 }
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index f093f2b..e84f90e 100755
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -850,6 +850,19 @@
                                 sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
                                         null, null,
                                         res.pkg.applicationInfo.packageName, null, updateUsers);
+
+                                // treat asec-hosted packages like removable media on upgrade
+                                if (isForwardLocked(res.pkg) || isExternal(res.pkg)) {
+                                    if (DEBUG_INSTALL) {
+                                        Slog.i(TAG, "upgrading pkg " + res.pkg
+                                                + " is ASEC-hosted -> AVAILABLE");
+                                    }
+                                    int[] uidArray = new int[] { res.pkg.applicationInfo.uid };
+                                    ArrayList<String> pkgList = new ArrayList<String>(1);
+                                    pkgList.add(res.pkg.applicationInfo.packageName);
+                                    sendResourcesChangedBroadcast(true, false,
+                                            pkgList,uidArray, null);
+                                }
                             }
                             if (res.removedInfo.args != null) {
                                 // Remove the replaced package's older resources safely now
@@ -4643,6 +4656,20 @@
         // so that we do not end up in a confused state while the user is still using the older
         // version of the application while the new one gets installed.
         if ((parseFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
+            // If the package lives in an asec, tell everyone that the container is going
+            // away so they can clean up any references to its resources (which would prevent
+            // vold from being able to unmount the asec)
+            if (isForwardLocked(pkg) || isExternal(pkg)) {
+                if (DEBUG_INSTALL) {
+                    Slog.i(TAG, "upgrading pkg " + pkg + " is ASEC-hosted -> UNAVAILABLE");
+                }
+                final int[] uidArray = new int[] { pkg.applicationInfo.uid };
+                final ArrayList<String> pkgList = new ArrayList<String>(1);
+                pkgList.add(pkg.applicationInfo.packageName);
+                sendResourcesChangedBroadcast(false, true, pkgList, uidArray, null);
+            }
+
+            // Post the request that it be killed now that the going-away broadcast is en route
             killApplication(pkg.applicationInfo.packageName,
                         pkg.applicationInfo.uid, "update pkg");
         }
@@ -9120,6 +9147,17 @@
         }
     }
 
+    boolean locationIsPrivileged(File path) {
+        try {
+            final String privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app")
+                    .getCanonicalPath();
+            return path.getCanonicalPath().startsWith(privilegedAppDir);
+        } catch (IOException e) {
+            Slog.e(TAG, "Unable to access code path " + path);
+        }
+        return false;
+    }
+
     /*
      * Tries to delete system package.
      */
@@ -9175,9 +9213,12 @@
         }
         // Install the system package
         if (DEBUG_REMOVE) Slog.d(TAG, "Re-installing system package: " + disabledPs);
+        int parseFlags = PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM;
+        if (locationIsPrivileged(disabledPs.codePath)) {
+            parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
+        }
         PackageParser.Package newPkg = scanPackageLI(disabledPs.codePath,
-                PackageParser.PARSE_MUST_BE_APK | PackageParser.PARSE_IS_SYSTEM,
-                SCAN_MONITOR | SCAN_NO_PATHS, 0, null);
+                parseFlags, SCAN_MONITOR | SCAN_NO_PATHS, 0, null);
 
         if (newPkg == null) {
             Slog.w(TAG, "Failed to restore system package:" + newPs.name
@@ -10731,8 +10772,8 @@
         }
     }
 
-   private void sendResourcesChangedBroadcast(boolean mediaStatus, ArrayList<String> pkgList,
-            int uidArr[], IIntentReceiver finishedReceiver) {
+   private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
+           ArrayList<String> pkgList, int uidArr[], IIntentReceiver finishedReceiver) {
         int size = pkgList.size();
         if (size > 0) {
             // Send broadcasts here
@@ -10742,6 +10783,9 @@
             if (uidArr != null) {
                 extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidArr);
             }
+            if (replacing && !mediaStatus) {
+                extras.putBoolean(Intent.EXTRA_REPLACING, replacing);
+            }
             String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
                     : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
             sendPackageBroadcast(action, null, extras, null, finishedReceiver, null);
@@ -10844,7 +10888,7 @@
         }
         // Send a broadcast to let everyone know we are done processing
         if (pkgList.size() > 0) {
-            sendResourcesChangedBroadcast(true, pkgList, uidArr, null);
+            sendResourcesChangedBroadcast(true, false, pkgList, uidArr, null);
         }
         // Force gc to avoid any stale parser references that we might have.
         if (doGc) {
@@ -10921,7 +10965,8 @@
         // broadcast when packages get disabled, force a gc to clean things up.
         // and unload all the containers.
         if (pkgList.size() > 0) {
-            sendResourcesChangedBroadcast(false, pkgList, uidArr, new IIntentReceiver.Stub() {
+            sendResourcesChangedBroadcast(false, false, pkgList, uidArr,
+                    new IIntentReceiver.Stub() {
                 public void performReceive(Intent intent, int resultCode, String data,
                         Bundle extras, boolean ordered, boolean sticky,
                         int sendingUser) throws RemoteException {
@@ -11041,7 +11086,7 @@
                     }
                     if (returnCode == PackageManager.MOVE_SUCCEEDED) {
                         // Send resources unavailable broadcast
-                        sendResourcesChangedBroadcast(false, pkgList, uidArr, null);
+                        sendResourcesChangedBroadcast(false, true, pkgList, uidArr, null);
                         // Update package code and resource paths
                         synchronized (mInstallLock) {
                             synchronized (mPackages) {
@@ -11119,7 +11164,7 @@
                             }
                         }
                         // Send resources available broadcast
-                        sendResourcesChangedBroadcast(true, pkgList, uidArr, null);
+                        sendResourcesChangedBroadcast(true, false, pkgList, uidArr, null);
                     }
                 }
                 if (returnCode != PackageManager.MOVE_SUCCEEDED) {