Merge "StorageNotification: Move notification / usb storage activity into StatusBarPolicy"
diff --git a/api/current.xml b/api/current.xml
index 31ab3ce..7169d0d 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -4585,6 +4585,17 @@
  visibility="public"
 >
 </field>
+<field name="installLocation"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843448"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="interpolator"
  type="int"
  transient="false"
@@ -66274,6 +66285,10 @@
 </parameter>
 <parameter name="format" type="int">
 </parameter>
+<parameter name="width" type="int">
+</parameter>
+<parameter name="height" type="int">
+</parameter>
 <parameter name="strides" type="int[]">
 </parameter>
 </constructor>
@@ -66287,17 +66302,24 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="width" type="int">
-</parameter>
-<parameter name="height" type="int">
-</parameter>
-<parameter name="offsets" type="int[]">
+<parameter name="rectangle" type="android.graphics.Rect">
 </parameter>
 <parameter name="quality" type="int">
 </parameter>
 <parameter name="stream" type="java.io.OutputStream">
 </parameter>
 </method>
+<method name="getHeight"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getStrides"
  return="int[]"
  abstract="false"
@@ -66309,6 +66331,17 @@
  visibility="public"
 >
 </method>
+<method name="getWidth"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getYuvData"
  return="byte[]"
  abstract="false"
@@ -66331,25 +66364,6 @@
  visibility="public"
 >
 </method>
-<method name="validate"
- return="boolean"
- abstract="false"
- native="false"
- synchronized="false"
- static="false"
- final="false"
- deprecated="not deprecated"
- visibility="protected"
->
-<parameter name="format" type="int">
-</parameter>
-<parameter name="width" type="int">
-</parameter>
-<parameter name="height" type="int">
-</parameter>
-<parameter name="offsets" type="int[]">
-</parameter>
-</method>
 </class>
 </package>
 <package name="android.graphics.drawable"
@@ -72580,7 +72594,7 @@
  type="float"
  transient="false"
  volatile="false"
- value="0.0010f"
+ value="0.001f"
  static="true"
  final="true"
  deprecated="not deprecated"
diff --git a/common/java/com/android/common/Search.java b/common/java/com/android/common/Search.java
new file mode 100644
index 0000000..55fa6f5
--- /dev/null
+++ b/common/java/com/android/common/Search.java
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package com.android.common;
+
+/**
+ * Utilities for search implementations.
+ *
+ * @see android.app.SearchManager
+ */
+public class Search {
+
+    /**
+     * Key for the source identifier set by the application that launched a search intent.
+     * The identifier is search-source specific string. It can be used
+     * by the search provider to keep statistics of where searches are started from.
+     *
+     * The source identifier is stored in the {@link android.app.SearchManager#APP_DATA}
+     * Bundle in {@link android.content.Intent#ACTION_SEARCH} and
+     * {@link android.content.Intent#ACTION_WEB_SEARCH} intents.
+     */
+    public final static String SOURCE = "source";
+
+    private Search() { }   // don't instantiate
+}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 9b9cbd5..0a18fe5 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -52,6 +52,7 @@
 import android.content.pm.ProviderInfo;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.PackageParser.Package;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
@@ -85,6 +86,7 @@
 import android.os.Vibrator;
 import android.os.FileUtils.FileStatus;
 import android.os.storage.StorageManager;
+import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.text.ClipboardManager;
 import android.util.AndroidRuntimeException;
@@ -2646,14 +2648,13 @@
         // SD-to-internal app size threshold: currently set to 1 MB
         private static final long INSTALL_ON_SD_THRESHOLD = (1024 * 1024);
 
