Merge "Now showing: Navigation Bar, The Final Layout"
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index d7b1e71..656f5fd 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -74,8 +74,6 @@
 
 static sp<ANativeWindow> gSurface;
 
-#define USE_SURFACE_COMPOSER 0
-
 static int64_t getNowUs() {
     struct timeval tv;
     gettimeofday(&tv, NULL);
@@ -579,6 +577,7 @@
     fprintf(stderr, "       -x display a histogram of decoding times/fps "
                     "(video only)\n");
     fprintf(stderr, "       -S allocate buffers from a surface\n");
+    fprintf(stderr, "       -T allocate buffers from a surface texture\n");
 }
 
 int main(int argc, char **argv) {
@@ -590,6 +589,7 @@
     bool extractThumbnail = false;
     bool seekTest = false;
     bool useSurfaceAlloc = false;
+    bool useSurfaceTexAlloc = false;
     gNumRepetitions = 1;
     gMaxNumFrames = 0;
     gReproduceBug = -1;
@@ -604,7 +604,7 @@
     sp<LiveSession> liveSession;
 
     int res;
-    while ((res = getopt(argc, argv, "han:lm:b:ptsrow:kxS")) >= 0) {
+    while ((res = getopt(argc, argv, "han:lm:b:ptsrow:kxST")) >= 0) {
         switch (res) {
             case 'a':
             {
@@ -695,6 +695,12 @@
                 break;
             }
 
+            case 'T':
+            {
+                useSurfaceTexAlloc = true;
+                break;
+            }
+
             case '?':
             case 'h':
             default:
@@ -843,34 +849,35 @@
     sp<SurfaceComposerClient> composerClient;
     sp<SurfaceControl> control;
 
-    if (useSurfaceAlloc && !audioOnly) {
-#if USE_SURFACE_COMPOSER
-        composerClient = new SurfaceComposerClient;
-        CHECK_EQ(composerClient->initCheck(), (status_t)OK);
+    if ((useSurfaceAlloc || useSurfaceTexAlloc) && !audioOnly) {
+        if (useSurfaceAlloc) {
+            composerClient = new SurfaceComposerClient;
+            CHECK_EQ(composerClient->initCheck(), (status_t)OK);
 
-        control = composerClient->createSurface(
-                getpid(),
-                String8("A Surface"),
-                0,
-                1280,
-                800,
-                PIXEL_FORMAT_RGB_565,
-                0);
+            control = composerClient->createSurface(
+                    String8("A Surface"),
+                    0,
+                    1280,
+                    800,
+                    PIXEL_FORMAT_RGB_565,
+                    0);
 
-        CHECK(control != NULL);
-        CHECK(control->isValid());
+            CHECK(control != NULL);
+            CHECK(control->isValid());
 
-        CHECK_EQ(composerClient->openTransaction(), (status_t)OK);
-        CHECK_EQ(control->setLayer(30000), (status_t)OK);
-        CHECK_EQ(control->show(), (status_t)OK);
-        CHECK_EQ(composerClient->closeTransaction(), (status_t)OK);
+            CHECK_EQ(composerClient->openTransaction(), (status_t)OK);
+            CHECK_EQ(control->setLayer(30000), (status_t)OK);
+            CHECK_EQ(control->show(), (status_t)OK);
+            CHECK_EQ(composerClient->closeTransaction(), (status_t)OK);
 
-        gSurface = control->getSurface();
-        CHECK(gSurface != NULL);
-#else
-        sp<SurfaceTexture> texture = new SurfaceTexture(0 /* tex */);
-        gSurface = new SurfaceTextureClient(texture);
-#endif
+            gSurface = control->getSurface();
+            CHECK(gSurface != NULL);
+        } else {
+            CHECK(useSurfaceTexAlloc);
+
+            sp<SurfaceTexture> texture = new SurfaceTexture(0 /* tex */);
+            gSurface = new SurfaceTextureClient(texture);
+        }
     }
 
     DataSource::RegisterDefaultSniffers();
@@ -1061,12 +1068,12 @@
         }
     }
 
-    if (useSurfaceAlloc && !audioOnly) {
+    if ((useSurfaceAlloc || useSurfaceTexAlloc) && !audioOnly) {
         gSurface.clear();
 
-#if USE_SURFACE_COMPOSER
-        composerClient->dispose();
-#endif
+        if (useSurfaceAlloc) {
+            composerClient->dispose();
+        }
     }
 
     client.disconnect();
diff --git a/core/java/android/app/ISearchManager.aidl b/core/java/android/app/ISearchManager.aidl
index cb03d2c..688cdfd 100644
--- a/core/java/android/app/ISearchManager.aidl
+++ b/core/java/android/app/ISearchManager.aidl
@@ -19,6 +19,7 @@
 import android.app.SearchableInfo;
 import android.app.ISearchManagerCallback;
 import android.content.ComponentName;
+import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.os.Bundle;
 
@@ -26,6 +27,7 @@
 interface ISearchManager {
    SearchableInfo getSearchableInfo(in ComponentName launchActivity);
    List<SearchableInfo> getSearchablesInGlobalSearch();
+   List<ResolveInfo> getGlobalSearchActivities();
    ComponentName getGlobalSearchActivity();
    ComponentName getWebSearchActivity();
 }
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index aab087f..85a2fa8 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.pm.ResolveInfo;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
@@ -374,6 +375,17 @@
             = "android.search.action.SEARCHABLES_CHANGED";
 
     /**
+     * Intent action to be broadcast to inform that the global search provider
+     * has changed. Normal components will have no need to handle this intent since
+     * they should be using API methods from this class to access the global search
+     * activity
+     *
+     * @hide
+     */
+    public final static String INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED
+            = "android.search.action.GLOBAL_SEARCH_ACTIVITY_CHANGED";
+
+    /**
      * Intent action broadcasted to inform that the search settings have changed in some way.
      * Either searchables have been enabled or disabled, or a different web search provider
      * has been chosen.
@@ -526,6 +538,21 @@
     }
 
     /**
+     * Returns a list of installed apps that handle the global search
+     * intent.
+     *
+     * @hide
+     */
+    public List<ResolveInfo> getGlobalSearchActivities() {
+        try {
+            return mService.getGlobalSearchActivities();
+        } catch (RemoteException ex) {
+            Log.e(TAG, "getGlobalSearchActivities() failed: " + ex);
+            return null;
+        }
+    }
+
+    /**
      * Gets the name of the global search activity.
      *
      * @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index afff7e24..65babc2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -20,6 +20,7 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.app.SearchManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.ContentValues;
@@ -3490,6 +3491,18 @@
                 "sms_outgoing_check_max_count";
 
         /**
+         * The global search provider chosen by the user (if multiple global
+         * search providers are installed). This will be the provider returned
+         * by {@link SearchManager#getGlobalSearchActivity()} if it's still
+         * installed. This setting is stored as a flattened component name as
+         * per {@link ComponentName#flattenToString()}.
+         *
+         * @hide
+         */
+        public static final String SEARCH_GLOBAL_SEARCH_ACTIVITY =
+                "search_global_search_activity";
+
+        /**
          * The number of promoted sources in GlobalSearch.
          * @hide
          */
diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java
index 9966753..ab0cb50 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -63,15 +63,7 @@
     // TODO: Move ACTION_NEW_VOICEMAIL to the Intent class.
     /** Broadcast intent when a new voicemail record is inserted. */
     public static final String ACTION_NEW_VOICEMAIL = "android.intent.action.NEW_VOICEMAIL";
-    /**
-     * Extra included in {@value Intent#ACTION_PROVIDER_CHANGED} and
-     * {@value #ACTION_NEW_VOICEMAIL} broadcast intents to indicate the package
-     * that caused the change in content provider.
-     * <p>Receivers of the broadcast can use this field to determine if this is
-     * a self change.
-     * @deprecated {@link #EXTRA_SELF_CHANGE} is now populated instead.
-     */
-    public static final String EXTRA_CHANGED_BY = "com.android.voicemail.extra.CHANGED_BY";
+
     /**
      * Extra included in {@value Intent#ACTION_PROVIDER_CHANGED} and
      * {@value #ACTION_NEW_VOICEMAIL} broadcast intents to indicate if the receiving
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index 3826a01..79ade260 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -23,10 +23,14 @@
 import android.app.SearchableInfo;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ResolveInfo;
+import android.database.ContentObserver;
 import android.os.Process;
+import android.provider.Settings;
 import android.util.Log;
 
 import java.util.List;
@@ -46,6 +50,8 @@
     // This field is initialized lazily in getSearchables(), and then never modified.
     private Searchables mSearchables;
 
+    private ContentObserver mGlobalSearchObserver;
+
     /**
      * Initializes the Search Manager service in the provided system context.
      * Only one instance of this object should be created!
@@ -56,6 +62,8 @@
         mContext = context;
         mContext.registerReceiver(new BootCompletedReceiver(),
                 new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
+        mGlobalSearchObserver = new GlobalSearchProviderObserver(
+                mContext.getContentResolver());
     }
 
     private synchronized Searchables getSearchables() {
@@ -100,6 +108,28 @@
         }
     }
 
+    class GlobalSearchProviderObserver extends ContentObserver {
+        private final ContentResolver mResolver;
+
+        public GlobalSearchProviderObserver(ContentResolver resolver) {
+            super(null);
+            mResolver = resolver;
+            mResolver.registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY),
+                    false /* notifyDescendants */,
+                    this);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            getSearchables().buildSearchableList();
+            Intent intent = new Intent(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
+            intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+            mContext.sendBroadcast(intent);
+        }
+
+    }
+
     //
     // Searchable activities API
     //
@@ -126,6 +156,10 @@
         return getSearchables().getSearchablesInGlobalSearchList();
     }
 
+    public List<ResolveInfo> getGlobalSearchActivities() {
+        return getSearchables().getGlobalSearchActivities();
+    }
+
     /**
      * Gets the name of the global search activity.
      */
diff --git a/core/java/android/server/search/Searchables.java b/core/java/android/server/search/Searchables.java
index 279c17d..f24d52f 100644
--- a/core/java/android/server/search/Searchables.java
+++ b/core/java/android/server/search/Searchables.java
@@ -16,19 +16,23 @@
 
 package android.server.search;
 
-import android.Manifest;
 import android.app.SearchManager;
 import android.app.SearchableInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Bundle;
+import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 
@@ -50,7 +54,10 @@
     private HashMap<ComponentName, SearchableInfo> mSearchablesMap = null;
     private ArrayList<SearchableInfo> mSearchablesList = null;
     private ArrayList<SearchableInfo> mSearchablesInGlobalSearchList = null;
-    private ComponentName mGlobalSearchActivity = null;
+    // Contains all installed activities that handle the global search
+    // intent.
+    private List<ResolveInfo> mGlobalSearchActivities;
+    private ComponentName mCurrentGlobalSearchActivity = null;
     private ComponentName mWebSearchActivity = null;
 
     public static String GOOGLE_SEARCH_COMPONENT_NAME =
@@ -224,8 +231,11 @@
             }
         }
 
