Framework-side DropBox service that maintains a size-bounded
queue of data chunks (sort of a blob-oriented logcat).

In the future, this will be coupled with a revised checkin service that
would actually upload this data.
diff --git a/core/java/android/os/DropBoxEntry.aidl b/core/java/android/os/DropBoxEntry.aidl
new file mode 100644
index 0000000..225eee1
--- /dev/null
+++ b/core/java/android/os/DropBoxEntry.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2009 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.os;
+
+parcelable DropBoxEntry;
diff --git a/core/java/android/os/DropBoxEntry.java b/core/java/android/os/DropBoxEntry.java
new file mode 100644
index 0000000..e3816a8
--- /dev/null
+++ b/core/java/android/os/DropBoxEntry.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2009 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.os;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.zip.GZIPInputStream;
+
+/**
+ * A single entry retrieved from an {@link IDropBox} implementation.
+ * This may include a reference to a stream, so you must call
+ * {@link #close()} when you are done using it.
+ *
+ * {@pending}
+ */
+public class DropBoxEntry implements Parcelable {
+    private final String mTag;
+    private final long mTimeMillis;
+
+    private final String mText;
+    private final ParcelFileDescriptor mFileDescriptor;
+    private final int mFlags;
+
+    /** Flag value: Entry's content was deleted to save space. */
+    public static final int IS_EMPTY = 1;
+
+    /** Flag value: Content is human-readable UTF-8 text (possibly compressed). */
+    public static final int IS_TEXT = 2;
+
+    /** Flag value: Content can been decompressed with {@link GZIPOutputStream}. */
+    public static final int IS_GZIPPED = 4;
+
+    /** Create a new DropBoxEntry with the specified contents. */
+    public DropBoxEntry(String tag, long timeMillis, String text) {
+        if (tag == null || text == null) throw new NullPointerException();
+        mTag = tag;
+        mTimeMillis = timeMillis;
+        mText = text;
+        mFileDescriptor = null;
+        mFlags = IS_TEXT;
+    }
+
+    /** Create a new DropBoxEntry with the specified contents. */
+    public DropBoxEntry(String tag, long millis, File data, int flags) throws IOException {
+        if (tag == null) throw new NullPointerException();
+        if (((flags & IS_EMPTY) != 0) != (data == null)) throw new IllegalArgumentException();
+
+        mTag = tag;
+        mTimeMillis = millis;
+        mText = null;
+        mFlags = flags;
+        mFileDescriptor = data == null ? null :
+                ParcelFileDescriptor.open(data, ParcelFileDescriptor.MODE_READ_ONLY);
+    }
+
+    /** Internal constructor for CREATOR.createFromParcel(). */
+    private DropBoxEntry(String tag, long millis, Object value, int flags) {
+        if (tag == null) throw new NullPointerException();
+        if (((flags & IS_EMPTY) != 0) != (value == null)) throw new IllegalArgumentException();
+
+        mTag = tag;
+        mTimeMillis = millis;
+        mFlags = flags;
+
+        if (value == null) {
+            mText = null;
+            mFileDescriptor = null;
+        } else if (value instanceof String) {
+            if ((flags & IS_TEXT) == 0) throw new IllegalArgumentException();
+            mText = (String) value;
+            mFileDescriptor = null;
+        } else if (value instanceof ParcelFileDescriptor) {
+            mText = null;
+            mFileDescriptor = (ParcelFileDescriptor) value;
+        } else {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    /** Close the input stream associated with this entry. */
+    public synchronized void close() {
+        try { if (mFileDescriptor != null) mFileDescriptor.close(); } catch (IOException e) { }
+    }
+
+    /** @return the tag originally attached to the entry. */
+    public String getTag() { return mTag; }
+
+    /** @return time when the entry was originally created. */
+    public long getTimeMillis() { return mTimeMillis; }
+
+    /** @return flags describing the content returned by @{link #getInputStream()}. */
+    public int getFlags() { return mFlags & ~IS_GZIPPED; }  // getInputStream() decompresses.
+
+    /**
+     * @param maxLength of string to return (will truncate at this length).
+     * @return the uncompressed text contents of the entry, null if the entry is not text.
+     */
+    public String getText(int maxLength) {
+        if (mText != null) return mText.substring(0, Math.min(maxLength, mText.length()));
+        if ((mFlags & IS_TEXT) == 0) return null;
+
+        try {
+            InputStream stream = getInputStream();
+            if (stream == null) return null;
+            char[] buf = new char[maxLength];
+            InputStreamReader reader = new InputStreamReader(stream);
+            return new String(buf, 0, Math.max(0, reader.read(buf)));
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
+    /** @return the uncompressed contents of the entry, or null if the contents were lost */
+    public InputStream getInputStream() throws IOException {
+        if (mText != null) return new ByteArrayInputStream(mText.getBytes("UTF8"));
+        if (mFileDescriptor == null) return null;
+        InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(mFileDescriptor);
+        return (mFlags & IS_GZIPPED) != 0 ? new GZIPInputStream(is) : is;
+    }
+
+    public static final Parcelable.Creator<DropBoxEntry> CREATOR = new Parcelable.Creator() {
+        public DropBoxEntry[] newArray(int size) { return new DropBoxEntry[size]; }
+        public DropBoxEntry createFromParcel(Parcel in) {
+            return new DropBoxEntry(
+                    in.readString(), in.readLong(), in.readValue(null), in.readInt());
+        }
+    };
+
+    public int describeContents() {
+        return mFileDescriptor != null ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mTag);
+        out.writeLong(mTimeMillis);
+        if (mFileDescriptor != null) {
+            out.writeValue(mFileDescriptor);
+        } else {
+            out.writeValue(mText);
+        }
+        out.writeInt(mFlags);
+    }
+}
diff --git a/core/java/android/os/IDropBox.aidl b/core/java/android/os/IDropBox.aidl
new file mode 100644
index 0000000..f951e52
--- /dev/null
+++ b/core/java/android/os/IDropBox.aidl
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2009 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.os;
+
+import android.os.DropBoxEntry;
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Enqueues chunks of data (from various sources -- application crashes, kernel
+ * log records, etc.).  The queue is size bounded and will drop old data if the
+ * enqueued data exceeds the maximum size.
+ *
+ * <p>This interface is implemented by a system service you can access:
+ *
+ * <pre>IDropBox.Stub.asInterface(ServiceManager.getService("dropbox"));</pre>
+ *
+ * <p>Other system services and debugging tools may scan the drop box to upload
+ * entries for processing.
+ *
+ * {@pending}
+ */
+interface IDropBox {
+    /**
+     * Stores human-readable text.  The data may be discarded eventually (or even
+     * immediately) if space is limited, or ignored entirely if the tag has been
+     * blocked (see {@link #isTagEnabled}).
+     *
+     * @param tag describing the type of entry being stored
+     * @param data value to store
+     */
+    void addText(String tag, String data);
+
+    /**
+     * Stores binary data.  The data may be ignored or discarded as with
+     * {@link #addText}.
+     *
+     * @param tag describing the type of entry being stored
+     * @param data value to store
+     * @param flags describing the data, defined in {@link DropBoxEntry}
+     */
+    void addData(String tag, in byte[] data, int flags);
+
+    /**
+     * Stores data read from a file descriptor.  The data may be ignored or
+     * discarded as with {@link #addText}.  You must close your
+     * ParcelFileDescriptor object after calling this method!
+     *
+     * @param tag describing the type of entry being stored
+     * @param data file descriptor to read from
+     * @param flags describing the data, defined in {@link DropBoxEntry}
+     */
+    void addFile(String tag, in ParcelFileDescriptor data, int flags);
+
+    /**
+     * Checks any blacklists (set in system settings) to see whether a certain
+     * tag is allowed.  Entries with disabled tags will be dropped immediately,
+     * so you can save the work of actually constructing and sending the data.
+     *
+     * @param tag that would be used in {@link #addText} or {@link #addFile}
+     * @return whether events with that tag would be accepted
+     */
+    boolean isTagEnabled(String tag);
+
+    /**
+     * Gets the next entry from the drop box *after* the specified time.
+     * Requires android.permission.READ_LOGS.
+     *
+     * @param millis time of the last entry seen
+     * @return the next entry, or null if there are no more entries
+     */
+    DropBoxEntry getNextEntry(long millis);
+
+    // TODO: It may be useful to have some sort of notification mechanism
+    // when data is added to the dropbox, for demand-driven readers --
+    // for now readers need to poll the dropbox to find new data.
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8c9581d..9bacde6 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3611,7 +3611,6 @@
          */
         public static final String SEARCH_PER_SOURCE_CONCURRENT_QUERY_LIMIT =
                 "search_per_source_concurrent_query_limit";
-
         /**
          * Flag for allowing ActivityManagerService to send ACTION_APP_ERROR intents
          * on application crashes and ANRs. If this is disabled, the crash/ANR dialog
@@ -3626,6 +3625,32 @@
         public static final String LAST_KMSG_KB = "last_kmsg_kb";
 
         /**
+         * Maximum age of entries kept by {@link android.os.IDropBox}.
+         */
+        public static final String DROPBOX_AGE_SECONDS =
+                "dropbox_age_seconds";
+        /**
+         * Maximum amount of disk space used by {@link android.os.IDropBox} no matter what.
+         */
+        public static final String DROPBOX_QUOTA_KB =
+                "dropbox_quota_kb";
+        /**
+         * Percent of free disk (excluding reserve) which {@link android.os.IDropBox} will use.
+         */
+        public static final String DROPBOX_QUOTA_PERCENT =
+                "dropbox_quota_percent";
+        /**
+         * Percent of total disk which {@link android.os.IDropBox} will never dip into.
+         */
+        public static final String DROPBOX_RESERVE_PERCENT =
+                "dropbox_reserve_percent";
+        /**
+         * Prefix for per-tag dropbox disable/enable settings.
+         */
+        public static final String DROPBOX_TAG_PREFIX =
+                "dropbox:";
+
+        /**
          * @deprecated
          * @hide
          */