Merge "Add ability to run tests restricted to given annotation."
diff --git a/api/current.xml b/api/current.xml
index 7c0f4da..2729ff4 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -23748,7 +23748,7 @@
 >
 <parameter name="parcel" type="android.os.Parcel">
 </parameter>
-<parameter name="flagz" type="int">
+<parameter name="flags" type="int">
 </parameter>
 </method>
 <field name="CREATOR"
@@ -34626,7 +34626,7 @@
  visibility="public"
 >
 </field>
-<field name="UIMODE_SERVICE"
+<field name="UI_MODE_SERVICE"
  type="java.lang.String"
  transient="false"
  volatile="false"
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 05bbf3b..b38aeda 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1308,14 +1308,16 @@
         }
 
         // close any cursors we are managing.
-        int numCursors = mManagedCursors.size();
-        for (int i = 0; i < numCursors; i++) {
-            ManagedCursor c = mManagedCursors.get(i);
-            if (c != null) {
-                c.mCursor.close();
+        synchronized (mManagedCursors) {
+            int numCursors = mManagedCursors.size();
+            for (int i = 0; i < numCursors; i++) {
+                ManagedCursor c = mManagedCursors.get(i);
+                if (c != null) {
+                    c.mCursor.close();
+                }
             }
+            mManagedCursors.clear();
         }
-        mManagedCursors.clear();
     }
 
     /**
@@ -3778,13 +3780,15 @@
     }
     
     final void performRestart() {
-        final int N = mManagedCursors.size();
-        for (int i=0; i<N; i++) {
-            ManagedCursor mc = mManagedCursors.get(i);
-            if (mc.mReleased || mc.mUpdated) {
-                mc.mCursor.requery();
-                mc.mReleased = false;
-                mc.mUpdated = false;
+        synchronized (mManagedCursors) {
+            final int N = mManagedCursors.size();
+            for (int i=0; i<N; i++) {
+                ManagedCursor mc = mManagedCursors.get(i);
+                if (mc.mReleased || mc.mUpdated) {
+                    mc.mCursor.requery();
+                    mc.mReleased = false;
+                    mc.mUpdated = false;
+                }
             }
         }
 
@@ -3850,12 +3854,14 @@
                     " did not call through to super.onStop()");
             }
     
-            final int N = mManagedCursors.size();
-            for (int i=0; i<N; i++) {
-                ManagedCursor mc = mManagedCursors.get(i);
-                if (!mc.mReleased) {
-                    mc.mCursor.deactivate();
-                    mc.mReleased = true;
+            synchronized (mManagedCursors) {
+                final int N = mManagedCursors.size();
+                for (int i=0; i<N; i++) {
+                    ManagedCursor mc = mManagedCursors.get(i);
+                    if (!mc.mReleased) {
+                        mc.mCursor.deactivate();
+                        mc.mReleased = true;
+                    }
                 }
             }
     
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 13cc3ba..0355016 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2495,7 +2495,6 @@
                             " did not call through to super.onPostCreate()");
                     }
                 }
-                r.state = null;
             }
             r.paused = true;
 
@@ -2526,6 +2525,7 @@
 
         if (a != null) {
             r.createdConfig = new Configuration(mConfiguration);
+            Bundle oldState = r.state;
             handleResumeActivity(r.token, false, r.isForward);
 
             if (!r.activity.mFinished && r.startsNotResumed) {
@@ -2541,6 +2541,9 @@
                 try {
                     r.activity.mCalled = false;
                     mInstrumentation.callActivityOnPause(r.activity);
+                    // We need to keep around the original state, in case
+                    // we need to be created again.
+                    r.state = oldState;
                     if (!r.activity.mCalled) {
                         throw new SuperNotCalledException(
                             "Activity " + r.intent.getComponent().toShortString() +
diff --git a/core/java/android/app/ISearchManager.aidl b/core/java/android/app/ISearchManager.aidl
index 9ba7863..cb03d2c 100644
--- a/core/java/android/app/ISearchManager.aidl
+++ b/core/java/android/app/ISearchManager.aidl
@@ -24,9 +24,8 @@
 
 /** @hide */
 interface ISearchManager {
-   SearchableInfo getSearchableInfo(in ComponentName launchActivity, boolean globalSearch);
+   SearchableInfo getSearchableInfo(in ComponentName launchActivity);
    List<SearchableInfo> getSearchablesInGlobalSearch();
-   List<SearchableInfo> getSearchablesForWebSearch();
-   SearchableInfo getDefaultSearchableForWebSearch();
-   void setDefaultWebSearch(in ComponentName component);
+   ComponentName getGlobalSearchActivity();
+   ComponentName getWebSearchActivity();
 }
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index 581b436..6a02a58 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -274,7 +274,7 @@
         SearchManager searchManager = (SearchManager)
                 mContext.getSystemService(Context.SEARCH_SERVICE);
         // Try to get the searchable info for the provided component.
-        mSearchable = searchManager.getSearchableInfo(componentName, false);
+        mSearchable = searchManager.getSearchableInfo(componentName);
 
         if (mSearchable == null) {
             return false;
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 99edfac..b54e53d 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -137,21 +137,11 @@
  * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);   // search within your activity
  * setDefaultKeyMode(DEFAULT_KEYS_SEARCH_GLOBAL);  // search using platform global search</pre>
  * 
- * <p><b>How to enable global search with Quick Search Box.</b>  In addition to searching within
+ * <p><b>How to start global search.</b>  In addition to searching within
  * your activity or application, you can also use the Search Manager to invoke a platform-global
- * search, which uses Quick Search Box to search across the device and the web. There are two ways
- * to do this:
- * <ul><li>You can simply define "search" within your application or activity to mean global search.
- * This is described in more detail in the 
- * <a href="#SearchabilityMetadata">Searchability Metadata</a> section.  Briefly, you will
- * add a single meta-data entry to your manifest, declaring that the default search
- * for your application is "*".  This indicates to the system that no application-specific
- * search activity is provided, and that it should launch web-based search instead.</li>
- * <li>Simply do nothing and the default implementation of
- * {@link android.app.Activity#onSearchRequested} will cause global search to be triggered.
- * (You can also always trigger search via a direct call to {@link android.app.Activity#startSearch}.
- * This is most useful if you wish to provide local searchability <i>and</i> access to global
- * search.)</li></ul> 
+ * search, which uses Quick Search Box to search across the device and the web.
+ * Override {@link android.app.Activity#onSearchRequested} and call
+ * {@link android.app.Activity#startSearch} with {@code globalSearch} set to {@code true}.
  * 
  * <p><b>How to disable search from your activity.</b> Search is a system-wide feature and users
  * will expect it to be available in all contexts.  If your UI design absolutely precludes
@@ -871,12 +861,8 @@
  * 
  * <p>The simplest way to specify this is to add a <i>search reference</i> element to the
  * application entry in the <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> file.  
- * The value of this reference can be either of:
- * <ul><li>The name of your searchable activity.  
- * It is typically prefixed by '.' to indicate that it's in the same package.</li>
- * <li>A "*" indicates that the system may select a default searchable activity, in which
- * case it will typically select web-based search.</li>
- * </ul>
+ * The value of this reference should be the name of your searchable activity.
+ * It is typically prefixed by '.' to indicate that it's in the same package.
  *
  * <p>Here is a snippet showing the necessary addition to the manifest entry for your 
  * non-searchable activities.
@@ -1662,32 +1648,15 @@
     /**
      * Gets the name of the global search activity.
      *
-     * 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 acitivity to be installed, this code must be changed.
-     *
-     * TODO: Doing this every time we start global search is inefficient. Will fix that once
-     * we have settled on the right mechanism for finding the global search activity.
-     *
      * @hide
      */
     public ComponentName getGlobalSearchActivity() {
-        Intent intent = new Intent(INTENT_ACTION_GLOBAL_SEARCH);
-        PackageManager pm = mContext.getPackageManager();
-        List<ResolveInfo> activities =
-                pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
-        int count = 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(TAG, "Package " + ai.packageName + " wants to handle GLOBAL_SEARCH, "
-                        + "but does not have the GLOBAL_SEARCH permission.");
-            }
+        try {
+            return mService.getGlobalSearchActivity();
+        } catch (RemoteException ex) {
+            Log.e(TAG, "getGlobalSearchActivity() failed: " + ex);
+            return null;
         }
-        return null;
     }
 
     /**
@@ -1700,13 +1669,12 @@
      * @hide
      */
     public ComponentName getWebSearchActivity() {
-        ComponentName globalSearch = getGlobalSearchActivity();
-        if (globalSearch == null) {
+        try {
+            return mService.getWebSearchActivity();
+        } catch (RemoteException ex) {
+            Log.e(TAG, "getWebSearchActivity() failed: " + ex);
             return null;
         }
-        Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
-        intent.setPackage(globalSearch.getPackageName());
-        return intent.resolveActivity(mContext.getPackageManager());
     }
 
     /**
@@ -1840,27 +1808,7 @@
      */
     public SearchableInfo getSearchableInfo(ComponentName componentName) {
         try {
-            return mService.getSearchableInfo(componentName, false);
-        } catch (RemoteException ex) {
-            Log.e(TAG, "getSearchableInfo() failed: " + ex);
-            return null;
-        }
-    }
-
-    /**
-     * Gets information about a searchable activity.
-     *
-     * @param componentName The activity to get searchable information for.
-     * @param globalSearch If <code>false</code>, return information about the given activity.
-     *        If <code>true</code>, return information about the global search activity. 
-     * @return Searchable information, or <code>null</code> if the activity is not searchable.
-     * 
-     * @hide because SearchableInfo is not part of the API.
-     */
-    public SearchableInfo getSearchableInfo(ComponentName componentName,
-            boolean globalSearch) {
-        try {
-            return mService.getSearchableInfo(componentName, globalSearch);
+            return mService.getSearchableInfo(componentName);
         } catch (RemoteException ex) {
             Log.e(TAG, "getSearchableInfo() failed: " + ex);
             return null;
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index eea9257..aca8ab4 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -12,7 +12,7 @@
  *
  * <p>You do not instantiate this class directly; instead, retrieve it through
  * {@link android.content.Context#getSystemService
- * Context.getSystemService(Context.UIMODE_SERVICE)}.
+ * Context.getSystemService(Context.UI_MODE_SERVICE)}.
  */
 public class UiModeManager {
     private static final String TAG = "UiModeManager";
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 45f361e..29f388a 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -167,9 +167,9 @@
     /** @hide */
     public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff;
 
-    // Always log queries which take 100ms+; shorter queries are
+    // Always log queries which take 500ms+; shorter queries are
     // sampled accordingly.
-    private static final int SLOW_THRESHOLD_MILLIS = 100;
+    private static final int SLOW_THRESHOLD_MILLIS = 500;
     private final Random mRandom = new Random();  // guarded by itself
 
     public ContentResolver(Context context) {
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 8fd8e28..c13dd23 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -208,11 +208,11 @@
 
     // Things related to query logging/sampling for debugging
     // slow/frequent queries during development.  Always log queries
-    // which take 100ms+; shorter queries are sampled accordingly.
+    // which take 500ms+; shorter queries are sampled accordingly.
     // Commit statements, which are typically slow, are logged
     // together with the most recently executed SQL statement, for
     // disambiguation.
-    private static final int QUERY_LOG_TIME_IN_MILLIS = 100;
+    private static final int QUERY_LOG_TIME_IN_MILLIS = 500;
     private static final int QUERY_LOG_SQL_LENGTH = 64;
     private static final String COMMIT_SQL = "COMMIT;";
     private final Random mRandom = new Random();
diff --git a/core/java/android/server/search/SearchManagerService.java b/core/java/android/server/search/SearchManagerService.java
index fbc4a81..2eebe77 100644
--- a/core/java/android/server/search/SearchManagerService.java
+++ b/core/java/android/server/search/SearchManagerService.java
@@ -123,24 +123,15 @@
      * Returns the SearchableInfo for a given activity.
      *
      * @param launchActivity The activity from which we're launching this search.
-     * @param globalSearch If false, this will only launch the search that has been specifically
-     * defined by the application (which is usually defined as a local search).  If no default
-     * search is defined in the current application or activity, no search will be launched.
-     * If true, this will always launch a platform-global (e.g. web-based) search instead.
      * @return Returns a SearchableInfo record describing the parameters of the search,
      * or null if no searchable metadata was available.
      */
-    public SearchableInfo getSearchableInfo(final ComponentName launchActivity,
-            final boolean globalSearch) {
-        if (globalSearch) {
-            return getSearchables().getDefaultSearchable();
-        } else {
-            if (launchActivity == null) {
-                Log.e(TAG, "getSearchableInfo(), activity == null");
-                return null;
-            }
-            return getSearchables().getSearchableInfo(launchActivity);
+    public SearchableInfo getSearchableInfo(final ComponentName launchActivity) {
+        if (launchActivity == null) {
+            Log.e(TAG, "getSearchableInfo(), activity == null");
+            return null;
         }
+        return getSearchables().getSearchableInfo(launchActivity);
     }
 
     /**
@@ -151,27 +142,17 @@
     }
 
     /**
-     * Returns a list of the searchable activities that handle web searches.
-     * Can be called from any thread.
+     * Gets the name of the global search activity.
      */
-    public List<SearchableInfo> getSearchablesForWebSearch() {
-        return getSearchables().getSearchablesForWebSearchList();
+    public ComponentName getGlobalSearchActivity() {
+        return getSearchables().getGlobalSearchActivity();
     }
 
     /**
-     * Returns the default searchable activity for web searches.
-     * Can be called from any thread.
+     * Gets the name of the web search activity.
      */
-    public SearchableInfo getDefaultSearchableForWebSearch() {
-        return getSearchables().getDefaultSearchableForWebSearch();
+    public ComponentName getWebSearchActivity() {
+        return getSearchables().getWebSearchActivity();
     }
 
-    /**
-     * Sets the default searchable activity for web searches.
-     * Can be called from any thread.
-     */
-    public void setDefaultWebSearch(final ComponentName component) {
-        getSearchables().setDefaultWebSearch(component);
-        broadcastSearchablesChanged();
-    }
 }
diff --git a/core/java/android/server/search/Searchables.java b/core/java/android/server/search/Searchables.java
index cbb63a5..279c17d 100644
--- a/core/java/android/server/search/Searchables.java
+++ b/core/java/android/server/search/Searchables.java
@@ -16,14 +16,12 @@
 
 package android.server.search;
 
-import com.android.internal.app.ResolverActivity;
-
+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.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -52,9 +50,8 @@
     private HashMap<ComponentName, SearchableInfo> mSearchablesMap = null;
     private ArrayList<SearchableInfo> mSearchablesList = null;
     private ArrayList<SearchableInfo> mSearchablesInGlobalSearchList = null;
-    private ArrayList<SearchableInfo> mSearchablesForWebSearchList = null;
-    private SearchableInfo mDefaultSearchable = null;
-    private SearchableInfo mDefaultSearchableForWebSearch = null;
+    private ComponentName mGlobalSearchActivity = null;
+    private ComponentName mWebSearchActivity = null;
 
     public static String GOOGLE_SEARCH_COMPONENT_NAME =
             "com.android.googlesearch/.GoogleSearch";
@@ -131,10 +128,9 @@
             // Irrespective of source, if a reference was found, follow it.
             if (refActivityName != null)
             {
-                // An app or activity can declare that we should simply launch
-                // "system default search" if search is invoked.
+                // This value is deprecated, return null
                 if (refActivityName.equals(MD_SEARCHABLE_SYSTEM_SEARCH)) {
-                    return getDefaultSearchable();
+                    return null;
                 }
                 String pkg = activity.getPackageName();
                 ComponentName referredActivity;
@@ -164,20 +160,6 @@
     }
 
     /**
-     * Provides the system-default search activity, which you can use
-     * whenever getSearchableInfo() returns null;
-     *
-     * @return Returns the system-default search activity, null if never defined
-     */
-    public synchronized SearchableInfo getDefaultSearchable() {
-        return mDefaultSearchable;
-    }
-
-    public synchronized boolean isDefaultSearchable(SearchableInfo searchable) {
-        return searchable == mDefaultSearchable;
-    }
-
-    /**
      * Builds an entire list (suitable for display) of
      * activities that are searchable, by iterating the entire set of
      * ACTION_SEARCH & ACTION_WEB_SEARCH intents.
@@ -205,8 +187,6 @@
                                 = new ArrayList<SearchableInfo>();
         ArrayList<SearchableInfo> newSearchablesInGlobalSearchList
                                 = new ArrayList<SearchableInfo>();
-        ArrayList<SearchableInfo> newSearchablesForWebSearchList
-                                = new ArrayList<SearchableInfo>();
 
         final PackageManager pm = mContext.getPackageManager();
 
@@ -244,127 +224,71 @@
             }
         }
 
-        if (webSearchInfoList != null) {
-            for (int i = 0; i < webSearchInfoList.size(); ++i) {
-                ActivityInfo ai = webSearchInfoList.get(i).activityInfo;
-                ComponentName component = new ComponentName(ai.packageName, ai.name);
-                SearchableInfo searchable = newSearchablesMap.get(component);
-                if (searchable == null) {
-                    Log.w(LOG_TAG, "did not find component in searchables: " + component);
-                } else {
-                    newSearchablesForWebSearchList.add(searchable);
-                }
-            }
-        }
+        // Find the global search activity
+        ComponentName newGlobalSearchActivity = findGlobalSearchActivity();
 
-        // Find the global search provider
-        Intent globalSearchIntent = new Intent(SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
-        ComponentName globalSearchActivity = globalSearchIntent.resolveActivity(pm);
-        SearchableInfo newDefaultSearchable = newSearchablesMap.get(globalSearchActivity);
-
-        if (newDefaultSearchable == null) {
-            Log.w(LOG_TAG, "No searchable info found for new default searchable activity "
-                    + globalSearchActivity);
-        }
-
-        // Find the default web search provider.
-        ComponentName webSearchActivity = getPreferredWebSearchActivity(mContext);
-        SearchableInfo newDefaultSearchableForWebSearch = null;
-        if (webSearchActivity != null) {
-            newDefaultSearchableForWebSearch = newSearchablesMap.get(webSearchActivity);
-        }
-        if (newDefaultSearchableForWebSearch == null) {
-            Log.w(LOG_TAG, "No searchable info found for new default web search activity "
-                    + webSearchActivity);
-        }
+        // Find the web search activity
+        ComponentName newWebSearchActivity = findWebSearchActivity(newGlobalSearchActivity);
 
         // Store a consistent set of new values
         synchronized (this) {
             mSearchablesMap = newSearchablesMap;
             mSearchablesList = newSearchablesList;
             mSearchablesInGlobalSearchList = newSearchablesInGlobalSearchList;
-            mSearchablesForWebSearchList = newSearchablesForWebSearchList;
-            mDefaultSearchable = newDefaultSearchable;
-            mDefaultSearchableForWebSearch = newDefaultSearchableForWebSearch;
+            mGlobalSearchActivity = newGlobalSearchActivity;
+            mWebSearchActivity = newWebSearchActivity;
         }
     }
 
     /**
-     * Checks if the given activity component is present in the system and if so makes it the
-     * preferred activity for handling ACTION_WEB_SEARCH.
-     * @param component Name of the component to check and set as preferred.
-     * @param action Intent action for which this activity is to be set as preferred.
-     * @return true if component was detected and set as preferred activity, false if not.
+     * Finds the global search activity.
+     *
+     * 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.
      */
-    private static boolean setPreferredActivity(Context context,
-            ComponentName component, String action) {
-        Log.d(LOG_TAG, "Checking component " + component);
-        PackageManager pm = context.getPackageManager();
-        ActivityInfo ai;
-        try {
-            ai = pm.getActivityInfo(component, 0);
-        } catch (PackageManager.NameNotFoundException e) {
-            return false;
+    private ComponentName findGlobalSearchActivity() {
+        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.");
+            }
         }
-
-        // The code here to find the value for bestMatch is heavily inspired by the code
-        // in ResolverActivity where the preferred activity is set.
-        Intent intent = new Intent(action);
-        intent.addCategory(Intent.CATEGORY_DEFAULT);
-        List<ResolveInfo> webSearchActivities = pm.queryIntentActivities(intent, 0);
-        ComponentName set[] = new ComponentName[webSearchActivities.size()];
-        int bestMatch = 0;
-        for (int i = 0; i < webSearchActivities.size(); ++i) {
-            ResolveInfo ri = webSearchActivities.get(i);
-            set[i] = new ComponentName(ri.activityInfo.packageName,
-                                       ri.activityInfo.name);
-            if (ri.match > bestMatch) bestMatch = ri.match;
-        }
-
-        Log.d(LOG_TAG, "Setting preferred web search activity to " + component);
-        IntentFilter filter = new IntentFilter(action);
-        filter.addCategory(Intent.CATEGORY_DEFAULT);
-        pm.replacePreferredActivity(filter, bestMatch, set, component);
-        return true;
+        Log.w(LOG_TAG, "No global search activity found");
+        return null;
     }
 
-    private static ComponentName getPreferredWebSearchActivity(Context context) {
-        // Check if we have a preferred web search activity.
-        Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
-        PackageManager pm = context.getPackageManager();
-        ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
-
-        if (ri == null || ri.activityInfo.name.equals(ResolverActivity.class.getName())) {
-            Log.d(LOG_TAG, "No preferred activity set for action web search.");
-
-            // The components in the providers array are checked in the order of declaration so the
-            // first one has the highest priority. If the component exists in the system it is set
-            // as the preferred activity to handle intent action web search.
-            String[] preferredActivities = context.getResources().getStringArray(
-                    com.android.internal.R.array.default_web_search_providers);
-            for (String componentName : preferredActivities) {
-                ComponentName component = ComponentName.unflattenFromString(componentName);
-                if (setPreferredActivity(context, component, Intent.ACTION_WEB_SEARCH)) {
-                    return component;
-                }
-            }
-        } else {
-            // If the current preferred activity is GoogleSearch, and we detect
-            // EnhancedGoogleSearch installed as well, set the latter as preferred since that
-            // is a superset and provides more functionality.
-            ComponentName cn = new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name);
-            if (cn.flattenToShortString().equals(GOOGLE_SEARCH_COMPONENT_NAME)) {
-                ComponentName enhancedGoogleSearch = ComponentName.unflattenFromString(
-                        ENHANCED_GOOGLE_SEARCH_COMPONENT_NAME);
-                if (setPreferredActivity(context, enhancedGoogleSearch,
-                        Intent.ACTION_WEB_SEARCH)) {
-                    return enhancedGoogleSearch;
-                }
-            }
+    /**
+     * Finds the web search activity.
+     *
+     * Only looks in the package of the global search activity.
+     */
+    private ComponentName findWebSearchActivity(ComponentName globalSearchActivity) {
+        if (globalSearchActivity == null) {
+            return null;
         }
-
-        if (ri == null) return null;
-        return new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name);
+        Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
+        intent.setPackage(globalSearchActivity.getPackageName());
+        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;
+            // TODO: do some sanity checks here?
+            return new ComponentName(ai.packageName, ai.name);
+        }
+        Log.w(LOG_TAG, "No web search activity found");
+        return null;
     }
 
     /**
@@ -383,24 +307,16 @@
     }
 
     /**
-     * Returns a list of the searchable activities that handle web searches.
+     * Gets the name of the global search activity.
      */
-    public synchronized ArrayList<SearchableInfo> getSearchablesForWebSearchList() {
-        return new ArrayList<SearchableInfo>(mSearchablesForWebSearchList);
+    public synchronized ComponentName getGlobalSearchActivity() {
+        return mGlobalSearchActivity;
     }
 
     /**
-     * Returns the default searchable activity for web searches.
+     * Gets the name of the web search activity.
      */
-    public synchronized SearchableInfo getDefaultSearchableForWebSearch() {
-        return mDefaultSearchableForWebSearch;
-    }
-
-    /**
-     * Sets the default searchable activity for web searches.
-     */
-    public synchronized void setDefaultWebSearch(ComponentName component) {
-        setPreferredActivity(mContext, component, Intent.ACTION_WEB_SEARCH);
-        buildSearchableList();
+    public synchronized ComponentName getWebSearchActivity() {
+        return mWebSearchActivity;
     }
 }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index a79bbee..17d5bb7 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1960,7 +1960,6 @@
             if (getHeight() > 0 && getChildCount() > 0) {
                 // We do not lose focus initiating a touch (since AbsListView is focusable in
                 // touch mode). Force an initial layout to get rid of the selection.
-                mLayoutMode = LAYOUT_NORMAL;
                 layoutChildren();
             }
         } else {
@@ -3118,7 +3117,9 @@
 
     void hideSelector() {
         if (mSelectedPosition != INVALID_POSITION) {
-            mResurrectToPosition = mSelectedPosition;
+            if (mLayoutMode != LAYOUT_SPECIFIC) {
+                mResurrectToPosition = mSelectedPosition;
+            }
             if (mNextSelectedPosition >= 0 && mNextSelectedPosition != mSelectedPosition) {
                 mResurrectToPosition = mNextSelectedPosition;
             }
diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java
index c939e3f..2b3b98d 100644
--- a/core/java/android/widget/AbsSpinner.java
+++ b/core/java/android/widget/AbsSpinner.java
@@ -28,8 +28,6 @@
 import android.util.SparseArray;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.animation.Interpolator;
-
 
 /**
  * An abstract base class for spinner widgets. SDK users will probably not
@@ -38,24 +36,21 @@
  * @attr ref android.R.styleable#AbsSpinner_entries
  */
 public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> {
-
     SpinnerAdapter mAdapter;
 
     int mHeightMeasureSpec;
     int mWidthMeasureSpec;
     boolean mBlockLayoutRequests;
+
     int mSelectionLeftPadding = 0;
     int mSelectionTopPadding = 0;
     int mSelectionRightPadding = 0;
     int mSelectionBottomPadding = 0;
-    Rect mSpinnerPadding = new Rect();
-    View mSelectedView = null;
-    Interpolator mInterpolator;
+    final Rect mSpinnerPadding = new Rect();
 
-    RecycleBin mRecycler = new RecycleBin();
+    final RecycleBin mRecycler = new RecycleBin();
     private DataSetObserver mDataSetObserver;
 
-
     /** Temporary frame to hold a child View's frame rectangle */
     private Rect mTouchFrame;
 
@@ -95,7 +90,6 @@
         setWillNotDraw(false);
     }
 
-
     /**
      * The Adapter is used to provide the data which backs this Spinner.
      * It also provides methods to transform spinner items based on their position
@@ -190,7 +184,7 @@
         boolean needsMeasuring = true;
         
         int selectedPosition = getSelectedItemPosition();
-        if (selectedPosition >= 0 && mAdapter != null) {
+        if (selectedPosition >= 0 && mAdapter != null && selectedPosition < mAdapter.getCount()) {
             // Try looking in the recycler. (Maybe we were measured once already)
             View view = mRecycler.get(selectedPosition);
             if (view == null) {
@@ -237,7 +231,6 @@
         mWidthMeasureSpec = widthMeasureSpec;
     }
 
-    
     int getChildHeight(View child) {
         return child.getMeasuredHeight();
     }
@@ -254,26 +247,17 @@
     }
     
     void recycleAllViews() {
-        int childCount = getChildCount();
+        final int childCount = getChildCount();
         final AbsSpinner.RecycleBin recycleBin = mRecycler;
+        final int position = mFirstPosition;
 
         // All views go in recycler
-        for (int i=0; i<childCount; i++) {
+        for (int i = 0; i < childCount; i++) {
             View v = getChildAt(i);
-            int index = mFirstPosition + i;
+            int index = position + i;
             recycleBin.put(index, v);
         }  
     }
-    
-    @Override
-    void handleDataChanged() {
-        // FIXME -- this is called from both measure and layout.
-        // This is harmless right now, but we don't want to do redundant work if
-        // this gets more complicated
-       super.handleDataChanged();
-    }
-    
-  
 
     /**
      * Jump directly to a specific item in the adapter data.
@@ -284,7 +268,6 @@
                 position <= mFirstPosition + getChildCount() - 1;
         setSelectionInt(position, shouldAnimate);
     }
-    
 
     @Override
     public void setSelection(int position) {
@@ -335,8 +318,6 @@
         }
     }
 
- 
-
     @Override
     public SpinnerAdapter getAdapter() {
         return mAdapter;
@@ -452,7 +433,7 @@
     }
 
     class RecycleBin {
-        private SparseArray<View> mScrapHeap = new SparseArray<View>();
+        private final SparseArray<View> mScrapHeap = new SparseArray<View>();
 
         public void put(int position, View v) {
             mScrapHeap.put(position, v);
@@ -469,12 +450,7 @@
             }
             return result;
         }
-        
-        View peek(int position) {
-            // System.out.print("Looking for " + position);
-            return mScrapHeap.get(position);
-        }
-        
+
         void clear() {
             final SparseArray<View> scrapHeap = mScrapHeap;
             final int count = scrapHeap.size();
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index f56b15c..107b145 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -26,6 +26,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.text.TextUtils;
+import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -48,7 +49,6 @@
 import android.widget.SimpleCursorAdapter;
 import android.widget.TextView;
 import android.widget.AdapterView.OnItemClickListener;
-import android.util.AttributeSet;
 
 import com.android.internal.R;
 
@@ -322,24 +322,24 @@
     public Button getButton(int whichButton) {
         switch (whichButton) {
             case DialogInterface.BUTTON_POSITIVE:
-                return mButtonPositiveMessage != null ? mButtonPositive : null;
+                return mButtonPositive;
             case DialogInterface.BUTTON_NEGATIVE:
-                return mButtonNegativeMessage != null ? mButtonNegative : null;
+                return mButtonNegative;
             case DialogInterface.BUTTON_NEUTRAL:
-                return mButtonNeutralMessage != null ? mButtonNeutral : null;
+                return mButtonNeutral;
             default:
                 return null;
         }
     }
     
+    @SuppressWarnings({"UnusedDeclaration"})
     public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (mScrollView != null && mScrollView.executeKeyEvent(event)) return true;
-        return false;
+        return mScrollView != null && mScrollView.executeKeyEvent(event);
     }
 
+    @SuppressWarnings({"UnusedDeclaration"})
     public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (mScrollView != null && mScrollView.executeKeyEvent(event)) return true;
-        return false;
+        return mScrollView != null && mScrollView.executeKeyEvent(event);
     }
     
     private void setupView() {
@@ -469,7 +469,6 @@
     }
 
     private boolean setupButtons() {
-        View defaultButton = null;
         int BIT_BUTTON_POSITIVE = 1;
         int BIT_BUTTON_NEGATIVE = 2;
         int BIT_BUTTON_NEUTRAL = 4;
@@ -482,7 +481,6 @@
         } else {
             mButtonPositive.setText(mButtonPositiveText);
             mButtonPositive.setVisibility(View.VISIBLE);
-            defaultButton = mButtonPositive;
             whichButtons = whichButtons | BIT_BUTTON_POSITIVE;
         }
 
@@ -495,9 +493,6 @@
             mButtonNegative.setText(mButtonNegativeText);
             mButtonNegative.setVisibility(View.VISIBLE);
 
-            if (defaultButton == null) {
-                defaultButton = mButtonNegative;
-            }
             whichButtons = whichButtons | BIT_BUTTON_NEGATIVE;
         }
 
@@ -510,9 +505,6 @@
             mButtonNeutral.setText(mButtonNeutralText);
             mButtonNeutral.setVisibility(View.VISIBLE);
 
-            if (defaultButton == null) {
-                defaultButton = mButtonNeutral;
-            }
             whichButtons = whichButtons | BIT_BUTTON_NEUTRAL;
         }
 
@@ -565,8 +557,6 @@
                 R.styleable.AlertDialog_bottomBright, R.drawable.popup_bottom_bright);
         int bottomMedium = a.getResourceId(
                 R.styleable.AlertDialog_bottomMedium, R.drawable.popup_bottom_medium);
-        int centerMedium = a.getResourceId(
-                R.styleable.AlertDialog_centerMedium, R.drawable.popup_center_medium);
         
         /*
          * We now set the background of all of the sections of the alert.
@@ -596,7 +586,7 @@
          */
         views[pos] = (contentPanel.getVisibility() == View.GONE) 
                 ? null : contentPanel;
-        light[pos] = mListView == null ? false : true;
+        light[pos] = mListView != null;
         pos++;
         if (customPanel != null) {
             views[pos] = customPanel;
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index dfcc8f7..71ccb3b 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -59,6 +59,13 @@
     // Current on-disk Parcel version
     private static final int VERSION = 42;
 
+    // The maximum number of names wakelocks we will keep track of
+    // per uid; once the limit is reached, we batch the remaining wakelocks
+    // in to one common name.
+    private static final int MAX_WAKELOCKS_PER_UID = 20;
+    
+    private static final String BATCHED_WAKELOCK_NAME = "*overflow*";
+    
     private static int sNumSpeedSteps;
 
     private final File mFile;
@@ -1757,7 +1764,12 @@
                 String wakelockName = in.readString();
                 Uid.Wakelock wakelock = new Wakelock();
                 wakelock.readFromParcelLocked(unpluggables, in);
-                mWakelockStats.put(wakelockName, wakelock);
+                if (mWakelockStats.size() < MAX_WAKELOCKS_PER_UID) {
+                    // We will just drop some random set of wakelocks if
+                    // the previous run of the system was an older version
+                    // that didn't impose a limit.
+                    mWakelockStats.put(wakelockName, wakelock);
+                }
             }
 
             int numSensors = in.readInt();
@@ -2583,8 +2595,14 @@
         public StopwatchTimer getWakeTimerLocked(String name, int type) {
             Wakelock wl = mWakelockStats.get(name);
             if (wl == null) {
-                wl = new Wakelock();
-                mWakelockStats.put(name, wl);
+                if (mWakelockStats.size() > MAX_WAKELOCKS_PER_UID) {
+                    name = BATCHED_WAKELOCK_NAME;
+                    wl = mWakelockStats.get(name);
+                }
+                if (wl == null) {
+                    wl = new Wakelock();
+                    mWakelockStats.put(name, wl);
+                }
             }
             StopwatchTimer t = null;
             switch (type) {
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_default.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_default.9.png
index 7acd0df..72faccf 100644
--- a/core/res/res/drawable-hdpi/btn_search_dialog_default.9.png
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_pressed.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_pressed.9.png
index a8b843a..369be10 100644
--- a/core/res/res/drawable-hdpi/btn_search_dialog_pressed.9.png
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_selected.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_selected.9.png
index cfbc092..7e996ec 100644
--- a/core/res/res/drawable-hdpi/btn_search_dialog_selected.9.png
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.png
index 3d0d16e..44668b3 100644
--- a/core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.png
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_voice_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.png
index 2ccd3da..3a4571e 100644
--- a/core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.png
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_voice_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.png b/core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.png
index 966ea44..60dc632 100644
--- a/core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.png
+++ b/core/res/res/drawable-hdpi/btn_search_dialog_voice_selected.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_search_dialog_voice_default.9.png b/core/res/res/drawable-mdpi/btn_search_dialog_voice_default.9.png
index febf222..437fbc7 100644
--- a/core/res/res/drawable-mdpi/btn_search_dialog_voice_default.9.png
+++ b/core/res/res/drawable-mdpi/btn_search_dialog_voice_default.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_search_dialog_voice_pressed.9.png b/core/res/res/drawable-mdpi/btn_search_dialog_voice_pressed.9.png
index 70a200b..a679426 100644
--- a/core/res/res/drawable-mdpi/btn_search_dialog_voice_pressed.9.png
+++ b/core/res/res/drawable-mdpi/btn_search_dialog_voice_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_search_dialog_voice_selected.9.png b/core/res/res/drawable-mdpi/btn_search_dialog_voice_selected.9.png
index 6f2989f..eb95f22 100644
--- a/core/res/res/drawable-mdpi/btn_search_dialog_voice_selected.9.png
+++ b/core/res/res/drawable-mdpi/btn_search_dialog_voice_selected.9.png
Binary files differ
diff --git a/core/res/res/layout/alert_dialog.xml b/core/res/res/layout/alert_dialog.xml
index 7ae68f9..25a41f8 100644
--- a/core/res/res/layout/alert_dialog.xml
+++ b/core/res/res/layout/alert_dialog.xml
@@ -80,7 +80,8 @@
             android:paddingTop="2dip"
             android:paddingBottom="12dip"
             android:paddingLeft="14dip"
-            android:paddingRight="10dip">
+            android:paddingRight="10dip"
+            android:overscrollMode="ifContentScrolls">
             <TextView android:id="@+id/message"
                 style="?android:attr/textAppearanceMedium"
                 android:layout_width="match_parent"
diff --git a/core/res/res/layout/search_bar.xml b/core/res/res/layout/search_bar.xml
index cf246ba..7935e2a 100644
--- a/core/res/res/layout/search_bar.xml
+++ b/core/res/res/layout/search_bar.xml
@@ -34,7 +34,7 @@
         android:layout_height="wrap_content"
         android:orientation="vertical"
         android:paddingLeft="12dip"
-        android:paddingRight="12dip"
+        android:paddingRight="6dip"
         android:paddingTop="7dip"
         android:paddingBottom="16dip"
         android:background="@drawable/search_plate_global" >
@@ -95,7 +95,10 @@
                 android:id="@+id/search_voice_btn"
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
-                android:layout_marginLeft="8dip"
+                android:layout_marginLeft="0dip"
+                android:layout_marginTop="-6.5dip"
+                android:layout_marginBottom="-7dip"
+                android:layout_marginRight="-5dip"
                 android:background="@drawable/btn_search_dialog_voice"
                 android:src="@android:drawable/ic_btn_speak_now"
             />
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index cdc15c2..0c065ef 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -127,15 +127,4 @@
         <item><xliff:g id="id">ime</xliff:g></item>
     </string-array>
 
-    <!-- Do not translate. Each string points to the component name of an ACTION_WEB_SEARCH
-         handling activity. On startup if there were no preferred ACTION_WEB_SEARCH handlers,
-         the first component from this list which is found to be installed is set as the
-         preferred activity. -->
-    <string-array name="default_web_search_providers">
-        <item>com.google.android.googlequicksearchbox/.google.GoogleSearch</item>
-        <item>com.android.quicksearchbox/.google.GoogleSearch</item>
-        <item>com.google.android.providers.enhancedgooglesearch/.Launcher</item>
-        <item>com.android.googlesearch/.GoogleSearch</item>
-        <item>com.android.websearch/.Search.1</item>
-    </string-array>
 </resources>
diff --git a/core/tests/coretests/src/android/app/SearchablesTest.java b/core/tests/coretests/src/android/app/SearchablesTest.java
index 9b4520e..6cb31d4 100644
--- a/core/tests/coretests/src/android/app/SearchablesTest.java
+++ b/core/tests/coretests/src/android/app/SearchablesTest.java
@@ -64,39 +64,7 @@
      *  findActionKey works
      *  getIcon works
      */
-    
-    /**
-     * The goal of this test is to confirm proper operation of the 
-     * SearchableInfo helper class.
-     * 
-     * TODO:  The metadata source needs to be mocked out because adding
-     * searchability metadata via this test is causing it to leak into the
-     * real system.  So for now I'm just going to test for existence of the
-     * GlobalSearch app (which is searchable).
-     */
-    public void testSearchableGlobalSearch() {
-        // test basic array & hashmap
-        Searchables searchables = new Searchables(mContext);
-        searchables.buildSearchableList();
 
-        // test linkage from another activity
-        // TODO inject this via mocking into the package manager.
-        // TODO for now, just check for searchable GlobalSearch app (this isn't really a unit test)
-        ComponentName thisActivity = new ComponentName(
-                "com.android.globalsearch",
-                "com.android.globalsearch.GlobalSearch");
-
-        SearchableInfo si = searchables.getSearchableInfo(thisActivity);
-        assertNotNull(si);
-        assertEquals(thisActivity, si.getSearchActivity());
-        
-        Context appContext = si.getActivityContext(mContext);
-        assertNotNull(appContext);
-        MoreAsserts.assertNotEqual(appContext, mContext);
-        assertEquals("Quick Search Box", appContext.getString(si.getHintId()));
-        assertEquals("Quick Search Box", appContext.getString(si.getLabelId()));
-    }
-    
     /**
      * Test that non-searchable activities return no searchable info (this would typically
      * trigger the use of the default searchable e.g. contacts)
@@ -113,18 +81,7 @@
         SearchableInfo si = searchables.getSearchableInfo(nonActivity);
         assertNull(si);
     }
-    
-    /**
-     * Test that there is a default searchable (aka global search provider).
-     */
-    public void testDefaultSearchable() {
-        Searchables searchables = new Searchables(mContext);
-        searchables.buildSearchableList();
-        SearchableInfo si = searchables.getDefaultSearchable();
-        checkSearchable(si);
-        assertTrue(searchables.isDefaultSearchable(si));
-    }
-    
+
     /**
      * This is an attempt to run the searchable info list with a mocked context.  Here are some
      * things I'd like to test.
@@ -371,7 +328,8 @@
         public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
             assertNotNull(intent);
             assertTrue(intent.getAction().equals(Intent.ACTION_SEARCH)
-                    || intent.getAction().equals(Intent.ACTION_WEB_SEARCH));
+                    || intent.getAction().equals(Intent.ACTION_WEB_SEARCH)
+                    || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH));
             switch (mSearchablesMode) {
             case SEARCHABLES_PASSTHROUGH:
                 return mRealPackageManager.queryIntentActivities(intent, flags);
@@ -472,6 +430,20 @@
                 throw new UnsupportedOperationException();
             }
         }
+
+        @Override
+        public int checkPermission(String permName, String pkgName) {
+            assertNotNull(permName);
+            assertNotNull(pkgName);
+            switch (mSearchablesMode) {
+                case SEARCHABLES_PASSTHROUGH:
+                    return mRealPackageManager.checkPermission(permName, pkgName);
+                case SEARCHABLES_MOCK_ZERO:
+                    return PackageManager.PERMISSION_DENIED;
+                default:
+                    throw new UnsupportedOperationException();
+                }
+        }
     }
 }
 
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
index ba6c711..66c34b2 100644
--- a/include/binder/Parcel.h
+++ b/include/binder/Parcel.h
@@ -30,6 +30,7 @@
 class ProcessState;
 class String8;
 class TextOutput;
+class Flattenable;
 
 struct flat_binder_object;  // defined in support_p/binder_module.h
 
@@ -81,6 +82,7 @@
     status_t            writeString16(const char16_t* str, size_t len);
     status_t            writeStrongBinder(const sp<IBinder>& val);
     status_t            writeWeakBinder(const wp<IBinder>& val);
+    status_t            write(const Flattenable& val);
 
     // Place a native_handle into the parcel (the native_handle's file-
     // descriptors are dup'ed, so it is safe to delete the native_handle
@@ -119,7 +121,7 @@
     const char16_t*     readString16Inplace(size_t* outLen) const;
     sp<IBinder>         readStrongBinder() const;
     wp<IBinder>         readWeakBinder() const;
-
+    status_t            read(Flattenable& val) const;
     
     // Retrieve native_handle from the parcel. This returns a copy of the
     // parcel's native_handle (the caller takes ownership). The caller
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index f8bc7ab..974075d 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -96,6 +96,7 @@
         kRequiresFlushBeforeShutdown         = 64,
         kDefersOutputBufferAllocation        = 128,
         kDecoderLiesAboutNumberOfChannels    = 256,
+        kInputBufferSizesAreBogus            = 512,
     };
 
     struct BufferInfo {
diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h
index b9c491be..e72b6b3 100644
--- a/include/ui/GraphicBuffer.h
+++ b/include/ui/GraphicBuffer.h
@@ -23,6 +23,7 @@
 #include <ui/android_native_buffer.h>
 #include <ui/PixelFormat.h>
 #include <ui/Rect.h>
+#include <utils/Flattenable.h>
 #include <pixelflinger/pixelflinger.h>
 
 struct android_native_buffer_t;
@@ -30,7 +31,6 @@
 namespace android {
 
 class GraphicBufferMapper;
-class Parcel;
 
 // ===========================================================================
 // GraphicBuffer
@@ -40,7 +40,7 @@
     : public EGLNativeBase<
         android_native_buffer_t, 
         GraphicBuffer, 
-        LightRefBase<GraphicBuffer> >
+        LightRefBase<GraphicBuffer> >, public Flattenable
 {
 public:
 
@@ -97,7 +97,6 @@
     uint32_t getVerticalStride() const;
 
 protected:
-    GraphicBuffer(const Parcel& reply);
     virtual ~GraphicBuffer();
 
     enum {
@@ -122,8 +121,16 @@
     status_t initSize(uint32_t w, uint32_t h, PixelFormat format, 
             uint32_t usage);
 
-    static status_t writeToParcel(Parcel* reply, 
-            android_native_buffer_t const* buffer);
+    void free_handle();
+
+    // Flattenable interface
+    size_t getFlattenedSize() const;
+    size_t getFdCount() const;
+    status_t flatten(void* buffer, size_t size,
+            int fds[], size_t count) const;
+    status_t unflatten(void const* buffer, size_t size,
+            int fds[], size_t count);
+
 
     GraphicBufferMapper& mBufferMapper;
     ssize_t mInitCheck;
diff --git a/include/utils/Flattenable.h b/include/utils/Flattenable.h
new file mode 100644
index 0000000..852be3b
--- /dev/null
+++ b/include/utils/Flattenable.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_UTILS_FLATTENABLE_H
+#define ANDROID_UTILS_FLATTENABLE_H
+
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/Errors.h>
+
+namespace android {
+
+class Flattenable
+{
+public:
+    // size in bytes of the flattened object
+    virtual size_t getFlattenedSize() const = 0;
+
+    // number of file descriptors to flatten
+    virtual size_t getFdCount() const = 0;
+
+    // flattens the object into buffer.
+    // size should be at least of getFlattenedSize()
+    // file descriptors are written in the fds[] array but ownership is
+    // not transfered (ie: they must be dupped by the caller of
+    // flatten() if needed).
+    virtual status_t flatten(void* buffer, size_t size,
+            int fds[], size_t count) const = 0;
+
+    // unflattens the object from buffer.
+    // size should be equal to the value of getFlattenedSize() when the
+    // object was flattened.
+    // unflattened file descriptors are found in the fds[] array and
+    // don't need to be dupped(). ie: the caller of unflatten doesn't
+    // keep ownership. If a fd is not retained by unflatten() it must be
+    // explicitly closed.
+    virtual status_t unflatten(void const* buffer, size_t size,
+            int fds[], size_t count) = 0;
+
+protected:
+    virtual ~Flattenable() = 0;
+
+};
+
+}; // namespace android
+
+
+#endif /* ANDROID_UTILS_FLATTENABLE_H */
diff --git a/keystore/java/android/security/SystemKeyStore.java b/keystore/java/android/security/SystemKeyStore.java
index 61a4293..abdb0ae 100644
--- a/keystore/java/android/security/SystemKeyStore.java
+++ b/keystore/java/android/security/SystemKeyStore.java
@@ -17,6 +17,7 @@
 package android.security;
 
 import android.os.Environment;
+import android.os.FileUtils;
 import android.os.Process;
 
 import java.io.File;
@@ -92,6 +93,8 @@
             fos.write(retKey);
             fos.flush();
             fos.close();
+            FileUtils.setPermissions(keyFile.getName(), (FileUtils.S_IRUSR | FileUtils.S_IWUSR),
+                -1, -1);
         } catch (IOException ioe) {
             return null;
         }
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index e397bce..00d2210 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -28,6 +28,7 @@
 #include <utils/String16.h>
 #include <utils/TextOutput.h>
 #include <utils/misc.h>
+#include <utils/Flattenable.h>
 
 #include <private/binder/binder_module.h>
 
@@ -675,6 +676,42 @@
     return writeObject(obj, true);
 }
 
+status_t Parcel::write(const Flattenable& val)
+{
+    status_t err;
+
+    // size if needed
+    size_t len = val.getFlattenedSize();
+    size_t fd_count = val.getFdCount();
+
+    err = this->writeInt32(len);
+    if (err) return err;
+
+    err = this->writeInt32(fd_count);
+    if (err) return err;
+
+    // payload
+    void* buf = this->writeInplace(PAD_SIZE(len));
+    if (buf == NULL)
+        return BAD_VALUE;
+
+    int* fds = NULL;
+    if (fd_count) {
+        fds = new int[fd_count];
+    }
+
+    err = val.flatten(buf, len, fds, fd_count);
+    for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) {
+        err = this->writeDupFileDescriptor( fds[i] );
+    }
+
+    if (fd_count) {
+        delete [] fds;
+    }
+
+    return err;
+}
+
 status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)
 {
     const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
@@ -713,7 +750,6 @@
     goto restart_write;
 }
 
-
 void Parcel::remove(size_t start, size_t amt)
 {
     LOG_ALWAYS_FATAL("Parcel::remove() not yet implemented!");
@@ -940,6 +976,38 @@
     return BAD_TYPE;
 }
 
+status_t Parcel::read(Flattenable& val) const
+{
+    // size
+    const size_t len = this->readInt32();
+    const size_t fd_count = this->readInt32();
+
+    // payload
+    void const* buf = this->readInplace(PAD_SIZE(len));
+    if (buf == NULL)
+        return BAD_VALUE;
+
+    int* fds = NULL;
+    if (fd_count) {
+        fds = new int[fd_count];
+    }
+
+    status_t err = NO_ERROR;
+    for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) {
+        fds[i] = dup(this->readFileDescriptor());
+        if (fds[i] < 0) err = BAD_VALUE;
+    }
+
+    if (err == NO_ERROR) {
+        err = val.unflatten(buf, len, fds, fd_count);
+    }
+
+    if (fd_count) {
+        delete [] fds;
+    }
+
+    return err;
+}
 const flat_binder_object* Parcel::readObject(bool nullMetaData) const
 {
     const size_t DPOS = mDataPos;
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index dec993a..cc3a74fb 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -546,6 +546,8 @@
 
             glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &mGL.mMaxFragmentTextureImageUnits);
             glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &mGL.mMaxFragmentUniformVectors);
+
+            mGL.OES_texture_npot = NULL != strstr((const char *)mGL.mExtensions, "GL_OES_texture_npot");
         }
 
     }
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 03e65f1..04bd748 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -163,6 +163,8 @@
 
     mutable const ObjectBase * mObjHead;
 
+    bool ext_OES_texture_npot() const {return mGL.OES_texture_npot;}
+
 protected:
     Device *mDev;
 
@@ -196,6 +198,8 @@
         int32_t mMaxVertexAttribs;
         int32_t mMaxVertexUniformVectors;
         int32_t mMaxVertexTextureUnits;
+
+        bool OES_texture_npot;
     } mGL;
 
     uint32_t mWidth;
diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp
index 15f3269..c17b94c 100644
--- a/libs/rs/rsProgramFragment.cpp
+++ b/libs/rs/rsProgramFragment.cpp
@@ -109,7 +109,7 @@
         }
 
         if (mSamplers[ct].get()) {
-            mSamplers[ct]->setupGL(rsc);
+            mSamplers[ct]->setupGL(rsc, mTextures[ct]->getType()->getIsNp2());
         } else {
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -159,7 +159,7 @@
         glBindTexture(GL_TEXTURE_2D, mTextures[ct]->getTextureID());
         rsc->checkError("ProgramFragment::setupGL2 tex bind");
         if (mSamplers[ct].get()) {
-            mSamplers[ct]->setupGL(rsc);
+            mSamplers[ct]->setupGL(rsc, mTextures[ct]->getType()->getIsNp2());
         } else {
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
diff --git a/libs/rs/rsSampler.cpp b/libs/rs/rsSampler.cpp
index 7552d54..71f508f 100644
--- a/libs/rs/rsSampler.cpp
+++ b/libs/rs/rsSampler.cpp
@@ -53,7 +53,7 @@
 {
 }
 
-void Sampler::setupGL(const Context *rsc)
+void Sampler::setupGL(const Context *rsc, bool npot)
 {
     GLenum trans[] = {
         GL_NEAREST, //RS_SAMPLER_NEAREST,
@@ -64,11 +64,21 @@
 
     };
 
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]);
+    bool forceNonMip = false;
+    if (!rsc->ext_OES_texture_npot() && npot) {
+        forceNonMip = true;
+    }
+
+    if ((mMinFilter == RS_SAMPLER_LINEAR_MIP_LINEAR) && forceNonMip) {
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    } else {
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, trans[mMinFilter]);
+    }
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, trans[mMagFilter]);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, trans[mWrapS]);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, trans[mWrapT]);
 
+
     rsc->checkError("ProgramFragment::setupGL2 tex env");
 }
 
diff --git a/libs/rs/rsSampler.h b/libs/rs/rsSampler.h
index 9e20a2f..0506081 100644
--- a/libs/rs/rsSampler.h
+++ b/libs/rs/rsSampler.h
@@ -41,7 +41,7 @@
     virtual ~Sampler();
 
     void bind(Allocation *);
-    void setupGL(const Context *);
+    void setupGL(const Context *, bool npot);
 
     void bindToContext(SamplerState *, uint32_t slot);
     void unbindFromContext(SamplerState *);
diff --git a/libs/rs/rsType.cpp b/libs/rs/rsType.cpp
index 9d24c6c..c09e979 100644
--- a/libs/rs/rsType.cpp
+++ b/libs/rs/rsType.cpp
@@ -283,6 +283,24 @@
     mElement->dumpLOGV(buf);
 }
 