+        List<ResolveInfo> newGlobalSearchActivities = findGlobalSearchActivities();
+
         // Find the global search activity
-        ComponentName newGlobalSearchActivity = findGlobalSearchActivity();
+        ComponentName newGlobalSearchActivity = findGlobalSearchActivity(
+                newGlobalSearchActivities);
 
         // Find the web search activity
         ComponentName newWebSearchActivity = findWebSearchActivity(newGlobalSearchActivity);
@@ -235,38 +245,124 @@
             mSearchablesMap = newSearchablesMap;
             mSearchablesList = newSearchablesList;
             mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList;
-            mGlobalSearchActivity = newGlobalSearchActivity;
+            mGlobalSearchActivities = newGlobalSearchActivities;
+            mCurrentGlobalSearchActivity = newGlobalSearchActivity;
             mWebSearchActivity = newWebSearchActivity;
         }
     }
-
     /**
-     * Finds the global search activity.
+     * Returns a sorted list of installed search providers as per
+     * the following heuristics:
      *
-     * This is currently implemented by returning the first activity that handles
-     * the GLOBAL_SEARCH intent and has the GLOBAL_SEARCH permission. If we allow
-     * more than one global search activity to be installed, this code must be changed.
+     * (a) System apps are given priority over non system apps.
+     * (b) Among system apps and non system apps, the relative ordering
+     * is defined by their declared priority.
      */