-        @Override
-        public int recommendAppInstallLocation(ApplicationInfo appInfo, Uri packageURI) {
+        public int recommendAppInstallLocation(Package pkg) {
             // Initial implementation:
             // Package size = code size + cache size + data size
             // If code size > 1 MB, install on SD card.
             // Else install on internal NAND flash, unless space on NAND is less than 10%
 
-            if ((packageURI == null) || (appInfo == null)) {
+            if (pkg == null) {
                 return INSTALL_PARSE_FAILED_NOT_APK;
             }
 
@@ -2669,44 +2670,71 @@
 
             double pctNandFree = (double)availInternalFlashSize / (double)totalInternalFlashSize;
 
-            final String archiveFilePath = packageURI.getPath();
+            final String archiveFilePath = pkg.mScanPath;
             File apkFile = new File(archiveFilePath);
             long pkgLen = apkFile.length();
 
+            boolean auto = true;
+            // To make final copy
+            long reqInstallSize = pkgLen;
+            // For dex files
+            long reqInternalSize = 1 * pkgLen;
+            boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD);
+            boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalFlashSize);
+            boolean fitsOnSd = (reqInstallSize < availSDSize) && intThresholdOk &&
+                    (reqInternalSize < availInternalFlashSize);
+            boolean fitsOnInt = intThresholdOk && intAvailOk;
+
             // Consider application flags preferences as well...
-            boolean installOnlyOnSD = ((appInfo.flags & PackageManager.INSTALL_ON_SDCARD) != 0);
-
-            // These are not very precise measures, but I guess it is hard to estimate sizes
-            // before installing the package.
-            // As a shortcut, I am assuming that the package fits on NAND flash if the available
-            // space is three times that of the APK size. For SD, we only worry about the APK size.
-            // Since packages are downloaded into SD, this might not even be necessary.
-            boolean fitsOnSD = (pkgLen < availSDSize) && ((2 * pkgLen) < availInternalFlashSize);
-            boolean fitsOnInternalFlash = ((pkgLen * 3) < availInternalFlashSize);
-
-            // Does not fit, recommend no installation.
-            if (!fitsOnSD && !fitsOnInternalFlash) {
-                return INSTALL_FAILED_INSUFFICIENT_STORAGE;
-            }
-
-            if (pkgLen < (INSTALL_ON_SD_THRESHOLD) && fitsOnInternalFlash && !(installOnlyOnSD)) {
-                // recommend internal NAND likely
-                if (pctNandFree < LOW_NAND_FLASH_TRESHOLD) {
-                    // Low space on NAND (<10%) - install on SD
-                    return INSTALL_ON_SDCARD;
+            boolean installOnlyOnSd = (pkg.installLocation ==
+                    PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL);
+            boolean installOnlyInternal = (pkg.installLocation ==
+                    PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
+            if (installOnlyInternal) {
+                // If set explicitly in manifest,
+                // let that override everything else
+                auto = false;
+            } else if (installOnlyOnSd){
+                // Check if this can be accommodated on the sdcard
+                if (fitsOnSd) {
+                    auto = false;
                 }
-                return INSTALL_ON_INTERNAL_FLASH;
             } else {
-                if (fitsOnSD) {
-                    // Recommend SD card
-                    return INSTALL_ON_SDCARD;
-                } else if (fitsOnInternalFlash && (pctNandFree >= LOW_NAND_FLASH_TRESHOLD) &&
-                        !(installOnlyOnSD)) {
-                    return INSTALL_ON_INTERNAL_FLASH;
-                } else {
-                    return INSTALL_FAILED_INSUFFICIENT_STORAGE;
+                // Check if user option is enabled
+                boolean setInstallLoc = Settings.System.getInt(mContext.getContentResolver(),
+                        Settings.System.SET_INSTALL_LOCATION, 0) != 0;
+                if (setInstallLoc) {
+                    // Pick user preference
+                    int installPreference = Settings.System.getInt(mContext.getContentResolver(),
+                            Settings.System.DEFAULT_INSTALL_LOCATION,
+                            PackageInfo.INSTALL_LOCATION_AUTO);
+                    if (installPreference == 1) {
+                        installOnlyInternal = true;
+                        auto = false;
+                    } else if (installPreference == 2) {
+                        installOnlyOnSd = true;
+                        auto = false;
+                    }
                 }
             }
+            if (!auto) {
+                if (installOnlyOnSd) {
+                    return fitsOnSd ? INSTALL_ON_SDCARD : INSTALL_FAILED_INSUFFICIENT_STORAGE;
+                } else if (installOnlyInternal){
+                    // Check on internal flash
+                    return fitsOnInt ? INSTALL_ON_INTERNAL_FLASH : INSTALL_FAILED_INSUFFICIENT_STORAGE;
+                }
+            }
+            // Try to install internally
+            if (fitsOnInt) {
+                return INSTALL_ON_INTERNAL_FLASH;
+            }
+            // Try the sdcard now.
+            if (fitsOnSd) {
+                return INSTALL_ON_SDCARD;
+            }
+            // Return error code
+            return INSTALL_FAILED_INSUFFICIENT_STORAGE;
         }
 
         private final ContextImpl mContext;
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 3046a2c..5a295b4 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -1279,16 +1279,6 @@
     public final static String APP_DATA = "app_data";
 
     /**
-     * Intent app_data bundle key: Use this key with the bundle from
-     * {@link android.content.Intent#getBundleExtra
-     * content.Intent.getBundleExtra(APP_DATA)} to obtain the source identifier
-     * set by the activity that launched the search.
-     *
-     * @hide
-     */
-    public final static String SOURCE = "source";
-
-    /**
      * Intent extra data key: Use {@link android.content.Intent#getBundleExtra
      * content.Intent.getBundleExtra(SEARCH_MODE)} to get the search mode used
      * to launch the intent.
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index a8ce889..c003355 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -131,6 +131,34 @@
      * The features that this application has said it requires.
      */
     public FeatureInfo[] reqFeatures;
+
+    /**
+     * Constant corresponding to <code>auto</code> in
+     * the {@link android.R.attr#installLocation} attribute.
+     * @hide
+     */
+    public static final int INSTALL_LOCATION_AUTO = 0;
+    /**
+     * Constant corresponding to <code>internalOnly</code> in
+     * the {@link android.R.attr#installLocation} attribute.
+     * @hide
+     */
+    public static final int INSTALL_LOCATION_INTERNAL_ONLY = 1;
+    /**
+     * Constant corresponding to <code>preferExternal</code> in
+     * the {@link android.R.attr#installLocation} attribute.
+     * @hide
+     */
+    public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2;
+    /**
+     * The launch mode style requested by the activity.  From the
+     * {@link android.R.attr#installLocation} attribute, one of
+     * {@link #INSTALL_LOCATION_AUTO},
+     * {@link #INSTALL_LOCATION_INTERNAL_ONLY},
+     * {@link #INSTALL_LOCATION_PREFER_EXTERNAL}
+     * @hide
+     */
+    public int installLocation = INSTALL_LOCATION_INTERNAL_ONLY;
     
     public PackageInfo() {
     }
@@ -168,6 +196,7 @@
         dest.writeTypedArray(signatures, parcelableFlags);
         dest.writeTypedArray(configPreferences, parcelableFlags);
         dest.writeTypedArray(reqFeatures, parcelableFlags);
+        dest.writeInt(installLocation);
     }
 
     public static final Parcelable.Creator<PackageInfo> CREATOR
@@ -202,5 +231,6 @@
         signatures = source.createTypedArray(Signature.CREATOR);
         configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR);
         reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
+        installLocation = source.readInt();
     }
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index fca8588..a61eab9 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -630,10 +630,11 @@
     
     /**
      * Determines best place to install an application: either SD or internal FLASH.
-     * Tweak the algorithm for best results.
-     * @param appInfo ApplicationInfo object of the package to install.
+     * If applications explicitly set installLocation in their manifest, that
+     * preference takes precedence. If not a recommended location is returned
+     * based on current available storage on internal flash or sdcard.
+     * @param pkgInfo PackageParser.Package of the package that is to be installed.
      * Call utility method to obtain.
-     * @param packageURI URI identifying the package's APK file.
      * @return {@link INSTALL_ON_INTERNAL_FLASH} if it is best to install package on internal
      * storage, {@link INSTALL_ON_SDCARD} if it is best to install package on SD card,
      * and {@link INSTALL_FAILED_INSUFFICIENT_STORAGE} if insufficient space to safely install
@@ -642,7 +643,7 @@
      * This recommendation does take into account the package's own flags.
      * @hide
      */
-    public abstract int recommendAppInstallLocation(ApplicationInfo appInfo, Uri packageURI);
+    public abstract int recommendAppInstallLocation(PackageParser.Package pkg);
 
     /**
      * Retrieve overall information about an application package that is
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index b31df32..0a6195f 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -177,6 +177,7 @@
         pi.sharedUserId = p.mSharedUserId;
         pi.sharedUserLabel = p.mSharedUserLabel;
         pi.applicationInfo = p.applicationInfo;
+        pi.installLocation = p.installLocation;
         if ((flags&PackageManager.GET_GIDS) != 0) {
             pi.gids = gids;
         }
@@ -709,6 +710,9 @@
                     com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
         }
         sa.recycle();
+        pkg.installLocation = sa.getInteger(
+                com.android.internal.R.styleable.AndroidManifest_installLocation,
+                PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY);
 
         // Resource boolean are -1, so 1 means we don't know the value.
         int supportsSmallScreens = 1;
@@ -2610,6 +2614,8 @@
          */
         public ArrayList<FeatureInfo> reqFeatures = null;
 
+        public int installLocation;
+
         public Package(String _name) {
             packageName = _name;
             applicationInfo.packageName = _name;
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 47c2cac..c0bff66 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -723,6 +723,7 @@
         private static final String KEY_FOCAL_LENGTH = "focal-length";
         private static final String KEY_HORIZONTAL_VIEW_ANGLE = "horizontal-view-angle";
         private static final String KEY_VERTICAL_VIEW_ANGLE = "vertical-view-angle";
+        private static final String KEY_EXPOSURE_COMPENSATION = "exposure-compensation";
         // Parameter key suffix for supported values.
         private static final String SUPPORTED_VALUES_SUFFIX = "-values";
 
@@ -1051,7 +1052,8 @@
         }
 
         /**
-         * Sets the rate at which preview frames are received.
+         * Sets the rate at which preview frames are received. This is the
+         * target frame rate. The actual frame rate depends on the driver.
          *
          * @param fps the frame rate (frames per second)
          */
@@ -1060,8 +1062,9 @@
         }
 
         /**
-         * Returns the setting for the rate at which preview frames
-         * are received.
+         * Returns the setting for the rate at which preview frames are
+         * received. This is the target frame rate. The actual frame rate
+         * depends on the driver.
          *
          * @return the frame rate setting (frames per second)
          */