+bool Type::getIsNp2() const
+{
+    uint32_t x = getDimX();
+    uint32_t y = getDimY();
+    uint32_t z = getDimZ();
+
+    if (x && (x & (x-1))) {
+        return true;
+    }
+    if (y && (y & (y-1))) {
+        return true;
+    }
+    if (z && (z & (z-1))) {
+        return true;
+    }
+    return false;
+}
+
 
 //////////////////////////////////////////////////
 //
diff --git a/libs/rs/rsType.h b/libs/rs/rsType.h
index 28e6274..c25577c 100644
--- a/libs/rs/rsType.h
+++ b/libs/rs/rsType.h
@@ -56,6 +56,7 @@
     uint32_t getLODOffset(uint32_t lod, uint32_t x, uint32_t y, uint32_t z) const;
 
     uint32_t getLODCount() const {return mLODCount;}
+    bool getIsNp2() const;
 
 
     void setElement(const Element *e) {mElement.set(e);}
@@ -65,6 +66,7 @@
     void setDimFaces(bool v) {mFaces = v;}
     void setDimLOD(bool v) {mDimLOD = v;}
 
+
     void clear();
     void compute();
 
diff --git a/libs/surfaceflinger_client/ISurface.cpp b/libs/surfaceflinger_client/ISurface.cpp
index 9125146..bb86199 100644
--- a/libs/surfaceflinger_client/ISurface.cpp
+++ b/libs/surfaceflinger_client/ISurface.cpp
@@ -78,7 +78,8 @@
         data.writeInt32(bufferIdx);
         data.writeInt32(usage);
         remote()->transact(REQUEST_BUFFER, data, &reply);