-    private ComponentName findGlobalSearchActivity() {
+    private List<ResolveInfo> findGlobalSearchActivities() {
+        // Step 1 : Query the package manager for a list
+        // of activities that can handle the GLOBAL_SEARCH intent.
         Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
         PackageManager pm = mContext.getPackageManager();
         List<ResolveInfo> activities =
                 pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
-        int count = activities == null ? 0 : activities.size();
-        for (int i = 0; i < count; i++) {
-            ActivityInfo ai = activities.get(i).activityInfo;
-            if (pm.checkPermission(Manifest.permission.GLOBAL_SEARCH,
-                    ai.packageName) == PackageManager.PERMISSION_GRANTED) {
-                return new ComponentName(ai.packageName, ai.name);
-            } else {
-                Log.w(LOG_TAG, "Package " + ai.packageName + " wants to handle GLOBAL_SEARCH, "
-                        + "but does not have the GLOBAL_SEARCH permission.");
+
+        if (activities != null && !activities.isEmpty()) {
+            // Step 2: Rank matching activities according to our heuristics.
+            Collections.sort(activities, GLOBAL_SEARCH_RANKER);
+        }
+
+        return activities;
+    }
+
+    /**
+     * Finds the global search activity.
+     */
+    private ComponentName findGlobalSearchActivity(List<ResolveInfo> installed) {
+        // Fetch the global search provider from the system settings,
+        // and if it's still installed, return it.
+        final String searchProviderSetting = getGlobalSearchProviderSetting();
+        if (!TextUtils.isEmpty(searchProviderSetting)) {
+            final ComponentName globalSearchComponent = ComponentName.unflattenFromString(
+                    searchProviderSetting);
+            if (globalSearchComponent != null && isInstalled(globalSearchComponent)) {
+                return globalSearchComponent;
             }
         }
+
+        return getDefaultGlobalSearchProvider(installed);
+    }
+
+    /**
+     * Checks whether the global search provider with a given
+     * component name is installed on the system or not. This deals with
+     * cases such as the removal of an installed provider.
+     */
+    private boolean isInstalled(ComponentName globalSearch) {
+        Intent intent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
+        intent.setComponent(globalSearch);
+
+        PackageManager pm = mContext.getPackageManager();
+        List<ResolveInfo> activities =
+                pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+
+        if (activities != null && !activities.isEmpty()) {
+            return true;
+        }
+
+        return false;
+    }
+
+    private static final Comparator<ResolveInfo> GLOBAL_SEARCH_RANKER =
+            new Comparator<ResolveInfo>() {
+        @Override
+        public int compare(ResolveInfo lhs, ResolveInfo rhs) {
+            if (lhs == rhs) {
+                return 0;
+            }
+            boolean lhsSystem = isSystemApp(lhs);
+            boolean rhsSystem = isSystemApp(rhs);
+
+            if (lhsSystem && !rhsSystem) {
+                return -1;
+            } else if (rhsSystem && !lhsSystem) {
+                return 1;
+            } else {
+                // Either both system engines, or both non system
+                // engines.
+                //
+                // Note, this isn't a typo. Higher priority numbers imply
+                // higher priority, but are "lower" in the sort order.
+                return rhs.priority - lhs.priority;
+            }
+        }
+    };
+
+    /**
+     * @return true iff. the resolve info corresponds to a system application.
+     */
+    private static final boolean isSystemApp(ResolveInfo res) {
+        return (res.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+    }
+
+    /**
+     * Returns the highest ranked search provider as per the
+     * ranking defined in {@link #getGlobalSearchActivities()}.
+     */
+    private ComponentName getDefaultGlobalSearchProvider(List<ResolveInfo> providerList) {
+        if (providerList != null && !providerList.isEmpty()) {
+            ActivityInfo ai = providerList.get(0).activityInfo;
+            return new ComponentName(ai.packageName, ai.name);
+        }
+
         Log.w(LOG_TAG, "No global search activity found");
         return null;
     }
 
+    private String getGlobalSearchProviderSetting() {
+        return Settings.Secure.getString(mContext.getContentResolver(),
+                Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY);
+    }
+
     /**
      * Finds the web search activity.
      *
@@ -281,9 +377,9 @@
         PackageManager pm = mContext.getPackageManager();
         List<ResolveInfo> activities =
                 pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
-        int count = activities == null ? 0 : activities.size();
-        for (int i = 0; i < count; i++) {
-            ActivityInfo ai = activities.get(i).activityInfo;
+
+        if (activities != null && !activities.isEmpty()) {
+            ActivityInfo ai = activities.get(0).activityInfo;
             // TODO: do some sanity checks here?
             return new ComponentName(ai.packageName, ai.name);
         }
@@ -307,10 +403,17 @@
     }
 
     /**
+     * Returns a list of activities that handle the global search intent.
+     */
+    public synchronized ArrayList<ResolveInfo> getGlobalSearchActivities() {
+        return new ArrayList<ResolveInfo>(mGlobalSearchActivities);
+    }
+
+    /**
      * Gets the name of the global search activity.
      */
     public synchronized ComponentName getGlobalSearchActivity() {
-        return mGlobalSearchActivity;
+        return mCurrentGlobalSearchActivity;
     }
 
     /**
diff --git a/media/libstagefright/WVMExtractor.cpp b/media/libstagefright/WVMExtractor.cpp
index 26eda0c..7072d58 100644
--- a/media/libstagefright/WVMExtractor.cpp
+++ b/media/libstagefright/WVMExtractor.cpp
@@ -33,26 +33,25 @@
 
 #include <utils/Errors.h>
 
-/* The extractor lifetime is short - just long enough to get
- * the media sources constructed - so the shared lib needs to remain open
- * beyond the lifetime of the extractor.  So keep the handle as a global
- * rather than a member of the extractor
- */
-void *gVendorLibHandle = NULL;
-
 namespace android {
 
-static Mutex gWVMutex;
+Mutex WVMExtractor::sMutex;
+uint32_t WVMExtractor::sActiveExtractors = 0;
+void *WVMExtractor::sVendorLibHandle = NULL;
 
 WVMExtractor::WVMExtractor(const sp<DataSource> &source)
     : mDataSource(source) {
     {
-        Mutex::Autolock autoLock(gWVMutex);
-        if (gVendorLibHandle == NULL) {
-            gVendorLibHandle = dlopen("libwvm.so", RTLD_NOW);
+        Mutex::Autolock autoLock(sMutex);
+
+        if (sVendorLibHandle == NULL) {
+            CHECK(sActiveExtractors == 0);
+            sVendorLibHandle = dlopen("libwvm.so", RTLD_NOW);
         }
 
-        if (gVendorLibHandle == NULL) {
+        sActiveExtractors++;
+
+        if (sVendorLibHandle == NULL) {
             LOGE("Failed to open libwvm.so");
             return;
         }
@@ -60,7 +59,7 @@
 
     typedef WVMLoadableExtractor *(*GetInstanceFunc)(sp<DataSource>);
     GetInstanceFunc getInstanceFunc =
-        (GetInstanceFunc) dlsym(gVendorLibHandle,
+        (GetInstanceFunc) dlsym(sVendorLibHandle,
                 "_ZN7android11GetInstanceENS_2spINS_10DataSourceEEE");
 
     if (getInstanceFunc) {
@@ -72,6 +71,17 @@
 }
 
 WVMExtractor::~WVMExtractor() {
+    Mutex::Autolock autoLock(sMutex);
+
+    CHECK(sActiveExtractors > 0);
+    sActiveExtractors--;
+
+    // Close lib after last use
+    if (sActiveExtractors == 0) {
+        if (sVendorLibHandle != NULL)
+            dlclose(sVendorLibHandle);
+        sVendorLibHandle = NULL;
+    }
 }
 
 size_t WVMExtractor::countTracks() {
diff --git a/media/libstagefright/include/WVMExtractor.h b/media/libstagefright/include/WVMExtractor.h
index deecd25..0817bab 100644
--- a/media/libstagefright/include/WVMExtractor.h
+++ b/media/libstagefright/include/WVMExtractor.h
@@ -18,6 +18,7 @@
 
 #define WVM_EXTRACTOR_H_
 
+#include <media/stagefright/DataSource.h>
 #include <media/stagefright/MediaExtractor.h>
 #include <utils/Errors.h>
 
@@ -67,6 +68,10 @@
 
     WVMExtractor(const WVMExtractor &);
     WVMExtractor &operator=(const WVMExtractor &);
+
+    static Mutex sMutex;
+    static uint32_t sActiveExtractors;
+    static void *sVendorLibHandle;
 };
 
 }  // namespace android