@@ -1540,6 +1543,41 @@
         }
 
         /**
+         * Gets the current exposure compensation setting.
+         *
+         * @return the current exposure compensation value multiplied by 100.
+         *         null if exposure compensation is not supported. Ex: -100
+         *         means -1 EV. 130 means +1.3 EV.
+         * @hide
+         */
+        public int getExposureCompensation() {
+            return getInt(KEY_EXPOSURE_COMPENSATION);
+        }
+
+        /**
+         * Sets the exposure compensation.
+         *
+         * @param value exposure compensation multiplied by 100. Ex: -100 means
+         *        -1 EV. 130 means +1.3 EV.
+         * @hide
+         */
+        public void setExposureCompensation(int value) {
+            set(KEY_EXPOSURE_COMPENSATION, value);
+        }
+
+        /**
+         * Gets the supported exposure compensation.
+         *
+         * @return a List of Integer constants. null if exposure compensation is
+         *         not supported. The list is sorted from small to large. Ex:
+         *         -100, -66, -33, 0, 33, 66, 100.
+         * @hide
+         */
+        public List<Integer> getSupportedExposureCompensation() {
+            return splitInt(get(KEY_EXPOSURE_COMPENSATION + SUPPORTED_VALUES_SUFFIX));
+        }
+
+        /**
          * Gets current zoom value. This also works when smooth zoom is in
          * progress.
          *
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index bacaf43..c2cdcc0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1473,6 +1473,21 @@
         public static final String NOTIFICATION_LIGHT_PULSE = "notification_light_pulse";
 
         /**
+         * Let user pick default install location.
+         * @hide
+         */
+        public static final String SET_INSTALL_LOCATION = "set_install_location";
+
+        /**
+         * Default install location value.
+         * 0 = auto, let system decide
+         * 1 = internal
+         * 2 = sdcard
+         * @hide
+         */
+        public static final String DEFAULT_INSTALL_LOCATION = "default_install_location";
+
+        /**
          * Settings to backup. This is here so that it's in the same place as the settings
          * keys and easy to update.
          * @hide
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 54781e3..2da23eb 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -600,6 +600,20 @@
          Application class to avoid interference with application logic. -->
     <attr name="restoreNeedsApplication" format="boolean" />
 
+    <!-- The default install location defined by an application. -->
+    <attr name="installLocation">
+        <!-- Let the system decide ideal install location -->
+        <enum name="auto" value="0" />
+        <!-- Explicitly request to be installed on internal phone storate
+             only. -->
+        <enum name="internalOnly" value="1" />
+        <!-- Prefer to be installed on sdcard. There is no guarantee that
+             the system will honour this request. The application might end
+             up being installed on internal storage if external media
+             is unavailable or too full. -->
+        <enum name="preferExternal" value="2" />
+    </attr>
+
     <!-- The <code>manifest</code> tag is the root of an
          <code>AndroidManifest.xml</code> file,
          describing the contents of an Android package (.apk) file.  One
@@ -624,6 +638,7 @@
         <attr name="versionName" />
         <attr name="sharedUserId" />
         <attr name="sharedUserLabel" />
+        <attr name="installLocation" />
     </declare-styleable>
     
     <!-- The <code>application</code> tag describes application-level components
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index bd4a3eb..596e0b2 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1224,9 +1224,10 @@
   <public type="attr" name="expandableListViewWhiteStyle" id="0x010102b6" />
 
 <!-- ===============================================================
-     Resources proposed for Flan.
+     Resources proposed for Froyo.
      =============================================================== -->
   <eat-comment />
   <public type="attr" name="neverEncrypt" id="0x010102b7" />
+  <public type="attr" name="installLocation" id="0x010102b8" />
     
 </resources>
diff --git a/docs/html/shareables/latest_samples.zip b/docs/html/shareables/latest_samples.zip
index 42fad99..34102c5 100644
--- a/docs/html/shareables/latest_samples.zip
+++ b/docs/html/shareables/latest_samples.zip
Binary files differ
diff --git a/graphics/java/android/graphics/YuvImage.java b/graphics/java/android/graphics/YuvImage.java
index 09b4bf4..5a4531b 100644
--- a/graphics/java/android/graphics/YuvImage.java
+++ b/graphics/java/android/graphics/YuvImage.java
@@ -21,13 +21,11 @@
 /**
  * YuvImage contains YUV data and provides a method that compresses a region of
  * the YUV data to a Jpeg. The YUV data should be provided as a single byte
- * array irrespective of the number of image planes in it. The stride of each
- * image plane should be provided as well.
+ * array irrespective of the number of image planes in it.
+ * Currently only PixelFormat.YCbCr_420_SP and PixelFormat.YCbCr_422_I are supported.
  *
- * To compress a rectangle region in the YUV data, users have to specify a
- * region by width, height and offsets, where each image plane has a
- * corresponding offset. All offsets are measured as a displacement in bytes
- * from yuv[0], where yuv[0] is the beginning of the yuv data.
+ * To compress a rectangle region in the YUV data, users have to specify the
+ * region by left, top, width and height.
  */
 public class YuvImage {
 
@@ -55,21 +53,56 @@
     private int[] mStrides;
 
     /**
+     * The width of the image.
+     */
+    private int mWidth;
+
+    /**
+     * The height of the the image.
+     */
+    private int mHeight;
+
+    /**
      * Construct an YuvImage.
      *
-     * @param yuv The YUV data. In the case of more than one image plane, all the planes must be
-     *            concatenated into a single byte array.
-     * @param format The YUV data format as defined in {@link PixelFormat}.
-     * @param strides Row bytes of each image plane.
+     * @param yuv     The YUV data. In the case of more than one image plane, all the planes must be
+     *                concatenated into a single byte array.
+     * @param format  The YUV data format as defined in {@link PixelFormat}.
+     * @param width   The width of the YuvImage.
+     * @param height  The height of the YuvImage.
+     * @param strides (Optional) Row bytes of each image plane. If yuv contains padding, the stride
+     *                of each image must be provided. If strides is null, the method assumes no
+     *                padding and derives the row bytes by format and width itself.
+     * @throws IllegalArgumentException if format is not support; width or height <= 0; or yuv is
+     *                null.
      */
-    public YuvImage(byte[] yuv, int format, int[] strides) {
-        if ((yuv == null) || (strides == null)) {
+    public YuvImage(byte[] yuv, int format, int width, int height, int[] strides) {
+        if (format != PixelFormat.YCbCr_420_SP &&
+                format != PixelFormat.YCbCr_422_I) {
             throw new IllegalArgumentException(
-                    "yuv or strides cannot be null");
+                    "only support PixelFormat.YCbCr_420_SP " +
+                    "and PixelFormat.YCbCr_422_I for now");
         }
+
+        if (width <= 0  || height <= 0) {
+            throw new IllegalArgumentException(
+                    "width and height must large than 0");
+        }
+
+        if (yuv == null) {
+            throw new IllegalArgumentException("yuv cannot be null");
+        }
+
+        if (strides == null) {
+            mStrides = calculateStrides(width, format);
+        } else {
+            mStrides = strides;
+        }
+
         mData = yuv;
         mFormat = format;
-        mStrides = strides;
+        mWidth = width;
+        mHeight = height;
     }
 
     /**
@@ -77,22 +110,21 @@
      * Only PixelFormat.YCbCr_420_SP and PixelFormat.YCbCr_422_I
      * are supported for now.
      *
-     * @param width The width of the rectangle region.
-     * @param height The height of the rectangle region.
-     * @param offsets The offsets of the rectangle region in each image plane.
-     *                The offsets are measured as a displacement in bytes from
-     *                yuv[0], where yuv[0] is the beginning of the yuv data.
-     * @param quality  Hint to the compressor, 0-100. 0 meaning compress for
-     *                 small size, 100 meaning compress for max quality.
-     * @param stream   The outputstream to write the compressed data.
-     *
-     * @return true if successfully compressed to the specified stream.
-     *
+     * @param rectangle The rectangle region to be compressed. The medthod checks if rectangle is
+     *                  inside the image. Also, the method modifies rectangle if the chroma pixels
+     *                  in it are not matched with the luma pixels in it.
+     * @param quality   Hint to the compressor, 0-100. 0 meaning compress for
+     *                  small size, 100 meaning compress for max quality.
+     * @param stream    OutputStream to write the compressed data.
+     * @return          True if the compression is successful.
+     * @throws IllegalArgumentException if rectangle is invalid; quality is not within [0,
+     *                  100]; or stream is null.
      */
-    public boolean compressToJpeg(int width, int height, int[] offsets, int quality,
-            OutputStream stream) {
-        if (!validate(mFormat, width, height, offsets)) {
-            return false;
+    public boolean compressToJpeg(Rect rectangle, int quality, OutputStream stream) {
+        Rect wholeImage = new Rect(0, 0, mWidth, mHeight);
+        if (!wholeImage.contains(rectangle)) {
+            throw new IllegalArgumentException(
+                    "rectangle is not inside the image");
         }
 
         if (quality < 0 || quality > 100) {
@@ -100,14 +132,19 @@
         }
 
         if (stream == null) {
-            throw new NullPointerException();
+            throw new IllegalArgumentException("stream cannot be null");
         }
 
-        return nativeCompressToJpeg(mData, mFormat, width, height, offsets,
-                mStrides, quality, stream, new byte[WORKING_COMPRESS_STORAGE]);
+        adjustRectangle(rectangle);
+        int[] offsets = calculateOffsets(rectangle.left, rectangle.top);
+
+        return nativeCompressToJpeg(mData, mFormat, rectangle.width(),
+                rectangle.height(), offsets, mStrides, quality, stream,
+                new byte[WORKING_COMPRESS_STORAGE]);
     }
 
-    /**
+
+   /**
      * @return the YUV data.
      */
     public byte[] getYuvData() {
@@ -128,37 +165,71 @@
         return mStrides;
     }
 
-    protected boolean validate(int format, int width, int height, int[] offsets) {
-        if (format != PixelFormat.YCbCr_420_SP &&
-                format != PixelFormat.YCbCr_422_I) {
-            throw new IllegalArgumentException(
-                    "only support PixelFormat.YCbCr_420_SP " +
-                    "and PixelFormat.YCbCr_422_I for now");
+    /**
+     * @return the width of the image.
+     */
+    public int getWidth() {
+        return mWidth;
+    }
+
+    /**
+     * @return the height of the image.
+     */
+    public int getHeight() {
+        return mHeight;
+    }
+
+    int[] calculateOffsets(int left, int top) {
+        int[] offsets = null;
+        if (mFormat == PixelFormat.YCbCr_420_SP) {
+            offsets = new int[] {top * mStrides[0] + left,
+                  mHeight * mStrides[0] + top / 2 * mStrides[1]
+                  + left / 2 * 2 };
+            return offsets;
         }
 
-        if (offsets.length != mStrides.length) {
-            throw new IllegalArgumentException(
-                    "the number of image planes are mismatched");
+        if (mFormat == PixelFormat.YCbCr_422_I) {
+            offsets = new int[] {top * mStrides[0] + left / 2 * 4};
+            return offsets;
         }
 
-        if (width <= 0  || height <= 0) {
-            throw new IllegalArgumentException(
-                    "width and height must large than 0");
-        }
+        return offsets;
+    }
 
-        int requiredSize;
+    private int[] calculateStrides(int width, int format) {
+        int[] strides = null;
         if (format == PixelFormat.YCbCr_420_SP) {
-            requiredSize = height * mStrides[0] +(height >> 1) * mStrides[1];
-        } else {
-            requiredSize = height * mStrides[0];
+            strides = new int[] {width, width};
+            return strides;
         }
 
-        if (requiredSize > mData.length) {
-            throw new IllegalArgumentException(
-                    "width or/and height is larger than the yuv data");
+        if (format == PixelFormat.YCbCr_422_I) {
+            strides = new int[] {width * 2};
+            return strides;
         }
 
-        return true;
+        return strides;
+    }
+
+   private void adjustRectangle(Rect rect) {
+       int width = rect.width();
+       int height = rect.height();
+       if (mFormat == PixelFormat.YCbCr_420_SP) {
+           // Make sure left, top, width and height are all even.
+           width &= ~1;
+           height &= ~1;
+           rect.left &= ~1;
+           rect.top &= ~1;
+           rect.right = rect.left + width;
+           rect.bottom = rect.top + height;
+        }
+
+        if (mFormat == PixelFormat.YCbCr_422_I) {
+            // Make sure left and width are both even.
+            width &= ~1;
+            rect.left &= ~1;
+            rect.right = rect.left + width;
+        }
     }
 
     //////////// native methods
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index a5a1bb8..be06e33 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -62,7 +62,8 @@
     // AudioSink: abstraction layer for audio output
     class AudioSink : public RefBase {
     public:
-        typedef void (*AudioCallback)(
+        // Callback returns the number of bytes actually written to the buffer.
+        typedef size_t (*AudioCallback)(
                 AudioSink *audioSink, void *buffer, size_t size, void *cookie);
 
         virtual             ~AudioSink() {}
@@ -77,8 +78,7 @@
         virtual status_t    getPosition(uint32_t *position) = 0;
 
         // If no callback is specified, use the "write" API below to submit
-        // audio data. Otherwise return a full buffer of audio data on each
-        // callback.
+        // audio data.
         virtual status_t    open(
                 uint32_t sampleRate, int channelCount,
                 int format=AudioSystem::PCM_16_BIT,
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
index 843e051..8e5f05f 100644
--- a/include/media/stagefright/AudioPlayer.h
+++ b/include/media/stagefright/AudioPlayer.h
@@ -90,11 +90,11 @@
     static void AudioCallback(int event, void *user, void *info);
     void AudioCallback(int event, void *info);
 
-    static void AudioSinkCallback(
+    static size_t AudioSinkCallback(
             MediaPlayerBase::AudioSink *audioSink,
             void *data, size_t size, void *me);
 
-    void fillBuffer(void *data, size_t size);
+    size_t fillBuffer(void *data, size_t size);
 
     int64_t getRealTimeUsLocked() const;
 
diff --git a/include/ui/CameraParameters.h b/include/ui/CameraParameters.h
index 2c29bfb..e328f33 100644
--- a/include/ui/CameraParameters.h
+++ b/include/ui/CameraParameters.h
@@ -187,6 +187,13 @@
     // Vertical angle of view in degrees.
     // Example value: "42.5". Read only.
     static const char KEY_VERTICAL_VIEW_ANGLE[];
+    // Exposure compensation. The value is multiplied by 100. -100 means -1 EV.
+    // 130 means +1.3 EV.
+    // Example value: "0" or "133". Read/write.
+    static const char KEY_EXPOSURE_COMPENSATION[];
+    // Supported exposure compensation.
+    // Example value: "-100,-66,-33,0,33,66,100". Read only.
+    static const char KEY_SUPPORTED_EXPOSURE_COMPENSATION[];
 
 
         // Values for white balance settings.
diff --git a/libs/ui/Camera.cpp b/libs/ui/Camera.cpp
index 09a36f1..f374fbc 100644
--- a/libs/ui/Camera.cpp
+++ b/libs/ui/Camera.cpp
@@ -281,7 +281,7 @@
 // send command to camera driver
 status_t Camera::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2)
 {
-    LOGD("sendCommand");
+    LOGV("sendCommand");
     sp <ICamera> c = mCamera;
     if (c == 0) return NO_INIT;
     return c->sendCommand(cmd, arg1, arg2);
diff --git a/libs/ui/CameraParameters.cpp b/libs/ui/CameraParameters.cpp
index c4958a0..493b9c1 100644
--- a/libs/ui/CameraParameters.cpp
+++ b/libs/ui/CameraParameters.cpp
@@ -59,6 +59,8 @@
 const char CameraParameters::KEY_FOCAL_LENGTH[] = "focal-length";
 const char CameraParameters::KEY_HORIZONTAL_VIEW_ANGLE[] = "horizontal-view-angle";
 const char CameraParameters::KEY_VERTICAL_VIEW_ANGLE[] = "vertical-view-angle";
+const char CameraParameters::KEY_EXPOSURE_COMPENSATION[] = "exposure-compensation";
+const char CameraParameters::KEY_SUPPORTED_EXPOSURE_COMPENSATION[] = "exposure-compensation-values";
 
 // Values for white balance settings.
 const char CameraParameters::WHITE_BALANCE_AUTO[] = "auto";
diff --git a/libs/ui/ICamera.cpp b/libs/ui/ICamera.cpp
index e1b3ec7..4154b05a 100644
--- a/libs/ui/ICamera.cpp
+++ b/libs/ui/ICamera.cpp
@@ -344,7 +344,7 @@
             return NO_ERROR;
          } break;
         case SEND_COMMAND: {
-            LOGD("SEND_COMMAND");
+            LOGV("SEND_COMMAND");
             CHECK_INTERFACE(ICamera, data, reply);
             int command = data.readInt32();
             int arg1 = data.readInt32();
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 628cb6b7..8c24ee1 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -22,7 +22,6 @@
 import android.location.IGeocodeProvider;
 import android.location.IGpsStatusListener;
 import android.location.ILocationListener;
-import android.location.ILocationProvider;
 import android.location.Location;
 import android.os.Bundle;
 
diff --git a/location/java/android/location/ILocationProvider.aidl b/location/java/android/location/ILocationProvider.aidl
index 9fe6ab4..5529b11 100644
--- a/location/java/android/location/ILocationProvider.aidl
+++ b/location/java/android/location/ILocationProvider.aidl
@@ -21,7 +21,7 @@
 import android.os.Bundle;
 
 /**
- * Binder interface for location providers.
+ * Binder interface for services that implement location providers.
  *
  * {@hide}
  */
diff --git a/location/java/android/location/LocationProviderInterface.java b/location/java/android/location/LocationProviderInterface.java
new file mode 100644
index 0000000..98beffe
--- /dev/null
+++ b/location/java/android/location/LocationProviderInterface.java
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package android.location;
+
+import android.location.Location;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+
+/**
+ * Location Manager's interface for location providers.
+ *
+ * {@hide}
+ */
+public interface LocationProviderInterface {
+    String getName();
+    boolean requiresNetwork();
+    boolean requiresSatellite();
+    boolean requiresCell();
+    boolean hasMonetaryCost();
+    boolean supportsAltitude();
+    boolean supportsSpeed();
+    boolean supportsBearing();
+    int getPowerRequirement();
+    int getAccuracy();
+    boolean isEnabled();
+    void enable();
+    void disable();
+    int getStatus(Bundle extras);
+    long getStatusUpdateTime();
+    void enableLocationTracking(boolean enable);
+    void setMinTime(long minTime);
+    void updateNetworkState(int state, NetworkInfo info);
+    void updateLocation(Location location);
+    boolean sendExtraCommand(String command, Bundle extras);
+    void addListener(int uid);
+    void removeListener(int uid);
+}
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index 8b5f702..dce3b27 100755
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -26,11 +26,11 @@
 import android.location.IGpsStatusListener;
 import android.location.IGpsStatusProvider;
 import android.location.ILocationManager;
-import android.location.ILocationProvider;
 import android.location.INetInitiatedListener;
 import android.location.Location;
 import android.location.LocationManager;
 import android.location.LocationProvider;
+import android.location.LocationProviderInterface;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.SntpClient;
@@ -65,7 +65,7 @@
  *
  * {@hide}
  */
-public class GpsLocationProvider extends ILocationProvider.Stub {
+public class GpsLocationProvider implements LocationProviderInterface {
 
     private static final String TAG = "GpsLocationProvider";
 
@@ -374,6 +374,13 @@
     }
 
     /**
+     * Returns the name of this provider.
+     */
+    public String getName() {
+        return LocationManager.GPS_PROVIDER;
+    }
+
+    /**
      * Returns true if the provider requires access to a
      * data network (e.g., the Internet), false otherwise.
      */
@@ -576,6 +583,10 @@
         }
     }
 
+    public boolean isEnabled() {
+        return mEnabled;
+    }
+
     public int getStatus(Bundle extras) {
         if (extras != null) {
             extras.putInt("satellites", mSvCount);
diff --git a/location/java/com/android/internal/location/LocationProviderProxy.java b/location/java/com/android/internal/location/LocationProviderProxy.java
index 361104f..abb90b7 100644
--- a/location/java/com/android/internal/location/LocationProviderProxy.java
+++ b/location/java/com/android/internal/location/LocationProviderProxy.java
@@ -22,6 +22,7 @@
 import android.content.ServiceConnection;
 import android.location.ILocationProvider;
 import android.location.Location;
+import android.location.LocationProviderInterface;
 import android.net.NetworkInfo;
 import android.os.Bundle;
 import android.os.Handler;
@@ -31,18 +32,17 @@
 import android.util.Log;
 
 /**
- * A class for proxying ILocationProvider implementations.
+ * A class for proxying location providers implemented as services.
  *
  * {@hide}
  */
-public class LocationProviderProxy {
+public class LocationProviderProxy implements LocationProviderInterface {
 
     private static final String TAG = "LocationProviderProxy";
 
     private final Context mContext;
     private final String mName;
     private ILocationProvider mProvider;
-    private Intent mIntent;
     private Handler mHandler;
     private final Connection mServiceConnection = new Connection();
 
@@ -56,21 +56,13 @@
     // for caching requiresNetwork, requiresSatellite, etc.
     private DummyLocationProvider mCachedAttributes;
 
-    // constructor for proxying built-in location providers
-    public LocationProviderProxy(Context context, String name, ILocationProvider provider) {
-        mContext = context;
-        mName = name;
-        mProvider = provider;
-    }
-
     // constructor for proxying location providers implemented in a separate service
     public LocationProviderProxy(Context context, String name, String serviceName,
             Handler handler) {
         mContext = context;
         mName = name;
-        mIntent = new Intent(serviceName);
         mHandler = handler;
-        mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+        mContext.bindService(new Intent(serviceName), mServiceConnection, Context.BIND_AUTO_CREATE);
     }
 
     private class Connection implements ServiceConnection {
diff --git a/location/java/com/android/internal/location/MockProvider.java b/location/java/com/android/internal/location/MockProvider.java
index 7d9e86c..2f6fdee 100644
--- a/location/java/com/android/internal/location/MockProvider.java
+++ b/location/java/com/android/internal/location/MockProvider.java
@@ -17,9 +17,9 @@
 package com.android.internal.location;
 
 import android.location.ILocationManager;
-import android.location.ILocationProvider;
 import android.location.Location;
 import android.location.LocationProvider;
+import android.location.LocationProviderInterface;
 import android.net.NetworkInfo;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -33,7 +33,7 @@
  *
  * {@hide}
  */
-public class MockProvider extends ILocationProvider.Stub {
+public class MockProvider implements LocationProviderInterface {
     private final String mName;
     private final ILocationManager mLocationManager;
     private final boolean mRequiresNetwork;
@@ -73,6 +73,10 @@
         mLocation = new Location(name);
     }
 
+    public String getName() {
+        return mName;
+    }
+
     public void disable() {
         mEnabled = false;
     }
@@ -81,6 +85,10 @@
         mEnabled = true;
     }
 
+    public boolean isEnabled() {
+        return mEnabled;
+    }
+
     public int getStatus(Bundle extras) {
         if (mHasStatus) {
             extras.clear();
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 8c1b0ea..34252ab 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -16,10 +16,14 @@
 
 package android.media;
 
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.IContentProvider;
-import android.content.ContentUris;
 import android.database.Cursor;
 import android.database.SQLException;
 import android.graphics.BitmapFactory;
@@ -42,11 +46,12 @@
 import android.util.Log;
 import android.util.Xml;
 
-import org.xml.sax.Attributes;
-import org.xml.sax.ContentHandler;
-import org.xml.sax.SAXException;
-
-import java.io.*;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -586,6 +591,9 @@
                     }
                     if (genreCode >= 0 && genreCode < ID3_GENRES.length) {
                         value = ID3_GENRES[genreCode];
+                    } else if (genreCode == 255) {
+                        // 255 is defined to be unknown
+                        value = null;
                     }
                 }
                 mGenre = value;
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 8e61011..55b06f4 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1597,9 +1597,12 @@
     AudioOutput *me = (AudioOutput *)cookie;
     AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
 
-    (*me->mCallback)(
+    size_t actualSize = (*me->mCallback)(
             me, buffer->raw, buffer->size, me->mCallbackCookie);
-    me->snoopWrite(buffer->raw, buffer->size);
+
+    if (actualSize > 0) {
+        me->snoopWrite(buffer->raw, actualSize);
+    }
 }
 
 #undef LOG_TAG
@@ -1629,14 +1632,75 @@
     return NO_ERROR;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+
+struct CallbackThread : public Thread {
+    CallbackThread(const wp<MediaPlayerBase::AudioSink> &sink,
+                   MediaPlayerBase::AudioSink::AudioCallback cb,
+                   void *cookie);
+
+protected:
+    virtual ~CallbackThread();
+
+    virtual bool threadLoop();
+
+private:
+    wp<MediaPlayerBase::AudioSink> mSink;
+    MediaPlayerBase::AudioSink::AudioCallback mCallback;
+    void *mCookie;
+    void *mBuffer;
+    size_t mBufferSize;
+
+    CallbackThread(const CallbackThread &);
+    CallbackThread &operator=(const CallbackThread &);
+};
+
+CallbackThread::CallbackThread(
+        const wp<MediaPlayerBase::AudioSink> &sink,
+        MediaPlayerBase::AudioSink::AudioCallback cb,
+        void *cookie)
+    : mSink(sink),
+      mCallback(cb),
+      mCookie(cookie),
+      mBuffer(NULL),
+      mBufferSize(0) {
+}
+
+CallbackThread::~CallbackThread() {
+    if (mBuffer) {
+        free(mBuffer);
+        mBuffer = NULL;
+    }
+}
+
+bool CallbackThread::threadLoop() {
+    sp<MediaPlayerBase::AudioSink> sink = mSink.promote();
+    if (sink == NULL) {
+        return false;
+    }
+
+    if (mBuffer == NULL) {
+        mBufferSize = sink->bufferSize();
+        mBuffer = malloc(mBufferSize);
+    }
+
+    size_t actualSize =
+        (*mCallback)(sink.get(), mBuffer, mBufferSize, mCookie);
+
+    if (actualSize > 0) {
+        sink->write(mBuffer, actualSize);
+    }
+
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 status_t MediaPlayerService::AudioCache::open(
         uint32_t sampleRate, int channelCount, int format, int bufferCount,
         AudioCallback cb, void *cookie)
 {
     LOGV("open(%u, %d, %d, %d)", sampleRate, channelCount, format, bufferCount);
-    if (cb != NULL) {
-        return UNKNOWN_ERROR;  // TODO: implement this.
-    }
     if (mHeap->getHeapID() < 0) {
         return NO_INIT;
     }
@@ -1645,9 +1709,25 @@
     mChannelCount = (uint16_t)channelCount;
     mFormat = (uint16_t)format;
     mMsecsPerFrame = 1.e3 / (float) sampleRate;
+
+    if (cb != NULL) {
+        mCallbackThread = new CallbackThread(this, cb, cookie);
+    }
     return NO_ERROR;
 }
 
+void MediaPlayerService::AudioCache::start() {
+    if (mCallbackThread != NULL) {
+        mCallbackThread->run("AudioCache callback");
+    }
+}
+
+void MediaPlayerService::AudioCache::stop() {
+    if (mCallbackThread != NULL) {
+        mCallbackThread->requestExitAndWait();
+    }
+}
+
 ssize_t MediaPlayerService::AudioCache::write(const void* buffer, size_t size)
 {
     LOGV("write(%p, %u)", buffer, size);
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index ffe1ba0..5c03e47 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -139,9 +139,9 @@
                 int bufferCount = 1,
                 AudioCallback cb = NULL, void *cookie = NULL);
 
-        virtual void            start() {}
+        virtual void            start();
         virtual ssize_t         write(const void* buffer, size_t size);
-        virtual void            stop() {}
+        virtual void            stop();
         virtual void            flush() {}
         virtual void            pause() {}
         virtual void            close() {}
@@ -171,6 +171,8 @@
         uint32_t            mSize;
         int                 mError;
         bool                mCommandComplete;
+
+        sp<Thread>          mCallbackThread;
     };
 
 public:
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 4926920..12d7ee2 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -187,12 +187,12 @@
 }
 
 // static
-void AudioPlayer::AudioSinkCallback(
+size_t AudioPlayer::AudioSinkCallback(
         MediaPlayerBase::AudioSink *audioSink,
         void *buffer, size_t size, void *cookie) {
     AudioPlayer *me = (AudioPlayer *)cookie;
 
-    me->fillBuffer(buffer, size);
+    return me->fillBuffer(buffer, size);
 }
 
 void AudioPlayer::AudioCallback(int event, void *info) {
@@ -201,17 +201,18 @@
     }
 
     AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
-    fillBuffer(buffer->raw, buffer->size);
+    size_t numBytesWritten = fillBuffer(buffer->raw, buffer->size);
+
+    buffer->size = numBytesWritten;
 }
 
-void AudioPlayer::fillBuffer(void *data, size_t size) {
+size_t AudioPlayer::fillBuffer(void *data, size_t size) {
     if (mNumFramesPlayed == 0) {
         LOGV("AudioCallback");
     }
 
     if (mReachedEOS) {
-        memset(data, 0, size);
-        return;
+        return 0;
     }
 
     size_t size_done = 0;
@@ -244,7 +245,6 @@
 
             if (err != OK) {
                 mReachedEOS = true;
-                memset((char *)data + size_done, 0, size_remaining);
                 break;
             }
 
@@ -285,7 +285,9 @@
     }
 
     Mutex::Autolock autoLock(mLock);
-    mNumFramesPlayed += size / mFrameSize;
+    mNumFramesPlayed += size_done / mFrameSize;
+
+    return size_done;
 }
 
 int64_t AudioPlayer::getRealTimeUs() {
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 4458006..1ff38ee 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1540,7 +1540,7 @@
 
     if (!memcmp(header, "ftyp3gp", 7) || !memcmp(header, "ftypmp42", 8)
         || !memcmp(header, "ftypisom", 8) || !memcmp(header, "ftypM4V ", 8)
-        || !memcmp(header, "ftypM4A ", 8)) {
+        || !memcmp(header, "ftypM4A ", 8) || !memcmp(header, "ftypf4v ", 8)) {
         *mimeType = MEDIA_MIMETYPE_CONTAINER_MPEG4;
         *confidence = 0.1;
 
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index c6c6f21..75b7b6f 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -112,7 +112,6 @@
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" },
     { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder" },
     { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.decoder.h263" },
-    { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.Video.Decoder" },
     { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.decoder.avc" },
     { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" },
diff --git a/media/tests/SoundPoolTest/src/com/android/SoundPoolTest.java b/media/tests/SoundPoolTest/src/com/android/SoundPoolTest.java
index 6b3093f..1434d3f 100644
--- a/media/tests/SoundPoolTest/src/com/android/SoundPoolTest.java
+++ b/media/tests/SoundPoolTest/src/com/android/SoundPoolTest.java
@@ -43,10 +43,9 @@
     private TestThread mThread;
 
     private static final int[] mTestFiles = new int[] {
-        // FIXME: Restore when Stagefright bug is fixed
         R.raw.organ441,
         R.raw.sine441,
-        //R.raw.test1,
+        R.raw.test1,
         R.raw.test2,
         R.raw.test3,
         R.raw.test4,
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 7686aa0..ba6024f 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -56,5 +56,6 @@
     <bool name="def_mount_ums_autostart">false</bool>
     <bool name="def_mount_ums_prompt">true</bool>
     <bool name="def_mount_ums_notify_enabled">true</bool>
+    <bool name="set_install_location">true</bool>
 
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 015b487..ac20297 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -864,6 +864,8 @@
 
         loadBooleanSetting(stmt, Settings.System.NOTIFICATION_LIGHT_PULSE,
                 R.bool.def_notification_pulse);
+        loadBooleanSetting(stmt, Settings.System.SET_INSTALL_LOCATION, R.bool.set_install_location);
+        loadIntegerSetting(stmt, Settings.System.DEFAULT_INSTALL_LOCATION, 0);
         stmt.close();
     }
 
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index fff6c54..e12f2e1 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -45,11 +45,11 @@
 import android.location.IGpsStatusProvider;
 import android.location.ILocationListener;
 import android.location.ILocationManager;
-import android.location.ILocationProvider;
 import android.location.INetInitiatedListener;
 import android.location.Location;
 import android.location.LocationManager;
 import android.location.LocationProvider;
+import android.location.LocationProviderInterface;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.Uri;
@@ -114,8 +114,8 @@
     private LocationWorkerHandler mLocationHandler;
 
     // Cache the real providers for use in addTestProvider() and removeTestProvider()
-     LocationProviderProxy mNetworkLocationProvider;
-     LocationProviderProxy mGpsLocationProvider;
+     LocationProviderInterface mNetworkLocationProvider;
+     LocationProviderInterface mGpsLocationProvider;
 
     // Handler messages
     private static final int MESSAGE_LOCATION_CHANGED = 1;
@@ -134,10 +134,10 @@
     /**
      * List of location providers.
      */
-    private final ArrayList<LocationProviderProxy> mProviders =
-        new ArrayList<LocationProviderProxy>();
-    private final HashMap<String, LocationProviderProxy> mProvidersByName
-        = new HashMap<String, LocationProviderProxy>();
+    private final ArrayList<LocationProviderInterface> mProviders =
+        new ArrayList<LocationProviderInterface>();
+    private final HashMap<String, LocationProviderInterface> mProvidersByName
+        = new HashMap<String, LocationProviderInterface>();
 
     /**
      * Object used internally for synchronization
@@ -411,12 +411,12 @@
         }
     }
 
-    private void addProvider(LocationProviderProxy provider) {
+    private void addProvider(LocationProviderInterface provider) {
         mProviders.add(provider);
         mProvidersByName.put(provider.getName(), provider);
     }
 
-    private void removeProvider(LocationProviderProxy provider) {
+    private void removeProvider(LocationProviderInterface provider) {
         mProviders.remove(provider);
         mProvidersByName.remove(provider.getName());
     }
@@ -445,13 +445,11 @@
         // Attempt to load "real" providers first
         if (GpsLocationProvider.isSupported()) {
             // Create a gps location provider
-            GpsLocationProvider provider = new GpsLocationProvider(mContext, this);
-            mGpsStatusProvider = provider.getGpsStatusProvider();
-            mNetInitiatedListener = provider.getNetInitiatedListener();
-            LocationProviderProxy proxy =
-                    new LocationProviderProxy(mContext, LocationManager.GPS_PROVIDER, provider);
-            addProvider(proxy);
-            mGpsLocationProvider = proxy;
+            GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this);
+            mGpsStatusProvider = gpsProvider.getGpsStatusProvider();
+            mNetInitiatedListener = gpsProvider.getNetInitiatedListener();
+            addProvider(gpsProvider);
+            mGpsLocationProvider = gpsProvider;
         }
 
         // initialize external network location and geocoder services
@@ -591,7 +589,7 @@
         }
         ArrayList<String> out = new ArrayList<String>(mProviders.size());
         for (int i = mProviders.size() - 1; i >= 0; i--) {
-            LocationProviderProxy p = mProviders.get(i);
+            LocationProviderInterface p = mProviders.get(i);
             out.add(p.getName());
         }
         return out;
@@ -616,7 +614,7 @@
         }
         ArrayList<String> out = new ArrayList<String>(mProviders.size());
         for (int i = mProviders.size() - 1; i >= 0; i--) {
-            LocationProviderProxy p = mProviders.get(i);
+            LocationProviderInterface p = mProviders.get(i);
             String name = p.getName();
             if (isAllowedProviderSafe(name)) {
                 if (enabledOnly && !isAllowedBySettingsLocked(name)) {
@@ -630,7 +628,7 @@
 
     private void updateProvidersLocked() {
         for (int i = mProviders.size() - 1; i >= 0; i--) {
-            LocationProviderProxy p = mProviders.get(i);
+            LocationProviderInterface p = mProviders.get(i);
             boolean isEnabled = p.isEnabled();
             String name = p.getName();
             boolean shouldBeEnabled = isAllowedBySettingsLocked(name);
@@ -647,7 +645,7 @@
     private void updateProviderListenersLocked(String provider, boolean enabled) {
         int listeners = 0;
 
-        LocationProviderProxy p = mProvidersByName.get(provider);
+        LocationProviderInterface p = mProvidersByName.get(provider);
         if (p == null) {
             return;
         }
@@ -837,8 +835,8 @@
             Log.v(TAG, "_requestLocationUpdates: listener = " + receiver);
         }
 
-        LocationProviderProxy proxy = mProvidersByName.get(provider);
-        if (proxy == null) {
+        LocationProviderInterface p = mProvidersByName.get(provider);
+        if (p == null) {
             throw new IllegalArgumentException("provider=" + provider);
         }
 
@@ -856,14 +854,14 @@
             }
 
             if (newUid) {
-                proxy.addListener(callingUid);
+                p.addListener(callingUid);
             }
 
             boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
             if (isProviderEnabled) {
                 long minTimeForProvider = getMinTimeLocked(provider);
-                proxy.setMinTime(minTimeForProvider);
-                proxy.enableLocationTracking(true);
+                p.setMinTime(minTimeForProvider);
+                p.enableLocationTracking(true);
             } else {
                 // Notify the listener that updates are currently disabled
                 receiver.callProviderEnabledLocked(provider, false);
@@ -923,9 +921,9 @@
                 // Call dispose() on the obsolete update records.
                 for (UpdateRecord record : oldRecords.values()) {
                     if (!providerHasListener(record.mProvider, callingUid, receiver)) {
-                        LocationProviderProxy proxy = mProvidersByName.get(record.mProvider);
-                        if (proxy != null) {
-                            proxy.removeListener(callingUid);
+                        LocationProviderInterface p = mProvidersByName.get(record.mProvider);
+                        if (p != null) {
+                            p.removeListener(callingUid);
                         }
                     }
                     record.disposeLocked();
@@ -949,7 +947,7 @@
                     hasOtherListener = true;
                 }
 
-                LocationProviderProxy p = mProvidersByName.get(provider);
+                LocationProviderInterface p = mProvidersByName.get(provider);
                 if (p != null) {
                     if (hasOtherListener) {
                         p.setMinTime(getMinTimeLocked(provider));
@@ -1006,12 +1004,12 @@
         }
 
         synchronized (mLock) {
-            LocationProviderProxy proxy = mProvidersByName.get(provider);
-            if (proxy == null) {
+            LocationProviderInterface p = mProvidersByName.get(provider);
+            if (p == null) {
                 return false;
             }
     
-            return proxy.sendExtraCommand(command, extras);
+            return p.sendExtraCommand(command, extras);
         }
     }
 
@@ -1261,7 +1259,7 @@
             mProximityReceiver = new Receiver(mProximityListener);
 
             for (int i = mProviders.size() - 1; i >= 0; i--) {
-                LocationProviderProxy provider = mProviders.get(i);
+                LocationProviderInterface provider = mProviders.get(i);
                 requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityReceiver);
             }
         }
@@ -1311,7 +1309,7 @@
     }
 
     private Bundle _getProviderInfoLocked(String provider) {
-        LocationProviderProxy p = mProvidersByName.get(provider);
+        LocationProviderInterface p = mProvidersByName.get(provider);
         if (p == null || !p.isEnabled()) {
             return null;
         }
@@ -1359,7 +1357,7 @@
     private boolean _isProviderEnabledLocked(String provider) {
         checkPermissionsSafe(provider);
 
-        LocationProviderProxy p = mProvidersByName.get(provider);
+        LocationProviderInterface p = mProvidersByName.get(provider);
         if (p == null) {
             throw new IllegalArgumentException("provider=" + provider);
         }
@@ -1382,7 +1380,7 @@
     private Location _getLastKnownLocationLocked(String provider) {
         checkPermissionsSafe(provider);
 
-        LocationProviderProxy p = mProvidersByName.get(provider);
+        LocationProviderInterface p = mProvidersByName.get(provider);
         if (p == null) {
             throw new IllegalArgumentException("provider=" + provider);
         }
@@ -1424,7 +1422,7 @@
             return;
         }
 
-        LocationProviderProxy p = mProvidersByName.get(provider);
+        LocationProviderInterface p = mProvidersByName.get(provider);
         if (p == null) {
             return;
         }
@@ -1507,9 +1505,9 @@
 
                         // notify other providers of the new location
                         for (int i = mProviders.size() - 1; i >= 0; i--) {
-                            LocationProviderProxy proxy = mProviders.get(i);
-                            if (!provider.equals(proxy.getName())) {
-                                proxy.updateLocation(location);
+                            LocationProviderInterface p = mProviders.get(i);
+                            if (!provider.equals(p.getName())) {
+                                p.updateLocation(location);
                             }
                         }
 
@@ -1597,7 +1595,7 @@
                 // Notify location providers of current network state
                 synchronized (mLock) {
                     for (int i = mProviders.size() - 1; i >= 0; i--) {
-                        LocationProviderProxy provider = mProviders.get(i);
+                        LocationProviderInterface provider = mProviders.get(i);
                         if (provider.isEnabled() && provider.requiresNetwork()) {
                             provider.updateNetworkState(mNetworkState, info);
                         }
@@ -1698,16 +1696,16 @@
             // remove the real provider if we are replacing GPS or network provider
             if (LocationManager.GPS_PROVIDER.equals(name)
                     || LocationManager.NETWORK_PROVIDER.equals(name)) {
-                LocationProviderProxy proxy = mProvidersByName.get(name);
-                if (proxy != null) {
-                    proxy.enableLocationTracking(false);
-                    removeProvider(proxy);
+                LocationProviderInterface p = mProvidersByName.get(name);
+                if (p != null) {
+                    p.enableLocationTracking(false);
+                    removeProvider(p);
                 }
             }
             if (mProvidersByName.get(name) != null) {
                 throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
             }
-            addProvider(new LocationProviderProxy(mContext, name, provider));
+            addProvider(provider);
             mMockProviders.put(name, provider);
             mLastKnownLocation.put(name, null);
             updateProvidersLocked();
diff --git a/test-runner/android/test/TestLocationProvider.java b/test-runner/android/test/TestLocationProvider.java
deleted file mode 100644
index dc07585..0000000
--- a/test-runner/android/test/TestLocationProvider.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-package android.test;
-
-
-import android.location.Criteria;
-import android.location.ILocationManager;
-import android.location.ILocationProvider;
-import android.location.Location;
-import android.location.LocationProvider;
-import android.net.NetworkInfo;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.util.Log;
-
-/**
- * @hide - This is part of a framework that is under development and should not be used for
- * active development.
- */
-public class TestLocationProvider extends ILocationProvider.Stub {
-
-    public static final String PROVIDER_NAME = "test";
-    public static final double LAT = 0;
-    public static final double LON = 1;
-    public static final double ALTITUDE = 10000;
-    public static final float SPEED = 10;
-    public static final float BEARING = 1;
-    public static final int STATUS = LocationProvider.AVAILABLE;
-    private static final long LOCATION_INTERVAL = 1000;
-
-    private static final String TAG = "TestLocationProvider";
-
-    private final ILocationManager mLocationManager;
-    private Location mLocation;
-    private boolean mEnabled;
-    private TestLocationProviderThread mThread;
-
-    private class TestLocationProviderThread extends Thread {
-
-        private boolean mDone = false;
-
-        public TestLocationProviderThread() {
-            super("TestLocationProviderThread");
-        }
-
-        public void run() {            
-            // thread exits after disable() is called
-            synchronized (this) {
-                while (!mDone) {
-                    try {
-                        wait(LOCATION_INTERVAL);
-                    } catch (InterruptedException e) {
-                    }
-                    
-                    if (!mDone) {
-                        TestLocationProvider.this.updateLocation();
-                    }
-                }
-            }
-        }
-        
-        synchronized void setDone() {
-            mDone = true;
-            notify();
-        }
-    }
-
-    public TestLocationProvider(ILocationManager locationManager) {
-        mLocationManager = locationManager;
-        mLocation = new Location(PROVIDER_NAME);
-    }
-
-    public int getAccuracy() {
-        return Criteria.ACCURACY_COARSE;
-    }
-
-    public int getPowerRequirement() {
-        return Criteria.NO_REQUIREMENT;
-    }
-
-    public boolean hasMonetaryCost() {
-        return false;
-    }
-
-    public boolean requiresCell() {
-        return false;
-    }
-
-    public boolean requiresNetwork() {
-        return false;
-    }
-
-    public boolean requiresSatellite() {
-        return false;
-    }
-
-    public boolean supportsAltitude() {
-        return true;
-    }
-
-    public boolean supportsBearing() {
-        return true;
-    }
-
-    public boolean supportsSpeed() {
-        return true;
-    }
-
-    public synchronized void disable() {
-        mEnabled = false;
-        if (mThread != null) {
-            mThread.setDone();
-            try {
-                mThread.join();
-            } catch (InterruptedException e) {
-            }
-            mThread = null;
-        }
-    }
-
-    public synchronized void enable() {
-       mEnabled = true;
-        mThread = new TestLocationProviderThread();
-        mThread.start();
-    }
-
-    public boolean isEnabled() {
-        return mEnabled;
-    }
-
-    public int getStatus(Bundle extras) {
-        return STATUS;
-    }
-
-    public long getStatusUpdateTime() {
-        return 0;
-    }
-
-    public void enableLocationTracking(boolean enable) {
-    }
-
-    public void setMinTime(long minTime) {
-    }
-
-    public void updateNetworkState(int state, NetworkInfo info) {
-    }
-
-    public void updateLocation(Location location) {
-    }
-
-    public boolean sendExtraCommand(String command, Bundle extras) {
-        return false;
-    }
-
-    public void addListener(int uid) {
-    }
-
-    public void removeListener(int uid) {
-    }
-
-    private void updateLocation() {
-        long time = SystemClock.uptimeMillis();
-        long multiplier = (time/5000)%500000;
-        mLocation.setLatitude(LAT*multiplier);
-        mLocation.setLongitude(LON*multiplier);
-        mLocation.setAltitude(ALTITUDE);
-        mLocation.setSpeed(SPEED);
-        mLocation.setBearing(BEARING*multiplier);
-
-        Bundle extras = new Bundle();
-        extras.putInt("extraTest", 24);
-        mLocation.setExtras(extras);
-        mLocation.setTime(time);
-        try {
-            mLocationManager.reportLocation(mLocation);
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException calling updateLocation");
-        }
-    }
-
-}
diff --git a/test-runner/android/test/mock/MockPackageManager.java b/test-runner/android/test/mock/MockPackageManager.java
index cbe0253..c8339ed 100644
--- a/test-runner/android/test/mock/MockPackageManager.java
+++ b/test-runner/android/test/mock/MockPackageManager.java
@@ -30,6 +30,7 @@
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.content.pm.ProviderInfo;
@@ -443,7 +444,7 @@
      * @hide
      */
     @Override
-    public int recommendAppInstallLocation(ApplicationInfo appInfo, Uri packageURI) {
+    public int recommendAppInstallLocation(PackageParser.Package pkg) {
         throw new UnsupportedOperationException();
     }
 }
diff --git a/tests/AndroidTests/res/raw/install_loc_auto b/tests/AndroidTests/res/raw/install_loc_auto
new file mode 100644
index 0000000..60dda18
--- /dev/null
+++ b/tests/AndroidTests/res/raw/install_loc_auto
Binary files differ
diff --git a/tests/AndroidTests/res/raw/install_loc_internal b/tests/AndroidTests/res/raw/install_loc_internal
new file mode 100644
index 0000000..1bc33ca
--- /dev/null
+++ b/tests/AndroidTests/res/raw/install_loc_internal
Binary files differ
diff --git a/tests/AndroidTests/res/raw/install_loc_sdcard b/tests/AndroidTests/res/raw/install_loc_sdcard
new file mode 100644
index 0000000..6604e35
--- /dev/null
+++ b/tests/AndroidTests/res/raw/install_loc_sdcard
Binary files differ
diff --git a/tests/AndroidTests/res/raw/install_loc_unspecified b/tests/AndroidTests/res/raw/install_loc_unspecified
new file mode 100644
index 0000000..88bbace
--- /dev/null
+++ b/tests/AndroidTests/res/raw/install_loc_unspecified
Binary files differ
diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
index 3a4d38c..07bd489 100755
--- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -37,6 +37,7 @@
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageInstallObserver;
 import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageStats;
@@ -59,6 +60,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.StatFs;
+import android.provider.Settings;
 
 public class PackageManagerTests extends AndroidTestCase {
     private static final boolean localLOGV = true;
@@ -761,6 +763,170 @@
             outFile.delete();
         }
     }
+
+    public void invokeRecommendAppInstallLocation(String outFileName,
+            int fileResId, int expected) {
+        int origSetting = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.SET_INSTALL_LOCATION, 0);
+        try {
+            // Make sure the set install location setting is diabled.
+            Settings.System.putInt(mContext.getContentResolver(),
+                    Settings.System.SET_INSTALL_LOCATION, 0);
+            File filesDir = mContext.getFilesDir();
+            File outFile = new File(filesDir, outFileName);
+            Uri packageURI = getInstallablePackage(fileResId, outFile);
+            PackageParser.Package pkg = parsePackage(packageURI);
+            assertNotNull(pkg);
+            int installLoc = getPm().recommendAppInstallLocation(pkg);
+            Log.i(TAG, "expected=" + expected +", installLoc="+installLoc);
+            // Atleast one of the specified expected flags should be set.
+            boolean onFlash = (installLoc &
+                    PackageManager.INSTALL_ON_INTERNAL_FLASH) != 0;
+            boolean onSd = (installLoc &
+                    PackageManager.INSTALL_ON_SDCARD) != 0;
+            boolean expOnFlash = (expected &
+                    PackageManager.INSTALL_ON_INTERNAL_FLASH) != 0;
+            boolean expOnSd = (expected &
+                    PackageManager.INSTALL_ON_SDCARD) != 0;
+            assertTrue(expOnFlash == onFlash || expOnSd == onSd);
+        } finally {
+            // Restore original setting
+            Settings.System.putInt(mContext.getContentResolver(),
+                    Settings.System.SET_INSTALL_LOCATION, origSetting);
+        }
+    }
+
+    /*
+     * Tests if an apk can be installed on internal flash by
+     * explicitly specifying in its manifest.
+     */
+    public void testInstallLocationInternal() {
+        invokeRecommendAppInstallLocation("install.apk",
+                R.raw.install_loc_internal, PackageManager.INSTALL_ON_INTERNAL_FLASH);
+    }
+
+    /*
+     * Tests if an apk can be installed on internal flash by
+     * explicitly specifying in its manifest and filling up
+     * internal flash. Should fail to install.
+     * TODO
+     */
+    public void xxxtestInstallLocationInternalFail() {
+    }
+
+    /*
+     * Tests if an apk can be installed on sdcard by
+     * explicitly specifying in its manifest.
+     */
+    public void testInstallLocationSdcard() {
+        // TODO No guarantee this will be on sdcard.
+        invokeRecommendAppInstallLocation("install.apk",
+                R.raw.install_loc_sdcard, PackageManager.INSTALL_ON_SDCARD
+                | PackageManager.INSTALL_ON_INTERNAL_FLASH);
+    }
+
+    /*
+     * Tests if an apk can be installed on sdcard by
+     * explicitly specifying in its manifest and filling up
+     * the sdcard. Should result in install failure
+     * TODO
+     */
+    public void xxxtestInstallLocationSdcardFail() {
+    }
+
+    /*
+     * Tests if an apk can be installed by specifying
+     * auto for install location
+     */
+    public void xxxtestInstallLocationAutoInternal() {
+        // TODO clear and make room on internal flash
+        invokeRecommendAppInstallLocation("install.apk",
+                R.raw.install_loc_auto, PackageManager.INSTALL_ON_INTERNAL_FLASH);
+    }
+
+    /*
+     * Tests if an apk can be installed by specifying
+     * auto for install location
+     */
+    public void testInstallLocationAutoSdcard() {
+        // TODO clear and make room on sdcard.
+        // Fill up internal
+        invokeRecommendAppInstallLocation("install.apk",
+                R.raw.install_loc_auto, PackageManager.INSTALL_ON_SDCARD |
+                PackageManager.INSTALL_ON_INTERNAL_FLASH);
+    }
+
+    /*
+     * Tests if an apk can be installed by specifying
+     * auto for install location
+     * fill up both internal and sdcard
+     * TODO
+     */
+    public void xxxtestInstallLocationAutoFail() {
+    }
+    /*
+     * Tests where an apk gets installed based
+     * on a not specifying anything in manifest.
+     */
+    public void testInstallLocationUnspecifiedInt() {
+        invokeRecommendAppInstallLocation("install.apk",
+                R.raw.install_loc_unspecified, PackageManager.INSTALL_ON_INTERNAL_FLASH);
+    }
+
+    public void xxxtestInstallLocationUnspecifiedStorage() {
+        // TODO Fill up internal storage
+        invokeRecommendAppInstallLocation("install.apk",
+                R.raw.install_loc_unspecified, PackageManager.INSTALL_ON_SDCARD
+                | PackageManager.INSTALL_ON_INTERNAL_FLASH);
+    }
+
+    /*
+     * Tests where an apk gets installed by expcitly setting
+     * the user specified install location
+     */
+    public void testInstallLocationUserSpecifiedInternal() {
+        // Enable user setting
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SET_INSTALL_LOCATION, 1);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.DEFAULT_INSTALL_LOCATION, 1);
+        invokeRecommendAppInstallLocation("install.apk",
+                R.raw.install_loc_unspecified, PackageManager.INSTALL_ON_INTERNAL_FLASH);
+    }
+
+    /*
+     * Tests where an apk gets installed by expcitly setting
+     * the user specified install location
+     */
+    public void testInstallLocationUserSpecifiedSdcard() {
+        // Enable user setting
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SET_INSTALL_LOCATION, 1);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.DEFAULT_INSTALL_LOCATION, 2);
+        int i = Settings.System.getInt(mContext.getContentResolver(),
+                Settings.System.DEFAULT_INSTALL_LOCATION, 0);
+        invokeRecommendAppInstallLocation("install.apk",
+                R.raw.install_loc_unspecified, PackageManager.INSTALL_ON_SDCARD);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SET_INSTALL_LOCATION, 0);
+    }
+    /*
+     * Tests where an apk gets installed by expcitly setting
+     * the user specified install location
+     */
+    public void testInstallLocationUserSpecifiedAuto() {
+        // Enable user setting
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SET_INSTALL_LOCATION, 1);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.DEFAULT_INSTALL_LOCATION, 0);
+        invokeRecommendAppInstallLocation("install.apk",
+                R.raw.install_loc_unspecified, PackageManager.INSTALL_ON_INTERNAL_FLASH);
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SET_INSTALL_LOCATION, 0);
+    }
+
     /*
      * TODO's
      * check version numbers for upgrades