-        sp<GraphicBuffer> buffer = new GraphicBuffer(reply);
+        sp<GraphicBuffer> buffer = new GraphicBuffer();
+        reply.read(*buffer);
         return buffer;
     }
 
@@ -141,7 +142,9 @@
             int bufferIdx = data.readInt32();
             int usage = data.readInt32();
             sp<GraphicBuffer> buffer(requestBuffer(bufferIdx, usage));
-            return GraphicBuffer::writeToParcel(reply, buffer.get());
+            if (buffer == NULL)
+                return BAD_VALUE;
+            return reply->write(*buffer);
         }
         case REGISTER_BUFFERS: {
             CHECK_INTERFACE(ISurface, data, reply);
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 6a5c8a9..ba1fd9c 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -14,12 +14,12 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "GraphicBuffer"
+
 #include <stdlib.h>
 #include <stdint.h>
 #include <sys/types.h>
 
-#include <binder/Parcel.h>
-
 #include <utils/Errors.h>
 #include <utils/Log.h>
 
@@ -77,34 +77,21 @@
     handle = inHandle;
 }
 
-GraphicBuffer::GraphicBuffer(const Parcel& data) 
-    : BASE(), mOwner(ownHandle), mBufferMapper(GraphicBufferMapper::get()),
-      mInitCheck(NO_ERROR),  mVStride(0), mIndex(-1)
-{
-    // we own the handle in this case
-    width  = data.readInt32();
-    if (width < 0) {
-        width = height = stride = format = usage = 0;
-        handle = 0;
-    } else {
-        height = data.readInt32();
-        stride = data.readInt32();
-        format = data.readInt32();
-        usage  = data.readInt32();
-        handle = data.readNativeHandle();
-    }
-}
-
 GraphicBuffer::~GraphicBuffer()
 {
     if (handle) {
-        if (mOwner == ownHandle) {
-            native_handle_close(handle);
-            native_handle_delete(const_cast<native_handle*>(handle));
-        } else if (mOwner == ownData) {
-            GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
-            allocator.free(handle);
-        }
+        free_handle();
+    }
+}
+
+void GraphicBuffer::free_handle()
+{
+    if (mOwner == ownHandle) {
+        native_handle_close(handle);
+        native_handle_delete(const_cast<native_handle*>(handle));
+    } else if (mOwner == ownData) {
+        GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
+        allocator.free(handle);
     }
 }
 
