Merge "Fix race with Asset destruction and printing allocation stats"
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index aba53dc..718f141 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -84,6 +84,7 @@
 
 public final class Pm {
     private static final String TAG = "Pm";
+    private static final String STDIN_PATH = "-";
 
     IPackageManager mPm;
     IPackageInstaller mInstaller;
@@ -403,7 +404,7 @@
     private int runInstall() throws RemoteException {
         final InstallParams params = makeInstallParams();
         final String inPath = nextArg();
-        if (params.sessionParams.sizeBytes < 0 && inPath != null) {
+        if (params.sessionParams.sizeBytes == -1 && !STDIN_PATH.equals(inPath)) {
             File file = new File(inPath);
             if (file.isFile()) {
                 try {
@@ -413,9 +414,12 @@
                             PackageHelper.calculateInstalledSize(pkgLite, false,
                             params.sessionParams.abiOverride));
                 } catch (PackageParserException | IOException e) {
-                    System.err.println("Error: Failed to parse APK file : " + e);
+                    System.err.println("Error: Failed to parse APK file: " + e);
                     return 1;
                 }
+            } else {
+                System.err.println("Error: Can't open non-file: " + inPath);
+                return 1;
             }
         }
 
@@ -423,7 +427,7 @@
                 params.installerPackageName, params.userId);
 
         try {
-            if (inPath == null && params.sessionParams.sizeBytes == 0) {
+            if (inPath == null && params.sessionParams.sizeBytes == -1) {
                 System.err.println("Error: must either specify a package size or an APK file");
                 return 1;
             }
@@ -540,7 +544,11 @@
                     }
                     break;
                 case "-S":
-                    sessionParams.setSize(Long.parseLong(nextOptionData()));
+                    final long sizeBytes = Long.parseLong(nextOptionData());
+                    if (sizeBytes <= 0) {
+                        throw new IllegalArgumentException("Size must be positive");
+                    }
+                    sessionParams.setSize(sizeBytes);
                     break;
                 case "--abi":
                     sessionParams.abiOverride = checkAbiArgument(nextOptionData());
@@ -585,7 +593,7 @@
 
     private int doWriteSession(int sessionId, String inPath, long sizeBytes, String splitName,
             boolean logSuccess) throws RemoteException {
-        if ("-".equals(inPath)) {
+        if (STDIN_PATH.equals(inPath)) {
             inPath = null;
         } else if (inPath != null) {
             final File file = new File(inPath);
diff --git a/core/java/android/hardware/usb/UsbAccessory.java b/core/java/android/hardware/usb/UsbAccessory.java
index 2f9178c..4aeb40c 100644
--- a/core/java/android/hardware/usb/UsbAccessory.java
+++ b/core/java/android/hardware/usb/UsbAccessory.java
@@ -16,8 +16,11 @@
 
 package android.hardware.usb;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import com.android.internal.util.Preconditions;
 
 /**
  * A class representing a USB accessory, which is an external hardware component
@@ -46,12 +49,12 @@
 
     private static final String TAG = "UsbAccessory";
 
-    private final String mManufacturer;
-    private final String mModel;
-    private final String mDescription;
-    private final String mVersion;
-    private final String mUri;
-    private final String mSerial;
+    private final @NonNull String mManufacturer;
+    private final @NonNull String mModel;
+    private final @Nullable String mDescription;
+    private final @Nullable String mVersion;
+    private final @Nullable String mUri;
+    private final @Nullable String mSerial;
 
     /** @hide */
     public static final int MANUFACTURER_STRING = 0;
@@ -70,10 +73,11 @@
      * UsbAccessory should only be instantiated by UsbService implementation
      * @hide
      */
-    public UsbAccessory(String manufacturer, String model, String description,
-            String version, String uri, String serial) {
-        mManufacturer = manufacturer;
-        mModel = model;
+    public UsbAccessory(@NonNull String manufacturer, @NonNull String model,
+            @Nullable String description, @Nullable String version, @Nullable String uri,
+            @Nullable String serial) {
+        mManufacturer = Preconditions.checkNotNull(manufacturer);
+        mModel = Preconditions.checkNotNull(model);
         mDescription = description;
         mVersion = version;
         mUri = uri;
@@ -85,12 +89,8 @@
      * @hide
      */
     public UsbAccessory(String[] strings) {
-        mManufacturer = strings[MANUFACTURER_STRING];
-        mModel = strings[MODEL_STRING];
-        mDescription = strings[DESCRIPTION_STRING];
-        mVersion = strings[VERSION_STRING];
-        mUri = strings[URI_STRING];
-        mSerial = strings[SERIAL_STRING];
+        this(strings[MANUFACTURER_STRING], strings[MODEL_STRING], strings[DESCRIPTION_STRING],
+                strings[VERSION_STRING], strings[URI_STRING], strings[SERIAL_STRING]);
     }
 
     /**
@@ -98,7 +98,7 @@
      *
      * @return the accessory manufacturer
      */
-    public String getManufacturer() {
+    public @NonNull String getManufacturer() {
         return mManufacturer;
     }
 
@@ -107,25 +107,25 @@
      *
      * @return the accessory model
      */
-    public String getModel() {
+    public @NonNull String getModel() {
         return mModel;
     }
 
     /**
      * Returns a user visible description of the accessory.
      *
-     * @return the accessory description
+     * @return the accessory description, or {@code null} if not set
      */
-    public String getDescription() {
+    public @Nullable String getDescription() {
         return mDescription;
     }
 
     /**
      * Returns the version of the accessory.
      *
-     * @return the accessory version
+     * @return the accessory version, or {@code null} if not set
      */
-    public String getVersion() {
+    public @Nullable String getVersion() {
         return mVersion;
     }
 
@@ -134,9 +134,9 @@
      * This is an optional URI that might show information about the accessory
      * or provide the option to download an application for the accessory
      *
-     * @return the accessory URI
+     * @return the accessory URI, or {@code null} if not set
      */
-    public String getUri() {
+    public @Nullable String getUri() {
         return mUri;
     }
 
@@ -145,9 +145,9 @@
      * This is an optional serial number that can be used to differentiate
      * between individual accessories of the same model and manufacturer
      *
-     * @return the unique serial number
+     * @return the unique serial number, or {@code null} if not set
      */
-    public String getSerial() {
+    public @Nullable String getSerial() {
         return mSerial;
     }
 
@@ -172,12 +172,10 @@
 
     @Override
     public int hashCode() {
-        return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
-                (mModel == null ? 0 : mModel.hashCode()) ^
+        return mManufacturer.hashCode() ^ mModel.hashCode() ^
                 (mDescription == null ? 0 : mDescription.hashCode()) ^
                 (mVersion == null ? 0 : mVersion.hashCode()) ^
-                (mUri == null ? 0 : mUri.hashCode()) ^
-                (mSerial == null ? 0 : mSerial.hashCode()));
+                (mUri == null ? 0 : mUri.hashCode()) ^ (mSerial == null ? 0 : mSerial.hashCode());
     }
 
     @Override
diff --git a/core/java/android/hardware/usb/UsbConfiguration.java b/core/java/android/hardware/usb/UsbConfiguration.java
index da5c128..a171570 100644
--- a/core/java/android/hardware/usb/UsbConfiguration.java
+++ b/core/java/android/hardware/usb/UsbConfiguration.java
@@ -16,8 +16,11 @@
 
 package android.hardware.usb;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import com.android.internal.util.Preconditions;
 
 /**
  * A class representing a configuration on a {@link UsbDevice}.
@@ -35,20 +38,20 @@
 public class UsbConfiguration implements Parcelable {
 
     private final int mId;
-    private final String mName;
+    private final @Nullable String mName;
     private final int mAttributes;
     private final int mMaxPower;
-    private Parcelable[] mInterfaces;
+
+    /** All interfaces for this config, only null during creation */
+    private @Nullable Parcelable[] mInterfaces;
 
     /**
      * Mask for "self-powered" bit in the configuration's attributes.
-     * @see #getAttributes
      */
     private static final int ATTR_SELF_POWERED = 1 << 6;
 
     /**
      * Mask for "remote wakeup" bit in the configuration's attributes.
-     * @see #getAttributes
      */
     private static final int ATTR_REMOTE_WAKEUP = 1 << 5;
 
@@ -56,7 +59,7 @@
      * UsbConfiguration should only be instantiated by UsbService implementation
      * @hide
      */
-    public UsbConfiguration(int id, String name, int attributes, int maxPower) {
+    public UsbConfiguration(int id, @Nullable String name, int attributes, int maxPower) {
         mId = id;
         mName = name;
         mAttributes = attributes;
@@ -76,9 +79,9 @@
     /**
      * Returns the configuration's name.
      *
-     * @return the configuration's name
+     * @return the configuration's name, or {@code null} if the property could not be read
      */
-    public String getName() {
+    public @Nullable String getName() {
         return mName;
     }
 
@@ -125,7 +128,7 @@
      *
      * @return the interface
      */
-    public UsbInterface getInterface(int index) {
+    public @NonNull UsbInterface getInterface(int index) {
         return (UsbInterface)mInterfaces[index];
     }
 
@@ -133,8 +136,8 @@
      * Only used by UsbService implementation
      * @hide
      */
-    public void setInterfaces(Parcelable[] interfaces) {
-        mInterfaces = interfaces;
+    public void setInterfaces(@NonNull Parcelable[] interfaces) {
+        mInterfaces = Preconditions.checkArrayElementsNotNull(interfaces, "interfaces");
     }
 
     @Override
diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java
index 410d550..425a89d 100644
--- a/core/java/android/hardware/usb/UsbDevice.java
+++ b/core/java/android/hardware/usb/UsbDevice.java
@@ -16,8 +16,11 @@
 
 package android.hardware.usb;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import com.android.internal.util.Preconditions;
 
 /**
  * This class represents a USB device attached to the android device with the android device
@@ -42,29 +45,31 @@
     private static final String TAG = "UsbDevice";
     private static final boolean DEBUG = false;
 
-    private final String mName;
-    private final String mManufacturerName;
-    private final String mProductName;
-    private final String mVersion;
-    private final String mSerialNumber;
+    private final @NonNull String mName;
+    private final @Nullable String mManufacturerName;
+    private final @Nullable String mProductName;
+    private final @NonNull String mVersion;
+    private final @Nullable String mSerialNumber;
     private final int mVendorId;
     private final int mProductId;
     private final int mClass;
     private final int mSubclass;
     private final int mProtocol;
-    private Parcelable[] mConfigurations;
 
-    // list of all interfaces on the device
-    private UsbInterface[] mInterfaces;
+    /** All configurations for this device, only null during creation */
+    private @Nullable Parcelable[] mConfigurations;
+
+    /** All interfaces on the device. Initialized on first call to getInterfaceList */
+    private @Nullable UsbInterface[] mInterfaces;
 
     /**
      * UsbDevice should only be instantiated by UsbService implementation
      * @hide
      */
-    public UsbDevice(String name, int vendorId, int productId,
-            int Class, int subClass, int protocol,
-            String manufacturerName, String productName, String version, String serialNumber) {
-        mName = name;
+    public UsbDevice(@NonNull String name, int vendorId, int productId, int Class, int subClass,
+            int protocol, @Nullable String manufacturerName, @Nullable String productName,
+            @NonNull String version, @Nullable String serialNumber) {
+        mName = Preconditions.checkNotNull(name);
         mVendorId = vendorId;
         mProductId = productId;
         mClass = Class;
@@ -72,7 +77,7 @@
         mProtocol = protocol;
         mManufacturerName = manufacturerName;
         mProductName = productName;
-        mVersion = version;
+        mVersion = Preconditions.checkStringNotEmpty(version);
         mSerialNumber = serialNumber;
     }
 
@@ -83,25 +88,25 @@
      *
      * @return the device name
      */
-    public String getDeviceName() {
+    public @NonNull String getDeviceName() {
         return mName;
     }
 
     /**
      * Returns the manufacturer name of the device.
      *
-     * @return the manufacturer name
+     * @return the manufacturer name, or {@code null} if the property could not be read
      */
-    public String getManufacturerName() {
+    public @Nullable String getManufacturerName() {
         return mManufacturerName;
     }
 
     /**
      * Returns the product name of the device.
      *
-     * @return the product name
+     * @return the product name, or {@code null} if the property could not be read
      */
-    public String getProductName() {
+    public @Nullable String getProductName() {
         return mProductName;
     }
 
@@ -110,16 +115,16 @@
      *
      * @return the device version
      */
-    public String getVersion() {
+    public @NonNull String getVersion() {
         return mVersion;
     }
 
     /**
      * Returns the serial number of the device.
      *
-     * @return the serial number name
+     * @return the serial number name, or {@code null} if the property could not be read
      */
-    public String getSerialNumber() {
+    public @Nullable String getSerialNumber() {
         return mSerialNumber;
     }
 
@@ -195,11 +200,11 @@
      *
      * @return the configuration
      */
-    public UsbConfiguration getConfiguration(int index) {
+    public @NonNull UsbConfiguration getConfiguration(int index) {
         return (UsbConfiguration)mConfigurations[index];
     }
 
-    private UsbInterface[] getInterfaceList() {
+    private @Nullable UsbInterface[] getInterfaceList() {
         if (mInterfaces == null) {
             int configurationCount = mConfigurations.length;
             int interfaceCount = 0;
@@ -240,7 +245,7 @@
      *
      * @return the interface
      */
-    public UsbInterface getInterface(int index) {
+    public @NonNull UsbInterface getInterface(int index) {
         return getInterfaceList()[index];
     }
 
@@ -248,8 +253,8 @@
      * Only used by UsbService implementation
      * @hide
      */
-    public void setConfigurations(Parcelable[] configuration) {
-        mConfigurations = configuration;
+    public void setConfigurations(@NonNull Parcelable[] configuration) {
+        mConfigurations = Preconditions.checkArrayElementsNotNull(configuration, "configuration");
     }
 
     @Override
diff --git a/core/java/android/hardware/usb/UsbInterface.java b/core/java/android/hardware/usb/UsbInterface.java
index de01a88..4b5278c 100644
--- a/core/java/android/hardware/usb/UsbInterface.java
+++ b/core/java/android/hardware/usb/UsbInterface.java
@@ -16,8 +16,10 @@
 
 package android.hardware.usb;
 
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import com.android.internal.util.Preconditions;
 
 /**
  * A class representing an interface on a {@link UsbDevice}.
@@ -36,17 +38,19 @@
 
     private final int mId;
     private final int mAlternateSetting;
-    private final String mName;
+    private @Nullable final String mName;
     private final int mClass;
     private final int mSubclass;
     private final int mProtocol;
+
+    /** All endpoints of this interface, only null during creation */
     private Parcelable[] mEndpoints;
 
     /**
      * UsbInterface should only be instantiated by UsbService implementation
      * @hide
      */
-    public UsbInterface(int id, int alternateSetting, String name,
+    public UsbInterface(int id, int alternateSetting, @Nullable String name,
             int Class, int subClass, int protocol) {
         mId = id;
         mAlternateSetting = alternateSetting;
@@ -83,9 +87,9 @@
     /**
      * Returns the interface's name.
      *
-     * @return the interface's name
+     * @return the interface's name, or {@code null} if the property could not be read
      */
-    public String getName() {
+    public @Nullable String getName() {
         return mName;
     }
 
@@ -140,7 +144,7 @@
      * @hide
      */
     public void setEndpoints(Parcelable[] endpoints) {
-        mEndpoints = endpoints;
+        mEndpoints = Preconditions.checkArrayElementsNotNull(endpoints, "endpoints");
     }
 
     @Override
diff --git a/core/res/res/layout-notround-watch/alert_dialog_title_material.xml b/core/res/res/layout-notround-watch/alert_dialog_title_material.xml
index 307c6db..0ab56f9 100644
--- a/core/res/res/layout-notround-watch/alert_dialog_title_material.xml
+++ b/core/res/res/layout-notround-watch/alert_dialog_title_material.xml
@@ -22,6 +22,7 @@
         android:gravity="top|center_horizontal"
         android:minHeight="@dimen/alert_dialog_title_height">
     <ImageView android:id="@+id/icon"
+            android:adjustViewBounds="true"
             android:maxHeight="24dp"
             android:maxWidth="24dp"
             android:layout_marginTop="8dp"
diff --git a/core/res/res/layout-round-watch/alert_dialog_title_material.xml b/core/res/res/layout-round-watch/alert_dialog_title_material.xml
index 7e71e41..e543c9b 100644
--- a/core/res/res/layout-round-watch/alert_dialog_title_material.xml
+++ b/core/res/res/layout-round-watch/alert_dialog_title_material.xml
@@ -21,6 +21,7 @@
         android:gravity="top|center_horizontal"
         android:minHeight="@dimen/alert_dialog_title_height">
     <ImageView android:id="@+id/icon"
+            android:adjustViewBounds="true"
             android:maxHeight="24dp"
             android:maxWidth="24dp"
             android:layout_marginTop="12dp"
diff --git a/core/res/res/values-watch/styles_material.xml b/core/res/res/values-watch/styles_material.xml
index 8a080d9c..af4207e 100644
--- a/core/res/res/values-watch/styles_material.xml
+++ b/core/res/res/values-watch/styles_material.xml
@@ -95,7 +95,7 @@
     </style>
 
     <style name="DialogWindowTitle.Material">
-        <item name="maxLines">3</item>
+        <item name="maxLines">@empty</item>
         <item name="scrollHorizontally">false</item>
         <item name="textAppearance">@style/TextAppearance.Material.DialogWindowTitle</item>
         <item name="gravity">@integer/config_dialogTextGravity</item>
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 17ad0e3..dbb66d9 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -591,7 +591,7 @@
 }
 
 static bool hasMergeableClip(const BakedOpState& state) {
-    return state.computedState.clipState
+    return !state.computedState.clipState
             || state.computedState.clipState->mode == ClipMode::Rectangle;
 }
 
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index 18c35b3..33ee108 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -22,6 +22,8 @@
 
 #include "Typeface.h"
 
+#include <pthread.h>
+
 #include "MinikinSkia.h"
 #include "SkTypeface.h"
 #include "SkPaint.h"
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index 259686b..347d4a5 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -477,6 +477,35 @@
     EXPECT_EQ(4, renderer.getIndex());
 }
 
+RENDERTHREAD_TEST(FrameBuilder, regionClipStopsMerge) {
+    class RegionClipStopsMergeTestRenderer : public TestRendererBase {
+    public:
+        void onTextOp(const TextOp& op, const BakedOpState& state) override { mIndex++; }
+    };
+    auto node = TestUtils::createNode<RecordingCanvas>(0, 0, 400, 400,
+            [](RenderProperties& props, RecordingCanvas& canvas) {
+        SkPath path;
+        path.addCircle(200, 200, 200, SkPath::kCW_Direction);
+        canvas.save(SaveFlags::MatrixClip);
+        canvas.clipPath(&path, SkRegion::kIntersect_Op);
+        SkPaint paint;
+        paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+        paint.setAntiAlias(true);
+        paint.setTextSize(50);
+        TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
+        TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 200);
+        canvas.restore();
+    });
+
+    FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
+            sLightGeometry, Caches::getInstance());
+    frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
+    RegionClipStopsMergeTestRenderer renderer;
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(2, renderer.getIndex());
+}
+
 RENDERTHREAD_TEST(FrameBuilder, textMerging) {
     class TextMergingTestRenderer : public TestRendererBase {
     public:
diff --git a/media/jni/audioeffect/Android.mk b/media/jni/audioeffect/Android.mk
index 5c22c9b..91dd8a5 100644
--- a/media/jni/audioeffect/Android.mk
+++ b/media/jni/audioeffect/Android.mk
@@ -13,9 +13,6 @@
     libnativehelper \
     libmedia
 
-LOCAL_C_INCLUDES := \
-    $(call include-path-for, audio-effects)
-
 LOCAL_MODULE:= libaudioeffect_jni
 
 LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
diff --git a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
index a2e9e94..e948cf7 100644
--- a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
+++ b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
@@ -23,6 +23,7 @@
 
 #include <linux/fuse.h>
 #include <sys/stat.h>
+#include <sys/uio.h>
 
 #include <map>
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index a24217e..834d343 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -75,6 +75,11 @@
 import java.util.concurrent.TimeUnit;
 
 class PackageManagerShellCommand extends ShellCommand {
+    /** Path for streaming APK content */
+    private static final String STDIN_PATH = "-";
+    /** Whether or not APK content must be streamed from stdin */
+    private static final boolean FORCE_STREAM_INSTALL = true;
+
     final IPackageManager mInterface;
     final private WeakHashMap<String, Resources> mResourceCache =
             new WeakHashMap<String, Resources>();
@@ -139,30 +144,45 @@
         return -1;
     }
 
-    private int runInstall() throws RemoteException {
+    private void setParamsSize(InstallParams params, String inPath) {
+        // If we're forced to stream the package, the params size
+        // must be set via command-line argument. There's nothing
+        // to do here.
+        if (FORCE_STREAM_INSTALL) {
+            return;
+        }
         final PrintWriter pw = getOutPrintWriter();
-        final InstallParams params = makeInstallParams();
-        final String inPath = getNextArg();
-        if (params.sessionParams.sizeBytes < 0 && inPath != null) {
+        if (params.sessionParams.sizeBytes == -1 && !STDIN_PATH.equals(inPath)) {
             File file = new File(inPath);
             if (file.isFile()) {
                 try {
                     ApkLite baseApk = PackageParser.parseApkLite(file, 0);
                     PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null);
-                    params.sessionParams.setSize(
-                            PackageHelper.calculateInstalledSize(pkgLite,false, params.sessionParams.abiOverride));
+                    params.sessionParams.setSize(PackageHelper.calculateInstalledSize(
+                            pkgLite, false, params.sessionParams.abiOverride));
                 } catch (PackageParserException | IOException e) {
-                    pw.println("Error: Failed to parse APK file : " + e);
-                    return 1;
+                    pw.println("Error: Failed to parse APK file: " + file);
+                    throw new IllegalArgumentException(
+                            "Error: Failed to parse APK file: " + file, e);
                 }
+            } else {
+                pw.println("Error: Can't open non-file: " + inPath);
+                throw new IllegalArgumentException("Error: Can't open non-file: " + inPath);
             }
         }
+    }
 
+    private int runInstall() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        final InstallParams params = makeInstallParams();
+        final String inPath = getNextArg();
+
+        setParamsSize(params, inPath);
         final int sessionId = doCreateSession(params.sessionParams,
                 params.installerPackageName, params.userId);
         boolean abandonSession = true;
         try {
-            if (inPath == null && params.sessionParams.sizeBytes == 0) {
+            if (inPath == null && params.sessionParams.sizeBytes == -1) {
                 pw.println("Error: must either specify a package size or an APK file");
                 return 1;
             }
@@ -1075,7 +1095,11 @@
                     }
                     break;
                 case "-S":
-                    sessionParams.setSize(Long.parseLong(getNextArg()));
+                    final long sizeBytes = Long.parseLong(getNextArg());
+                    if (sizeBytes <= 0) {
+                        throw new IllegalArgumentException("Size must be positive");
+                    }
+                    sessionParams.setSize(sizeBytes);
                     break;
                 case "--abi":
                     sessionParams.abiOverride = checkAbiArgument(getNextArg());
@@ -1180,15 +1204,22 @@
     private int doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName,
             boolean logSuccess) throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
+        if (FORCE_STREAM_INSTALL && inPath != null && !STDIN_PATH.equals(inPath)) {
+            pw.println("Error: APK content must be streamed");
+            return 1;
+        }
+        if (STDIN_PATH.equals(inPath)) {
+            inPath = null;
+        } else if (inPath != null) {
+            final File file = new File(inPath);
+            if (file.isFile()) {
+                sizeBytes = file.length();
+            }
+        }
         if (sizeBytes <= 0) {
             pw.println("Error: must specify a APK size");
             return 1;
         }
-        if (inPath != null && !"-".equals(inPath)) {
-            pw.println("Error: APK content must be streamed");
-            return 1;
-        }
-        inPath = null;
 
         final SessionInfo info = mInterface.getPackageInstaller().getSessionInfo(sessionId);
 
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index b789e17..af78c05 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -16,6 +16,7 @@
 
 package com.android.server.usb;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
index 0dcd1521..610f9bc 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -29,6 +29,8 @@
 
 import java.util.Locale;
 import java.util.UUID;
+import java.util.List;
+import java.util.ArrayList;
 
 /**
  * Helper to manage the database of the sound models that have been registered on the device.
@@ -40,7 +42,7 @@
     static final boolean DBG = false;
 
     private static final String NAME = "sound_model.db";
-    private static final int VERSION = 5;
+    private static final int VERSION = 6;
 
     public static interface SoundModelContract {
         public static final String TABLE = "sound_model";
@@ -58,15 +60,19 @@
     // Table Create Statement
     private static final String CREATE_TABLE_SOUND_MODEL = "CREATE TABLE "
             + SoundModelContract.TABLE + "("
-            + SoundModelContract.KEY_MODEL_UUID + " TEXT PRIMARY KEY,"
-            + SoundModelContract.KEY_VENDOR_UUID + " TEXT, "
+            + SoundModelContract.KEY_MODEL_UUID + " TEXT,"
+            + SoundModelContract.KEY_VENDOR_UUID + " TEXT,"
             + SoundModelContract.KEY_KEYPHRASE_ID + " INTEGER,"
             + SoundModelContract.KEY_TYPE + " INTEGER,"
             + SoundModelContract.KEY_DATA + " BLOB,"
             + SoundModelContract.KEY_RECOGNITION_MODES + " INTEGER,"
             + SoundModelContract.KEY_LOCALE + " TEXT,"
             + SoundModelContract.KEY_HINT_TEXT + " TEXT,"
-            + SoundModelContract.KEY_USERS + " TEXT" + ")";
+            + SoundModelContract.KEY_USERS + " TEXT,"
+            + "PRIMARY KEY (" + SoundModelContract.KEY_KEYPHRASE_ID + ","
+                              + SoundModelContract.KEY_LOCALE + ","
+                              + SoundModelContract.KEY_USERS + ")"
+            + ")";
 
     public DatabaseHelper(Context context) {
         super(context, NAME, null, VERSION);
@@ -93,6 +99,44 @@
                 oldVersion++;
             }
         }
+        if (oldVersion == 5) {
+            // We need to enforce the new primary key constraint that the
+            // keyphrase id, locale, and users are unique. We have to first pull
+            // everything out of the database, remove duplicates, create the new
+            // table, then push everything back in.
+            String selectQuery = "SELECT * FROM " + SoundModelContract.TABLE;
+            Cursor c = db.rawQuery(selectQuery, null);
+            List<SoundModelRecord> old_records = new ArrayList<SoundModelRecord>();
+            try {
+                if (c.moveToFirst()) {
+                    do {
+                        try {
+                            old_records.add(new SoundModelRecord(5, c));
+                        } catch (Exception e) {
+                            Slog.e(TAG, "Failed to extract V5 record", e);
+                        }
+                    } while (c.moveToNext());
+                }
+            } finally {
+                c.close();
+            }
+            db.execSQL("DROP TABLE IF EXISTS " + SoundModelContract.TABLE);
+            onCreate(db);
+            for (SoundModelRecord record : old_records) {
+                if (!record.violatesV6PrimaryKeyConstraint(old_records)) {
+                    try {
+                        long return_value = record.writeToDatabase(6, db);
+                        if (return_value == -1) {
+                            Slog.e(TAG, "Database write failed " + record.modelUuid + ": "
+                                    + return_value);
+                        }
+                    } catch (Exception e) {
+                        Slog.e(TAG, "Failed to update V6 record " + record.modelUuid, e);
+                    }
+                }
+            }
+            oldVersion++;
+        }
     }
 
     /**
@@ -279,4 +323,73 @@
         }
         return users;
     }
+
+    private static class SoundModelRecord {
+        public final String modelUuid;
+        public final String vendorUuid;
+        public final int keyphraseId;
+        public final int type;
+        public final byte[] data;
+        public final int recognitionModes;
+        public final String locale;
+        public final String hintText;
+        public final String users;
+
+        public SoundModelRecord(int version, Cursor c) {
+            modelUuid = c.getString(c.getColumnIndex(SoundModelContract.KEY_MODEL_UUID));
+            if (version >= 5) {
+                vendorUuid = c.getString(c.getColumnIndex(SoundModelContract.KEY_VENDOR_UUID));
+            } else {
+                vendorUuid = null;
+            }
+            keyphraseId = c.getInt(c.getColumnIndex(SoundModelContract.KEY_KEYPHRASE_ID));
+            type = c.getInt(c.getColumnIndex(SoundModelContract.KEY_TYPE));
+            data = c.getBlob(c.getColumnIndex(SoundModelContract.KEY_DATA));
+            recognitionModes = c.getInt(c.getColumnIndex(SoundModelContract.KEY_RECOGNITION_MODES));
+            locale = c.getString(c.getColumnIndex(SoundModelContract.KEY_LOCALE));
+            hintText = c.getString(c.getColumnIndex(SoundModelContract.KEY_HINT_TEXT));
+            users = c.getString(c.getColumnIndex(SoundModelContract.KEY_USERS));
+        }
+
+        // Check to see if this record conflicts with some other record in the list of records.
+        public boolean violatesV6PrimaryKeyConstraint(List<SoundModelRecord> records) {
+            for (SoundModelRecord record : records) {
+                if (this == record) {
+                    continue;
+                }
+                if (keyphraseId == record.keyphraseId
+                        && stringComparisonHelper(locale, record.locale)
+                        && stringComparisonHelper(users, record.users)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public long writeToDatabase(int version, SQLiteDatabase db) {
+            ContentValues values = new ContentValues();
+            values.put(SoundModelContract.KEY_MODEL_UUID, modelUuid);
+            if (version >= 5) {
+                values.put(SoundModelContract.KEY_VENDOR_UUID, vendorUuid);
+            }
+            values.put(SoundModelContract.KEY_KEYPHRASE_ID, keyphraseId);
+            values.put(SoundModelContract.KEY_TYPE, type);
+            values.put(SoundModelContract.KEY_DATA, data);
+            values.put(SoundModelContract.KEY_RECOGNITION_MODES, recognitionModes);
+            values.put(SoundModelContract.KEY_LOCALE, locale);
+            values.put(SoundModelContract.KEY_HINT_TEXT, hintText);
+            values.put(SoundModelContract.KEY_USERS, users);
+
+            return db.insertWithOnConflict(
+                       SoundModelContract.TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE);
+        }
+
+        // Helper for checking string equality - including the case when they are null.
+        static private boolean stringComparisonHelper(String a, String b) {
+          if (a != null) {
+            return a.equals(b);
+          }
+          return a == b;
+        }
+    }
 }
diff --git a/tools/aapt/ZipEntry.cpp b/tools/aapt/ZipEntry.cpp
index 54a8e9c..5339285 100644
--- a/tools/aapt/ZipEntry.cpp
+++ b/tools/aapt/ZipEntry.cpp
@@ -23,9 +23,10 @@
 #include "ZipEntry.h"
 #include <utils/Log.h>
 
+#include <assert.h>
 #include <stdio.h>
 #include <string.h>
-#include <assert.h>
+#include <time.h>
 
 using namespace android;