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
*/