@@ -192,29 +179,83 @@
     return res;
 }
 
+size_t GraphicBuffer::getFlattenedSize() const {
+    return (8 + (handle ? handle->numInts : 0))*sizeof(int);
+}
 
-status_t GraphicBuffer::writeToParcel(Parcel* reply, 
-        android_native_buffer_t const* buffer)
+size_t GraphicBuffer::getFdCount() const {
+    return handle ? handle->numFds : 0;
+}
+
+status_t GraphicBuffer::flatten(void* buffer, size_t size,
+        int fds[], size_t count) const
 {
-    if (buffer == NULL)
-        return BAD_VALUE;
+    size_t sizeNeeded = GraphicBuffer::getFlattenedSize();
+    if (size < sizeNeeded) return NO_MEMORY;
 
-    if (buffer->width < 0 || buffer->height < 0)
-        return BAD_VALUE;
+    size_t fdCountNeeded = GraphicBuffer::getFdCount();
+    if (count < fdCountNeeded) return NO_MEMORY;
 
-    status_t err = NO_ERROR;
-    if (buffer->handle == NULL) {
-        // this buffer doesn't have a handle
-        reply->writeInt32(NO_MEMORY);
-    } else {
-        reply->writeInt32(buffer->width);
-        reply->writeInt32(buffer->height);
-        reply->writeInt32(buffer->stride);
-        reply->writeInt32(buffer->format);
-        reply->writeInt32(buffer->usage);
-        err = reply->writeNativeHandle(buffer->handle);
+    int* buf = static_cast<int*>(buffer);
+    buf[0] = 'GBFR';
+    buf[1] = width;
+    buf[2] = height;
+    buf[3] = stride;
+    buf[4] = format;
+    buf[5] = usage;
+    buf[6] = 0;
+    buf[7] = 0;
+
+    if (handle) {
+        buf[6] = handle->numFds;
+        buf[7] = handle->numInts;
+        native_handle_t const* const h = handle;
+        memcpy(fds,     h->data,             h->numFds*sizeof(int));
+        memcpy(&buf[8], h->data + h->numFds, h->numInts*sizeof(int));
     }
-    return err;
+
+    return NO_ERROR;
+}
+
+status_t GraphicBuffer::unflatten(void const* buffer, size_t size,
+        int fds[], size_t count)
+{
+    if (size < 8*sizeof(int)) return NO_MEMORY;
+
+    int const* buf = static_cast<int const*>(buffer);
+    if (buf[0] != 'GBFR') return BAD_TYPE;
+
+    const size_t numFds  = buf[6];
+    const size_t numInts = buf[7];
+
+    const size_t sizeNeeded = (8 + numInts) * sizeof(int);
+    if (size < sizeNeeded) return NO_MEMORY;
+
+    size_t fdCountNeeded = 0;
+    if (count < fdCountNeeded) return NO_MEMORY;
+
+    if (handle) {
+        // free previous handle if any
+        free_handle();
+    }
+
+    if (numFds || numInts) {
+        width  = buf[1];
+        height = buf[2];
+        stride = buf[3];
+        format = buf[4];
+        usage  = buf[5];
+        native_handle* h = native_handle_create(numFds, numInts);
+        memcpy(h->data,          fds,     numFds*sizeof(int));
+        memcpy(h->data + numFds, &buf[8], numInts*sizeof(int));
+        handle = h;
+    } else {
+        width = height = stride = format = usage = 0;
+        handle = NULL;
+    }
+
+    mOwner = ownHandle;
+    return NO_ERROR;
 }
 
 
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index d2cfd3b..d0eedb4 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -25,6 +25,7 @@
 	CallStack.cpp \
 	Debug.cpp \
 	FileMap.cpp \
+	Flattenable.cpp \
 	RefBase.cpp \
 	ResourceTypes.cpp \
 	SharedBuffer.cpp \
diff --git a/libs/utils/Flattenable.cpp b/libs/utils/Flattenable.cpp
new file mode 100644
index 0000000..1f2ffaa
--- /dev/null
+++ b/libs/utils/Flattenable.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/Flattenable.h>
+
+namespace android {
+
+Flattenable::~Flattenable() {
+}
+
+}; // namespace android
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index 2294069..9d1d420 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -17,13 +17,16 @@
 package android.media;
 
 import android.content.ContentValues;
-import android.os.SystemProperties;
 import android.provider.MediaStore.Audio;
 import android.provider.MediaStore.Images;
 import android.provider.MediaStore.Video;
+import android.media.DecoderCapabilities;
+import android.media.DecoderCapabilities.VideoDecoder;
+import android.media.DecoderCapabilities.AudioDecoder;
 
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 
 /**
  * MediaScanner helper class.
@@ -98,13 +101,34 @@
         sFileTypeMap.put(extension, new MediaFileType(fileType, mimeType));
         sMimeTypeMap.put(mimeType, Integer.valueOf(fileType));
     }
+
+    private static boolean isWMAEnabled() {
+        List<AudioDecoder> decoders = DecoderCapabilities.getAudioDecoders();
+        for (AudioDecoder decoder: decoders) {
+            if (decoder == AudioDecoder.AUDIO_DECODER_WMA) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean isWMVEnabled() {
+        List<VideoDecoder> decoders = DecoderCapabilities.getVideoDecoders();
+        for (VideoDecoder decoder: decoders) {
+            if (decoder == VideoDecoder.VIDEO_DECODER_WMV) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     static {
         addFileType("MP3", FILE_TYPE_MP3, "audio/mpeg");
         addFileType("M4A", FILE_TYPE_M4A, "audio/mp4");
         addFileType("WAV", FILE_TYPE_WAV, "audio/x-wav");
         addFileType("AMR", FILE_TYPE_AMR, "audio/amr");
         addFileType("AWB", FILE_TYPE_AWB, "audio/amr-wb");
-        if (SystemProperties.getInt("ro.media.dec.aud.wma.enabled", 0) != 0) {
+        if (isWMAEnabled()) {
             addFileType("WMA", FILE_TYPE_WMA, "audio/x-ms-wma");
         }
         addFileType("OGG", FILE_TYPE_OGG, "application/ogg");
@@ -127,7 +151,7 @@
         addFileType("3GPP", FILE_TYPE_3GPP, "video/3gpp");
         addFileType("3G2", FILE_TYPE_3GPP2, "video/3gpp2");
         addFileType("3GPP2", FILE_TYPE_3GPP2, "video/3gpp2");
-        if (SystemProperties.getInt("ro.media.dec.vid.wmv.enabled", 0) != 0) {
+        if (isWMVEnabled()) {
             addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv");
             addFileType("ASF", FILE_TYPE_ASF, "video/x-ms-asf");
         }
diff --git a/media/jni/android_media_ResampleInputStream.cpp b/media/jni/android_media_ResampleInputStream.cpp
index f248557..d965d9a 100644
--- a/media/jni/android_media_ResampleInputStream.cpp
+++ b/media/jni/android_media_ResampleInputStream.cpp
@@ -92,7 +92,7 @@
          jint jNpoints) {
     
     // safety first!
-    if (nFir21 + jNpoints > BUF_SIZE) {
+    if (nFir21 + jNpoints * 2 > BUF_SIZE) {
         throwException(env, "java/lang/IllegalArgumentException",
                 "FIR+data too long %d", nFir21 + jNpoints);
         return;
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 16635d3..a6053b3 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -463,7 +463,10 @@
                 return err;
             }
         }
-        CHECK_EQ(*offset, stop_offset);
+
+        if (*offset != stop_offset) {
+            return ERROR_MALFORMED;
+        }
 
         return OK;
     }
@@ -496,6 +499,23 @@
                 }
             }
 
+            if (chunk_type == FOURCC('t', 'r', 'a', 'k')) {
+                Track *track = new Track;
+                track->next = NULL;
+                if (mLastTrack) {
+                    mLastTrack->next = track;
+                } else {
+                    mFirstTrack = track;
+                }
+                mLastTrack = track;
+
+                track->meta = new MetaData;
+                track->includes_expensive_metadata = false;
+                track->timescale = 0;
+                track->sampleTable = new SampleTable(mDataSource);
+                track->meta->setCString(kKeyMIMEType, "application/octet-stream");
+            }
+
             off_t stop_offset = *offset + chunk_size;
             *offset = data_offset;
             while (*offset < stop_offset) {
@@ -504,9 +524,18 @@
                     return err;
                 }
             }
-            CHECK_EQ(*offset, stop_offset);
 
-            if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
+            if (*offset != stop_offset) {
+                return ERROR_MALFORMED;
+            }
+
+            if (chunk_type == FOURCC('t', 'r', 'a', 'k')) {
+                status_t err = verifyTrack(mLastTrack);
+
+                if (err != OK) {
+                    return err;
+                }
+            } else if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
                 mHaveMetadata = true;
 
                 return UNKNOWN_ERROR;  // Return a dummy error.
@@ -516,7 +545,9 @@
 
         case FOURCC('t', 'k', 'h', 'd'):
         {
-            CHECK(chunk_data_size >= 4);
+            if (chunk_data_size < 4) {
+                return ERROR_MALFORMED;
+            }
 
             uint8_t version;
             if (mDataSource->readAt(data_offset, &version, 1) < 1) {
@@ -562,21 +593,6 @@
                 height = U32_AT(&buffer[80]);
             }
 
-            Track *track = new Track;
-            track->next = NULL;
-            if (mLastTrack) {
-                mLastTrack->next = track;
-            } else {
-                mFirstTrack = track;
-            }
-            mLastTrack = track;
-
-            track->meta = new MetaData;
-            track->includes_expensive_metadata = false;
-            track->timescale = 0;
-            track->sampleTable = new SampleTable(mDataSource);
-            track->meta->setCString(kKeyMIMEType, "application/octet-stream");
-
             *offset += chunk_size;
             break;
         }
@@ -670,7 +686,10 @@
             }
 
             uint8_t buffer[8];
-            CHECK(chunk_data_size >= (off_t)sizeof(buffer));
+            if (chunk_data_size < (off_t)sizeof(buffer)) {
+                return ERROR_MALFORMED;
+            }
+
             if (mDataSource->readAt(
                         data_offset, buffer, 8) < 8) {
                 return ERROR_IO;
@@ -696,7 +715,10 @@
                     return err;
                 }
             }
-            CHECK_EQ(*offset, stop_offset);
+
+            if (*offset != stop_offset) {
+                return ERROR_MALFORMED;
+            }
             break;
         }
 
@@ -748,7 +770,10 @@
                     return err;
                 }
             }
-            CHECK_EQ(*offset, stop_offset);
+
+            if (*offset != stop_offset) {
+                return ERROR_MALFORMED;
+            }
             break;
         }
 
@@ -792,7 +817,10 @@
                     return err;
                 }
             }
-            CHECK_EQ(*offset, stop_offset);
+
+            if (*offset != stop_offset) {
+                return ERROR_MALFORMED;
+            }
             break;
         }
 
@@ -942,7 +970,10 @@
         case FOURCC('m', 'e', 't', 'a'):
         {
             uint8_t buffer[4];
-            CHECK(chunk_data_size >= (off_t)sizeof(buffer));
+            if (chunk_data_size < (off_t)sizeof(buffer)) {
+                return ERROR_MALFORMED;
+            }
+
             if (mDataSource->readAt(
                         data_offset, buffer, 4) < 4) {
                 return ERROR_IO;
@@ -961,7 +992,10 @@
                     return err;
                 }
             }
-            CHECK_EQ(*offset, stop_offset);
+
+            if (*offset != stop_offset) {
+                return ERROR_MALFORMED;
+            }
             break;
         }
 
@@ -995,8 +1029,9 @@
             int64_t creationTime;
             if (header[0] == 1) {
                 creationTime = U64_AT(&header[4]);
+            } else if (header[0] != 0) {
+                return ERROR_MALFORMED;
             } else {
-                CHECK_EQ(header[0], 0);
                 creationTime = U32_AT(&header[4]);
             }
 
@@ -1174,6 +1209,30 @@
             track->meta, mDataSource, track->timescale, track->sampleTable);
 }
 
+// static
+status_t MPEG4Extractor::verifyTrack(Track *track) {
+    const char *mime;
+    CHECK(track->meta->findCString(kKeyMIMEType, &mime));
+
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+        if (!track->meta->findData(kKeyAVCC, &type, &data, &size)
+                || type != kTypeAVCC) {
+            return ERROR_MALFORMED;
+        }
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)
+            || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+        if (!track->meta->findData(kKeyESDS, &type, &data, &size)
+                || type != kTypeESDS) {
+            return ERROR_MALFORMED;
+        }
+    }
+
+    return OK;
+}
+
 status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio(
         const void *esds_data, size_t esds_size) {
     ESDS esds(esds_data, esds_size);
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 6cf7cff..4075ec1 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -298,6 +298,10 @@
         quirks |= kRequiresAllocateBufferOnOutputPorts;
     }
 
+    if (!strcmp(componentName, "OMX.TI.Video.Decoder")) {
+        quirks |= kInputBufferSizesAreBogus;
+    }
+
     return quirks;
 }
 
@@ -561,7 +565,8 @@
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
     CHECK_EQ(err, OK);
 
-    if (def.nBufferSize < size) {
+    if ((portIndex == kPortIndexInput && (mQuirks & kInputBufferSizesAreBogus))
+        || (def.nBufferSize < size)) {
         def.nBufferSize = size;
     }
 
@@ -574,7 +579,12 @@
     CHECK_EQ(err, OK);
 
     // Make sure the setting actually stuck.
-    CHECK(def.nBufferSize >= size);
+    if (portIndex == kPortIndexInput
+            && (mQuirks & kInputBufferSizesAreBogus)) {
+        CHECK_EQ(def.nBufferSize, size);
+    } else {
+        CHECK(def.nBufferSize >= size);
+    }
 }
 
 status_t OMXCodec::setVideoPortFormatType(
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index 3a63e88..9d35e0c 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -68,6 +68,8 @@
     status_t updateAudioTrackInfoFromESDS_MPEG4Audio(
             const void *esds_data, size_t esds_size);
 
+    static status_t verifyTrack(Track *track);
+
     MPEG4Extractor(const MPEG4Extractor &);
     MPEG4Extractor &operator=(const MPEG4Extractor &);
 };
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
index 9ca060d..ff8757d 100644
--- a/media/libstagefright/omx/OMX.cpp
+++ b/media/libstagefright/omx/OMX.cpp
@@ -401,6 +401,33 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+struct SharedVideoRenderer : public VideoRenderer {
+    SharedVideoRenderer(void *libHandle, VideoRenderer *obj)
+        : mLibHandle(libHandle),
+          mObj(obj) {
+    }
+
+    virtual ~SharedVideoRenderer() {
+        delete mObj;
+        mObj = NULL;
+
+        dlclose(mLibHandle);
+        mLibHandle = NULL;
+    }
+
+    virtual void render(
+            const void *data, size_t size, void *platformPrivate) {
+        return mObj->render(data, size, platformPrivate);
+    }
+
+private:
+    void *mLibHandle;
+    VideoRenderer *mObj;
+
+    SharedVideoRenderer(const SharedVideoRenderer &);
+    SharedVideoRenderer &operator=(const SharedVideoRenderer &);
+};
+
 sp<IOMXRenderer> OMX::createRenderer(
         const sp<ISurface> &surface,
         const char *componentName,
@@ -411,11 +438,7 @@
 
     VideoRenderer *impl = NULL;
 
-    static void *libHandle = NULL;
-
-    if (!libHandle) {
-        libHandle = dlopen("libstagefrighthw.so", RTLD_NOW);
-    }
+    void *libHandle = dlopen("libstagefrighthw.so", RTLD_NOW);
 
     if (libHandle) {
         typedef VideoRenderer *(*CreateRendererFunc)(
@@ -434,6 +457,16 @@
         if (func) {
             impl = (*func)(surface, componentName, colorFormat,
                     displayWidth, displayHeight, encodedWidth, encodedHeight);
+
+            if (impl) {
+                impl = new SharedVideoRenderer(libHandle, impl);
+                libHandle = NULL;
+            }
+        }
+
+        if (libHandle) {
+            dlclose(libHandle);
+            libHandle = NULL;
         }
     }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 5b616b3..8036e52 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -76,7 +76,7 @@
     // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
     // is properly propagated through your change.  Not doing so will result in a loss of user
     // settings.
-    private static final int DATABASE_VERSION = 50;
+    private static final int DATABASE_VERSION = 51;
 
     private Context mContext;
 
@@ -642,6 +642,25 @@
            upgradeVersion = 50;
        }
 
+       if (upgradeVersion == 50) {
+           /*
+            * New settings for set install location UI.
+            */
+           db.beginTransaction();
+           try {
+                SQLiteStatement stmt = db.compileStatement("INSERT INTO system(name,value)"
+                        + " VALUES(?,?);");
+                loadBooleanSetting(stmt, Settings.System.SET_INSTALL_LOCATION,
+                        R.bool.set_install_location);
+                stmt.close();
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+            }
+
+           upgradeVersion = 51;
+       }
+
        if (upgradeVersion != currentVersion) {
             Log.w(TAG, "Got stuck trying to upgrade from version " + upgradeVersion
                     + ", must wipe the settings provider");
diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java
index 606b589..027f35c 100644
--- a/services/java/com/android/server/DockObserver.java
+++ b/services/java/com/android/server/DockObserver.java
@@ -416,7 +416,7 @@
                         mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
                                 LOCATION_UPDATE_MS, LOCATION_UPDATE_DISTANCE_METER, mLocationListener);
                         retrieveLocation();
-                        if (mLocation != null) {
+                        if (mCarModeEnabled && mLocation != null && mNightMode == MODE_NIGHT_AUTO) {
                             try {
                                 DockObserver.this.updateTwilight();
                             } catch (RemoteException e) {
@@ -462,8 +462,8 @@
             if (location == null) {
                 Time currentTime = new Time();
                 currentTime.set(System.currentTimeMillis());
-                double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE * currentTime.gmtoff
-                        - (currentTime.isDst > 0 ? 3600 : 0);
+                double lngOffset = FACTOR_GMT_OFFSET_LONGITUDE *
+                        (currentTime.gmtoff - (currentTime.isDst > 0 ? 3600 : 0));
                 location = new Location("fake");
                 location.setLongitude(lngOffset);
                 location.setLatitude(59.95);
@@ -587,20 +587,26 @@
             }
 
             // schedule next update
-            final int mLastTwilightState = tw.mState;
-            // add some extra time to be on the save side.
-            long nextUpdate = DateUtils.MINUTE_IN_MILLIS;
-            if (currentTime > tw.mSunset) {
-                // next update should be on the following day
-                tw.calculateTwilight(currentTime
-                        + DateUtils.DAY_IN_MILLIS, mLocation.getLatitude(),
-                        mLocation.getLongitude());
-            }
-
-            if (mLastTwilightState == TwilightCalculator.NIGHT) {
-                nextUpdate += tw.mSunrise;
+            long nextUpdate = 0;
+            if (tw.mSunrise == -1 || tw.mSunset == -1) {
+                // In the case the day or night never ends the update is scheduled 12 hours later.
+                nextUpdate = currentTime + 12 * DateUtils.HOUR_IN_MILLIS;
             } else {
-                nextUpdate += tw.mSunset;
+                final int mLastTwilightState = tw.mState;
+                // add some extra time to be on the save side.
+                nextUpdate += DateUtils.MINUTE_IN_MILLIS;
+                if (currentTime > tw.mSunset) {
+                    // next update should be on the following day
+                    tw.calculateTwilight(currentTime
+                            + DateUtils.DAY_IN_MILLIS, mLocation.getLatitude(),
+                            mLocation.getLongitude());
+                }
+
+                if (mLastTwilightState == TwilightCalculator.NIGHT) {
+                    nextUpdate += tw.mSunrise;
+                } else {
+                    nextUpdate += tw.mSunset;
+                }
             }
 
             Intent updateIntent = new Intent(ACTION_UPDATE_NIGHT_MODE);
@@ -609,8 +615,11 @@
             mAlarmManager.cancel(pendingIntent);
             mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
 
-            // set current mode
-            setMode(Configuration.UI_MODE_TYPE_CAR, nightMode << 4);
+            // Make sure that we really set the new mode only if we're in car mode and
+            // automatic switching is enables.
+            if (mCarModeEnabled && mNightMode == MODE_NIGHT_AUTO) {
+                setMode(Configuration.UI_MODE_TYPE_CAR, nightMode << 4);
+            }
         }
     }
 
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index dd351be..c0dcdf9 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -485,14 +485,17 @@
         super();
         mContext = context;
 
-        Thread thread = new Thread(null, this, "LocationManagerService");
-        thread.start();
-
         if (LOCAL_LOGV) {
             Log.v(TAG, "Constructed LocationManager Service");
         }
     }
 
+    void systemReady() {
+        // we defer starting up the service until the system is ready 
+        Thread thread = new Thread(null, this, "LocationManagerService");
+        thread.start();
+    }
+
     private void initialize() {
         // Create a wake lock, needs to be done before calling loadProviders() below
         PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 4fdcd59..4a7fc9c 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -5704,6 +5704,18 @@
                 // remove permissions associated with package
                 mSettings.updateSharedUserPermsLP(deletedPs, mGlobalGids);
             }
+            if (deletedPs != null) {
+                // remove from preferred activities.
+                ArrayList<PreferredActivity> removed = new ArrayList<PreferredActivity>();
+                for (PreferredActivity pa : mSettings.mPreferredActivities.filterSet()) {
+                    if (pa.mActivity.getPackageName().equals(deletedPs.name)) {
+                        removed.add(pa);
+                    }
+                }
+                for (PreferredActivity pa : removed) {
+                    mSettings.mPreferredActivities.removeFilter(pa);
+                }
+            }
             // Save settings now
             mSettings.writeLP();
         }
@@ -7461,9 +7473,9 @@
                         Log.w(TAG, "Trying to update system app code path from " +
                                 p.codePathString + " to " + codePath.toString());
                     } else {
-                        // Let the app continue with previous uid if code path changes.
-                        reportSettingsProblem(Log.WARN,
-                                "Package " + name + " codePath changed from " + p.codePath
+                        // Just a change in the code path is not an issue, but
+                        // let's log a message about it.
+                        Log.i(TAG, "Package " + name + " codePath changed from " + p.codePath
                                 + " to " + codePath + "; Retaining data and using new");
                     }
                 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 38df02f..1f46faf 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -209,6 +209,7 @@
         AppWidgetService appWidget = null;
         NotificationManagerService notification = null;
         WallpaperManagerService wallpaper = null;
+        LocationManagerService location = null;
 
         if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
             try {
@@ -303,8 +304,8 @@
 
             try {
                 Log.i(TAG, "Location Manager");
-                ServiceManager.addService(Context.LOCATION_SERVICE,
-                        new LocationManagerService(context));
+                location = new LocationManagerService(context);
+                ServiceManager.addService(Context.LOCATION_SERVICE, location);
             } catch (Throwable e) {
                 Log.e(TAG, "Failure starting Location Manager", e);
             }
@@ -444,6 +445,7 @@
         final WallpaperManagerService wallpaperF = wallpaper;
         final InputMethodManagerService immF = imm;
         final RecognitionManagerService recognitionF = recognition;
+        final LocationManagerService locationF = location;
 
         // We now tell the activity manager it is okay to run third party
         // code.  It will call back into us once it has gotten to the state
@@ -467,6 +469,7 @@
                 if (appWidgetF != null) appWidgetF.systemReady(safeMode);
                 if (wallpaperF != null) wallpaperF.systemReady();
                 if (immF != null) immF.systemReady();
+                if (locationF != null) locationF.systemReady();
             }
         });
 
diff --git a/services/java/com/android/server/TwilightCalculator.java b/services/java/com/android/server/TwilightCalculator.java
index a8f67d8..a5c93b5 100644
--- a/services/java/com/android/server/TwilightCalculator.java
+++ b/services/java/com/android/server/TwilightCalculator.java
@@ -46,10 +46,16 @@
     // Java time on Jan 1, 2000 12:00 UTC.
     private static final long UTC_2000 = 946728000000L;
 
-    /** Time of sunset (civil twilight) in milliseconds. */
+    /**
+     * Time of sunset (civil twilight) in milliseconds or -1 in the case the day
+     * or night never ends.
+     */
     public long mSunset;
 
-    /** Time of sunrise (civil twilight) in milliseconds. */
+    /**
+     * Time of sunrise (civil twilight) in milliseconds or -1 in the case the
+     * day or night never ends.
+     */
     public long mSunrise;
 
     /** Current state */
@@ -85,10 +91,24 @@
         double solarDec = Math.asin(FloatMath.sin(solarLng) * FloatMath.sin(OBLIQUITY));
 
         final double latRad = latiude * DEGREES_TO_RADIANS;
-        float hourAngle = (float) (Math
-                .acos((FloatMath.sin(ALTIDUTE_CORRECTION_CIVIL_TWILIGHT) - Math.sin(latRad)
-                        * Math.sin(solarDec))
-                        / (Math.cos(latRad) * Math.cos(solarDec))) / (2 * Math.PI));
+
+        double cosHourAngle = (FloatMath.sin(ALTIDUTE_CORRECTION_CIVIL_TWILIGHT) - Math.sin(latRad)
+                * Math.sin(solarDec)) / (Math.cos(latRad) * Math.cos(solarDec));
+        // The day or night never ends for the given date and location, if this value is out of
+        // range.
+        if (cosHourAngle >= 1) {
+            mState = NIGHT;
+            mSunset = -1;
+            mSunrise = -1;
+            return;
+        } else if (cosHourAngle <= -1) {
+            mState = DAY;
+            mSunset = -1;
+            mSunrise = -1;
+            return;
+        }
+
+        float hourAngle = (float) (Math.acos(cosHourAngle) / (2 * Math.PI));
 
         mSunset = Math.round((solarTransitJ2000 + hourAngle) * DateUtils.DAY_IN_MILLIS) + UTC_2000;
         mSunrise = Math.round((solarTransitJ2000 - hourAngle) * DateUtils.DAY_IN_MILLIS) + UTC_2000;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 5b37f47..4169cc5 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -135,6 +135,7 @@
     static final boolean DEBUG_VISBILITY = localLOGV || false;
     static final boolean DEBUG_PROCESSES = localLOGV || false;
     static final boolean DEBUG_PROVIDER = localLOGV || false;
+    static final boolean DEBUG_URI_PERMISSION = localLOGV || false;
     static final boolean DEBUG_USER_LEAVING = localLOGV || false;
     static final boolean DEBUG_RESULTS = localLOGV || false;
     static final boolean DEBUG_BACKUP = localLOGV || false;
@@ -968,6 +969,7 @@
     static final int PROC_START_TIMEOUT_MSG = 20;
     static final int DO_PENDING_ACTIVITY_LAUNCHES_MSG = 21;
     static final int KILL_APPLICATION_MSG = 22;
+    static final int FINALIZE_PENDING_INTENT_MSG = 23;
 
     AlertDialog mUidAlert;
 
@@ -1189,6 +1191,9 @@
                     forceStopPackageLocked(pkg, uid, restart, false, true);
                 }
             } break;
+            case FINALIZE_PENDING_INTENT_MSG: {
+                ((PendingIntentRecord)msg.obj).completeFinalize();
+            } break;
             }
         }
     };
@@ -3599,10 +3604,18 @@
         }
 
         synchronized(this) {
+            int callingPid;
+            int callingUid;
+            if (caller == null) {
+                callingPid = Binder.getCallingPid();
+                callingUid = Binder.getCallingUid();
+            } else {
+                callingPid = callingUid = -1;
+            }
             final long origId = Binder.clearCallingIdentity();
             int res = startActivityLocked(caller, intent, resolvedType,
                     grantedUriPermissions, grantedMode, aInfo,
-                    resultTo, resultWho, requestCode, -1, -1,
+                    resultTo, resultWho, requestCode, callingPid, callingUid,
                     onlyIfNeeded, componentSpecified);
             Binder.restoreCallingIdentity(origId);
             return res;
@@ -6166,10 +6179,15 @@
             return;
         }
 
+        if (DEBUG_URI_PERMISSION) Log.v(TAG, 
+                "Requested grant " + targetPkg + " permission to " + uri);
+        
         final IPackageManager pm = ActivityThread.getPackageManager();
 
         // If this is not a content: uri, we can't do anything with it.
         if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+            if (DEBUG_URI_PERMISSION) Log.v(TAG, 
+                    "Can't grant URI permission for non-content URI: " + uri);
             return;
         }
 
@@ -6195,6 +6213,8 @@
         try {
             targetUid = pm.getPackageUid(targetPkg);
             if (targetUid < 0) {
+                if (DEBUG_URI_PERMISSION) Log.v(TAG, 
+                        "Can't grant URI permission no uid for: " + targetPkg);
                 return;
             }
         } catch (RemoteException ex) {
@@ -6204,17 +6224,12 @@
         // First...  does the target actually need this permission?
         if (checkHoldingPermissionsLocked(pm, pi, targetUid, modeFlags)) {
             // No need to grant the target this permission.
+            if (DEBUG_URI_PERMISSION) Log.v(TAG, 
+                    "Target " + targetPkg + " already has full permission to " + uri);
             return;
         }
 
-        // Second...  maybe someone else has already granted the
-        // permission?
-        if (checkUriPermissionLocked(uri, targetUid, modeFlags)) {
-            // No need to grant the target this permission.
-            return;
-        }
-
-        // Third...  is the provider allowing granting of URI permissions?
+        // Second...  is the provider allowing granting of URI permissions?
         if (!pi.grantUriPermissions) {
             throw new SecurityException("Provider " + pi.packageName
                     + "/" + pi.name
@@ -6239,7 +6254,7 @@
             }
         }
 
-        // Fourth...  does the caller itself have permission to access
+        // Third...  does the caller itself have permission to access
         // this uri?
         if (!checkHoldingPermissionsLocked(pm, pi, callingUid, modeFlags)) {
             if (!checkUriPermissionLocked(uri, callingUid, modeFlags)) {
@@ -6252,6 +6267,9 @@
         // to the uri, and the target doesn't.  Let's now give this to
         // the target.
 
+        if (DEBUG_URI_PERMISSION) Log.v(TAG, 
+                "Granting " + targetPkg + " permission to " + uri);
+        
         HashMap<Uri, UriPermission> targetUris
                 = mGrantedUriPermissions.get(targetUid);
         if (targetUris == null) {
@@ -6325,6 +6343,8 @@
             HashMap<Uri, UriPermission> perms
                     = mGrantedUriPermissions.get(perm.uid);
             if (perms != null) {
+                if (DEBUG_URI_PERMISSION) Log.v(TAG, 
+                        "Removing " + perm.uid + " permission to " + perm.uri);
                 perms.remove(perm.uri);
                 if (perms.size() == 0) {
                     mGrantedUriPermissions.remove(perm.uid);
@@ -6364,6 +6384,9 @@
             return;
         }
 
+        if (DEBUG_URI_PERMISSION) Log.v(TAG, 
+                "Revoking all granted permissions to " + uri);
+        
         final IPackageManager pm = ActivityThread.getPackageManager();
 
         final String authority = uri.getAuthority();
@@ -6422,6 +6445,8 @@
                             continue toploop;
                         }
                     }
+                    if (DEBUG_URI_PERMISSION) Log.v(TAG, 
+                            "Revoking " + perm.uid + " permission to " + perm.uri);
                     perm.clearModes(modeFlags);
                     if (perm.modeFlags == 0) {
                         it.remove();
diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java
index b3086d5..94b5f56 100644
--- a/services/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/java/com/android/server/am/PendingIntentRecord.java
@@ -262,17 +262,26 @@
     }
     
     protected void finalize() throws Throwable {
-        if (!canceled) {
-            synchronized(owner) {
-                WeakReference<PendingIntentRecord> current =
-                        owner.mIntentSenderRecords.get(key);
-                if (current == ref) {
-                    owner.mIntentSenderRecords.remove(key);
-                }
+        try {
+            if (!canceled) {
+                owner.mHandler.sendMessage(owner.mHandler.obtainMessage(
+                        ActivityManagerService.FINALIZE_PENDING_INTENT_MSG, this));
             }
+        } finally {
+            super.finalize();
         }
     }
 
+    public void completeFinalize() {
+        synchronized(owner) {
+            WeakReference<PendingIntentRecord> current =
+                    owner.mIntentSenderRecords.get(key);
+            if (current == ref) {
+                owner.mIntentSenderRecords.remove(key);
+            }
+        }
+    }
+    
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("uid="); pw.print(uid);
                 pw.print(" packageName="); pw.print(key.packageName);
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 2f2cc32..5a02c40 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -269,8 +269,12 @@
                         inm.enqueueNotification(localPackageName, localForegroundId,
                                 localForegroundNoti, outId);
                     } catch (RuntimeException e) {
-                        Log.w(ActivityManagerService.TAG, "Error showing notification for service",
-                            e);
+                        Log.w(ActivityManagerService.TAG,
+                                "Error showing notification for service", e);
+                        // If it gave us a garbage notification, it doesn't
+                        // get to be foreground.
+                        ams.setServiceForeground(name, ServiceRecord.this,
+                                localForegroundId, null, true);
                     } catch (RemoteException e) {
                     }
                 }
@@ -293,8 +297,8 @@
                     try {
                         inm.cancelNotification(localPackageName, localForegroundId);
                     } catch (RuntimeException e) {
-                        Log.w(ActivityManagerService.TAG, "Error canceling notification for"
-                            + " service", e);
+                        Log.w(ActivityManagerService.TAG,
+                                "Error canceling notification for service", e);
                     } catch (RemoteException e) {
                     }
                 }
diff --git a/services/java/com/android/server/status/StorageNotification.java b/services/java/com/android/server/status/StorageNotification.java
index 3b79049..e0b288d 100644
--- a/services/java/com/android/server/status/StorageNotification.java
+++ b/services/java/com/android/server/status/StorageNotification.java
@@ -36,6 +36,7 @@
 import android.os.storage.StorageEventListener;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageResultCode;
+import android.provider.Settings;
 import android.util.Log;
 import android.view.View;
 import android.widget.Button;
@@ -46,6 +47,8 @@
 public class StorageNotification extends StorageEventListener {
     private static final String TAG = "StorageNotification";
 
+    private static final boolean POP_UMS_ACTIVITY_ON_CONNECT = true;
+
     /**
      * Binder context for this service
      */
@@ -239,12 +242,28 @@
             Intent intent = new Intent();
             intent.setClass(mContext, com.android.server.status.UsbStorageActivity.class);
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+            final boolean adbOn = 1 == Settings.Secure.getInt(
+                mContext.getContentResolver(),
+                Settings.Secure.ADB_ENABLED,
+                0);
+
             PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
             setUsbStorageNotification(
                     com.android.internal.R.string.usb_storage_notification_title,
                     com.android.internal.R.string.usb_storage_notification_message,
                     com.android.internal.R.drawable.stat_sys_data_usb,
                     false, true, pi);
+
+            if (POP_UMS_ACTIVITY_ON_CONNECT && !adbOn) {
+                // We assume that developers don't want to enable UMS every
+                // time they attach a device to a USB host. The average user,
+                // however, is looking to charge the phone (in which case this
+                // is harmless) or transfer files (in which case this coaches
+                // the user about how to complete that task and saves several
+                // steps).
+                mContext.startActivity(intent);
+            }
         } else {
             setUsbStorageNotification(0, 0, 0, false, false, null);
         }
diff --git a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
index 31cf6a7..9f8e57f 100644
--- a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java
@@ -22,10 +22,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.ServiceManager;
-import android.telephony.PhoneNumberUtils;
-import android.util.Log;
 
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -37,7 +34,7 @@
 
     protected PhoneBase phone;
     protected AdnRecordCache adnCache;
-    protected Object mLock = new Object();
+    protected final Object mLock = new Object();
     protected int recordSize[];
     protected boolean success;
     protected List<AdnRecord> records;
@@ -80,8 +77,7 @@
                     ar = (AsyncResult)msg.obj;
                     synchronized (mLock) {
                         if (ar.exception == null) {
-                            records = (List<AdnRecord>)
-                                    ((ArrayList<AdnRecord>) ar.result);
+                            records = (List<AdnRecord>) ar.result;
                         } else {
                             if(DBG) logd("Cannot load ADN records");
                             if (records != null) {
diff --git a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
index 19900c8..1ac2da3 100644
--- a/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
+++ b/telephony/java/com/android/internal/telephony/PhoneSubInfo.java
@@ -39,6 +39,11 @@
     }
 
     protected void finalize() {
+        try {
+            super.finalize();
+        } catch (Throwable throwable) {
+            Log.e(LOG_TAG, "Error while finalizing:", throwable);
+        }
         Log.d(LOG_TAG, "PhoneSubInfo finalized");
     }
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java
index 78e89d5..6e12f24a 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimPhoneBookInterfaceManager.java
@@ -16,22 +16,10 @@
 
 package com.android.internal.telephony.cdma;
 
-import android.content.pm.PackageManager;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Looper;
 import android.os.Message;
-import android.os.ServiceManager;
-import android.telephony.PhoneNumberUtils;
 import android.util.Log;
 
-import com.android.internal.telephony.AdnRecord;
-import com.android.internal.telephony.AdnRecordCache;
 import com.android.internal.telephony.IccPhoneBookInterfaceManager;
-import com.android.internal.telephony.PhoneProxy;
-
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * RuimPhoneBookInterfaceManager to provide an inter-process communication to
@@ -42,20 +30,6 @@
 public class RuimPhoneBookInterfaceManager extends IccPhoneBookInterfaceManager {
     static final String LOG_TAG = "CDMA";
 
-
-    Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            AsyncResult ar;
-
-            switch(msg.what) {
-                default:
-                    mBaseHandler.handleMessage(msg);
-                    break;
-            }
-        }
-    };
-
     public RuimPhoneBookInterfaceManager(CDMAPhone phone) {
         super(phone);
         adnCache = phone.mRuimRecords.getAdnCache();
@@ -67,6 +41,11 @@
     }
 
     protected void finalize() {
+        try {
+            super.finalize();
+        } catch (Throwable throwable) {
+            Log.e(LOG_TAG, "Error while finalizing:", throwable);
+        }
         if(DBG) Log.d(LOG_TAG, "RuimPhoneBookInterfaceManager finalized");
     }
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
index 9439359..cfcfd98 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
@@ -30,6 +30,7 @@
 import com.android.internal.telephony.SmsRawData;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import static android.telephony.SmsManager.STATUS_ON_ICC_FREE;
@@ -89,6 +90,11 @@
     }
 
     protected void finalize() {
+        try {
+            super.finalize();
+        } catch (Throwable throwable) {
+            Log.e(LOG_TAG, "Error while finalizing:", throwable);
+        }
         if(DBG) Log.d(LOG_TAG, "RuimSmsInterfaceManager finalized");
     }
 
@@ -143,7 +149,7 @@
     public boolean copyMessageToIccEf(int status, byte[] pdu, byte[] smsc) {
         //NOTE smsc not used in RUIM
         if (DBG) log("copyMessageToIccEf: status=" + status + " ==> " +
-                "pdu=("+ pdu + ")");
+                "pdu=("+ Arrays.toString(pdu) + ")");
         enforceReceiveAndSend("Copying message to RUIM");
         synchronized(mLock) {
             mSuccess = false;
diff --git a/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java
index 076da6b..feb508a 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimPhoneBookInterfaceManager.java
@@ -16,22 +16,10 @@
 
 package com.android.internal.telephony.gsm;
 
-import android.content.pm.PackageManager;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Looper;
 import android.os.Message;
-import android.os.ServiceManager;
-import android.telephony.PhoneNumberUtils;
 import android.util.Log;
 
-import com.android.internal.telephony.AdnRecord;
-import com.android.internal.telephony.AdnRecordCache;
 import com.android.internal.telephony.IccPhoneBookInterfaceManager;
-import com.android.internal.telephony.PhoneProxy;
-
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * SimPhoneBookInterfaceManager to provide an inter-process communication to
@@ -42,20 +30,6 @@
 public class SimPhoneBookInterfaceManager extends IccPhoneBookInterfaceManager {
     static final String LOG_TAG = "GSM";
 
-
-    Handler mHandler = new Handler() {
-        @Override
-        public void handleMessage(Message msg) {
-            AsyncResult ar;
-
-            switch(msg.what) {
-                default:
-                    mBaseHandler.handleMessage(msg);
-                    break;
-            }
-        }
-    };
-
     public SimPhoneBookInterfaceManager(GSMPhone phone) {
         super(phone);
         adnCache = phone.mSIMRecords.getAdnCache();
@@ -67,6 +41,11 @@
     }
 
     protected void finalize() {
+        try {
+            super.finalize();
+        } catch (Throwable throwable) {
+            Log.e(LOG_TAG, "Error while finalizing:", throwable);
+        }
         if(DBG) Log.d(LOG_TAG, "SimPhoneBookInterfaceManager finalized");
     }
 
diff --git a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
index 875d8d0..2028ca4 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
@@ -25,10 +25,10 @@
 import com.android.internal.telephony.IccConstants;
 import com.android.internal.telephony.IccSmsInterfaceManager;
 import com.android.internal.telephony.IccUtils;
-import com.android.internal.telephony.PhoneProxy;
 import com.android.internal.telephony.SmsRawData;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import static android.telephony.SmsManager.STATUS_ON_ICC_FREE;
@@ -65,8 +65,7 @@
                     ar = (AsyncResult)msg.obj;
                     synchronized (mLock) {
                         if (ar.exception == null) {
-                            mSms  = (List<SmsRawData>)
-                                    buildValidRawData((ArrayList<byte[]>) ar.result);
+                            mSms  = buildValidRawData((ArrayList<byte[]>) ar.result);
                         } else {
                             if(DBG) log("Cannot load Sms records");
                             if (mSms != null)
@@ -88,6 +87,11 @@
     }
 
     protected void finalize() {
+        try {
+            super.finalize();
+        } catch (Throwable throwable) {
+            Log.e(LOG_TAG, "Error while finalizing:", throwable);
+        }
         if(DBG) Log.d(LOG_TAG, "SimSmsInterfaceManager finalized");
     }
 
@@ -106,7 +110,7 @@
     updateMessageOnIccEf(int index, int status, byte[] pdu) {
         if (DBG) log("updateMessageOnIccEf: index=" + index +
                 " status=" + status + " ==> " +
-                "("+ pdu + ")");
+                "("+ Arrays.toString(pdu) + ")");
         enforceReceiveAndSend("Updating message on SIM");
         synchronized(mLock) {
             mSuccess = false;
@@ -118,7 +122,7 @@
                 mPhone.mCM.deleteSmsOnSim(index, response);
             } else {
                 byte[] record = makeSmsRecordData(status, pdu);
-                ((SIMFileHandler)mPhone.getIccFileHandler()).updateEFLinearFixed(
+                mPhone.getIccFileHandler().updateEFLinearFixed(
                         IccConstants.EF_SMS,
                         index, record, null, response);
             }
@@ -142,7 +146,8 @@
      */
     public boolean copyMessageToIccEf(int status, byte[] pdu, byte[] smsc) {
         if (DBG) log("copyMessageToIccEf: status=" + status + " ==> " +
-                "pdu=("+ pdu + "), smsm=(" + smsc +")");
+                "pdu=("+ Arrays.toString(pdu) +
+                "), smsm=(" + Arrays.toString(smsc) +")");
         enforceReceiveAndSend("Copying message to SIM");
         synchronized(mLock) {
             mSuccess = false;
@@ -175,8 +180,7 @@
                 "Reading messages from SIM");
         synchronized(mLock) {
             Message response = mHandler.obtainMessage(EVENT_LOAD_DONE);
-            ((SIMFileHandler)mPhone.getIccFileHandler()).loadEFLinearFixedAll(IccConstants.EF_SMS,
-                    response);
+            mPhone.getIccFileHandler().loadEFLinearFixedAll(IccConstants.EF_SMS, response);
 
             try {
                 mLock.wait();
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
index 8983612..634d683 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
@@ -339,14 +339,7 @@
         this.mTestList = new Vector<String>();
 
         // Read settings
-        try {
-            this.mTestPathPrefix =
-                (new File(LAYOUT_TESTS_ROOT + runner.mTestPath)).getCanonicalPath();
-        } catch (IOException e) {
-            Log.e(LOGTAG, "Cannot find test path prefix: " + e.getMessage());
-            return;
-        }
-
+        this.mTestPathPrefix = (new File(LAYOUT_TESTS_ROOT + runner.mTestPath)).getAbsolutePath();
         this.mRebaselineResults = runner.mRebaseline;
 
         int timeout = runner.mTimeoutInMillis;
@@ -395,11 +388,7 @@
         if (runner.mTestPath != null) {
             test_path += runner.mTestPath;
         }
-        try {
-            test_path = new File(test_path).getCanonicalPath();
-        } catch (IOException e) {
-            Log.e("LayoutTestsAutoTest", "Cannot get cannonical path " + e.getMessage());
-        }
+        test_path = new File(test_path).getAbsolutePath();
         Log.v("LayoutTestsAutoTest", " Test path : " + test_path);
 
         return test_path;
diff --git a/tests/LocationTracker/Android.mk b/tests/LocationTracker/Android.mk
new file mode 100644
index 0000000..b142d22
--- /dev/null
+++ b/tests/LocationTracker/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := LocationTracker
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/LocationTracker/AndroidManifest.xml b/tests/LocationTracker/AndroidManifest.xml
new file mode 100644
index 0000000..dc7ea99
--- /dev/null
+++ b/tests/LocationTracker/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.locationtracker">
+
+    <!-- Permissions for the Location Service -->
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+
+    <!--  Permission for wifi -->
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+
+    <!-- give the location tracker ability to induce device insomnia -->
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+    <!--  Permission for SD card -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <application android:label="@string/app_label">
+        <activity android:name="TrackerActivity" android:label="Location Tracker">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <service android:name=".TrackerService" />
+        <activity android:label="@string/settings_menu" android:name="SettingsActivity" />
+        <provider android:name=".data.TrackerProvider"
+            android:authorities="com.android.locationtracker" />
+    </application>
+
+</manifest>
diff --git a/tests/LocationTracker/res/layout/entrylist_item.xml b/tests/LocationTracker/res/layout/entrylist_item.xml
new file mode 100644
index 0000000..8187677
--- /dev/null
+++ b/tests/LocationTracker/res/layout/entrylist_item.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2006-2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/entrylist_item"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:textAppearance="?android:attr/textAppearanceSmall"
+/>
diff --git a/tests/LocationTracker/res/menu/menu.xml b/tests/LocationTracker/res/menu/menu.xml
new file mode 100644
index 0000000..94c589a
--- /dev/null
+++ b/tests/LocationTracker/res/menu/menu.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+* Copyright (c) 2008 Google Inc.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/start_service_menu"
+          android:title="@string/start_service_menu" />
+    <item android:id="@+id/stop_service_menu"
+          android:title="@string/stop_service_menu" />
+    <item android:id="@+id/settings_menu"
+          android:title="@string/settings_menu" />
+    <item android:id="@+id/export_sub_menu"
+          android:title="@string/export_sub_menu">
+        <menu>
+            <item android:id="@+id/export_kml"
+                  android:title="@string/export_kml" />
+            <item android:id="@+id/export_csv"
+                  android:title="@string/export_csv" />
+        </menu>
+    </item>
+    <item android:title="@string/clear_data"
+          android:id="@+id/clear_data_menu"/>
+</menu>
diff --git a/tests/LocationTracker/res/values/strings.xml b/tests/LocationTracker/res/values/strings.xml
new file mode 100644
index 0000000..bb3ea86
--- /dev/null
+++ b/tests/LocationTracker/res/values/strings.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright (C) 2008 Google Inc.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*      http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+
+<resources>
+    <string name="start_service_menu">Start Service</string>
+    <string name="stop_service_menu">Stop Service</string>
+    <string name="settings_menu">Settings</string>
+    <string name="update_preference">Update frequency</string>
+    <string name="title_mintime_preference">Minimum update time</string>
+    <string name="summary_mintime_preference">The suggested minimum time interval for location updates, in seconds</string>
+    <string name="dialog_title_mintime_preference">Minimum update time</string>
+    <string name="title_mindistance_preference">Minimum distance</string>
+    <string name="summary_mindistance_preference">Minimum distance interval for location updates, in meters</string>
+    <string name="dialog_title_mindistance_preference">Minimum distance</string>
+    <string name="provider_preferences">Location providers</string>
+    <string name="title_network_preference">Network location</string>
+    <string name="summary_network_preference">Listen for updates to network location (Wi-Fi/cellid)</string>
+    <string name="title_gps_preference">GPS location</string>
+    <string name="summary_gps_preference">Listen for updates to GPS location</string>
+    <string name="title_signal_preference">Signal strength</string>
+    <string name="summary_signal_preference">Listen for updates to signal strength</string>
+    <string name="advanced_preferences">Advanced</string>
+    <string name="title_advanced_log_preference">Location debug logging</string>
+    <string name="summary_advanced_preference">Logs detailed location data, only relevant for location/test engineers</string>
+    <string name="app_label">Location Tracker</string>
+    <string name="export_sub_menu">Export\u2026</string>
+    <string name="export_kml">Export As KML</string>
+    <string name="export_csv">Export As CSV</string>
+    <string name="clear_data">Clear data</string>
+    <string name="delete_confirm">All current tracking data will be deleted.</string>
+    <string name="confirm_title">Clear data</string>
+</resources>
diff --git a/tests/LocationTracker/res/xml/preferences.xml b/tests/LocationTracker/res/xml/preferences.xml
new file mode 100755
index 0000000..b57837f
--- /dev/null
+++ b/tests/LocationTracker/res/xml/preferences.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 Google Inc.
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- The Location preferences UI -->
+<PreferenceScreen
+    xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <PreferenceCategory android:title="@string/update_preference">
+        <EditTextPreference android:key="mintime_preference"
+            android:defaultValue="0"
+            android:title="@string/title_mintime_preference"
+            android:summary="@string/summary_mintime_preference"
+            android:dialogTitle="@string/dialog_title_mintime_preference" />
+
+        <EditTextPreference android:key="mindistance_preference"
+            android:defaultValue="0"
+            android:title="@string/title_mindistance_preference"
+            android:summary="@string/summary_mindistance_preference"
+            android:dialogTitle="@string/dialog_title_mindistance_preference" />
+
+    </PreferenceCategory>
+
+    <PreferenceCategory android:title="@string/provider_preferences">
+
+        <CheckBoxPreference android:key="network_preference"
+            android:defaultValue="true"
+            android:title="@string/title_network_preference"
+            android:summary="@string/summary_network_preference" />
+
+        <CheckBoxPreference android:key="gps_preference"
+            android:defaultValue="true"
+            android:title="@string/title_gps_preference"
+            android:summary="@string/summary_gps_preference" />
+
+        <CheckBoxPreference android:key="signal_preference"
+            android:defaultValue="false"
+            android:title="@string/title_signal_preference"
+            android:summary="@string/summary_signal_preference" />
+
+    </PreferenceCategory>
+
+    <PreferenceCategory android:title="@string/advanced_preferences">
+
+        <CheckBoxPreference android:key="advanced_log_preference"
+            android:defaultValue="false"
+            android:title="@string/title_advanced_log_preference"
+            android:summary="@string/summary_advanced_preference" />
+    </PreferenceCategory>
+
+</PreferenceScreen>
diff --git a/tests/LocationTracker/src/com/android/locationtracker/SettingsActivity.java b/tests/LocationTracker/src/com/android/locationtracker/SettingsActivity.java
new file mode 100755
index 0000000..c5b2432
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/SettingsActivity.java
@@ -0,0 +1,35 @@
+/*

+ * Copyright (C) 2008 Google Inc.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package com.android.locationtracker;

+

+import android.os.Bundle;

+import android.preference.PreferenceActivity;

+

+/**

+ * Settings preference screen for location tracker

+ */

+public class SettingsActivity extends PreferenceActivity {

+

+    @Override

+    protected void onCreate(Bundle savedInstanceState) {

+        super.onCreate(savedInstanceState);

+

+        // Load the preferences from an XML resource

+        addPreferencesFromResource(R.xml.preferences);

+    }

+

+}

diff --git a/tests/LocationTracker/src/com/android/locationtracker/TrackerActivity.java b/tests/LocationTracker/src/com/android/locationtracker/TrackerActivity.java
new file mode 100644
index 0000000..92e7803
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/TrackerActivity.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.locationtracker;
+
+import com.android.locationtracker.data.DateUtils;
+import com.android.locationtracker.data.TrackerDataHelper;
+import com.android.locationtracker.data.TrackerListHelper;
+
+import android.app.AlertDialog;
+import android.app.ListActivity;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.database.Cursor;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Activity for location tracker service
+ *
+ * Contains facilities for starting and
+ * stopping location tracker service, as well as displaying the current location
+ * data
+ *
+ * Future enhancements:
+ *   - export data as dB
+ *   - enable/disable "start service" and "stop service" menu items as
+ *     appropriate
+ */
+public class TrackerActivity extends ListActivity {
+
+    static final String LOG_TAG = "LocationTracker";
+
+    private TrackerListHelper mDataHelper;
+
+    /**
+     * Retrieves and displays the currently logged location data from file
+     *
+     * @param icicle
+     */
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        mDataHelper = new TrackerListHelper(this);
+        mDataHelper.bindListUI(R.layout.entrylist_item);
+    }
+
+    /**
+     * Builds the menu
+     *
+     * @param menu - menu to add items to
+     */
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater menuInflater = getMenuInflater();
+        menuInflater.inflate(R.menu.menu, menu);
+        return true;
+    }
+
+    /**
+     * Handles menu item selection
+     *
+     * @param item - the selected menu item
+     */
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.start_service_menu: {
+                startService(new Intent(TrackerActivity.this,
+                        TrackerService.class));
+                break;
+            }
+            case R.id.stop_service_menu: {
+                stopService(new Intent(TrackerActivity.this,
+                        TrackerService.class));
+                break;
+            }
+            case R.id.settings_menu: {
+                launchSettings();
+                break;
+            }
+            case R.id.export_kml: {
+                exportKML();
+                break;
+            }
+            case R.id.export_csv: {
+                exportCSV();
+                break;
+            }
+            case R.id.clear_data_menu: {
+                clearData();
+                break;
+            }
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private void clearData() {
+        Runnable clearAction = new Runnable() {
+            public void run() {
+                TrackerDataHelper helper =
+                    new TrackerDataHelper(TrackerActivity.this);
+                helper.deleteAll();
+            }
+        };
+        showConfirm(R.string.delete_confirm, clearAction);
+    }
+
+    private void showConfirm(int textId, final Runnable onConfirmAction) {
+        AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
+        dialogBuilder.setTitle(R.string.confirm_title);
+        dialogBuilder.setMessage(textId);
+        dialogBuilder.setPositiveButton(android.R.string.ok,
+                new OnClickListener() {
+            public void onClick(DialogInterface dialog, int which) {
+                onConfirmAction.run();
+            }
+        });
+        dialogBuilder.setNegativeButton(android.R.string.cancel,
+                new OnClickListener() {
+            public void onClick(DialogInterface dialog, int which) {
+                // do nothing
+            }
+        });
+        dialogBuilder.show();
+    }
+
+    private void exportCSV() {
+        String exportFileName = getUniqueFileName("csv");
+        exportFile(null, exportFileName, new TrackerDataHelper(this,
+                TrackerDataHelper.CSV_FORMATTER));
+    }
+
+    private void exportKML() {
+        String exportFileName = getUniqueFileName(
+                LocationManager.NETWORK_PROVIDER + ".kml");
+        exportFile(LocationManager.NETWORK_PROVIDER, exportFileName,
+                new TrackerDataHelper(this, TrackerDataHelper.KML_FORMATTER));
+        exportFileName = getUniqueFileName(
+                LocationManager.GPS_PROVIDER + ".kml");
+        exportFile(LocationManager.GPS_PROVIDER, exportFileName,
+                new TrackerDataHelper(this, TrackerDataHelper.KML_FORMATTER));
+    }
+
+    private void exportFile(String tagFilter,
+                            String exportFileName,
+                            TrackerDataHelper trackerData) {
+        BufferedWriter exportWriter = null;
+        Cursor cursor = trackerData.query(tagFilter);
+        try {
+            exportWriter = new BufferedWriter(new FileWriter(exportFileName));
+            exportWriter.write(trackerData.getOutputHeader());
+
+            String line = null;
+
+            while ((line = trackerData.getNextOutput(cursor)) != null) {
+                exportWriter.write(line);
+            }
+            exportWriter.write(trackerData.getOutputFooter());
+            Toast.makeText(this, "Successfully exported data to " +
+                    exportFileName, Toast.LENGTH_SHORT).show();
+
+        } catch (IOException e) {
+            Toast.makeText(this, "Error exporting file: " +
+                    e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
+
+            Log.e(LOG_TAG, "Error exporting file", e);
+        } finally {
+            closeWriter(exportWriter);
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    private void closeWriter(Writer exportWriter) {
+        if (exportWriter != null) {
+            try {
+                exportWriter.close();
+            } catch (IOException e) {
+                Log.e(LOG_TAG, "error closing file", e);
+            }
+        }
+    }
+
+    private String getUniqueFileName(String ext) {
+        File dir = new File("/sdcard/locationtracker");
+        if (!dir.exists()) {
+            dir.mkdir();
+        }
+        return "/sdcard/locationtracker/tracking-" +
+            DateUtils.getCurrentTimestamp() + "." + ext;
+    }
+
+    private void launchSettings() {
+        Intent settingsIntent = new Intent();
+        settingsIntent.setClass(this, SettingsActivity.class);
+        startActivity(settingsIntent);
+    }
+}
diff --git a/tests/LocationTracker/src/com/android/locationtracker/TrackerService.java b/tests/LocationTracker/src/com/android/locationtracker/TrackerService.java
new file mode 100644
index 0000000..4206da7
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/TrackerService.java
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.locationtracker;
+
+import com.android.locationtracker.data.TrackerDataHelper;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.net.ConnectivityManager;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.preference.PreferenceManager;
+import android.telephony.CellLocation;
+import android.telephony.PhoneStateListener;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+import android.telephony.cdma.CdmaCellLocation;
+import android.telephony.gsm.GsmCellLocation;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Location Tracking service
+ *
+ * Records location updates for all registered location providers, and cell
+ * location updates
+ */
+public class TrackerService extends Service {
+
+    private List<LocationTrackingListener> mListeners;
+
+    private static final String LOG_TAG = TrackerActivity.LOG_TAG;
+
+    // controls which location providers to track
+    private Set<String> mTrackedProviders;
+
+    private TrackerDataHelper mTrackerData;
+
+    private TelephonyManager mTelephonyManager;
+    private Location mNetworkLocation;
+
+    // Handlers and Receivers for phone and network state
+    private NetworkStateBroadcastReceiver mNetwork;
+    private static final String CELL_PROVIDER_TAG = "cell";
+    // signal strength updates
+    private static final String SIGNAL_PROVIDER_TAG = "signal";
+    private static final String WIFI_PROVIDER_TAG = "wifi";
+    // tracking tag for data connectivity issues
+    private static final String DATA_CONN_PROVIDER_TAG = "data";
+
+    // preference constants
+    private static final String MIN_TIME_PREF = "mintime_preference";
+    private static final String MIN_DIS_PREF = "mindistance_preference";
+    private static final String GPS_PREF = "gps_preference";
+    private static final String NETWORK_PREF = "network_preference";
+    private static final String SIGNAL_PREF = "signal_preference";
+    private static final String DEBUG_PREF = "advanced_log_preference";
+
+    private PreferenceListener mPrefListener;
+
+    public TrackerService() {
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        // ignore - nothing to do
+        return null;
+    }
+
+    /**
+     * registers location listeners
+     *
+     * @param intent
+     * @param startId
+     */
+    @Override
+    public void onStart(Intent intent, int startId) {
+        super.onStart(intent, startId);
+        mNetworkLocation = null;
+
+        initLocationListeners();
+        Toast.makeText(this, "Tracking service started", Toast.LENGTH_SHORT);
+    }
+
+    private synchronized void initLocationListeners() {
+        mTrackerData = new TrackerDataHelper(this);
+        LocationManager lm = getLocationManager();
+
+        mTrackedProviders = getTrackedProviders();
+
+        List<String> locationProviders = lm.getAllProviders();
+        mListeners = new ArrayList<LocationTrackingListener>(
+                locationProviders.size());
+
+        long minUpdateTime = getLocationUpdateTime();
+        float minDistance = getLocationMinDistance();
+
+        for (String providerName : locationProviders) {
+            if (mTrackedProviders.contains(providerName)) {
+                Log.d(LOG_TAG, "Adding location listener for provider " +
+                        providerName);
+                if (doDebugLogging()) {
+                    mTrackerData.writeEntry("init", String.format(
+                            "start listening to %s : %d ms; %f meters",
+                            providerName, minUpdateTime, minDistance));
+                }    
+                LocationTrackingListener listener =
+                    new LocationTrackingListener();
+                lm.requestLocationUpdates(providerName, minUpdateTime,
+                        minDistance, listener);
+                mListeners.add(listener);
+            }
+        }
+        mTelephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
+
+        if (doDebugLogging()) {
+            // register for cell location updates
+            mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CELL_LOCATION);
+
+            // Register for Network (Wifi or Mobile) updates
+            mNetwork = new NetworkStateBroadcastReceiver();
+            IntentFilter mIntentFilter;
+            mIntentFilter = new IntentFilter();
+            mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+            mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+            mIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+            Log.d(LOG_TAG, "registering receiver");
+            registerReceiver(mNetwork, mIntentFilter);
+        }
+
+        if (trackSignalStrength()) {
+            mTelephonyManager.listen(mPhoneStateListener,
+                    PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
+        }
+
+        // register for preference changes, so we can restart listeners on
+        // pref changes
+        mPrefListener = new PreferenceListener();
+        getPreferences().registerOnSharedPreferenceChangeListener(mPrefListener);
+    }
+
+    private Set<String> getTrackedProviders() {
+        Set<String> providerSet = new HashSet<String>();
+
+        if (trackGPS()) {
+            providerSet.add(LocationManager.GPS_PROVIDER);
+        }
+        if (trackNetwork()) {
+            providerSet.add(LocationManager.NETWORK_PROVIDER);
+        }
+        return providerSet;
+    }
+
+    private SharedPreferences getPreferences() {
+        return PreferenceManager.getDefaultSharedPreferences(this);
+    }
+
+    private boolean trackNetwork() {
+        return getPreferences().getBoolean(NETWORK_PREF, true);
+    }
+
+    private boolean trackGPS() {
+        return getPreferences().getBoolean(GPS_PREF, true);
+    }
+
+    private boolean doDebugLogging() {
+        return getPreferences().getBoolean(DEBUG_PREF, true);
+    }
+
+    private boolean trackSignalStrength() {
+        return getPreferences().getBoolean(SIGNAL_PREF, true);
+    }
+
+    private float getLocationMinDistance() {
+        try {
+            String disString = getPreferences().getString(MIN_DIS_PREF, "0");
+            return Float.parseFloat(disString);
+        }
+        catch (NumberFormatException e) {
+            Log.e(LOG_TAG, "Invalid preference for location min distance", e);
+        }
+        return 0;
+    }
+
+    private long getLocationUpdateTime() {
+        try {
+            String timeString = getPreferences().getString(MIN_TIME_PREF, "0");
+            long secondsTime = Long.valueOf(timeString);
+            return secondsTime * 1000;
+        }
+        catch (NumberFormatException e) {
+            Log.e(LOG_TAG, "Invalid preference for location min time", e);
+        }
+        return 0;
+    }
+
+    /**
+     * Shuts down this service
+     */
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Log.d(LOG_TAG, "Removing location listeners");
+        stopListeners();
+        Toast.makeText(this, "Tracking service stopped", Toast.LENGTH_SHORT);
+    }
+
+    /**
+     * De-registers all location listeners, closes persistent storage
+     */
+    protected synchronized void stopListeners() {
+        LocationManager lm = getLocationManager();
+        if (mListeners != null) {
+            for (LocationTrackingListener listener : mListeners) {
+                lm.removeUpdates(listener);
+            }
+            mListeners.clear();
+        }
+        mListeners = null;
+
+        // stop cell state listener
+        if (mTelephonyManager != null) {
+            mTelephonyManager.listen(mPhoneStateListener, 0);
+        }    
+        
+        // stop network/wifi listener
+        if (mNetwork != null) {
+            unregisterReceiver(mNetwork);
+        }
+        mNetwork = null;
+
+        mTrackerData = null;
+        if (mPrefListener != null) {
+            getPreferences().unregisterOnSharedPreferenceChangeListener(mPrefListener);
+            mPrefListener = null;
+        }
+    }
+
+    private LocationManager getLocationManager() {
+        return (LocationManager) getSystemService(Context.LOCATION_SERVICE);
+    }
+
+    /**
+     * Determine the current distance from given location to the last
+     * approximated network location
+     *
+     * @param location - new location
+     *
+     * @return float distance in meters
+     */
+    private synchronized float getDistanceFromNetwork(Location location) {
+        float value = 0;
+        if (mNetworkLocation != null) {
+            value = location.distanceTo(mNetworkLocation);
+        }
+        if (LocationManager.NETWORK_PROVIDER.equals(location.getProvider())) {
+            mNetworkLocation = location;
+        }
+        return value;
+    }
+
+    private class LocationTrackingListener implements LocationListener {
+
+        /**
+         * Writes details of location update to tracking file, including
+         * recording the distance between this location update and the last
+         * network location update
+         *
+         * @param location - new location
+         */
+        public void onLocationChanged(Location location) {
+            if (location == null) {
+                return;
+            }
+            float distance = getDistanceFromNetwork(location);
+            mTrackerData.writeEntry(location, distance);
+        }
+
+        /**
+         * Writes update to tracking file
+         *
+         * @param provider - name of disabled provider
+         */
+        public void onProviderDisabled(String provider) {
+            if (doDebugLogging()) {
+                mTrackerData.writeEntry(provider, "provider disabled");
+            }
+        }
+
+        /**
+         * Writes update to tracking file
+         * 
+         * @param provider - name of enabled provider
+         */
+        public void onProviderEnabled(String provider) {
+            if (doDebugLogging()) {
+                mTrackerData.writeEntry(provider,  "provider enabled");
+            }
+        }
+
+        /**
+         * Writes update to tracking file 
+         * 
+         * @param provider - name of provider whose status changed
+         * @param status - new status
+         * @param extras - optional set of extra status messages
+         */
+        public void onStatusChanged(String provider, int status, Bundle extras) {
+            if (doDebugLogging()) {
+                mTrackerData.writeEntry(provider,  "status change: " + status);
+            }
+        }
+    }
+
+    PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+        @Override
+        public void onCellLocationChanged(CellLocation location) {
+            try {
+                if (location instanceof GsmCellLocation) {
+                    GsmCellLocation cellLocation = (GsmCellLocation)location;
+                    String updateMsg = "cid=" + cellLocation.getCid() +
+                            ", lac=" + cellLocation.getLac();
+                    mTrackerData.writeEntry(CELL_PROVIDER_TAG, updateMsg);
+                } else if (location instanceof CdmaCellLocation) {
+                    CdmaCellLocation cellLocation = (CdmaCellLocation)location;
+                    String updateMsg = "BID=" + cellLocation.getBaseStationId() +
+                            ", SID=" + cellLocation.getSystemId() +
+                            ", NID=" + cellLocation.getNetworkId() +
+                            ", lat=" + cellLocation.getBaseStationLatitude() +
+                            ", long=" + cellLocation.getBaseStationLongitude() +
+                            ", SID=" + cellLocation.getSystemId() +
+                            ", NID=" + cellLocation.getNetworkId();
+                    mTrackerData.writeEntry(CELL_PROVIDER_TAG, updateMsg);
+                }
+            } catch (Exception e) {
+                Log.e(LOG_TAG, "Exception in CellStateHandler.handleMessage:", e);
+            }
+        }
+
+        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+            if (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
+                String updateMsg = "cdma dBM=" + signalStrength.getCdmaDbm();
+                mTrackerData.writeEntry(SIGNAL_PROVIDER_TAG, updateMsg);
+            } else if  (mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {
+                String updateMsg = "gsm signal=" + signalStrength.getGsmSignalStrength();
+                mTrackerData.writeEntry(SIGNAL_PROVIDER_TAG, updateMsg);
+            }
+        }
+    };
+
+    /**
+     * Listener + recorder for mobile or wifi updates
+     */
+    private class NetworkStateBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+
+            if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+                WifiManager wifiManager =
+                    (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+                List<ScanResult> wifiScanResults = wifiManager.getScanResults();
+                String updateMsg = "num scan results=" +
+                    (wifiScanResults == null ? "0" : wifiScanResults.size());
+                mTrackerData.writeEntry(WIFI_PROVIDER_TAG, updateMsg);
+
+            } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+                String updateMsg;
+                boolean noConnectivity =
+                    intent.getBooleanExtra(
+                            ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+                if (noConnectivity) {
+                    updateMsg = "no connectivity";
+                }
+                else {
+                    updateMsg = "connection available";
+                }
+                mTrackerData.writeEntry(DATA_CONN_PROVIDER_TAG, updateMsg);
+
+            } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+                int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+                    WifiManager.WIFI_STATE_UNKNOWN);
+
+                String stateString = "unknown";
+                switch (state) {
+                    case WifiManager.WIFI_STATE_DISABLED:
+                        stateString = "disabled";
+                        break;
+                    case WifiManager.WIFI_STATE_DISABLING:
+                        stateString = "disabling";
+                        break;
+                    case WifiManager.WIFI_STATE_ENABLED:
+                        stateString = "enabled";
+                        break;
+                    case WifiManager.WIFI_STATE_ENABLING:
+                        stateString = "enabling";
+                        break;
+                }
+                mTrackerData.writeEntry(WIFI_PROVIDER_TAG,
+                        "state = " + stateString);
+            }
+        }
+    }
+
+    private class PreferenceListener implements OnSharedPreferenceChangeListener {
+
+        public void onSharedPreferenceChanged(
+                SharedPreferences sharedPreferences, String key) {
+            Log.d(LOG_TAG, "restarting listeners due to preference change");
+            synchronized (TrackerService.this) {
+                stopListeners();
+                initLocationListeners();
+            }
+        }
+    }
+}
diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/CSVFormatter.java b/tests/LocationTracker/src/com/android/locationtracker/data/CSVFormatter.java
new file mode 100644
index 0000000..22ddf45
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/data/CSVFormatter.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.locationtracker.data;
+
+import com.android.locationtracker.data.TrackerEntry.EntryType;
+
+/**
+ * Formats tracker data as CSV output
+ */
+class CSVFormatter implements IFormatter {
+
+    private static final String DELIMITER = ", ";
+
+    public String getHeader() {
+        StringBuilder csvBuilder = new StringBuilder();
+        for (String col : TrackerEntry.ATTRIBUTES) {
+            // skip type and id column
+            if (!TrackerEntry.ENTRY_TYPE.equals(col) &&
+                !TrackerEntry.ID_COL.equals(col)) {
+                csvBuilder.append(col);
+                csvBuilder.append(DELIMITER);
+            }
+        }
+        csvBuilder.append("\n");
+        return csvBuilder.toString();
+    }
+
+    public String getOutput(TrackerEntry entry) {
+        StringBuilder rowOutput = new StringBuilder();
+        // these must match order of columns added in getHeader
+        rowOutput.append(entry.getTimestamp());
+        rowOutput.append(DELIMITER);
+        rowOutput.append(entry.getTag());
+        rowOutput.append(DELIMITER);
+        //rowOutput.append(entry.getType());
+        //rowOutput.append(DELIMITER);
+        if (entry.getType() == EntryType.LOCATION_TYPE) {
+            if (entry.getLocation().hasAccuracy()) {
+                rowOutput.append(entry.getLocation().getAccuracy());
+            }
+            rowOutput.append(DELIMITER);
+            rowOutput.append(entry.getLocation().getLatitude());
+            rowOutput.append(DELIMITER);
+            rowOutput.append(entry.getLocation().getLongitude());
+            rowOutput.append(DELIMITER);
+            if (entry.getLocation().hasAltitude()) {
+                rowOutput.append(entry.getLocation().getAltitude());
+            }
+            rowOutput.append(DELIMITER);
+            if (entry.getLocation().hasSpeed()) {
+                rowOutput.append(entry.getLocation().getSpeed());
+            }
+            rowOutput.append(DELIMITER);
+            if (entry.getLocation().hasBearing()) {
+                rowOutput.append(entry.getLocation().getBearing());
+            }
+            rowOutput.append(DELIMITER);
+            rowOutput.append(entry.getDistFromNetLocation());
+            rowOutput.append(DELIMITER);
+            rowOutput.append(DateUtils.getKMLTimestamp(entry.getLocation()
+                    .getTime()));
+            rowOutput.append(DELIMITER);
+        }
+        rowOutput.append(entry.getLogMsg());
+        rowOutput.append("\n");
+        return rowOutput.toString();
+    }
+
+    public String getFooter() {
+        // not needed, return empty string
+        return "";
+    }
+}
diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/DateUtils.java b/tests/LocationTracker/src/com/android/locationtracker/data/DateUtils.java
new file mode 100644
index 0000000..1691f27
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/data/DateUtils.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.locationtracker.data;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * Provides formatting date as string utilities
+ */
+public class DateUtils {
+
+    private DateUtils() {
+
+    }
+
+    /**
+     * Returns timestamp given by param in KML format ie yyyy-mm-ddThh:mm:ssZ,
+     * where T is the separator between the date and the time and the time zone
+     * is Z (for UTC)
+     *
+     * @return KML timestamp as String
+     */
+    public static String getKMLTimestamp(long when) {
+        TimeZone tz = TimeZone.getTimeZone("GMT");
+        Calendar c = Calendar.getInstance(tz);
+        c.setTimeInMillis(when);
+        return String.format("%tY-%tm-%tdT%tH:%tM:%tSZ", c, c, c, c, c, c);
+    }
+
+    /**
+     * Helper version of getKMLTimestamp, that returns timestamp for current
+     * time
+     */
+    public static String getCurrentKMLTimestamp() {
+        return getKMLTimestamp(System.currentTimeMillis());
+    }
+
+    /**
+     * Returns timestamp in following format: yyyy-mm-dd-hh-mm-ss
+     */
+    public static String getCurrentTimestamp() {
+        Calendar c = Calendar.getInstance();
+        c.setTimeInMillis(System.currentTimeMillis());
+        return String.format("%tY-%tm-%td-%tH-%tM-%tS", c, c, c, c, c, c);
+    }
+}
diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/IFormatter.java b/tests/LocationTracker/src/com/android/locationtracker/data/IFormatter.java
new file mode 100644
index 0000000..d413191
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/data/IFormatter.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.locationtracker.data;
+
+/**
+ * interface for formatting tracker data output
+ */
+interface IFormatter {
+
+    String getHeader();
+    String getOutput(TrackerEntry entry);
+    String getFooter();
+}
diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/KMLFormatter.java b/tests/LocationTracker/src/com/android/locationtracker/data/KMLFormatter.java
new file mode 100644
index 0000000..ef4bbbb
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/data/KMLFormatter.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.locationtracker.data;
+
+import com.android.locationtracker.data.TrackerEntry.EntryType;
+
+import android.location.Location;
+
+/**
+ * Formats tracker data as KML output
+ */
+class KMLFormatter implements IFormatter {
+
+    public String getHeader() {
+        LineBuilder builder = new LineBuilder();
+        builder.addLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+        builder.addLine("<kml xmlns=\"http://earth.google.com/kml/2.2\">");
+        builder.addLine("<Document>");
+        return builder.toString();
+    }
+
+    public String getFooter() {
+        LineBuilder builder = new LineBuilder();
+        builder.addLine("</Document>");
+        builder.addLine("</kml>");
+        return builder.toString();
+    }
+
+    public String getOutput(TrackerEntry entry) {
+        LineBuilder builder = new LineBuilder();
+
+        if (entry.getType() == EntryType.LOCATION_TYPE) {
+
+            Location loc = entry.getLocation();
+            builder.addLine("<Placemark>");
+            builder.addLine("<description>");
+            builder.addLine("accuracy = " + loc.getAccuracy());
+            builder.addLine("distance from last network location  = "
+                    + entry.getDistFromNetLocation());
+            builder.addLine("</description>");
+            builder.addLine("<TimeStamp>");
+            builder.addLine("<when>" + entry.getTimestamp() + "</when>");
+            builder.addLine("</TimeStamp>");
+            builder.addLine("<Point>");
+            builder.addLine("<coordinates>");
+            builder.addLine(loc.getLongitude() + "," + loc.getLatitude() + ","
+                    + loc.getAltitude());
+            builder.addLine("</coordinates>");
+            builder.addLine("</Point>");
+            builder.addLine("</Placemark>");
+        }
+        return builder.toString();
+    }
+
+    private static class LineBuilder {
+        private StringBuilder mBuilder;
+
+        public LineBuilder() {
+            mBuilder = new StringBuilder();
+        }
+
+        public void addLine(String line) {
+            mBuilder.append(line);
+            mBuilder.append("\n");
+        }
+
+        @Override
+        public String toString() {
+            return mBuilder.toString();
+        }
+
+    }
+
+}
diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/TrackerDataHelper.java b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerDataHelper.java
new file mode 100644
index 0000000..ad25126
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerDataHelper.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.locationtracker.data;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.location.Location;
+
+/**
+ * Helper class for writing and retrieving data using the TrackerProvider
+ * content provider
+ *
+ */
+public class TrackerDataHelper {
+
+    private Context mContext;
+    /** formats data output */
+    protected IFormatter mFormatter;
+
+    /** formats output as Comma separated value CSV file */
+    public static final IFormatter CSV_FORMATTER = new CSVFormatter();
+    /** formats output as KML file */
+    public static final IFormatter KML_FORMATTER = new KMLFormatter();
+    /** provides no formatting */
+    public static final IFormatter NO_FORMATTER = new IFormatter() {
+        public String getFooter() {
+            return "";
+        }
+
+        public String getHeader() {
+            return "";
+        }
+
+        public String getOutput(TrackerEntry entry) {
+            return "";
+        }
+    };
+
+    /**
+     * Creates instance
+     *
+     * @param context - content context
+     * @param formatter - formats the output from the get*Output* methods
+     */
+    public TrackerDataHelper(Context context, IFormatter formatter) {
+        mContext = context;
+        mFormatter = formatter;
+    }
+
+    /**
+     * Creates a instance with no output formatting capabilities. Useful for
+     * clients that require write-only access
+     */
+    public TrackerDataHelper(Context context) {
+        this(context, NO_FORMATTER);
+    }
+
+    /**
+     * insert given TrackerEntry into content provider
+     */
+    void writeEntry(TrackerEntry entry) {
+        mContext.getContentResolver().insert(TrackerProvider.CONTENT_URI,
+                entry.getAsContentValues());
+    }
+
+    /**
+     * insert given location into tracker data
+     */
+    public void writeEntry(Location loc, float distFromNetLoc) {
+        writeEntry(TrackerEntry.createEntry(loc, distFromNetLoc));
+    }
+
+    /**
+     * insert given log message into tracker data
+     */
+    public void writeEntry(String tag, String logMsg) {
+        writeEntry(TrackerEntry.createEntry(tag, logMsg));
+    }
+
+    /**
+     * Deletes all tracker entries
+     */
+    public void deleteAll() {
+        mContext.getContentResolver().delete(TrackerProvider.CONTENT_URI, null,
+                null);
+    }
+
+    /**
+     * Query tracker data, filtering by given tag
+     *
+     * @param tag
+     * @return Cursor to data
+     */
+    public Cursor query(String tag, int limit) {
+        String selection = (tag == null ? null : TrackerEntry.TAG + "=?");
+        String[] selectionArgs = (tag == null ? null : new String[] {tag});
+        Cursor cursor = mContext.getContentResolver().query(
+                TrackerProvider.CONTENT_URI, TrackerEntry.ATTRIBUTES,
+                selection, selectionArgs, null);
+        if (cursor == null) {
+            return cursor;
+        }
+        int pos = (cursor.getCount() < limit ? 0 : cursor.getCount() - limit);
+        cursor.moveToPosition(pos);
+        return cursor;
+    }
+
+    /**
+     * Retrieves a cursor that starts at the last limit rows
+     *
+     * @param limit
+     * @return a cursor, null if bad things happened
+     */
+    public Cursor query(int limit) {
+        return query(null, limit);
+    }
+
+    /**
+     * Query tracker data, filtering by given tag. mo limit to number of rows
+     * returned
+     *
+     * @param tag
+     * @return Cursor to data
+     */
+    public Cursor query(String tag) {
+        return query(tag, Integer.MAX_VALUE);
+    }
+
+    /**
+     * Returns the output header particular to the associated formatter
+     */
+    public String getOutputHeader() {
+        return mFormatter.getHeader();
+    }
+
+    /**
+     * Returns the output footer particular to the associated formatter
+     */
+    public String getOutputFooter() {
+        return mFormatter.getFooter();
+    }
+
+    /**
+     * Helper method which converts row referenced by given cursor to a string
+     * output
+     *
+     * @param cursor
+     * @return CharSequence output, null if given cursor is invalid or no more
+     *         data
+     */
+    public String getNextOutput(Cursor cursor) {
+        if (cursor == null || cursor.isAfterLast()) {
+            return null;
+        }
+        String output = mFormatter.getOutput(TrackerEntry.createEntry(cursor));
+        cursor.moveToNext();
+        return output;
+    }
+}
diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/TrackerEntry.java b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerEntry.java
new file mode 100644
index 0000000..8c961d1
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerEntry.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.locationtracker.data;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.location.Location;
+
+
+/**
+ * Class that holds a tracker entry. An entry can be either a valid location, or
+ * a simple log msg
+ *
+ * It provides a concrete data structure to represent data stored in the
+ * TrackerProvider
+ */
+class TrackerEntry {
+
+    static final String TIMESTAMP = "Timestamp";
+    static final String TAG = "Tag";
+    static final String ENTRY_TYPE = "Type";
+
+    private Location mLocation;
+    private float mDistFromNetLocation;
+    private String mLogMsg;
+
+    static final String ID_COL = "_id";
+    static final String ACCURACY = "Accuracy";
+    static final String LATITUDE = "Latitude";
+    static final String LONGITUDE = "Longitude";
+    static final String ALTITUDE = "Altitude";
+    static final String SPEED = "Speed";
+    static final String BEARING = "Bearing";
+    static final String DIST_NET_LOCATION = "DistFromNetLocation";
+    static final String LOC_TIME = "LocationTime";
+    static final String DEBUG_INFO = "DebugInfo";
+
+    static final String STRING_DATA = "STRING";
+    static final String INT_DATA = "INTEGER";
+    static final String REAL_DATA = "REAL";
+    static final String BLOB_DATA = "BLOB";
+
+    static final String[] ATTRIBUTES = {
+            ID_COL, TIMESTAMP, TAG, ENTRY_TYPE, ACCURACY, LATITUDE, LONGITUDE,
+            ALTITUDE, SPEED, BEARING, DIST_NET_LOCATION, LOC_TIME, DEBUG_INFO};
+    static final String[] ATTRIBUTES_DATA_TYPE = {
+            INT_DATA + " PRIMARY KEY", STRING_DATA, STRING_DATA, STRING_DATA,
+            REAL_DATA, REAL_DATA, REAL_DATA, REAL_DATA, REAL_DATA, REAL_DATA,
+            REAL_DATA, INT_DATA, STRING_DATA};
+
+    // location extra keys used to retrieve debug info
+    private static final String NETWORK_LOCATION_SOURCE_KEY =
+        "networkLocationSource";
+    private static final String NETWORK_LOCATION_TYPE_KEY =
+        "networkLocationType";
+    private static final String[] LOCATION_DEBUG_KEYS = {
+            NETWORK_LOCATION_SOURCE_KEY, NETWORK_LOCATION_TYPE_KEY};
+
+    enum EntryType {
+        LOCATION_TYPE, LOG_TYPE
+    }
+
+    private String mTimestamp;
+    private String mTag;
+    private EntryType mType;
+
+    private TrackerEntry(String tag, EntryType type) {
+        mType = type;
+        mTag = tag;
+        mLocation = null;
+    }
+
+    private TrackerEntry(Location loc) {
+        this(loc.getProvider(), EntryType.LOCATION_TYPE);
+        mLocation = new Location(loc);
+    }
+
+    /**
+     * Creates a TrackerEntry from a Location
+     */
+    static TrackerEntry createEntry(Location loc, float distFromNetLocation) {
+        TrackerEntry entry = new TrackerEntry(loc);
+
+        String timestampVal = DateUtils.getCurrentKMLTimestamp();
+        entry.setTimestamp(timestampVal);
+        entry.setDistFromNetLocation(distFromNetLocation);
+        return entry;
+    }
+
+    /**
+     * Creates a TrackerEntry from a log msg
+     */
+    static TrackerEntry createEntry(String tag, String msg) {
+        TrackerEntry entry = new TrackerEntry(tag, EntryType.LOG_TYPE);
+        String timestampVal = DateUtils.getCurrentKMLTimestamp();
+        entry.setTimestamp(timestampVal);
+        entry.setLogMsg(msg);
+        return entry;
+    }
+
+    private void setTimestamp(String timestamp) {
+        mTimestamp = timestamp;
+    }
+
+    EntryType getType() {
+        return mType;
+    }
+
+    private void setDistFromNetLocation(float distFromNetLocation) {
+        mDistFromNetLocation = distFromNetLocation;
+    }
+
+    private void setLogMsg(String msg) {
+        mLogMsg = msg;
+    }
+
+    private void setLocation(Location location) {
+        mLocation = location;
+    }
+
+    String getTimestamp() {
+        return mTimestamp;
+    }
+
+    String getTag() {
+        return mTag;
+    }
+
+    Location getLocation() {
+        return mLocation;
+    }
+
+    String getLogMsg() {
+        return mLogMsg;
+    }
+
+    float getDistFromNetLocation() {
+        return mDistFromNetLocation;
+    }
+
+    static void buildCreationString(StringBuilder builder) {
+        if (ATTRIBUTES.length != ATTRIBUTES_DATA_TYPE.length) {
+            throw new IllegalArgumentException(
+                    "Attribute length does not match data type length");
+        }
+        for (int i = 0; i < ATTRIBUTES_DATA_TYPE.length; i++) {
+            if (i != 0) {
+                builder.append(", ");
+            }
+            builder.append(String.format("%s %s", ATTRIBUTES[i],
+                    ATTRIBUTES_DATA_TYPE[i]));
+        }
+    }
+
+    ContentValues getAsContentValues() {
+        ContentValues cValues = new ContentValues(ATTRIBUTES.length);
+        cValues.put(TIMESTAMP, mTimestamp);
+        cValues.put(TAG, mTag);
+        cValues.put(ENTRY_TYPE, mType.toString());
+        if (mType == EntryType.LOCATION_TYPE) {
+            cValues.put(LATITUDE, mLocation.getLatitude());
+            cValues.put(LONGITUDE, mLocation.getLongitude());
+            if (mLocation.hasAccuracy()) {
+                cValues.put(ACCURACY, mLocation.getAccuracy());
+            }
+            if (mLocation.hasAltitude()) {
+                cValues.put(ALTITUDE, mLocation.getAltitude());
+            }
+            if (mLocation.hasSpeed()) {
+                cValues.put(SPEED, mLocation.getSpeed());
+            }
+            if (mLocation.hasBearing()) {
+                cValues.put(BEARING, mLocation.getBearing());
+            }
+            cValues.put(DIST_NET_LOCATION, mDistFromNetLocation);
+            cValues.put(LOC_TIME, mLocation.getTime());
+            StringBuilder debugBuilder = new StringBuilder("");
+            if (mLocation.getExtras() != null) {
+                for (String key : LOCATION_DEBUG_KEYS) {
+                    Object val = mLocation.getExtras().get(key);
+                    if (val != null) {
+                        debugBuilder.append(String.format("%s=%s; ", key, val
+                                .toString()));
+                    }
+                }
+            }
+            cValues.put(DEBUG_INFO, debugBuilder.toString());
+        } else {
+            cValues.put(DEBUG_INFO, mLogMsg);
+        }
+        return cValues;
+    }
+
+    static TrackerEntry createEntry(Cursor cursor) {
+        String timestamp = cursor.getString(cursor.getColumnIndex(TIMESTAMP));
+        String tag = cursor.getString(cursor.getColumnIndex(TAG));
+        String sType = cursor.getString(cursor.getColumnIndex(ENTRY_TYPE));
+        TrackerEntry entry = new TrackerEntry(tag, EntryType.valueOf(sType));
+        entry.setTimestamp(timestamp);
+        if (entry.getType() == EntryType.LOCATION_TYPE) {
+            Location location = new Location(tag);
+            location.setLatitude(cursor.getFloat(cursor
+                    .getColumnIndexOrThrow(LATITUDE)));
+            location.setLongitude(cursor.getFloat(cursor
+                    .getColumnIndexOrThrow(LONGITUDE)));
+
+            Float accuracy = getNullableFloat(cursor, ACCURACY);
+            if (accuracy != null) {
+                location.setAccuracy(accuracy);
+            }
+            Float altitude = getNullableFloat(cursor, ALTITUDE);
+            if (altitude != null) {
+                location.setAltitude(altitude);
+            }
+            Float bearing = getNullableFloat(cursor, BEARING);
+            if (bearing != null) {
+                location.setBearing(bearing);
+            }
+            Float speed = getNullableFloat(cursor, SPEED);
+            if (speed != null) {
+                location.setSpeed(speed);
+            }
+            location.setTime(cursor.getLong(cursor.getColumnIndex(LOC_TIME)));
+            entry.setLocation(location);
+        }
+        entry.setLogMsg(cursor.getString(cursor.getColumnIndex(DEBUG_INFO)));
+
+        return entry;
+    }
+
+    private static Float getNullableFloat(Cursor cursor, String colName) {
+        Float retValue = null;
+        int colIndex = cursor.getColumnIndexOrThrow(colName);
+        if (!cursor.isNull(colIndex)) {
+            retValue = cursor.getFloat(colIndex);
+        }
+        return retValue;
+    }
+}
diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/TrackerListHelper.java b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerListHelper.java
new file mode 100644
index 0000000..0247ef0
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerListHelper.java
@@ -0,0 +1,75 @@
+/*

+ * Copyright (C) 2008 Google Inc.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not

+ * use this file except in compliance with the License. You may obtain a copy of

+ * the License at

+ *

+ * http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT

+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the

+ * License for the specific language governing permissions and limitations under

+ * the License.

+ */

+package com.android.locationtracker.data;

+

+import android.app.ListActivity;

+import android.content.Context;

+import android.database.Cursor;

+import android.view.View;

+import android.widget.ResourceCursorAdapter;

+import android.widget.TextView;

+

+import com.android.locationtracker.R;

+

+/**

+ * Used to bind Tracker data to a list view UI

+ */

+public class TrackerListHelper extends TrackerDataHelper {

+

+    private ListActivity mActivity;

+

+    // sort entries by most recent first

+    private static final String SORT_ORDER = TrackerEntry.ID_COL + " DESC";

+

+    public TrackerListHelper(ListActivity activity) {

+        super(activity, TrackerDataHelper.CSV_FORMATTER);

+        mActivity = activity;

+    }

+

+    /**

+     * Helper method for binding the list activities UI to the tracker data

+     * Tracker data will be sorted in most-recent first order

+     * Will enable automatic UI changes as tracker data changes

+     *

+     * @param layout - layout to populate data

+     */

+    public void bindListUI(int layout) {

+        Cursor cursor = mActivity.managedQuery(TrackerProvider.CONTENT_URI,

+                TrackerEntry.ATTRIBUTES, null, null, SORT_ORDER);

+        // Used to map tracker entries from the database to views

+        TrackerAdapter adapter = new TrackerAdapter(mActivity, layout, cursor);

+        mActivity.setListAdapter(adapter);

+        cursor.setNotificationUri(mActivity.getContentResolver(),

+                TrackerProvider.CONTENT_URI);

+

+    }

+

+    private class TrackerAdapter extends ResourceCursorAdapter {

+

+        public TrackerAdapter(Context context, int layout, Cursor c) {

+            super(context, layout, c);

+        }

+

+        @Override

+        public void bindView(View view, Context context, Cursor cursor) {

+            final TextView v = (TextView) view

+                    .findViewById(R.id.entrylist_item);

+            String rowText = mFormatter.getOutput(TrackerEntry

+                    .createEntry(cursor));

+            v.setText(rowText);

+        }

+    }

+}

diff --git a/tests/LocationTracker/src/com/android/locationtracker/data/TrackerProvider.java b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerProvider.java
new file mode 100644
index 0000000..ea849e0
--- /dev/null
+++ b/tests/LocationTracker/src/com/android/locationtracker/data/TrackerProvider.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.locationtracker.data;
+
+import android.content.ContentProvider;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.net.Uri;
+import android.util.Log;
+
+/**
+ * Content provider for location tracking.
+ *
+ * It is recommended to use the TrackerDataHelper class to access this data
+ * rather than this class directly
+ */
+public class TrackerProvider extends ContentProvider {
+
+    public static final Uri CONTENT_URI = Uri
+            .parse("content://com.android.locationtracker");
+
+    private static final String DB_NAME = "tracking.db";
+    private static final String TABLE_NAME = "tracking";
+    private static final int DB_VERSION = 1;
+
+    private static final String LOG_TAG = "TrackerProvider";
+
+    /**
+     * This class helps open, create, and upgrade the database file.
+     */
+    private static class DatabaseHelper extends SQLiteOpenHelper {
+
+        DatabaseHelper(Context context) {
+            super(context, DB_NAME, null, DB_VERSION);
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            StringBuilder queryBuilder = new StringBuilder();
+            queryBuilder.append(String.format("CREATE TABLE %s (", TABLE_NAME));
+            TrackerEntry.buildCreationString(queryBuilder);
+
+            queryBuilder.append(");");
+            db.execSQL(queryBuilder.toString());
+            db.setVersion(DB_VERSION);
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            // TODO: reimplement this when dB version changes
+            Log.w(LOG_TAG, "Upgrading database from version " + oldVersion
+                            + " to " + newVersion
+                            + ", which will destroy all old data");
+            db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
+            onCreate(db);
+        }
+    }
+
+    private DatabaseHelper mOpenHelper;
+
+    @Override
+    public boolean onCreate() {
+        mOpenHelper = new DatabaseHelper(getContext());
+        return true;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        int result = db.delete(TABLE_NAME, selection, selectionArgs);
+        getContext().getContentResolver().notifyChange(uri, null);
+        return result;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        long rowId = db.insert(TABLE_NAME, null, values);
+        if (rowId > 0) {
+            Uri addedUri = ContentUris.withAppendedId(CONTENT_URI, rowId);
+            getContext().getContentResolver().notifyChange(addedUri, null);
+            return addedUri;
+        }
+        return null;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder) {
+        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+        // TODO: extract limit from URI ?
+        Cursor cursor = db.query(TABLE_NAME, projection, selection,
+                selectionArgs, null, null, sortOrder);
+        getContext().getContentResolver().notifyChange(uri, null);
+        return cursor;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection,
+            String[] selectionArgs) {
+        throw new UnsupportedOperationException();
+    }
+}