Date: Mon, 1 Jun 2009 14:39:20 -0700
Subject: TODO's from Teleca with modifications from wink.
---
.../drawable/stat_sys_vp_phone_call_bluetooth.png | Bin 0 -> 815 bytes
core/res/res/values/strings.xml | 18 ++
.../com/android/server/status/StatusBarPolicy.java | 28 +-
.../internal/telephony/CommandsInterface.java | 5 +-
.../com/android/internal/telephony/PhoneBase.java | 9 -
.../java/com/android/internal/telephony/RIL.java | 5 +-
.../android/internal/telephony/cdma/CDMAPhone.java | 210 +-------------
.../internal/telephony/cdma/CallFailCause.java | 17 +-
.../telephony/cdma/CdmaDataConnectionTracker.java | 4 +-
.../telephony/cdma/CdmaServiceStateTracker.java | 301 ++++++++++-----------
.../android/internal/telephony/cdma/EriInfo.java | 22 --
.../internal/telephony/cdma/EriManager.java | 230 +++++++++++++++-
.../internal/telephony/cdma/RuimRecords.java | 81 ++----
.../telephony/gsm/GsmDataConnectionTracker.java | 1 -
.../telephony/gsm/GsmServiceStateTracker.java | 7 -
.../internal/telephony/gsm/stk/StkService.java | 5 +-
16 files changed, 420 insertions(+), 523 deletions(-)
create mode 100644 core/res/res/drawable/stat_sys_vp_phone_call_bluetooth.png
diff --git a/core/res/res/drawable/stat_sys_vp_phone_call_bluetooth.png b/core/res/res/drawable/stat_sys_vp_phone_call_bluetooth.png
new file mode 100644
index 000000000000..7abfd194fb80
Binary files /dev/null and b/core/res/res/drawable/stat_sys_vp_phone_call_bluetooth.png differ
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7078bbf0d65c..e048ffe97402 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -148,6 +148,24 @@
PAD
+
+
+ Roaming Indicator On
+ Roaming Indicator Off
+ Roaming Indicator Flashing
+ Out of Neighborhood
+ Out of Building
+ Roaming - Preferred System
+ Roaming - Available System
+ Roaming - Alliance Partner
+ Roaming - Premium Partner
+ Roaming - Full Service Functionality
+ Roaming - Partial Service Functionality
+ Roaming Banner On
+ Roaming Banner Off
+ Searching for Service
+
+
+ Handle case where no activity matches
Consider how to launch your activities
Allow activities to add to current task
- Notifications should be easy to return from
+ Notifications should let user easily get back
Use the notification system
Don't take over BACK key unless you absolutely need to
@@ -49,7 +49,6 @@ page.title=Activity and Task Design Guidelines
Application Fundamentals
- Activities and Tasks blog post
@@ -630,12 +629,12 @@ page.title=Activity and Task Design Guidelines
When the user takes an action on some data, such as touching a
mailto:info@example.com link, they are actually initiating an Intent
- object which then gets resolved to a particular component (we will
- consider only activity components here). So, the result of a user
- touching a mailto: link is an Intent object that the system tries to
- match to an activity. If that Intent object was written explicitly
- naming an activity (an explicit intent ), then the system
- immediately launches that activity in response to the user
+ object, or just an intent , which then gets resolved to a
+ particular component (we consider only activity components here).
+ So, the result of a user touching a mailto: link is an Intent object
+ that the system tries to match to an activity. If that Intent object was
+ written explicitly naming an activity (an explicit intent ),
+ then the system immediately launches that activity in response to the user
action. However, if that Intent object was written without naming an
activity (an implicit intent ), the system compares the Intent
object to the intent filters of available activities. If more
@@ -872,12 +871,29 @@ page.title=Activity and Task Design Guidelines
Your applications can re-use activities made available from other
- applications. In doing so, you cannot presume that external activity
- will always be present — you must handle the case that the
- external activity is not installed. Do this in the way you find most
- appropriate, such as dimming the user control that accesses it (such
- as a button or menu item), or displaying a message to the user that
- sends them to the location to download it, such as the Market.
+ applications. In doing so, you cannot presume your intent will always
+ be resolved to a matching external activity — you must handle the case
+ where no application installed on the device can handle the intent.
+
+
+
+ You can either test that an activity matches the intent, which you can do
+ before starting the activity, or catch an exception if starting the
+ activity fails. Both approaches are descibed in the blog posting
+ Can
+ I use this Intent? .
+
+
+
+ To test whether an intent can be resolved, your code can query the package manager.
+ The blog post provides an example in the isIntentAvailable() helper method.
+ You can perform this test when initializing the user interface.
+ For instance, you could disable the user control that initiates
+ the Intent object, or display a message to the user that lets them go
+ to a location, such as the Market, to download its application.
+ In this way, your code can start the activity (using either startActivity()
+ or startActivityForResult()) only if the intent has tested to resolve
+ to an activity that is actually present.
Consider how you want your activities to be launched or used by other applications
@@ -1054,15 +1070,14 @@ page.title=Activity and Task Design Guidelines
-Notifications should be easy for the user to return from
-
-
- Applications that are in the background or haven't been run can
- send out notifications to the user letting them know about events
- of interest. For example, Calendar can send out notifications of
- upcoming events, and Email can send out notifications when new
- messages arrive. One of the user interface rules is that when the
- user is in activity A and gets a notification for activity B and
+
Notifications should let the user easily get back to the previous activity
+
+ Applications that are in the background or not running can have
+ services that send out notifications to the user letting them know about
+ events of interest. Two examples are Calendar, which can send out notifications of
+ upcoming events, and Email, which can send out notifications when new
+ messages arrive. One of the user interface guidelines is that when the
+ user is in activity A, gets a notification for activity B and
picks that notification, when they press the BACK key, they should
go back to activity A.
@@ -1108,11 +1123,11 @@ Notifications generally happen primarily in one of two ways:
- The application has a dedicated activity for
- notification - For example, when the user receives a
- Calendar notification, the act of selecting that
+ The chosen activity is dedicated for notification only -
+ For example, when the user receives a
+ Calendar notification, choosing that
notification starts a special activity that displays a list
- of upcoming calendar events — a view available only
+ of upcoming calendar events — this view is available only
from the notification, not through the Calendar's own user
interface. After viewing this upcoming event, to ensure that
the user pressing the BACK key will return to the activity
@@ -1140,25 +1155,25 @@ Notifications generally happen primarily in one of two ways:
- The user choosing the notification brings the activity to
+ The chosen activity is not dedicated, but always comes to
the foreground in its initial state - For example, in
- response to a notification, the Gmail application is brought
- to the foreground presenting the list of conversations. You
- do this by having the user's response to the notification
- trigger an intent to launch the activity with the clear top
- flag set. (That is, you put {@link
+ response to a notification, when the Gmail application comes
+ to the foreground, it always presents the list of conversations.
+ You can ensure this happens by setting a "clear top" flag in the
+ intent that the notification triggers. This ensures that when the
+ activity is launched, it displays its initial activity, preventing
+ Gmail from coming to the foreground in whatever state the user last
+ happened to be viewing it. (To do this, you put {@link
android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP
- FLAG_ACTIVITY_CLEAR_TOP} in the intent you pass to
- startActivity()). This prevents Gmail from coming to the
- foreground in whatever state the user last happened to be
- viewing it.
+ FLAG_ACTIVITY_CLEAR_TOP} in the intent you pass to startActivity()).
There are other ways to handle notifications, such as bringing the
- activity to the foreground set to display specific data, such as the
- ongoing text message thread of a particular person.
+ activity to the foreground, set to display specific data, such as
+ displaying the text message thread for the person who just sent a
+ new text message.
--
cgit v1.2.3-59-g8ed1b
From 5b4718ba8aeac969095b75c20fbe74ced0c04725 Mon Sep 17 00:00:00 2001
From: Mike Reed
Date: Tue, 2 Jun 2009 15:30:57 -0400
Subject: make FEATURE_OPENGL public (but hidden for now) so we can test with
it
---
core/java/android/view/Window.java | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index d7457a030468..2c32d8b54af6 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -56,11 +56,11 @@ public abstract class Window {
public static final int FEATURE_CONTEXT_MENU = 6;
/** Flag for custom title. You cannot combine this feature with other title features. */
public static final int FEATURE_CUSTOM_TITLE = 7;
- /* Flag for asking for an OpenGL enabled window.
+ /** Flag for asking for an OpenGL enabled window.
All 2D graphics will be handled by OpenGL ES.
- Private for now, until it is better tested (not shipping in 1.0)
+ @hide
*/
- private static final int FEATURE_OPENGL = 8;
+ public static final int FEATURE_OPENGL = 8;
/** Flag for setting the progress bar's visibility to VISIBLE */
public static final int PROGRESS_VISIBILITY_ON = -1;
/** Flag for setting the progress bar's visibility to GONE */
--
cgit v1.2.3-59-g8ed1b
From 47e82dee6b18c33fab8c2cdf4f68b20d3663079e Mon Sep 17 00:00:00 2001
From: Nick Pelly
Date: Mon, 1 Jun 2009 19:09:37 -0700
Subject: Implement bulk read and writes for Bluetooth sockets.
Before: 0.1 kB/s
After: 100 kB/s
(in my java BT speed test app)
---
.../android/bluetooth/BluetoothInputStream.java | 42 ++++++++++++++++--
.../android/bluetooth/BluetoothOutputStream.java | 34 ++++++++++++++-
core/java/android/bluetooth/BluetoothSocket.java | 4 +-
core/jni/android_bluetooth_BluetoothSocket.cpp | 50 +++++++++++++++++-----
4 files changed, 112 insertions(+), 18 deletions(-)
diff --git a/core/java/android/bluetooth/BluetoothInputStream.java b/core/java/android/bluetooth/BluetoothInputStream.java
index ceae70c586d3..e6f501c9b69f 100644
--- a/core/java/android/bluetooth/BluetoothInputStream.java
+++ b/core/java/android/bluetooth/BluetoothInputStream.java
@@ -24,7 +24,6 @@ import java.io.InputStream;
*
* Used to write to a Bluetooth socket.
*
- * TODO: Implement bulk writes (instead of one byte at a time).
* @hide
*/
/*package*/ final class BluetoothInputStream extends InputStream {
@@ -54,9 +53,46 @@ import java.io.InputStream;
* @return the byte read or -1 if the end of stream has been reached.
* @throws IOException
* if the stream is closed or another IOException occurs.
- * @since Android 1.0
+ * @since Android 1.5
*/
public int read() throws IOException {
- return mSocket.readNative();
+ byte b[] = new byte[1];
+ int ret = mSocket.readNative(b, 0, 1);
+ if (ret == 1) {
+ return (int)b[0];
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * Reads at most {@code length} bytes from this stream and stores them in
+ * the byte array {@code b} starting at {@code offset}.
+ *
+ * @param b
+ * the byte array in which to store the bytes read.
+ * @param offset
+ * the initial position in {@code buffer} to store the bytes
+ * read from this stream.
+ * @param length
+ * the maximum number of bytes to store in {@code b}.
+ * @return the number of bytes actually read or -1 if the end of the stream
+ * has been reached.
+ * @throws IndexOutOfBoundsException
+ * if {@code offset < 0} or {@code length < 0}, or if
+ * {@code offset + length} is greater than the length of
+ * {@code b}.
+ * @throws IOException
+ * if the stream is closed or another IOException occurs.
+ * @since Android 1.5
+ */
+ public int read(byte[] b, int offset, int length) throws IOException {
+ if (b == null) {
+ throw new NullPointerException("byte array is null");
+ }
+ if ((offset | length) < 0 || length > b.length - offset) {
+ throw new ArrayIndexOutOfBoundsException("invalid offset or length");
+ }
+ return mSocket.readNative(b, offset, length);
}
}
diff --git a/core/java/android/bluetooth/BluetoothOutputStream.java b/core/java/android/bluetooth/BluetoothOutputStream.java
index 32e6d17c82a8..7e2ead478afb 100644
--- a/core/java/android/bluetooth/BluetoothOutputStream.java
+++ b/core/java/android/bluetooth/BluetoothOutputStream.java
@@ -24,7 +24,6 @@ import java.io.OutputStream;
*
* Used to read from a Bluetooth socket.
*
- * TODO: Implement bulk reads (instead of one byte at a time).
* @hide
*/
/*package*/ final class BluetoothOutputStream extends OutputStream {
@@ -52,6 +51,37 @@ import java.io.OutputStream;
* @since Android 1.0
*/
public void write(int oneByte) throws IOException {
- mSocket.writeNative(oneByte);
+ byte b[] = new byte[1];
+ b[0] = (byte)oneByte;
+ mSocket.writeNative(b, 0, 1);
+ }
+
+ /**
+ * Writes {@code count} bytes from the byte array {@code buffer} starting
+ * at position {@code offset} to this stream.
+ *
+ * @param b
+ * the buffer to be written.
+ * @param offset
+ * the start position in {@code buffer} from where to get bytes.
+ * @param count
+ * the number of bytes from {@code buffer} to write to this
+ * stream.
+ * @throws IOException
+ * if an error occurs while writing to this stream.
+ * @throws IndexOutOfBoundsException
+ * if {@code offset < 0} or {@code count < 0}, or if
+ * {@code offset + count} is bigger than the length of
+ * {@code buffer}.
+ * @since Android 1.0
+ */
+ public void write(byte[] b, int offset, int count) throws IOException {
+ if (b == null) {
+ throw new NullPointerException("buffer is null");
+ }
+ if ((offset | count) < 0 || count > b.length - offset) {
+ throw new IndexOutOfBoundsException("invalid offset or length");
+ }
+ mSocket.writeNative(b, offset, count);
}
}
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index fd8885ece993..670146b245ee 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -169,8 +169,8 @@ public final class BluetoothSocket implements Closeable {
/*package*/ native void bindListenNative(int port) throws IOException;
/*package*/ native BluetoothSocket acceptNative(int timeout) throws IOException;
/*package*/ native int availableNative();
- /*package*/ native int readNative();
- /*package*/ native void writeNative(int data);
+ /*package*/ native int readNative(byte[] b, int offset, int length);
+ /*package*/ native int writeNative(byte[] b, int offset, int length);
/*package*/ native void closeNative();
private native void destroyNative();
}
diff --git a/core/jni/android_bluetooth_BluetoothSocket.cpp b/core/jni/android_bluetooth_BluetoothSocket.cpp
index dc4c1d43ab0e..e9c04a54f49a 100644
--- a/core/jni/android_bluetooth_BluetoothSocket.cpp
+++ b/core/jni/android_bluetooth_BluetoothSocket.cpp
@@ -227,44 +227,72 @@ static jint availableNative(JNIEnv *env, jobject obj) {
return -1;
}
-static jint readNative(JNIEnv *env, jobject obj) {
+/** jb must not be null. offset and offset+length must be within array */
+static jint readNative(JNIEnv *env, jobject obj, jbyteArray jb, jint offset,
+ jint length) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
- char buf;
+ int ret;
+ jbyte *b;
struct asocket *s = get_socketData(env, obj);
if (!s)
return -1;
- if (asocket_read(s, &buf, 1, -1) < 0) {
+ b = env->GetByteArrayElements(jb, NULL);
+ if (b == NULL) {
+ jniThrowIOException(env, EINVAL);
+ return -1;
+ }
+
+ ret = asocket_read(s, &b[offset], length, -1);
+ if (ret < 0) {
jniThrowIOException(env, errno);
+ env->ReleaseByteArrayElements(jb, b, JNI_ABORT);
return -1;
}
- return (jint)buf;
+ env->ReleaseByteArrayElements(jb, b, 0);
+ return (jint)ret;
#endif
jniThrowIOException(env, ENOSYS);
return -1;
}
-static void writeNative(JNIEnv *env, jobject obj, jint data) {
+/** jb must not be null. offset and offset+length must be within array */
+static jint writeNative(JNIEnv *env, jobject obj, jbyteArray jb, jint offset,
+ jint length) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
- const char buf = (char)data;
+ int ret;
+ jbyte *b;
struct asocket *s = get_socketData(env, obj);
if (!s)
- return;
+ return -1;
- if (asocket_write(s, &buf, 1, -1) < 0)
+ b = env->GetByteArrayElements(jb, NULL);
+ if (b == NULL) {
+ jniThrowIOException(env, EINVAL);
+ return -1;
+ }
+
+ ret = asocket_write(s, &b[offset], length, -1);
+ if (ret < 0) {
jniThrowIOException(env, errno);
+ env->ReleaseByteArrayElements(jb, b, JNI_ABORT);
+ return -1;
+ }
+
+ env->ReleaseByteArrayElements(jb, b, JNI_ABORT); // no need to commit
+ return (jint)ret;
- return;
#endif
jniThrowIOException(env, ENOSYS);
+ return -1;
}
static void closeNative(JNIEnv *env, jobject obj) {
@@ -301,8 +329,8 @@ static JNINativeMethod sMethods[] = {
{"bindListenNative", "(I)V", (void *) bindListenNative},
{"acceptNative", "(I)Landroid/bluetooth/BluetoothSocket;", (void *) acceptNative},
{"availableNative", "()I", (void *) availableNative},
- {"readNative", "()I", (void *) readNative},
- {"writeNative", "(I)V", (void *) writeNative},
+ {"readNative", "([BII)I", (void *) readNative},
+ {"writeNative", "([BII)I", (void *) writeNative},
{"closeNative", "()V", (void *) closeNative},
{"destroyNative", "()V", (void *) destroyNative},
};
--
cgit v1.2.3-59-g8ed1b
From c9260540729d731e22458ce48127ca2ffaef33ee Mon Sep 17 00:00:00 2001
From: Dmitri Plotnikov
Date: Thu, 28 May 2009 17:55:12 -0700
Subject: Consolidating data needed for contact aggregator into the data1 and
data2 fields.
The aggregator will then only read data1 and
data2 and do the matching on those (taking mime type into account, of course).
---
core/java/android/provider/ContactsContract.java | 51 +++++++++++++-----------
1 file changed, 27 insertions(+), 24 deletions(-)
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 9d48bc7831e6..3763f9ae64f9 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -399,16 +399,16 @@ public final class ContactsContract {
public static final String TYPE = "data1";
/**
- * The user defined label for the the contact method.
+ * The data for the contact method.
* Type: TEXT
*/
- public static final String LABEL = "data2";
+ public static final String DATA = "data2";
/**
- * The data for the contact method.
+ * The user defined label for the the contact method.
* Type: TEXT
*/
- public static final String DATA = "data3";
+ public static final String LABEL = "data3";
/**
* Whether this is the primary entry of its kind for the contact it belongs to
@@ -427,27 +427,28 @@ public final class ContactsContract {
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/name";
/**
- * The contact's honorific prefix, e.g. "Sir"
+ * The given name for the contact.
+ * Type: TEXT
*/
- public static final String PREFIX = "data1";
+ public static final String GIVEN_NAME = "data1";
/**
- * The given name for the contact.
+ * The family name for the contact.
* Type: TEXT
*/
- public static final String GIVEN_NAME = "data2";
+ public static final String FAMILY_NAME = "data2";
/**
- * The contact's middle name
+ * The contact's honorific prefix, e.g. "Sir"
* Type: TEXT
*/
- public static final String MIDDLE_NAME = "data3";
+ public static final String PREFIX = "data3";
/**
- * The family name for the contact.
+ * The contact's middle name
* Type: TEXT
*/
- public static final String FAMILY_NAME = "data4";
+ public static final String MIDDLE_NAME = "data4";
/**
* The contact's honorific suffix, e.g. "Jr"
@@ -502,15 +503,15 @@ public final class ContactsContract {
public static final int TYPE_INITIALS = 6;
/**
- * The user provided label, only used if TYPE is {@link #TYPE_CUSTOM}.
- * Type: TEXT
+ * The name itself
*/
- public static final String LABEL = "data2";
+ public static final String NAME = "data2";
/**
- * The name itself
+ * The user provided label, only used if TYPE is {@link #TYPE_CUSTOM}.
+ * Type: TEXT
*/
- public static final String NAME = "data3";
+ public static final String LABEL = "data3";
}
/**
@@ -535,8 +536,7 @@ public final class ContactsContract {
* The phone number as the user entered it.
* Type: TEXT
*/
- public static final String NUMBER = "data3";
-
+ public static final String NUMBER = "data2";
}
/**
@@ -552,7 +552,6 @@ public final class ContactsContract {
public static final int TYPE_HOME = 1;
public static final int TYPE_WORK = 2;
public static final int TYPE_OTHER = 3;
-
}
/**
@@ -590,9 +589,11 @@ public final class ContactsContract {
* The predefined IM protocol types. The protocol can either be non-present, one
* of these types, or a free-form string. These cases are encoded in the PROTOCOL
* column as:
- * - null
- * - pre:
- * - custom:
+ *
+ * null
+ * pre:<an integer, one of the protocols below>
+ * custom:<a string>
+ *
*/
public static final int PROTOCOL_AIM = 0;
public static final int PROTOCOL_MSN = 1;
@@ -647,7 +648,6 @@ public final class ContactsContract {
* Type: INTEGER (if set, non-0 means true)
*/
public static final String ISPRIMARY = "data5";
-
}
/**
@@ -684,6 +684,9 @@ public final class ContactsContract {
public static final String NOTE = "data1";
}
+ /**
+ * Custom ringtone associated with the contact.
+ */
public static final class CustomRingtone implements BaseCommonColumns {
private CustomRingtone() {}
--
cgit v1.2.3-59-g8ed1b
From 78ebbabfe18f2c6fb4825e4bdbb1613e0901f0f3 Mon Sep 17 00:00:00 2001
From: Jean-Michel Trivi
Date: Tue, 2 Jun 2009 15:09:51 -0700
Subject: Addressed comments of change 2515 for the TtsService class: - made
the SpeechItem and SoundResource inner classes static, - prefixed the
TtsService member variables by 'm', - changed indentation from 2 to 4
characters.
---
tts/java/android/tts/TtsService.java | 1270 +++++++++++++++++-----------------
1 file changed, 635 insertions(+), 635 deletions(-)
diff --git a/tts/java/android/tts/TtsService.java b/tts/java/android/tts/TtsService.java
index 4b794db7f52c..d31718181aaf 100755
--- a/tts/java/android/tts/TtsService.java
+++ b/tts/java/android/tts/TtsService.java
@@ -45,635 +45,158 @@ import java.util.concurrent.locks.ReentrantLock;
*/
public class TtsService extends Service implements OnCompletionListener {
- private class SpeechItem {
- public static final int SPEECH = 0;
- public static final int EARCON = 1;
- public static final int SILENCE = 2;
- public String mText = null;
- public ArrayList mParams = null;
- public int mType = SPEECH;
- public long mDuration = 0;
-
- public SpeechItem(String text, ArrayList params, int itemType) {
- mText = text;
- mParams = params;
- mType = itemType;
- }
-
- public SpeechItem(long silenceTime) {
- mDuration = silenceTime;
- }
- }
-
- /**
- * Contains the information needed to access a sound resource; the name of
- * the package that contains the resource and the resID of the resource
- * within that package.
- */
- private class SoundResource {
- public String mSourcePackageName = null;
- public int mResId = -1;
- public String mFilename = null;
-
- public SoundResource(String packageName, int id) {
- mSourcePackageName = packageName;
- mResId = id;
- mFilename = null;
- }
+ private static class SpeechItem {
+ public static final int SPEECH = 0;
+ public static final int EARCON = 1;
+ public static final int SILENCE = 2;
+ public String mText = null;
+ public ArrayList mParams = null;
+ public int mType = SPEECH;
+ public long mDuration = 0;
+
+ public SpeechItem(String text, ArrayList params, int itemType) {
+ mText = text;
+ mParams = params;
+ mType = itemType;
+ }
- public SoundResource(String file) {
- mSourcePackageName = null;
- mResId = -1;
- mFilename = file;
+ public SpeechItem(long silenceTime) {
+ mDuration = silenceTime;
+ }
}
- }
- private static final String ACTION = "android.intent.action.USE_TTS";
- private static final String CATEGORY = "android.intent.category.TTS";
- private static final String PKGNAME = "android.tts";
-
- final RemoteCallbackList mCallbacks = new RemoteCallbackList();
-
- private Boolean isSpeaking;
- private ArrayList speechQueue;
- private HashMap earcons;
- private HashMap utterances;
- private MediaPlayer player;
- private TtsService self;
+ /**
+ * Contains the information needed to access a sound resource; the name of
+ * the package that contains the resource and the resID of the resource
+ * within that package.
+ */
+ private static class SoundResource {
+ public String mSourcePackageName = null;
+ public int mResId = -1;
+ public String mFilename = null;
+
+ public SoundResource(String packageName, int id) {
+ mSourcePackageName = packageName;
+ mResId = id;
+ mFilename = null;
+ }
- private SharedPreferences prefs;
+ public SoundResource(String file) {
+ mSourcePackageName = null;
+ mResId = -1;
+ mFilename = file;
+ }
+ }
- private final ReentrantLock speechQueueLock = new ReentrantLock();
- private final ReentrantLock synthesizerLock = new ReentrantLock();
+ private static final String ACTION = "android.intent.action.USE_TTS";
+ private static final String CATEGORY = "android.intent.category.TTS";
+ private static final String PKGNAME = "android.tts";
- // TODO support multiple SpeechSynthesis objects
- private SynthProxy nativeSynth;
+ final RemoteCallbackList mCallbacks = new RemoteCallbackList();
- @Override
- public void onCreate() {
- super.onCreate();
- Log.i("TTS", "TTS starting");
+ private Boolean mIsSpeaking;
+ private ArrayList mSpeechQueue;
+ private HashMap mEarcons;
+ private HashMap mUtterances;
+ private MediaPlayer mPlayer;
+ private TtsService mSelf;
+ private SharedPreferences prefs;
- // TODO: Make this work when the settings are done in the main Settings
- // app.
- prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ private final ReentrantLock speechQueueLock = new ReentrantLock();
+ private final ReentrantLock synthesizerLock = new ReentrantLock();
- // TODO: This should be changed to work by requesting the path
- // from the default engine.
- nativeSynth = new SynthProxy(prefs.getString("engine_pref", ""));
+ // TODO support multiple SpeechSynthesis objects
+ private SynthProxy nativeSynth;
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.i("TTS", "TTS starting");
- self = this;
- isSpeaking = false;
- earcons = new HashMap();
- utterances = new HashMap();
+ // TODO: Make this work when the settings are done in the main Settings
+ // app.
+ prefs = PreferenceManager.getDefaultSharedPreferences(this);
- speechQueue = new ArrayList();
- player = null;
+ // TODO: This should be changed to work by requesting the path
+ // from the default engine.
+ nativeSynth = new SynthProxy(prefs.getString("engine_pref", ""));
- setLanguage(prefs.getString("lang_pref", "en-rUS"));
- setSpeechRate(Integer.parseInt(prefs.getString("rate_pref", "140")));
- }
- @Override
- public void onDestroy() {
- super.onDestroy();
- // Don't hog the media player
- cleanUpPlayer();
+ mSelf = this;
+ mIsSpeaking = false;
- nativeSynth.shutdown();
+ mEarcons = new HashMap();
+ mUtterances = new HashMap();
- // Unregister all callbacks.
- mCallbacks.kill();
- }
+ mSpeechQueue = new ArrayList();
+ mPlayer = null;
- private void setSpeechRate(int rate) {
- if (prefs.getBoolean("override_pref", false)) {
- // This is set to the default here so that the preview in the prefs
- // activity will show the change without a restart, even if apps are
- // not allowed to change the defaults.
- rate = Integer.parseInt(prefs.getString("rate_pref", "140"));
- }
- nativeSynth.setSpeechRate(rate);
- }
-
- private void setLanguage(String lang) {
- if (prefs.getBoolean("override_pref", false)) {
- // This is set to the default here so that the preview in the prefs
- // activity will show the change without a restart, even if apps are
- // not
- // allowed to change the defaults.
- lang = prefs.getString("lang_pref", "en-rUS");
+ setLanguage(prefs.getString("lang_pref", "en-rUS"));
+ setSpeechRate(Integer.parseInt(prefs.getString("rate_pref", "140")));
}
- nativeSynth.setLanguage(lang);
- }
- private void setEngine(String engineName, String[] requestedLanguages,
- int strictness) {
- // TODO: Implement engine selection code here.
- Intent engineIntent = new Intent(
- "android.intent.action.START_TTS_ENGINE");
- if (engineName != null) {
- engineIntent.addCategory("android.intent.action.tts_engine."
- + engineName);
- }
- for (int i = 0; i < requestedLanguages.length; i++) {
- engineIntent.addCategory("android.intent.action.tts_lang."
- + requestedLanguages[i]);
- }
- ResolveInfo[] enginesArray = new ResolveInfo[0];
- PackageManager pm = getPackageManager();
- enginesArray = pm.queryIntentActivities(engineIntent, 0).toArray(
- enginesArray);
- }
-
- private void setEngine(Intent engineIntent) {
- // TODO: Implement engine selection code here.
- }
-
- private int getEngineStatus() {
- // TODO: Proposal - add a sanity check method that
- // TTS engine plugins must implement.
- return 0;
- }
-
- /**
- * Adds a sound resource to the TTS.
- *
- * @param text
- * The text that should be associated with the sound resource
- * @param packageName
- * The name of the package which has the sound resource
- * @param resId
- * The resource ID of the sound within its package
- */
- private void addSpeech(String text, String packageName, int resId) {
- utterances.put(text, new SoundResource(packageName, resId));
- }
-
- /**
- * Adds a sound resource to the TTS.
- *
- * @param text
- * The text that should be associated with the sound resource
- * @param filename
- * The filename of the sound resource. This must be a complete
- * path like: (/sdcard/mysounds/mysoundbite.mp3).
- */
- private void addSpeech(String text, String filename) {
- utterances.put(text, new SoundResource(filename));
- }
-
- /**
- * Adds a sound resource to the TTS as an earcon.
- *
- * @param earcon
- * The text that should be associated with the sound resource
- * @param packageName
- * The name of the package which has the sound resource
- * @param resId
- * The resource ID of the sound within its package
- */
- private void addEarcon(String earcon, String packageName, int resId) {
- earcons.put(earcon, new SoundResource(packageName, resId));
- }
-
- /**
- * Adds a sound resource to the TTS as an earcon.
- *
- * @param earcon
- * The text that should be associated with the sound resource
- * @param filename
- * The filename of the sound resource. This must be a complete
- * path like: (/sdcard/mysounds/mysoundbite.mp3).
- */
- private void addEarcon(String earcon, String filename) {
- earcons.put(earcon, new SoundResource(filename));
- }
-
- /**
- * Speaks the given text using the specified queueing mode and parameters.
- *
- * @param text
- * The text that should be spoken
- * @param queueMode
- * 0 for no queue (interrupts all previous utterances), 1 for
- * queued
- * @param params
- * An ArrayList of parameters. This is not implemented for all
- * engines.
- */
- private void speak(String text, int queueMode, ArrayList params) {
- if (queueMode == 0) {
- stop();
- }
- speechQueue.add(new SpeechItem(text, params, SpeechItem.SPEECH));
- if (!isSpeaking) {
- processSpeechQueue();
- }
- }
-
- /**
- * Plays the earcon using the specified queueing mode and parameters.
- *
- * @param earcon
- * The earcon that should be played
- * @param queueMode
- * 0 for no queue (interrupts all previous utterances), 1 for
- * queued
- * @param params
- * An ArrayList of parameters. This is not implemented for all
- * engines.
- */
- private void playEarcon(String earcon, int queueMode,
- ArrayList params) {
- if (queueMode == 0) {
- stop();
- }
- speechQueue.add(new SpeechItem(earcon, params, SpeechItem.EARCON));
- if (!isSpeaking) {
- processSpeechQueue();
- }
- }
-
- /**
- * Stops all speech output and removes any utterances still in the queue.
- */
- private void stop() {
- Log.i("TTS", "Stopping");
- speechQueue.clear();
-
- nativeSynth.stop();
- isSpeaking = false;
- if (player != null) {
- try {
- player.stop();
- } catch (IllegalStateException e) {
- // Do nothing, the player is already stopped.
- }
- }
- Log.i("TTS", "Stopped");
- }
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ // Don't hog the media player
+ cleanUpPlayer();
- public void onCompletion(MediaPlayer arg0) {
- processSpeechQueue();
- }
+ nativeSynth.shutdown();
- private void playSilence(long duration, int queueMode,
- ArrayList params) {
- if (queueMode == 0) {
- stop();
+ // Unregister all callbacks.
+ mCallbacks.kill();
}
- speechQueue.add(new SpeechItem(duration));
- if (!isSpeaking) {
- processSpeechQueue();
- }
- }
- private void silence(final long duration) {
- class SilenceThread implements Runnable {
- public void run() {
- try {
- Thread.sleep(duration);
- } catch (InterruptedException e) {
- e.printStackTrace();
- } finally {
- processSpeechQueue();
+ private void setSpeechRate(int rate) {
+ if (prefs.getBoolean("override_pref", false)) {
+ // This is set to the default here so that the preview in the prefs
+ // activity will show the change without a restart, even if apps are
+ // not allowed to change the defaults.
+ rate = Integer.parseInt(prefs.getString("rate_pref", "140"));
}
- }
- }
- Thread slnc = (new Thread(new SilenceThread()));
- slnc.setPriority(Thread.MIN_PRIORITY);
- slnc.start();
- }
-
- private void speakInternalOnly(final String text,
- final ArrayList params) {
- class SynthThread implements Runnable {
- public void run() {
- boolean synthAvailable = false;
- try {
- synthAvailable = synthesizerLock.tryLock();
- if (!synthAvailable) {
- Thread.sleep(100);
- Thread synth = (new Thread(new SynthThread()));
- synth.setPriority(Thread.MIN_PRIORITY);
- synth.start();
- return;
- }
- nativeSynth.speak(text);
- } catch (InterruptedException e) {
- e.printStackTrace();
- } finally {
- // This check is needed because finally will always run;
- // even if the
- // method returns somewhere in the try block.
- if (synthAvailable) {
- synthesizerLock.unlock();
- }
- }
- }
- }
- Thread synth = (new Thread(new SynthThread()));
- synth.setPriority(Thread.MIN_PRIORITY);
- synth.start();
- }
-
- private SoundResource getSoundResource(SpeechItem speechItem) {
- SoundResource sr = null;
- String text = speechItem.mText;
- if (speechItem.mType == SpeechItem.SILENCE) {
- // Do nothing if this is just silence
- } else if (speechItem.mType == SpeechItem.EARCON) {
- sr = earcons.get(text);
- } else {
- sr = utterances.get(text);
- }
- return sr;
- }
-
- private void dispatchSpeechCompletedCallbacks(String mark) {
- Log.i("TTS callback", "dispatch started");
- // Broadcast to all clients the new value.
- final int N = mCallbacks.beginBroadcast();
- for (int i = 0; i < N; i++) {
- try {
- mCallbacks.getBroadcastItem(i).markReached(mark);
- } catch (RemoteException e) {
- // The RemoteCallbackList will take care of removing
- // the dead object for us.
- }
+ nativeSynth.setSpeechRate(rate);
}
- mCallbacks.finishBroadcast();
- Log.i("TTS callback", "dispatch completed to " + N);
- }
-
- private void processSpeechQueue() {
- boolean speechQueueAvailable = false;
- try {
- speechQueueAvailable = speechQueueLock.tryLock();
- if (!speechQueueAvailable) {
- return;
- }
- if (speechQueue.size() < 1) {
- isSpeaking = false;
- // Dispatch a completion here as this is the
- // only place where speech completes normally.
- // Nothing left to say in the queue is a special case
- // that is always a "mark" - associated text is null.
- dispatchSpeechCompletedCallbacks("");
- return;
- }
-
- SpeechItem currentSpeechItem = speechQueue.get(0);
- isSpeaking = true;
- SoundResource sr = getSoundResource(currentSpeechItem);
- // Synth speech as needed - synthesizer should call
- // processSpeechQueue to continue running the queue
- Log.i("TTS processing: ", currentSpeechItem.mText);
- if (sr == null) {
- if (currentSpeechItem.mType == SpeechItem.SPEECH) {
- // TODO: Split text up into smaller chunks before accepting
- // them
- // for processing.
- speakInternalOnly(currentSpeechItem.mText,
- currentSpeechItem.mParams);
- } else {
- // This is either silence or an earcon that was missing
- silence(currentSpeechItem.mDuration);
- }
- } else {
- cleanUpPlayer();
- if (sr.mSourcePackageName == PKGNAME) {
- // Utterance is part of the TTS library
- player = MediaPlayer.create(this, sr.mResId);
- } else if (sr.mSourcePackageName != null) {
- // Utterance is part of the app calling the library
- Context ctx;
- try {
- ctx = this.createPackageContext(sr.mSourcePackageName,
- 0);
- } catch (NameNotFoundException e) {
- e.printStackTrace();
- speechQueue.remove(0); // Remove it from the queue and
- // move on
- isSpeaking = false;
- return;
- }
- player = MediaPlayer.create(ctx, sr.mResId);
- } else {
- // Utterance is coming from a file
- player = MediaPlayer.create(this, Uri.parse(sr.mFilename));
- }
- // Check if Media Server is dead; if it is, clear the queue and
- // give up for now - hopefully, it will recover itself.
- if (player == null) {
- speechQueue.clear();
- isSpeaking = false;
- return;
+ private void setLanguage(String lang) {
+ if (prefs.getBoolean("override_pref", false)) {
+ // This is set to the default here so that the preview in the prefs
+ // activity will show the change without a restart, even if apps are
+ // not
+ // allowed to change the defaults.
+ lang = prefs.getString("lang_pref", "en-rUS");
}
- player.setOnCompletionListener(this);
- try {
- player.start();
- } catch (IllegalStateException e) {
- speechQueue.clear();
- isSpeaking = false;
- cleanUpPlayer();
- return;
- }
- }
- if (speechQueue.size() > 0) {
- speechQueue.remove(0);
- }
- } finally {
- // This check is needed because finally will always run; even if the
- // method returns somewhere in the try block.
- if (speechQueueAvailable) {
- speechQueueLock.unlock();
- }
- }
- }
-
- private void cleanUpPlayer() {
- if (player != null) {
- player.release();
- player = null;
- }
- }
-
- /**
- * Synthesizes the given text using the specified queuing mode and
- * parameters.
- *
- * @param text
- * The String of text that should be synthesized
- * @param params
- * An ArrayList of parameters. The first element of this array
- * controls the type of voice to use.
- * @param filename
- * The string that gives the full output filename; it should be
- * something like "/sdcard/myappsounds/mysound.wav".
- * @return A boolean that indicates if the synthesis succeeded
- */
- private boolean synthesizeToFile(String text, ArrayList params,
- String filename, boolean calledFromApi) {
- // Only stop everything if this is a call made by an outside app trying
- // to
- // use the API. Do NOT stop if this is a call from within the service as
- // clearing the speech queue here would be a mistake.
- if (calledFromApi) {
- stop();
- }
- Log.i("TTS", "Synthesizing to " + filename);
- boolean synthAvailable = false;
- try {
- synthAvailable = synthesizerLock.tryLock();
- if (!synthAvailable) {
- return false;
- }
- // Don't allow a filename that is too long
- // TODO use platform constant
- if (filename.length() > 250) {
- return false;
- }
- nativeSynth.synthesizeToFile(text, filename);
- } finally {
- // This check is needed because finally will always run; even if the
- // method returns somewhere in the try block.
- if (synthAvailable) {
- synthesizerLock.unlock();
- }
- }
- Log.i("TTS", "Completed synthesis for " + filename);
- return true;
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- if (ACTION.equals(intent.getAction())) {
- for (String category : intent.getCategories()) {
- if (category.equals(CATEGORY)) {
- return mBinder;
- }
- }
- }
- return null;
- }
-
- private final ITts.Stub mBinder = new Stub() {
-
- public void registerCallback(ITtsCallback cb) {
- if (cb != null)
- mCallbacks.register(cb);
+ nativeSynth.setLanguage(lang);
}
- public void unregisterCallback(ITtsCallback cb) {
- if (cb != null)
- mCallbacks.unregister(cb);
- }
-
- /**
- * Gives a hint about the type of engine that is preferred.
- *
- * @param selectedEngine
- * The TTS engine that should be used
- */
- public void setEngine(String engineName, String[] supportedLanguages,
- int strictness) {
- self.setEngine(engineName, supportedLanguages, strictness);
- }
-
- /**
- * Specifies exactly what the engine has to support. Will always be
- * considered "strict"; can be used for implementing
- * optional/experimental features that are not supported by all engines.
- *
- * @param engineIntent
- * An intent that specifies exactly what the engine has to
- * support.
- */
- public void setEngineWithIntent(Intent engineIntent) {
- self.setEngine(engineIntent);
- }
-
- /**
- * Speaks the given text using the specified queueing mode and
- * parameters.
- *
- * @param text
- * The text that should be spoken
- * @param queueMode
- * 0 for no queue (interrupts all previous utterances), 1 for
- * queued
- * @param params
- * An ArrayList of parameters. The first element of this
- * array controls the type of voice to use.
- */
- public void speak(String text, int queueMode, String[] params) {
- ArrayList speakingParams = new ArrayList();
- if (params != null) {
- speakingParams = new ArrayList(Arrays.asList(params));
- }
- self.speak(text, queueMode, speakingParams);
- }
-
- /**
- * Plays the earcon using the specified queueing mode and parameters.
- *
- * @param earcon
- * The earcon that should be played
- * @param queueMode
- * 0 for no queue (interrupts all previous utterances), 1 for
- * queued
- * @param params
- * An ArrayList of parameters.
- */
- public void playEarcon(String earcon, int queueMode, String[] params) {
- ArrayList speakingParams = new ArrayList();
- if (params != null) {
- speakingParams = new ArrayList(Arrays.asList(params));
- }
- self.playEarcon(earcon, queueMode, speakingParams);
- }
-
- /**
- * Plays the silence using the specified queueing mode and parameters.
- *
- * @param duration
- * The duration of the silence that should be played
- * @param queueMode
- * 0 for no queue (interrupts all previous utterances), 1 for
- * queued
- * @param params
- * An ArrayList of parameters.
- */
- public void playSilence(long duration, int queueMode, String[] params) {
- ArrayList speakingParams = new ArrayList();
- if (params != null) {
- speakingParams = new ArrayList(Arrays.asList(params));
- }
- self.playSilence(duration, queueMode, speakingParams);
+ private void setEngine(String engineName, String[] requestedLanguages,
+ int strictness) {
+ // TODO: Implement engine selection code here.
+ Intent engineIntent = new Intent(
+ "android.intent.action.START_TTS_ENGINE");
+ if (engineName != null) {
+ engineIntent.addCategory("android.intent.action.tts_engine."
+ + engineName);
+ }
+ for (int i = 0; i < requestedLanguages.length; i++) {
+ engineIntent.addCategory("android.intent.action.tts_lang."
+ + requestedLanguages[i]);
+ }
+ ResolveInfo[] enginesArray = new ResolveInfo[0];
+ PackageManager pm = getPackageManager();
+ enginesArray = pm.queryIntentActivities(engineIntent, 0).toArray(
+ enginesArray);
}
-
- /**
- * Stops all speech output and removes any utterances still in the
- * queue.
- */
- public void stop() {
- self.stop();
+ private void setEngine(Intent engineIntent) {
+ // TODO: Implement engine selection code here.
}
- /**
- * Returns whether or not the TTS is speaking.
- *
- * @return Boolean to indicate whether or not the TTS is speaking
- */
- public boolean isSpeaking() {
- return (self.isSpeaking && (speechQueue.size() < 1));
+ private int getEngineStatus() {
+ // TODO: Proposal - add a sanity check method that
+ // TTS engine plugins must implement.
+ return 0;
}
/**
@@ -686,8 +209,8 @@ public class TtsService extends Service implements OnCompletionListener {
* @param resId
* The resource ID of the sound within its package
*/
- public void addSpeech(String text, String packageName, int resId) {
- self.addSpeech(text, packageName, resId);
+ private void addSpeech(String text, String packageName, int resId) {
+ mUtterances.put(text, new SoundResource(packageName, resId));
}
/**
@@ -696,11 +219,11 @@ public class TtsService extends Service implements OnCompletionListener {
* @param text
* The text that should be associated with the sound resource
* @param filename
- * The filename of the sound resource. This must be a
- * complete path like: (/sdcard/mysounds/mysoundbite.mp3).
+ * The filename of the sound resource. This must be a complete
+ * path like: (/sdcard/mysounds/mysoundbite.mp3).
*/
- public void addSpeechFile(String text, String filename) {
- self.addSpeech(text, filename);
+ private void addSpeech(String text, String filename) {
+ mUtterances.put(text, new SoundResource(filename));
}
/**
@@ -713,8 +236,8 @@ public class TtsService extends Service implements OnCompletionListener {
* @param resId
* The resource ID of the sound within its package
*/
- public void addEarcon(String earcon, String packageName, int resId) {
- self.addEarcon(earcon, packageName, resId);
+ private void addEarcon(String earcon, String packageName, int resId) {
+ mEarcons.put(earcon, new SoundResource(packageName, resId));
}
/**
@@ -723,61 +246,538 @@ public class TtsService extends Service implements OnCompletionListener {
* @param earcon
* The text that should be associated with the sound resource
* @param filename
- * The filename of the sound resource. This must be a
- * complete path like: (/sdcard/mysounds/mysoundbite.mp3).
+ * The filename of the sound resource. This must be a complete
+ * path like: (/sdcard/mysounds/mysoundbite.mp3).
*/
- public void addEarconFile(String earcon, String filename) {
- self.addEarcon(earcon, filename);
+ private void addEarcon(String earcon, String filename) {
+ mEarcons.put(earcon, new SoundResource(filename));
}
/**
- * Sets the speech rate for the TTS. Note that this will only have an
- * effect on synthesized speech; it will not affect pre-recorded speech.
+ * Speaks the given text using the specified queueing mode and parameters.
*
- * @param speechRate
- * The speech rate that should be used
+ * @param text
+ * The text that should be spoken
+ * @param queueMode
+ * 0 for no queue (interrupts all previous utterances), 1 for
+ * queued
+ * @param params
+ * An ArrayList of parameters. This is not implemented for all
+ * engines.
*/
- public void setSpeechRate(int speechRate) {
- self.setSpeechRate(speechRate);
+ private void speak(String text, int queueMode, ArrayList params) {
+ if (queueMode == 0) {
+ stop();
+ }
+ mSpeechQueue.add(new SpeechItem(text, params, SpeechItem.SPEECH));
+ if (!mIsSpeaking) {
+ processSpeechQueue();
+ }
}
- // TODO: Fix comment about language
/**
- * Sets the speech rate for the TTS. Note that this will only have an
- * effect on synthesized speech; it will not affect pre-recorded speech.
+ * Plays the earcon using the specified queueing mode and parameters.
*
- * @param language
- * The language to be used. The languages are specified by
- * their IETF language tags as defined by BCP 47. This is the
- * same standard used for the lang attribute in HTML. See:
- * http://en.wikipedia.org/wiki/IETF_language_tag
+ * @param earcon
+ * The earcon that should be played
+ * @param queueMode
+ * 0 for no queue (interrupts all previous utterances), 1 for
+ * queued
+ * @param params
+ * An ArrayList of parameters. This is not implemented for all
+ * engines.
*/
- public void setLanguage(String language) {
- self.setLanguage(language);
+ private void playEarcon(String earcon, int queueMode,
+ ArrayList params) {
+ if (queueMode == 0) {
+ stop();
+ }
+ mSpeechQueue.add(new SpeechItem(earcon, params, SpeechItem.EARCON));
+ if (!mIsSpeaking) {
+ processSpeechQueue();
+ }
+ }
+
+ /**
+ * Stops all speech output and removes any utterances still in the queue.
+ */
+ private void stop() {
+ Log.i("TTS", "Stopping");
+ mSpeechQueue.clear();
+
+ nativeSynth.stop();
+ mIsSpeaking = false;
+ if (mPlayer != null) {
+ try {
+ mPlayer.stop();
+ } catch (IllegalStateException e) {
+ // Do nothing, the player is already stopped.
+ }
+ }
+ Log.i("TTS", "Stopped");
+ }
+
+ public void onCompletion(MediaPlayer arg0) {
+ processSpeechQueue();
+ }
+
+ private void playSilence(long duration, int queueMode,
+ ArrayList params) {
+ if (queueMode == 0) {
+ stop();
+ }
+ mSpeechQueue.add(new SpeechItem(duration));
+ if (!mIsSpeaking) {
+ processSpeechQueue();
+ }
+ }
+
+ private void silence(final long duration) {
+ class SilenceThread implements Runnable {
+ public void run() {
+ try {
+ Thread.sleep(duration);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } finally {
+ processSpeechQueue();
+ }
+ }
+ }
+ Thread slnc = (new Thread(new SilenceThread()));
+ slnc.setPriority(Thread.MIN_PRIORITY);
+ slnc.start();
+ }
+
+ private void speakInternalOnly(final String text,
+ final ArrayList params) {
+ class SynthThread implements Runnable {
+ public void run() {
+ boolean synthAvailable = false;
+ try {
+ synthAvailable = synthesizerLock.tryLock();
+ if (!synthAvailable) {
+ Thread.sleep(100);
+ Thread synth = (new Thread(new SynthThread()));
+ synth.setPriority(Thread.MIN_PRIORITY);
+ synth.start();
+ return;
+ }
+ nativeSynth.speak(text);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } finally {
+ // This check is needed because finally will always run;
+ // even if the
+ // method returns somewhere in the try block.
+ if (synthAvailable) {
+ synthesizerLock.unlock();
+ }
+ }
+ }
+ }
+ Thread synth = (new Thread(new SynthThread()));
+ synth.setPriority(Thread.MIN_PRIORITY);
+ synth.start();
+ }
+
+ private SoundResource getSoundResource(SpeechItem speechItem) {
+ SoundResource sr = null;
+ String text = speechItem.mText;
+ if (speechItem.mType == SpeechItem.SILENCE) {
+ // Do nothing if this is just silence
+ } else if (speechItem.mType == SpeechItem.EARCON) {
+ sr = mEarcons.get(text);
+ } else {
+ sr = mUtterances.get(text);
+ }
+ return sr;
+ }
+
+ private void dispatchSpeechCompletedCallbacks(String mark) {
+ Log.i("TTS callback", "dispatch started");
+ // Broadcast to all clients the new value.
+ final int N = mCallbacks.beginBroadcast();
+ for (int i = 0; i < N; i++) {
+ try {
+ mCallbacks.getBroadcastItem(i).markReached(mark);
+ } catch (RemoteException e) {
+ // The RemoteCallbackList will take care of removing
+ // the dead object for us.
+ }
+ }
+ mCallbacks.finishBroadcast();
+ Log.i("TTS callback", "dispatch completed to " + N);
+ }
+
+ private void processSpeechQueue() {
+ boolean speechQueueAvailable = false;
+ try {
+ speechQueueAvailable = speechQueueLock.tryLock();
+ if (!speechQueueAvailable) {
+ return;
+ }
+ if (mSpeechQueue.size() < 1) {
+ mIsSpeaking = false;
+ // Dispatch a completion here as this is the
+ // only place where speech completes normally.
+ // Nothing left to say in the queue is a special case
+ // that is always a "mark" - associated text is null.
+ dispatchSpeechCompletedCallbacks("");
+ return;
+ }
+
+ SpeechItem currentSpeechItem = mSpeechQueue.get(0);
+ mIsSpeaking = true;
+ SoundResource sr = getSoundResource(currentSpeechItem);
+ // Synth speech as needed - synthesizer should call
+ // processSpeechQueue to continue running the queue
+ Log.i("TTS processing: ", currentSpeechItem.mText);
+ if (sr == null) {
+ if (currentSpeechItem.mType == SpeechItem.SPEECH) {
+ // TODO: Split text up into smaller chunks before accepting
+ // them
+ // for processing.
+ speakInternalOnly(currentSpeechItem.mText,
+ currentSpeechItem.mParams);
+ } else {
+ // This is either silence or an earcon that was missing
+ silence(currentSpeechItem.mDuration);
+ }
+ } else {
+ cleanUpPlayer();
+ if (sr.mSourcePackageName == PKGNAME) {
+ // Utterance is part of the TTS library
+ mPlayer = MediaPlayer.create(this, sr.mResId);
+ } else if (sr.mSourcePackageName != null) {
+ // Utterance is part of the app calling the library
+ Context ctx;
+ try {
+ ctx = this.createPackageContext(sr.mSourcePackageName,
+ 0);
+ } catch (NameNotFoundException e) {
+ e.printStackTrace();
+ mSpeechQueue.remove(0); // Remove it from the queue and
+ // move on
+ mIsSpeaking = false;
+ return;
+ }
+ mPlayer = MediaPlayer.create(ctx, sr.mResId);
+ } else {
+ // Utterance is coming from a file
+ mPlayer = MediaPlayer.create(this, Uri.parse(sr.mFilename));
+ }
+
+ // Check if Media Server is dead; if it is, clear the queue and
+ // give up for now - hopefully, it will recover itself.
+ if (mPlayer == null) {
+ mSpeechQueue.clear();
+ mIsSpeaking = false;
+ return;
+ }
+ mPlayer.setOnCompletionListener(this);
+ try {
+ mPlayer.start();
+ } catch (IllegalStateException e) {
+ mSpeechQueue.clear();
+ mIsSpeaking = false;
+ cleanUpPlayer();
+ return;
+ }
+ }
+ if (mSpeechQueue.size() > 0) {
+ mSpeechQueue.remove(0);
+ }
+ } finally {
+ // This check is needed because finally will always run; even if the
+ // method returns somewhere in the try block.
+ if (speechQueueAvailable) {
+ speechQueueLock.unlock();
+ }
+ }
+ }
+
+ private void cleanUpPlayer() {
+ if (mPlayer != null) {
+ mPlayer.release();
+ mPlayer = null;
+ }
}
/**
- * Speaks the given text using the specified queueing mode and
+ * Synthesizes the given text using the specified queuing mode and
* parameters.
*
* @param text
* The String of text that should be synthesized
* @param params
- * An ArrayList of parameters. The first element of this
- * array controls the type of voice to use.
+ * An ArrayList of parameters. The first element of this array
+ * controls the type of voice to use.
* @param filename
- * The string that gives the full output filename; it should
- * be something like "/sdcard/myappsounds/mysound.wav".
+ * The string that gives the full output filename; it should be
+ * something like "/sdcard/myappsounds/mysound.wav".
* @return A boolean that indicates if the synthesis succeeded
*/
- public boolean synthesizeToFile(String text, String[] params,
- String filename) {
- ArrayList speakingParams = new ArrayList();
- if (params != null) {
- speakingParams = new ArrayList(Arrays.asList(params));
- }
- return self.synthesizeToFile(text, speakingParams, filename, true);
+ private boolean synthesizeToFile(String text, ArrayList params,
+ String filename, boolean calledFromApi) {
+ // Only stop everything if this is a call made by an outside app trying
+ // to
+ // use the API. Do NOT stop if this is a call from within the service as
+ // clearing the speech queue here would be a mistake.
+ if (calledFromApi) {
+ stop();
+ }
+ Log.i("TTS", "Synthesizing to " + filename);
+ boolean synthAvailable = false;
+ try {
+ synthAvailable = synthesizerLock.tryLock();
+ if (!synthAvailable) {
+ return false;
+ }
+ // Don't allow a filename that is too long
+ // TODO use platform constant
+ if (filename.length() > 250) {
+ return false;
+ }
+ nativeSynth.synthesizeToFile(text, filename);
+ } finally {
+ // This check is needed because finally will always run; even if the
+ // method returns somewhere in the try block.
+ if (synthAvailable) {
+ synthesizerLock.unlock();
+ }
+ }
+ Log.i("TTS", "Completed synthesis for " + filename);
+ return true;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (ACTION.equals(intent.getAction())) {
+ for (String category : intent.getCategories()) {
+ if (category.equals(CATEGORY)) {
+ return mBinder;
+ }
+ }
+ }
+ return null;
}
- };
+
+ private final ITts.Stub mBinder = new Stub() {
+
+ public void registerCallback(ITtsCallback cb) {
+ if (cb != null)
+ mCallbacks.register(cb);
+ }
+
+ public void unregisterCallback(ITtsCallback cb) {
+ if (cb != null)
+ mCallbacks.unregister(cb);
+ }
+
+ /**
+ * Gives a hint about the type of engine that is preferred.
+ *
+ * @param selectedEngine
+ * The TTS engine that should be used
+ */
+ public void setEngine(String engineName, String[] supportedLanguages,
+ int strictness) {
+ mSelf.setEngine(engineName, supportedLanguages, strictness);
+ }
+
+ /**
+ * Specifies exactly what the engine has to support. Will always be
+ * considered "strict"; can be used for implementing
+ * optional/experimental features that are not supported by all engines.
+ *
+ * @param engineIntent
+ * An intent that specifies exactly what the engine has to
+ * support.
+ */
+ public void setEngineWithIntent(Intent engineIntent) {
+ mSelf.setEngine(engineIntent);
+ }
+
+ /**
+ * Speaks the given text using the specified queueing mode and
+ * parameters.
+ *
+ * @param text
+ * The text that should be spoken
+ * @param queueMode
+ * 0 for no queue (interrupts all previous utterances), 1 for
+ * queued
+ * @param params
+ * An ArrayList of parameters. The first element of this
+ * array controls the type of voice to use.
+ */
+ public void speak(String text, int queueMode, String[] params) {
+ ArrayList speakingParams = new ArrayList();
+ if (params != null) {
+ speakingParams = new ArrayList(Arrays.asList(params));
+ }
+ mSelf.speak(text, queueMode, speakingParams);
+ }
+
+ /**
+ * Plays the earcon using the specified queueing mode and parameters.
+ *
+ * @param earcon
+ * The earcon that should be played
+ * @param queueMode
+ * 0 for no queue (interrupts all previous utterances), 1 for
+ * queued
+ * @param params
+ * An ArrayList of parameters.
+ */
+ public void playEarcon(String earcon, int queueMode, String[] params) {
+ ArrayList speakingParams = new ArrayList();
+ if (params != null) {
+ speakingParams = new ArrayList(Arrays.asList(params));
+ }
+ mSelf.playEarcon(earcon, queueMode, speakingParams);
+ }
+
+ /**
+ * Plays the silence using the specified queueing mode and parameters.
+ *
+ * @param duration
+ * The duration of the silence that should be played
+ * @param queueMode
+ * 0 for no queue (interrupts all previous utterances), 1 for
+ * queued
+ * @param params
+ * An ArrayList of parameters.
+ */
+ public void playSilence(long duration, int queueMode, String[] params) {
+ ArrayList speakingParams = new ArrayList();
+ if (params != null) {
+ speakingParams = new ArrayList(Arrays.asList(params));
+ }
+ mSelf.playSilence(duration, queueMode, speakingParams);
+ }
+
+
+ /**
+ * Stops all speech output and removes any utterances still in the
+ * queue.
+ */
+ public void stop() {
+ mSelf.stop();
+ }
+
+ /**
+ * Returns whether or not the TTS is speaking.
+ *
+ * @return Boolean to indicate whether or not the TTS is speaking
+ */
+ public boolean isSpeaking() {
+ return (mSelf.mIsSpeaking && (mSpeechQueue.size() < 1));
+ }
+
+ /**
+ * Adds a sound resource to the TTS.
+ *
+ * @param text
+ * The text that should be associated with the sound resource
+ * @param packageName
+ * The name of the package which has the sound resource
+ * @param resId
+ * The resource ID of the sound within its package
+ */
+ public void addSpeech(String text, String packageName, int resId) {
+ mSelf.addSpeech(text, packageName, resId);
+ }
+
+ /**
+ * Adds a sound resource to the TTS.
+ *
+ * @param text
+ * The text that should be associated with the sound resource
+ * @param filename
+ * The filename of the sound resource. This must be a
+ * complete path like: (/sdcard/mysounds/mysoundbite.mp3).
+ */
+ public void addSpeechFile(String text, String filename) {
+ mSelf.addSpeech(text, filename);
+ }
+
+ /**
+ * Adds a sound resource to the TTS as an earcon.
+ *
+ * @param earcon
+ * The text that should be associated with the sound resource
+ * @param packageName
+ * The name of the package which has the sound resource
+ * @param resId
+ * The resource ID of the sound within its package
+ */
+ public void addEarcon(String earcon, String packageName, int resId) {
+ mSelf.addEarcon(earcon, packageName, resId);
+ }
+
+ /**
+ * Adds a sound resource to the TTS as an earcon.
+ *
+ * @param earcon
+ * The text that should be associated with the sound resource
+ * @param filename
+ * The filename of the sound resource. This must be a
+ * complete path like: (/sdcard/mysounds/mysoundbite.mp3).
+ */
+ public void addEarconFile(String earcon, String filename) {
+ mSelf.addEarcon(earcon, filename);
+ }
+
+ /**
+ * Sets the speech rate for the TTS. Note that this will only have an
+ * effect on synthesized speech; it will not affect pre-recorded speech.
+ *
+ * @param speechRate
+ * The speech rate that should be used
+ */
+ public void setSpeechRate(int speechRate) {
+ mSelf.setSpeechRate(speechRate);
+ }
+
+ // TODO: Fix comment about language
+ /**
+ * Sets the speech rate for the TTS. Note that this will only have an
+ * effect on synthesized speech; it will not affect pre-recorded speech.
+ *
+ * @param language
+ * The language to be used. The languages are specified by
+ * their IETF language tags as defined by BCP 47. This is the
+ * same standard used for the lang attribute in HTML. See:
+ * http://en.wikipedia.org/wiki/IETF_language_tag
+ */
+ public void setLanguage(String language) {
+ mSelf.setLanguage(language);
+ }
+
+ /**
+ * Speaks the given text using the specified queueing mode and
+ * parameters.
+ *
+ * @param text
+ * The String of text that should be synthesized
+ * @param params
+ * An ArrayList of parameters. The first element of this
+ * array controls the type of voice to use.
+ * @param filename
+ * The string that gives the full output filename; it should
+ * be something like "/sdcard/myappsounds/mysound.wav".
+ * @return A boolean that indicates if the synthesis succeeded
+ */
+ public boolean synthesizeToFile(String text, String[] params,
+ String filename) {
+ ArrayList speakingParams = new ArrayList();
+ if (params != null) {
+ speakingParams = new ArrayList(Arrays.asList(params));
+ }
+ return mSelf.synthesizeToFile(text, speakingParams, filename, true);
+ }
+ };
}
--
cgit v1.2.3-59-g8ed1b
From ce16d787bdcefa393cb2da09652b580ba94b94a9 Mon Sep 17 00:00:00 2001
From: Romain Guy
Date: Tue, 2 Jun 2009 15:15:12 -0700
Subject: Fixes #1890914.
Bright theme's background color should be #fff9f9f9 instead of pure white (#ffffffff).
---
core/res/res/values/colors.xml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 96369f4099a4..f67f04cf2469 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -18,7 +18,7 @@
*/
-->
- #ffffffff
+ #fff9f9f9
#ff1a1a1a
#ff000000
#ff000000
@@ -37,7 +37,7 @@
#323232
#80323232
#808080
- #ffffffff
+ #fff9f9f9
#ff000000
#ffffffff
#80000000
@@ -58,7 +58,7 @@
@drawable/editbox_dropdown_background_dark
@drawable/editbox_dropdown_background
- #ffffffff
+ #fff9f9f9
#ff0092f4
--
cgit v1.2.3-59-g8ed1b
From 3dfd0e131efab40df4beba1e40d232a4fcf807dc Mon Sep 17 00:00:00 2001
From: Guang Zhu
Date: Tue, 2 Jun 2009 15:42:48 -0700
Subject: Fixed issue where code for extracting scripts was eclipsed by runTest
method.
---
tests/DumpRenderTree/assets/run_reliability_tests.py | 8 ++------
.../src/com/android/dumprendertree/ReliabilityTest.java | 3 +--
2 files changed, 3 insertions(+), 8 deletions(-)
diff --git a/tests/DumpRenderTree/assets/run_reliability_tests.py b/tests/DumpRenderTree/assets/run_reliability_tests.py
index c12c783060c4..84b9501620da 100755
--- a/tests/DumpRenderTree/assets/run_reliability_tests.py
+++ b/tests/DumpRenderTree/assets/run_reliability_tests.py
@@ -110,8 +110,8 @@ def main(options, args):
# Call ReliabilityTestsAutoTest#startReliabilityTests
test_cmd = (test_cmd_prefix + " -e class "
"com.android.dumprendertree.ReliabilityTest#"
- "runTest -e timeout %d %s" %
- (timeout_ms, test_cmd_postfix))
+ "runReliabilityTest -e timeout %s %s" %
+ (str(timeout_ms), test_cmd_postfix))
adb_output = subprocess.Popen(test_cmd, shell=True,
stdout=subprocess.PIPE,
@@ -125,10 +125,6 @@ def main(options, args):
crashed_tests.append(crashed_test)
logging.info("Resuming reliability test runner...")
- test_cmd = (test_cmd_prefix + " -e class "
- "com.android.dumprendertree.ReliabilityTest#"
- "runTest -e timeout %d %s" %
- (timeout_ms, test_cmd_postfix))
adb_output = subprocess.Popen(test_cmd, shell=True, stdout=subprocess.PIPE,
stderr=subprocess.PIPE).communicate()[0]
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
index 081ddafa24ec..aa3940ed9b04 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
@@ -30,8 +30,7 @@ public class ReliabilityTest extends ActivityInstrumentationTestCase2
Date: Tue, 2 Jun 2009 16:02:31 -0700
Subject: Corrected the name of the native library the SynthProxy class loads
(libttssynthproxy instead of libsynthproxy) to match the lib name from
frameworks/base/tts/jni/Android.mk.
---
tts/java/android/tts/SynthProxy.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tts/java/android/tts/SynthProxy.java b/tts/java/android/tts/SynthProxy.java
index 4ed975450d3c..e065f407599a 100755
--- a/tts/java/android/tts/SynthProxy.java
+++ b/tts/java/android/tts/SynthProxy.java
@@ -120,7 +120,7 @@ public class SynthProxy {
}
static {
- System.loadLibrary("synthproxy");
+ System.loadLibrary("ttssynthproxy");
}
private final static String TAG = "SynthProxy";
--
cgit v1.2.3-59-g8ed1b
From 6a669fac385b51b8bb01844b77a9a43840dda854 Mon Sep 17 00:00:00 2001
From: Nick Pelly
Date: Tue, 2 Jun 2009 15:57:18 -0700
Subject: Implement and expose SCO socket support in BluetoothSocket.java.
Implement L2CAP socket support, but do not expose it (untested).
NEXT: Switch to Builder style constructor instead of factory method.
---
.../android/bluetooth/BluetoothServerSocket.java | 44 ++++-
core/java/android/bluetooth/BluetoothSocket.java | 53 +++--
core/jni/android_bluetooth_BluetoothSocket.cpp | 219 ++++++++++++++++++---
3 files changed, 264 insertions(+), 52 deletions(-)
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index ca467011c91e..f3baeab18dee 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -27,7 +27,7 @@ import java.io.IOException;
* RFCOMM is a connection orientated, streaming transport over Bluetooth. It is
* also known as the Serial Port Profile (SPP).
*
- * TODO: Consider implementing SCO and L2CAP sockets.
+ * TODO: Consider exposing L2CAP sockets.
* TODO: Clean up javadoc grammer and formatting.
* TODO: Remove @hide
* @hide
@@ -45,9 +45,10 @@ public final class BluetoothServerSocket implements Closeable {
* insufficient permissions.
*/
public static BluetoothServerSocket listenUsingRfcommOn(int port) throws IOException {
- BluetoothServerSocket socket = new BluetoothServerSocket(true, true);
+ BluetoothServerSocket socket = new BluetoothServerSocket(
+ BluetoothSocket.TYPE_RFCOMM, true, true, port);
try {
- socket.mSocket.bindListenNative(port);
+ socket.mSocket.bindListenNative();
} catch (IOException e) {
try {
socket.close();
@@ -65,9 +66,31 @@ public final class BluetoothServerSocket implements Closeable {
* insufficient permissions.
*/
public static BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
- BluetoothServerSocket socket = new BluetoothServerSocket(false, false);
+ BluetoothServerSocket socket = new BluetoothServerSocket(
+ BluetoothSocket.TYPE_RFCOMM, false, false, port);
try {
- socket.mSocket.bindListenNative(port);
+ socket.mSocket.bindListenNative();
+ } catch (IOException e) {
+ try {
+ socket.close();
+ } catch (IOException e2) { }
+ throw e;
+ }
+ return socket;
+ }
+
+ /**
+ * Construct a SCO server socket.
+ * Call #accept to retrieve connections to this socket.
+ * @return A SCO BluetoothServerSocket
+ * @throws IOException On error, for example Bluetooth not available, or
+ * insufficient permissions.
+ */
+ public static BluetoothServerSocket listenUsingScoOn() throws IOException {
+ BluetoothServerSocket socket = new BluetoothServerSocket(
+ BluetoothSocket.TYPE_SCO, false, false, -1);
+ try {
+ socket.mSocket.bindListenNative();
} catch (IOException e) {
try {
socket.close();
@@ -79,13 +102,16 @@ public final class BluetoothServerSocket implements Closeable {
/**
* Construct a socket for incoming connections.
- * @param auth Require the remote device to be authenticated
- * @param encrypt Require the connection to be encrypted
+ * @param type type of socket
+ * @param auth require the remote device to be authenticated
+ * @param encrypt require the connection to be encrypted
+ * @param port remote port
* @throws IOException On error, for example Bluetooth not available, or
* insufficient priveleges
*/
- private BluetoothServerSocket(boolean auth, boolean encrypt) throws IOException {
- mSocket = new BluetoothSocket(-1, auth, encrypt, null, -1);
+ private BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port)
+ throws IOException {
+ mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port);
}
/**
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 670146b245ee..de1f32601db2 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -29,13 +29,19 @@ import java.io.OutputStream;
* RFCOMM is a connection orientated, streaming transport over Bluetooth. It is
* also known as the Serial Port Profile (SPP).
*
- * TODO: Consider implementing SCO and L2CAP sockets.
+ * TODO: Consider exposing L2CAP sockets.
* TODO: Clean up javadoc grammer and formatting.
* TODO: Remove @hide
* @hide
*/
public final class BluetoothSocket implements Closeable {
- private final int mPort;
+ /** Keep TYPE_RFCOMM etc in sync with BluetoothSocket.cpp */
+ /*package*/ static final int TYPE_RFCOMM = 1;
+ /*package*/ static final int TYPE_SCO = 2;
+ /*package*/ static final int TYPE_L2CAP = 3;
+
+ private final int mType; /* one of TYPE_RFCOMM etc */
+ private final int mPort; /* RFCOMM channel or L2CAP psm */
private final String mAddress; /* remote address */
private final boolean mAuth;
private final boolean mEncrypt;
@@ -57,7 +63,7 @@ public final class BluetoothSocket implements Closeable {
*/
public static BluetoothSocket createRfcommSocket(String address, int port)
throws IOException {
- return new BluetoothSocket(-1, true, true, address, port);
+ return new BluetoothSocket(TYPE_RFCOMM, -1, true, true, address, port);
}
/**
@@ -74,11 +80,25 @@ public final class BluetoothSocket implements Closeable {
*/
public static BluetoothSocket createInsecureRfcommSocket(String address, int port)
throws IOException {
- return new BluetoothSocket(-1, false, false, address, port);
+ return new BluetoothSocket(TYPE_RFCOMM, -1, false, false, address, port);
+ }
+
+ /**
+ * Construct a SCO socket ready to start an outgoing connection.
+ * Call #connect on the returned #BluetoothSocket to begin the connection.
+ * @param address remote Bluetooth address that this socket can connect to
+ * @return a SCO BluetoothSocket
+ * @throws IOException on error, for example Bluetooth not available, or
+ * insufficient permissions.
+ */
+ public static BluetoothSocket createScoSocket(String address, int port)
+ throws IOException {
+ return new BluetoothSocket(TYPE_SCO, -1, true, true, address, port);
}
/**
* Construct a Bluetooth.
+ * @param type type of socket
* @param fd fd to use for connected socket, or -1 for a new socket
* @param auth require the remote device to be authenticated
* @param encrypt require the connection to be encrypted
@@ -87,8 +107,9 @@ public final class BluetoothSocket implements Closeable {
* @throws IOException On error, for example Bluetooth not available, or
* insufficient priveleges
*/
- /*package*/ BluetoothSocket(int fd, boolean auth, boolean encrypt, String address, int port)
- throws IOException {
+ /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address,
+ int port) throws IOException {
+ mType = type;
mAuth = auth;
mEncrypt = encrypt;
mAddress = address;
@@ -120,7 +141,7 @@ public final class BluetoothSocket implements Closeable {
* @throws IOException On error, for example connection failure
*/
public void connect() throws IOException {
- connectNative(mAddress, mPort, -1);
+ connectNative();
}
/**
@@ -163,14 +184,14 @@ public final class BluetoothSocket implements Closeable {
return mOutputStream;
}
- private native void initSocketNative();
- private native void initSocketFromFdNative(int fd);
- private native void connectNative(String address, int port, int timeout);
- /*package*/ native void bindListenNative(int port) throws IOException;
+ private native void initSocketNative() throws IOException;
+ private native void initSocketFromFdNative(int fd) throws IOException;
+ private native void connectNative() throws IOException;
+ /*package*/ native void bindListenNative() throws IOException;
/*package*/ native BluetoothSocket acceptNative(int timeout) throws IOException;
- /*package*/ native int availableNative();
- /*package*/ native int readNative(byte[] b, int offset, int length);
- /*package*/ native int writeNative(byte[] b, int offset, int length);
- /*package*/ native void closeNative();
- private native void destroyNative();
+ /*package*/ native int availableNative() throws IOException;
+ /*package*/ native int readNative(byte[] b, int offset, int length) throws IOException;
+ /*package*/ native int writeNative(byte[] b, int offset, int length) throws IOException;
+ /*package*/ native void closeNative() throws IOException;
+ private native void destroyNative() throws IOException;
}
diff --git a/core/jni/android_bluetooth_BluetoothSocket.cpp b/core/jni/android_bluetooth_BluetoothSocket.cpp
index e9c04a54f49a..9c4f7c7336f0 100644
--- a/core/jni/android_bluetooth_BluetoothSocket.cpp
+++ b/core/jni/android_bluetooth_BluetoothSocket.cpp
@@ -31,16 +31,29 @@
#ifdef HAVE_BLUETOOTH
#include
#include
+#include
+#include
#endif
+#define TYPE_AS_STR(t) \
+ ((t) == TYPE_RFCOMM ? "RFCOMM" : ((t) == TYPE_SCO ? "SCO" : "L2CAP"))
+
namespace android {
static jfieldID field_mAuth; /* read-only */
static jfieldID field_mEncrypt; /* read-only */
+static jfieldID field_mType; /* read-only */
+static jfieldID field_mAddress; /* read-only */
+static jfieldID field_mPort; /* read-only */
static jfieldID field_mSocketData;
static jmethodID method_BluetoothSocket_ctor;
static jclass class_BluetoothSocket;
+/* Keep TYPE_RFCOMM etc in sync with BluetoothSocket.java */
+static const int TYPE_RFCOMM = 1;
+static const int TYPE_SCO = 2;
+static const int TYPE_L2CAP = 3; // TODO: Test l2cap code paths
+
static struct asocket *get_socketData(JNIEnv *env, jobject obj) {
struct asocket *s =
(struct asocket *) env->GetIntField(obj, field_mSocketData);
@@ -76,9 +89,25 @@ static void initSocketNative(JNIEnv *env, jobject obj) {
int lm = 0;
jboolean auth;
jboolean encrypt;
+ jint type;
+
+ type = env->GetIntField(obj, field_mType);
+
+ switch (type) {
+ case TYPE_RFCOMM:
+ fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ break;
+ case TYPE_SCO:
+ fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+ break;
+ case TYPE_L2CAP:
+ fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+ break;
+ default:
+ jniThrowIOException(env, ENOSYS);
+ return;
+ }
- /*TODO: do not hardcode to rfcomm */
- fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
if (fd < 0) {
LOGV("socket() failed, throwing");
jniThrowIOException(env, errno);
@@ -88,8 +117,17 @@ static void initSocketNative(JNIEnv *env, jobject obj) {
auth = env->GetBooleanField(obj, field_mAuth);
encrypt = env->GetBooleanField(obj, field_mEncrypt);
- lm |= auth ? RFCOMM_LM_AUTH : 0;
- lm |= encrypt? RFCOMM_LM_ENCRYPT : 0;
+ /* kernel does not yet support LM for SCO */
+ switch (type) {
+ case TYPE_RFCOMM:
+ lm |= auth ? RFCOMM_LM_AUTH : 0;
+ lm |= encrypt? RFCOMM_LM_ENCRYPT : 0;
+ break;
+ case TYPE_L2CAP:
+ lm |= auth ? L2CAP_LM_AUTH : 0;
+ lm |= encrypt? L2CAP_LM_ENCRYPT : 0;
+ break;
+ }
if (lm) {
if (setsockopt(fd, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm))) {
@@ -99,36 +137,83 @@ static void initSocketNative(JNIEnv *env, jobject obj) {
}
}
+ LOGV("...fd %d created (%s, lm = %x)", fd, TYPE_AS_STR(type), lm);
+
initSocketFromFdNative(env, obj, fd);
return;
#endif
jniThrowIOException(env, ENOSYS);
}
-static void connectNative(JNIEnv *env, jobject obj, jstring address,
- jint port, jint timeout) {
+static void connectNative(JNIEnv *env, jobject obj) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
int ret;
- struct sockaddr_rc addr;
+ jint type;
const char *c_address;
+ jstring address;
+ bdaddr_t bdaddress;
+ socklen_t addr_sz;
+ struct sockaddr *addr;
struct asocket *s = get_socketData(env, obj);
if (!s)
return;
- addr.rc_family = AF_BLUETOOTH;
- addr.rc_channel = port;
+ type = env->GetIntField(obj, field_mType);
+
+ /* parse address into bdaddress */
+ address = (jstring) env->GetObjectField(obj, field_mAddress);
c_address = env->GetStringUTFChars(address, NULL);
- if (get_bdaddr((const char *)c_address, &addr.rc_bdaddr)) {
+ if (get_bdaddr(c_address, &bdaddress)) {
env->ReleaseStringUTFChars(address, c_address);
jniThrowIOException(env, EINVAL);
return;
}
env->ReleaseStringUTFChars(address, c_address);
- ret = asocket_connect(s, (struct sockaddr *)&addr, sizeof(addr), timeout);
+ switch (type) {
+ case TYPE_RFCOMM:
+ struct sockaddr_rc addr_rc;
+ addr = (struct sockaddr *)&addr_rc;
+ addr_sz = sizeof(addr_rc);
+
+ memset(addr, 0, addr_sz);
+ addr_rc.rc_family = AF_BLUETOOTH;
+ addr_rc.rc_channel = env->GetIntField(obj, field_mPort);
+ memcpy(&addr_rc.rc_bdaddr, &bdaddress, sizeof(bdaddr_t));
+
+ break;
+ case TYPE_SCO:
+ struct sockaddr_sco addr_sco;
+ addr = (struct sockaddr *)&addr_sco;
+ addr_sz = sizeof(addr_sco);
+
+ memset(addr, 0, addr_sz);
+ addr_sco.sco_family = AF_BLUETOOTH;
+ memcpy(&addr_sco.sco_bdaddr, &bdaddress, sizeof(bdaddr_t));
+
+ break;
+ case TYPE_L2CAP:
+ struct sockaddr_l2 addr_l2;
+ addr = (struct sockaddr *)&addr_l2;
+ addr_sz = sizeof(addr_l2);
+
+ memset(addr, 0, addr_sz);
+ addr_l2.l2_family = AF_BLUETOOTH;
+ addr_l2.l2_psm = env->GetIntField(obj, field_mPort);
+ memcpy(&addr_l2.l2_bdaddr, &bdaddress, sizeof(bdaddr_t));
+
+ break;
+ default:
+ jniThrowIOException(env, ENOSYS);
+ return;
+ }
+
+ ret = asocket_connect(s, addr, addr_sz, -1);
+ LOGV("...connect(%d, %s) = %d (errno %d)",
+ s->fd, TYPE_AS_STR(type), ret, errno);
if (ret)
jniThrowIOException(env, errno);
@@ -138,22 +223,57 @@ static void connectNative(JNIEnv *env, jobject obj, jstring address,
jniThrowIOException(env, ENOSYS);
}
-static void bindListenNative(JNIEnv *env, jobject obj, jint port) {
+static void bindListenNative(JNIEnv *env, jobject obj) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
- struct sockaddr_rc addr;
+ jint type;
+ socklen_t addr_sz;
+ struct sockaddr *addr;
+ bdaddr_t bdaddr = *BDADDR_ANY;
struct asocket *s = get_socketData(env, obj);
if (!s)
return;
- memset(&addr, 0, sizeof(struct sockaddr_rc));
- addr.rc_family = AF_BLUETOOTH;
- addr.rc_bdaddr = *BDADDR_ANY;
- addr.rc_channel = port;
+ type = env->GetIntField(obj, field_mType);
+
+ switch (type) {
+ case TYPE_RFCOMM:
+ struct sockaddr_rc addr_rc;
+ addr = (struct sockaddr *)&addr_rc;
+ addr_sz = sizeof(addr_rc);
+
+ memset(addr, 0, addr_sz);
+ addr_rc.rc_family = AF_BLUETOOTH;
+ addr_rc.rc_channel = env->GetIntField(obj, field_mPort);
+ memcpy(&addr_rc.rc_bdaddr, &bdaddr, sizeof(bdaddr_t));
+ break;
+ case TYPE_SCO:
+ struct sockaddr_sco addr_sco;
+ addr = (struct sockaddr *)&addr_sco;
+ addr_sz = sizeof(addr_sco);
+
+ memset(addr, 0, addr_sz);
+ addr_sco.sco_family = AF_BLUETOOTH;
+ memcpy(&addr_sco.sco_bdaddr, &bdaddr, sizeof(bdaddr_t));
+ break;
+ case TYPE_L2CAP:
+ struct sockaddr_l2 addr_l2;
+ addr = (struct sockaddr *)&addr_l2;
+ addr_sz = sizeof(addr_l2);
+
+ memset(addr, 0, addr_sz);
+ addr_l2.l2_family = AF_BLUETOOTH;
+ addr_l2.l2_psm = env->GetIntField(obj, field_mPort);
+ memcpy(&addr_l2.l2_bdaddr, &bdaddr, sizeof(bdaddr_t));
+ break;
+ default:
+ jniThrowIOException(env, ENOSYS);
+ return;
+ }
- if (bind(s->fd, (struct sockaddr *)&addr, sizeof(addr))) {
+ if (bind(s->fd, addr, addr_sz)) {
jniThrowIOException(env, errno);
return;
}
@@ -173,10 +293,12 @@ static jobject acceptNative(JNIEnv *env, jobject obj, int timeout) {
LOGV(__FUNCTION__);
int fd;
- struct sockaddr_rc addr;
- int addrlen = sizeof(addr);
+ jint type;
+ struct sockaddr *addr;
+ socklen_t addr_sz;
jstring addr_jstr;
char addr_cstr[BTADDR_SIZE];
+ bdaddr_t *bdaddr;
jboolean auth;
jboolean encrypt;
@@ -185,7 +307,39 @@ static jobject acceptNative(JNIEnv *env, jobject obj, int timeout) {
if (!s)
return NULL;
- fd = asocket_accept(s, (struct sockaddr *)&addr, &addrlen, timeout);
+ type = env->GetIntField(obj, field_mType);
+
+ switch (type) {
+ case TYPE_RFCOMM:
+ struct sockaddr_rc addr_rc;
+ addr = (struct sockaddr *)&addr_rc;
+ addr_sz = sizeof(addr_rc);
+ bdaddr = &addr_rc.rc_bdaddr;
+ memset(addr, 0, addr_sz);
+ break;
+ case TYPE_SCO:
+ struct sockaddr_sco addr_sco;
+ addr = (struct sockaddr *)&addr_sco;
+ addr_sz = sizeof(addr_sco);
+ bdaddr = &addr_sco.sco_bdaddr;
+ memset(addr, 0, addr_sz);
+ break;
+ case TYPE_L2CAP:
+ struct sockaddr_l2 addr_l2;
+ addr = (struct sockaddr *)&addr_l2;
+ addr_sz = sizeof(addr_l2);
+ bdaddr = &addr_l2.l2_bdaddr;
+ memset(addr, 0, addr_sz);
+ break;
+ default:
+ jniThrowIOException(env, ENOSYS);
+ return NULL;
+ }
+
+ fd = asocket_accept(s, addr, &addr_sz, timeout);
+
+ LOGV("...accept(%d, %s) = %d (errno %d)",
+ s->fd, TYPE_AS_STR(type), fd, errno);
if (fd < 0) {
jniThrowIOException(env, errno);
@@ -195,10 +349,12 @@ static jobject acceptNative(JNIEnv *env, jobject obj, int timeout) {
/* Connected - return new BluetoothSocket */
auth = env->GetBooleanField(obj, field_mAuth);
encrypt = env->GetBooleanField(obj, field_mEncrypt);
- get_bdaddr_as_string(&addr.rc_bdaddr, addr_cstr);
+
+ get_bdaddr_as_string(bdaddr, addr_cstr);
+
addr_jstr = env->NewStringUTF(addr_cstr);
- return env->NewObject(class_BluetoothSocket, method_BluetoothSocket_ctor, fd,
- auth, encrypt, addr_jstr, -1);
+ return env->NewObject(class_BluetoothSocket, method_BluetoothSocket_ctor,
+ type, fd, auth, encrypt, addr_jstr, -1);
#endif
jniThrowIOException(env, ENOSYS);
@@ -304,6 +460,8 @@ static void closeNative(JNIEnv *env, jobject obj) {
return;
asocket_abort(s);
+
+ LOGV("...asocket_abort(%d) complete", s->fd);
return;
#endif
jniThrowIOException(env, ENOSYS);
@@ -313,10 +471,14 @@ static void destroyNative(JNIEnv *env, jobject obj) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
struct asocket *s = get_socketData(env, obj);
+ int fd = s->fd;
+
if (!s)
return;
asocket_destroy(s);
+
+ LOGV("...asocket_destroy(%d) complete", fd);
return;
#endif
jniThrowIOException(env, ENOSYS);
@@ -325,8 +487,8 @@ static void destroyNative(JNIEnv *env, jobject obj) {
static JNINativeMethod sMethods[] = {
{"initSocketNative", "()V", (void*) initSocketNative},
{"initSocketFromFdNative", "(I)V", (void*) initSocketFromFdNative},
- {"connectNative", "(Ljava/lang/String;II)V", (void *) connectNative},
- {"bindListenNative", "(I)V", (void *) bindListenNative},
+ {"connectNative", "()V", (void *) connectNative},
+ {"bindListenNative", "()V", (void *) bindListenNative},
{"acceptNative", "(I)Landroid/bluetooth/BluetoothSocket;", (void *) acceptNative},
{"availableNative", "()I", (void *) availableNative},
{"readNative", "([BII)I", (void *) readNative},
@@ -340,10 +502,13 @@ int register_android_bluetooth_BluetoothSocket(JNIEnv *env) {
if (clazz == NULL)
return -1;
class_BluetoothSocket = (jclass) env->NewGlobalRef(clazz);
+ field_mType = env->GetFieldID(clazz, "mType", "I");
+ field_mAddress = env->GetFieldID(clazz, "mAddress", "Ljava/lang/String;");
+ field_mPort = env->GetFieldID(clazz, "mPort", "I");
field_mAuth = env->GetFieldID(clazz, "mAuth", "Z");
field_mEncrypt = env->GetFieldID(clazz, "mEncrypt", "Z");
field_mSocketData = env->GetFieldID(clazz, "mSocketData", "I");
- method_BluetoothSocket_ctor = env->GetMethodID(clazz, "", "(IZZLjava/lang/String;I)V");
+ method_BluetoothSocket_ctor = env->GetMethodID(clazz, "", "(IIZZLjava/lang/String;I)V");
return AndroidRuntime::registerNativeMethods(env,
"android/bluetooth/BluetoothSocket", sMethods, NELEM(sMethods));
}
--
cgit v1.2.3-59-g8ed1b
From 03228fa8f950dfee3f679d280f98d040dea397dc Mon Sep 17 00:00:00 2001
From: johnwang
Date: Tue, 2 Jun 2009 14:58:35 -0700
Subject: Allow response payload on RIL_REQUEST error
Handle response payload while RIL_REQUEST returns error.
modified: gsm/GsmServiceStateTracker.java
---
.../java/com/android/internal/telephony/RIL.java | 51 ++++++++++++----------
1 file changed, 27 insertions(+), 24 deletions(-)
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index f206d9010810..0cdeefff7b6a 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -165,7 +165,7 @@ class RILRequest {
}
void
- onError(int error) {
+ onError(int error, Object ret) {
CommandException ex;
ex = CommandException.fromRilErrno(error);
@@ -175,7 +175,7 @@ class RILRequest {
+ " error: " + ex);
if (mResult != null) {
- AsyncResult.forMessage(mResult, null, ex);
+ AsyncResult.forMessage(mResult, ret, ex);
mResult.sendToTarget();
}
@@ -290,7 +290,7 @@ public final class RIL extends BaseCommands implements CommandsInterface {
s = mSocket;
if (s == null) {
- rr.onError(RADIO_NOT_AVAILABLE);
+ rr.onError(RADIO_NOT_AVAILABLE, null);
rr.release();
mRequestMessagesPending--;
alreadySubtracted = true;
@@ -331,7 +331,7 @@ public final class RIL extends BaseCommands implements CommandsInterface {
// make sure this request has not already been handled,
// eg, if RILReceiver cleared the list.
if (req != null || !alreadySubtracted) {
- rr.onError(RADIO_NOT_AVAILABLE);
+ rr.onError(RADIO_NOT_AVAILABLE, null);
rr.release();
}
} catch (RuntimeException exc) {
@@ -340,7 +340,7 @@ public final class RIL extends BaseCommands implements CommandsInterface {
// make sure this request has not already been handled,
// eg, if RILReceiver cleared the list.
if (req != null || !alreadySubtracted) {
- rr.onError(GENERIC_FAILURE);
+ rr.onError(GENERIC_FAILURE, null);
rr.release();
}
}
@@ -545,7 +545,7 @@ public final class RIL extends BaseCommands implements CommandsInterface {
synchronized (mRequestsList) {
for (int i = 0, sz = mRequestsList.size() ; i < sz ; i++) {
RILRequest rr = mRequestsList.get(i);
- rr.onError(RADIO_NOT_AVAILABLE);
+ rr.onError(RADIO_NOT_AVAILABLE, null);
rr.release();
}
@@ -1986,20 +1986,16 @@ public final class RIL extends BaseCommands implements CommandsInterface {
return;
}
- if (error != 0) {
- rr.onError(error);
- rr.release();
- return;
- }
+ Object ret = null;
- Object ret;
-
- try {switch (rr.mRequest) {
-/*
+ if (error == 0 || p.dataAvail() > 0) {
+ // either command succeeds or command fails but with data payload
+ try {switch (rr.mRequest) {
+ /*
cat libs/telephony/ril_commands.h \
| egrep "^ *{RIL_" \
| sed -re 's/\{([^,]+),[^,]+,([^}]+).+/case \1: ret = \2(p); break;/'
-*/
+ */
case RIL_REQUEST_GET_SIM_STATUS: ret = responseIccCardStatus(p); break;
case RIL_REQUEST_ENTER_SIM_PIN: ret = responseVoid(p); break;
case RIL_REQUEST_ENTER_SIM_PUK: ret = responseVoid(p); break;
@@ -2104,17 +2100,24 @@ public final class RIL extends BaseCommands implements CommandsInterface {
default:
throw new RuntimeException("Unrecognized solicited response: " + rr.mRequest);
//break;
- }} catch (Throwable tr) {
- // Exceptions here usually mean invalid RIL responses
+ }} catch (Throwable tr) {
+ // Exceptions here usually mean invalid RIL responses
- Log.w(LOG_TAG, rr.serialString() + "< "
- + requestToString(rr.mRequest)
- + " exception, possible invalid RIL response", tr);
+ Log.w(LOG_TAG, rr.serialString() + "< "
+ + requestToString(rr.mRequest)
+ + " exception, possible invalid RIL response", tr);
- if (rr.mResult != null) {
- AsyncResult.forMessage(rr.mResult, null, tr);
- rr.mResult.sendToTarget();
+ if (rr.mResult != null) {
+ AsyncResult.forMessage(rr.mResult, null, tr);
+ rr.mResult.sendToTarget();
+ }
+ rr.release();
+ return;
}
+ }
+
+ if (error != 0) {
+ rr.onError(error, ret);
rr.release();
return;
}
--
cgit v1.2.3-59-g8ed1b
From de15ddc86ce4d3c3acdff297c75ef56f65e10457 Mon Sep 17 00:00:00 2001
From: Mathias Agopian
Date: Tue, 2 Jun 2009 18:10:08 -0700
Subject: simplify this test
---
.../com/android/lightingtest/ClearActivity.java | 181 +++++----------------
1 file changed, 37 insertions(+), 144 deletions(-)
diff --git a/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java b/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java
index 3dc31cc617e2..3ae8c5ca29d7 100644
--- a/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java
+++ b/opengl/tests/lighting1709/src/com/android/lightingtest/ClearActivity.java
@@ -34,8 +34,6 @@ import android.view.MotionEvent;
public class ClearActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
- instance = counter++;
- Log.e("ClearActivity", ":::::: onCreate: instance" + instance + " is created");
super.onCreate(savedInstanceState);
mGLView = new ClearGLSurfaceView(this);
setContentView(mGLView);
@@ -43,96 +41,37 @@ public class ClearActivity extends Activity {
@Override
protected void onPause() {
- Log.e("ClearActivity", ":::::: instance" + instance + " onPause: is called");
super.onPause();
mGLView.onPause();
}
@Override
protected void onResume() {
- Log.e("ClearActivity", ":::::: instance" + instance + " onResume: is called");
super.onResume();
mGLView.onResume();
}
-
- @Override
- protected void onStop() {
- Log.e("ClearActivity", ":::::: instance" + instance + " onStop: is called");
- super.onStop();
- }
-
- @Override
- protected void onDestroy() {
- Log.e("ClearActivity", ":::::: instance" + instance + " onDestroy: is called");
- super.onDestroy();
- }
-
private GLSurfaceView mGLView;
-
- private static int counter = 0;
- private int instance;
}
class ClearGLSurfaceView extends GLSurfaceView {
public ClearGLSurfaceView(Context context) {
super(context);
- instance = counter++;
- Log.e("ClearGLSurfaceView", ":::::: instance" + instance + " is created");
mRenderer = new ClearRenderer();
setRenderer(mRenderer);
}
- public boolean onTouchEvent(final MotionEvent event) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_MOVE: {// falling through on purpose here
- Log.e("ClearGLSurfaceView", ":::::: instance" + instance + " onTouchEvent: handling down or move action");
- queueEvent(new Runnable(){
- public void run() {
- mRenderer.setColor(event.getX() / getWidth(),
- event.getY() / getHeight(), 1.0f);
- }}
- );
- return true;
- }
- case MotionEvent.ACTION_UP: {
- // launch a second instance of the same activity
- Log.e("ClearGLSurfaceView", ":::::: instance" + instance + " onTouchEvent: handling up action");
- // Intent intent = new Intent();
- // intent.setClass(getContext(), ClearActivity.class);
- // getContext().startActivity(intent);
- }
-
- }
- return true;
- }
-
- @Override
- protected void onDetachedFromWindow() {
- Log.e("ClearGLSurfaceView", ":::::: instance" + instance + " onDetachedFromWindow: is called");
- super.onDetachedFromWindow();
- }
-
ClearRenderer mRenderer;
-
- private static int counter = 0;
- private int instance;
}
class ClearRenderer implements GLSurfaceView.Renderer {
public ClearRenderer() {
- instance = counter++;
- Log.e("ClearRenderer", ":::::: instance" + instance + " is created");
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// Do nothing special.
- Log.e("ClearRenderer", ":::::: instance" + instance + " onSurfaceCreated: is called");
}
public void onSurfaceChanged(GL10 gl, int w, int h) {
- Log.e("ClearRenderer", ":::::: instance" + instance + " onSurfaceChanged: is called");
-
// Compute the projection matrix
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
@@ -153,129 +92,83 @@ class ClearRenderer implements GLSurfaceView.Renderer {
}
public void onDrawFrame(GL10 gl) {
- // gl.glClearColor(mRed, mGreen, mBlue, 1.0f);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
- float lightOff[] = {0.0f, 0.0f, 0.0f, 1.0f};
- float lightAmbient[] = {5.0f, 0.0f, 0.0f, 1.0f};
- float lightDiffuse[] = {0.0f, 2.0f, 0.0f, 0.0f};
- float lightPosAmbient[] = {0.0f, 0.0f, 0.0f, 1.0f};
- float lightPosSpot[] = {0.0f, 0.0f, -8.0f, 1.0f};
+ final float lightOff[] = {0.0f, 0.0f, 0.0f, 1.0f};
+ final float lightAmbient[] = {5.0f, 0.0f, 0.0f, 1.0f};
+ final float lightDiffuse[] = {0.0f, 2.0f, 0.0f, 0.0f};
+ final float lightPosSpot[] = {0.0f, 0.0f, -8.0f, 1.0f};
+ final float pos[] = {
+ -5.0f, -1.5f, 0.0f,
+ 0.0f, -1.5f, 0.0f,
+ 5.0f, -1.5f, 0.0f,
+ };
- float v[] = new float[9];
+ final float v[] = new float[9];
ByteBuffer vbb = ByteBuffer.allocateDirect(v.length*4);
vbb.order(ByteOrder.nativeOrder());
FloatBuffer vb = vbb.asFloatBuffer();
gl.glDisable(GL10.GL_DITHER);
- gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, lightOff, 0);
- gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightOff, 0);
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, lightAmbient, 0);
- gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPosAmbient, 0);
+ gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, lightDiffuse, 0);
+ gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, lightOff, 0);
+ gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, lightPosSpot, 0);
gl.glEnable(GL10.GL_LIGHT0);
-
- gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_SPECULAR, lightOff, 0);
- gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_DIFFUSE, lightDiffuse, 0);
- gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_AMBIENT, lightOff, 0);
- gl.glLightfv(GL10.GL_LIGHT1, GL10.GL_POSITION, lightPosSpot, 0);
- gl.glLightf(GL10.GL_LIGHT1, GL10.GL_CONSTANT_ATTENUATION, 1.0f);
- gl.glLightf(GL10.GL_LIGHT1, GL10.GL_LINEAR_ATTENUATION, 0.0f);
- gl.glLightf(GL10.GL_LIGHT1, GL10.GL_QUADRATIC_ATTENUATION, 0.022f);
- gl.glEnable(GL10.GL_LIGHT1);
-
+
gl.glEnable(GL10.GL_LIGHTING);
- // draw upper left triangle
- gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
- v[0] = -6f; v[1] = 0.5f; v[2] = -10f;
- v[3] = -5f; v[4] = 2.5f; v[5] = -10f;
- v[6] = -4f; v[7] = 0.5f; v[8] = -10f;
- vb.put(v).position(0);
- gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vb);
- gl.glNormal3f(0, 0, 1);
- gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
- // draw upper middle triangle
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
- v[0] = -1f; v[1] = 0.5f; v[2] = -10f;
- v[3] = 0f; v[4] = 2.5f; v[5] = -10f;
- v[6] = 1f; v[7] = 0.5f; v[8] = -10f;
- vb.put(v).position(0);
- gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vb);
gl.glNormal3f(0, 0, 1);
- gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+
- // draw upper right triangle
- gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
- v[0] = 4f; v[1] = 0.5f; v[2] = -10f;
- v[3] = 5f; v[4] = 2.5f; v[5] = -10f;
- v[6] = 6f; v[7] = 0.5f; v[8] = -10f;
+ // draw first 3 triangles, without using transforms
+ for (int i=0 ; i<3 ; i++) {
+ v[0] = -1; v[1] =-1; v[2] = -10;
+ v[3] = 0; v[4] = 1; v[5] = -10;
+ v[6] = 1; v[7] =-1; v[8] = -10;
+ for (int j=0 ; j<3 ; j++) {
+ v[j*3+0] -= pos[i*3+0];
+ v[j*3+1] -= pos[i*3+1];
+ v[j*3+2] -= pos[i*3+2];
+ }
+ vb.put(v).position(0);
+ gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vb);
+ gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
+ }
+
+ // draw the 2nd batch this time with transforms
+ v[0] = -1; v[1] =-1; v[2] = -10;
+ v[3] = 0; v[4] = 1; v[5] = -10;
+ v[6] = 1; v[7] =-1; v[8] = -10;
vb.put(v).position(0);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vb);
- gl.glNormal3f(0, 0, 1);
- gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
// draw lower left triangle
gl.glPushMatrix();
- gl.glTranslatef(-5.0f, -1.5f, 0.0f);
- gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
- v[0] = -1; v[1] = -1; v[2] = -10;
- v[3] = 0; v[4] = 1; v[5] = -10;
- v[6] = 1; v[7] = -1; v[8] = -10;
- vb.put(v).position(0);
- gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vb);
- gl.glNormal3f(0, 0, 1);
+ gl.glTranslatef(pos[0], pos[1], pos[2]);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
gl.glPopMatrix();
// draw lower middle triangle
gl.glPushMatrix();
- gl.glTranslatef(0.0f, -1.5f, 0.0f);
- gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
- v[0] = -1; v[1] = -1; v[2] = -10;
- v[3] = 0; v[4] = 1; v[5] = -10;
- v[6] = 1; v[7] = -1; v[8] = -10;
- vb.put(v).position(0);
- gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vb);
- gl.glNormal3f(0, 0, 1);
+ gl.glTranslatef(pos[3], pos[4], pos[5]);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
gl.glPopMatrix();
// draw lower right triangle
gl.glPushMatrix();
- gl.glTranslatef(5.0f, -1.5f, 0.0f);
- gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
- v[0] = -1; v[1] = -1; v[2] = -10;
- v[3] = 0; v[4] = 1; v[5] = -10;
- v[6] = 1; v[7] = -1; v[8] = -10;
- vb.put(v).position(0);
- gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vb);
- gl.glNormal3f(0, 0, 1);
+ gl.glTranslatef(pos[6], pos[7], pos[8]);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 3);
gl.glPopMatrix();
-
}
public int[] getConfigSpec() {
- Log.e("ClearRenderer", ":::::: instance" + instance + " getConfigSpec: is called");
int[] configSpec = { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };
return configSpec;
}
-
- public void setColor(float r, float g, float b) {
- Log.e("ClearRenderer", ":::::: instance" + instance + " setColor: is called");
- mRed = r;
- mGreen = g;
- mBlue = b;
- }
-
- private float mRed;
- private float mGreen;
- private float mBlue;
-
- private static int counter = 0;
- private int instance;
}
--
cgit v1.2.3-59-g8ed1b
From 9af0b4f7be14f2b3ed0ecc843c57ea47ec288e55 Mon Sep 17 00:00:00 2001
From: Romain Guy
Date: Tue, 2 Jun 2009 21:56:27 -0700
Subject: Add new listener to GestureOverlayView. This listener fires whenever
the overlay thinks the user is starting a new gesture. This allows Home to
snap the workspace back to its original position during a gesture operation.
---
api/current.xml | 83 +++++++++++++++++++++--
core/java/android/gesture/GestureOverlayView.java | 47 +++++++++++--
2 files changed, 119 insertions(+), 11 deletions(-)
diff --git a/api/current.xml b/api/current.xml
index 3a310347188c..9e9eaad45e0e 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -1145,33 +1145,33 @@
visibility="public"
>
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
mOnGesturePerformedListeners =
new ArrayList();
+ // TODO: Make this a list of WeakReferences
+ private final ArrayList mOnGesturingListeners =
+ new ArrayList();
private boolean mHandleGestureActions;
@@ -319,6 +322,18 @@ public class GestureOverlayView extends FrameLayout {
mHandleGestureActions = false;
}
+ public void addOnGesturingListener(OnGesturingListener listener) {
+ mOnGesturingListeners.add(listener);
+ }
+
+ public void removeOnGesturingListener(OnGesturingListener listener) {
+ mOnGesturingListeners.remove(listener);
+ }
+
+ public void removeAllOnGesturingListeners() {
+ mOnGesturingListeners.clear();
+ }
+
public boolean isGesturing() {
return mIsGesturing;
}
@@ -401,7 +416,7 @@ public class GestureOverlayView extends FrameLayout {
MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
final ArrayList listeners = mOnGestureListeners;
- final int count = listeners.size();
+ int count = listeners.size();
for (int i = 0; i < count; i++) {
listeners.get(i).onGestureCancelled(this, event);
}
@@ -411,6 +426,12 @@ public class GestureOverlayView extends FrameLayout {
clear(false);
mIsGesturing = false;
mStrokeBuffer.clear();
+
+ final ArrayList otherListeners = mOnGesturingListeners;
+ count = otherListeners.size();
+ for (int i = 0; i < count; i++) {
+ otherListeners.get(i).onGesturingEnded(this);
+ }
}
@Override
@@ -577,6 +598,12 @@ public class GestureOverlayView extends FrameLayout {
mIsGesturing = true;
setCurrentColor(mCertainGestureColor);
+
+ final ArrayList listeners = mOnGesturingListeners;
+ int count = listeners.size();
+ for (int i = 0; i < count; i++) {
+ listeners.get(i).onGesturingStarted(this);
+ }
}
}
}
@@ -621,6 +648,12 @@ public class GestureOverlayView extends FrameLayout {
mStrokeBuffer.clear();
mIsGesturing = false;
+
+ final ArrayList listeners = mOnGesturingListeners;
+ int count = listeners.size();
+ for (int i = 0; i < count; i++) {
+ listeners.get(i).onGesturingEnded(this);
+ }
}
private void cancelGesture(MotionEvent event) {
@@ -635,12 +668,10 @@ public class GestureOverlayView extends FrameLayout {
}
private void fireOnGesturePerformed() {
- final ArrayList actionListeners =
- mOnGesturePerformedListeners;
+ final ArrayList actionListeners = mOnGesturePerformedListeners;
final int count = actionListeners.size();
for (int i = 0; i < count; i++) {
- actionListeners.get(i).onGesturePerformed(GestureOverlayView.this,
- mCurrentGesture);
+ actionListeners.get(i).onGesturePerformed(GestureOverlayView.this, mCurrentGesture);
}
}
@@ -683,6 +714,12 @@ public class GestureOverlayView extends FrameLayout {
}
}
+ public static interface OnGesturingListener {
+ void onGesturingStarted(GestureOverlayView overlay);
+
+ void onGesturingEnded(GestureOverlayView overlay);
+ }
+
public static interface OnGestureListener {
void onGestureStarted(GestureOverlayView overlay, MotionEvent event);
--
cgit v1.2.3-59-g8ed1b
From 7c7dface993a66778c506179ae11cadd6a88f7b1 Mon Sep 17 00:00:00 2001
From: Mathias Agopian
Date: Tue, 2 Jun 2009 22:51:09 -0700
Subject: fix a bug in GL lighting where the specular component could be
ommited when vertex material was disabled.
the specular enable flag wasn't computed in that case.
---
opengl/libagl/light.cpp | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/opengl/libagl/light.cpp b/opengl/libagl/light.cpp
index 25c41d0c5b9d..bc9449c0bdef 100644
--- a/opengl/libagl/light.cpp
+++ b/opengl/libagl/light.cpp
@@ -318,6 +318,11 @@ void lightVertexMaterial(ogles_context_t* c, vertex_t* v)
vmul3(l.implicitAmbient.v, material.ambient.v, l.ambient.v);
vmul3(l.implicitDiffuse.v, material.diffuse.v, l.diffuse.v);
vmul3(l.implicitSpecular.v, material.specular.v, l.specular.v);
+ // this is just a flag to tell if we have a specular component
+ l.implicitSpecular.v[3] =
+ l.implicitSpecular.r |
+ l.implicitSpecular.g |
+ l.implicitSpecular.b;
}
// emission and ambient for the whole scene
vmla3( c->lighting.implicitSceneEmissionAndAmbient.v,
--
cgit v1.2.3-59-g8ed1b
From 8dbe612dc60526d635e57257b58627b33a099678 Mon Sep 17 00:00:00 2001
From: Satish Sampath
Date: Tue, 2 Jun 2009 23:35:54 +0100
Subject: Adding internal method replacePreferredActivity.
This was required because we need a way to set the preferred activity for a
particular intent filter based on user selection (in our case the
ACTION_WEB_SEARCH intent filter for selecting the preferred search engine from
the list of available search engines providers). The current
addPreferredActivity call was not sufficient since it leaves the existing
preferred activities in the list and does not remove them, which this call
does.
---
core/java/android/app/ApplicationContext.java | 10 ++++++
core/java/android/content/pm/IPackageManager.aidl | 5 +++
core/java/android/content/pm/PackageManager.java | 20 ++++++++++++
.../com/android/server/PackageManagerService.java | 37 ++++++++++++++++++++++
.../android/test/mock/MockPackageManager.java | 10 ++++++
5 files changed, 82 insertions(+)
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
index 2c2310a030d4..2d6381a632df 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -2424,6 +2424,16 @@ class ApplicationContext extends Context {
}
}
+ @Override
+ public void replacePreferredActivity(IntentFilter filter,
+ int match, ComponentName[] set, ComponentName activity) {
+ try {
+ mPM.replacePreferredActivity(filter, match, set, activity);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+
@Override
public void clearPackagePreferredActivities(String packageName) {
try {
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index bb913cdca90c..5f62248bcd14 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -167,7 +167,12 @@ interface IPackageManager {
void addPreferredActivity(in IntentFilter filter, int match,
in ComponentName[] set, in ComponentName activity);
+
+ void replacePreferredActivity(in IntentFilter filter, int match,
+ in ComponentName[] set, in ComponentName activity);
+
void clearPackagePreferredActivities(String packageName);
+
int getPreferredActivities(out List outFilters,
out List outActivities, String packageName);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 238a98afd683..a2c82e890e41 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1629,6 +1629,26 @@ public abstract class PackageManager {
public abstract void addPreferredActivity(IntentFilter filter, int match,
ComponentName[] set, ComponentName activity);
+ /**
+ * Replaces an existing preferred activity mapping to the system, and if that were not present
+ * adds a new preferred activity. This will be used
+ * to automatically select the given activity component when
+ * {@link Context#startActivity(Intent) Context.startActivity()} finds
+ * multiple matching activities and also matches the given filter.
+ *
+ * @param filter The set of intents under which this activity will be
+ * made preferred.
+ * @param match The IntentFilter match category that this preference
+ * applies to.
+ * @param set The set of activities that the user was picking from when
+ * this preference was made.
+ * @param activity The component name of the activity that is to be
+ * preferred.
+ * @hide
+ */
+ public abstract void replacePreferredActivity(IntentFilter filter, int match,
+ ComponentName[] set, ComponentName activity);
+
/**
* Remove all preferred activity mappings, previously added with
* {@link #addPreferredActivity}, from the
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index c9bdd3c7ebef..8da40acf9372 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -19,6 +19,7 @@ package com.android.server;
import com.android.internal.app.ResolverActivity;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
+import com.android.server.PackageManagerService.PreferredActivity;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -4507,6 +4508,42 @@ class PackageManagerService extends IPackageManager.Stub {
}
}
+ public void replacePreferredActivity(IntentFilter filter, int match,
+ ComponentName[] set, ComponentName activity) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
+ if (filter.countActions() != 1) {
+ throw new IllegalArgumentException(
+ "replacePreferredActivity expects filter to have only 1 action.");
+ }
+ if (filter.countCategories() != 1) {
+ throw new IllegalArgumentException(
+ "replacePreferredActivity expects filter to have only 1 category.");
+ }
+ if (filter.countDataAuthorities() != 0
+ || filter.countDataPaths() != 0
+ || filter.countDataSchemes() != 0
+ || filter.countDataTypes() != 0) {
+ throw new IllegalArgumentException(
+ "replacePreferredActivity expects filter to have no data authorities, " +
+ "paths, schemes or types.");
+ }
+ synchronized (mPackages) {
+ Iterator it = mSettings.mPreferredActivities.filterIterator();
+ String action = filter.getAction(0);
+ String category = filter.getCategory(0);
+ while (it.hasNext()) {
+ PreferredActivity pa = it.next();
+ if (pa.getAction(0).equals(action) && pa.getCategory(0).equals(category)) {
+ it.remove();
+ Log.i(TAG, "Removed preferred activity " + pa.mActivity + ":");
+ filter.dump(new LogPrinter(Log.INFO, TAG), " ");
+ }
+ }
+ addPreferredActivity(filter, match, set, activity);
+ }
+ }
+
public void clearPackagePreferredActivities(String packageName) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
diff --git a/test-runner/android/test/mock/MockPackageManager.java b/test-runner/android/test/mock/MockPackageManager.java
index 73ae3b91308e..6ef5539ad0c8 100644
--- a/test-runner/android/test/mock/MockPackageManager.java
+++ b/test-runner/android/test/mock/MockPackageManager.java
@@ -392,6 +392,16 @@ public class MockPackageManager extends PackageManager {
throw new UnsupportedOperationException();
}
+ /**
+ * @hide - to match hiding in superclass
+ */
+ @Override
+ public void replacePreferredActivity(IntentFilter filter,
+ int match, ComponentName[] set, ComponentName activity) {
+ throw new UnsupportedOperationException();
+ }
+
+
@Override
public void clearPackagePreferredActivities(String packageName) {
throw new UnsupportedOperationException();
--
cgit v1.2.3-59-g8ed1b
From 963cd006c45716b034f656bf7e7179e6476f7e4d Mon Sep 17 00:00:00 2001
From: Bjorn Bringert
Date: Fri, 29 May 2009 14:05:12 +0100
Subject: Allow creating AssetFileDescriptors for MemoryFiles.
This allows content providers to use in-memory data to implement
ContentProvider.openAssetFile(), instead of just normal files
and sockets as before.
To test cross-process use of AssetFileDescriptors for MemoryFiles,
a test content provider and a client for it are added to
AndroidTests.
Fixes http://b/issue?id=1871731
---
.../android/content/res/AssetFileDescriptor.java | 88 +++++++++
core/java/android/os/MemoryFile.java | 117 +++++++++++-
core/jni/android_os_MemoryFile.cpp | 30 ++-
tests/AndroidTests/AndroidManifest.xml | 6 +
.../unit_tests/content/MemoryFileProvider.java | 211 +++++++++++++++++++++
.../unit_tests/content/MemoryFileProviderTest.java | 83 ++++++++
.../com/android/unit_tests/os/MemoryFileTest.java | 52 ++++-
7 files changed, 575 insertions(+), 12 deletions(-)
create mode 100644 tests/AndroidTests/src/com/android/unit_tests/content/MemoryFileProvider.java
create mode 100644 tests/AndroidTests/src/com/android/unit_tests/content/MemoryFileProviderTest.java
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
index 231e3e24a27c..a37e4e8cc3bf 100644
--- a/core/java/android/content/res/AssetFileDescriptor.java
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -16,6 +16,7 @@
package android.content.res;
+import android.os.MemoryFile;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
@@ -24,6 +25,8 @@ import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.nio.channels.FileChannel;
/**
* File descriptor of an entry in the AssetManager. This provides your own
@@ -123,6 +126,13 @@ public class AssetFileDescriptor implements Parcelable {
mFd.close();
}
+ /**
+ * Checks whether this file descriptor is for a memory file.
+ */
+ private boolean isMemoryFile() throws IOException {
+ return MemoryFile.isMemoryFile(mFd.getFileDescriptor());
+ }
+
/**
* Create and return a new auto-close input stream for this asset. This
* will either return a full asset {@link AutoCloseInputStream}, or
@@ -132,6 +142,12 @@ public class AssetFileDescriptor implements Parcelable {
* should only call this once for a particular asset.
*/
public FileInputStream createInputStream() throws IOException {
+ if (isMemoryFile()) {
+ if (mLength > Integer.MAX_VALUE) {
+ throw new IOException("File length too large for a memory file: " + mLength);
+ }
+ return new AutoCloseMemoryFileInputStream(mFd, (int)mLength);
+ }
if (mLength < 0) {
return new ParcelFileDescriptor.AutoCloseInputStream(mFd);
}
@@ -261,6 +277,66 @@ public class AssetFileDescriptor implements Parcelable {
}
}
+ /**
+ * An input stream that reads from a MemoryFile and closes it when the stream is closed.
+ * This extends FileInputStream just because {@link #createInputStream} returns
+ * a FileInputStream. All the FileInputStream methods are
+ * overridden to use the MemoryFile instead.
+ */
+ private static class AutoCloseMemoryFileInputStream extends FileInputStream {
+ private ParcelFileDescriptor mParcelFd;
+ private MemoryFile mFile;
+ private InputStream mStream;
+
+ public AutoCloseMemoryFileInputStream(ParcelFileDescriptor fd, int length)
+ throws IOException {
+ super(fd.getFileDescriptor());
+ mParcelFd = fd;
+ mFile = new MemoryFile(fd.getFileDescriptor(), length, "r");
+ mStream = mFile.getInputStream();
+ }
+
+ @Override
+ public int available() throws IOException {
+ return mStream.available();
+ }
+
+ @Override
+ public void close() throws IOException {
+ mParcelFd.close(); // must close ParcelFileDescriptor, not just the file descriptor,
+ // since it could be a subclass of ParcelFileDescriptor.
+ // E.g. ContentResolver.ParcelFileDescriptorInner.close() releases
+ // a content provider
+ mFile.close(); // to unmap the memory file from the address space.
+ mStream.close(); // doesn't actually do anything
+ }
+
+ @Override
+ public FileChannel getChannel() {
+ return null;
+ }
+
+ @Override
+ public int read() throws IOException {
+ return mStream.read();
+ }
+
+ @Override
+ public int read(byte[] buffer, int offset, int count) throws IOException {
+ return mStream.read(buffer, offset, count);
+ }
+
+ @Override
+ public int read(byte[] buffer) throws IOException {
+ return mStream.read(buffer);
+ }
+
+ @Override
+ public long skip(long count) throws IOException {
+ return mStream.skip(count);
+ }
+ }
+
/**
* An OutputStream you can create on a ParcelFileDescriptor, which will
* take care of calling {@link ParcelFileDescriptor#close
@@ -345,4 +421,16 @@ public class AssetFileDescriptor implements Parcelable {
return new AssetFileDescriptor[size];
}
};
+
+ /**
+ * Creates an AssetFileDescriptor from a memory file.
+ *
+ * @hide
+ */
+ public static AssetFileDescriptor fromMemoryFile(MemoryFile memoryFile)
+ throws IOException {
+ ParcelFileDescriptor fd = memoryFile.getParcelFileDescriptor();
+ return new AssetFileDescriptor(fd, 0, memoryFile.length());
+ }
+
}
diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java
index 65e83c77957a..7e4cf8ae3284 100644
--- a/core/java/android/os/MemoryFile.java
+++ b/core/java/android/os/MemoryFile.java
@@ -37,9 +37,14 @@ public class MemoryFile
{
private static String TAG = "MemoryFile";
+ // mmap(2) protection flags from
+ private static final int PROT_READ = 0x1;
+ private static final int PROT_WRITE = 0x2;
+
private static native FileDescriptor native_open(String name, int length) throws IOException;
// returns memory address for ashmem region
- private static native int native_mmap(FileDescriptor fd, int length) throws IOException;
+ private static native int native_mmap(FileDescriptor fd, int length, int mode)
+ throws IOException;
private static native void native_munmap(int addr, int length) throws IOException;
private static native void native_close(FileDescriptor fd);
private static native int native_read(FileDescriptor fd, int address, byte[] buffer,
@@ -47,14 +52,16 @@ public class MemoryFile
private static native void native_write(FileDescriptor fd, int address, byte[] buffer,
int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException;
private static native void native_pin(FileDescriptor fd, boolean pin) throws IOException;
+ private static native boolean native_is_ashmem_region(FileDescriptor fd) throws IOException;
private FileDescriptor mFD; // ashmem file descriptor
private int mAddress; // address of ashmem memory
private int mLength; // total length of our ashmem region
private boolean mAllowPurging = false; // true if our ashmem region is unpinned
+ private final boolean mOwnsRegion; // false if this is a ref to an existing ashmem region
/**
- * MemoryFile constructor.
+ * Allocates a new ashmem region. The region is initially not purgable.
*
* @param name optional name for the file (can be null).
* @param length of the memory file in bytes.
@@ -63,11 +70,43 @@ public class MemoryFile
public MemoryFile(String name, int length) throws IOException {
mLength = length;
mFD = native_open(name, length);
- mAddress = native_mmap(mFD, length);
+ mAddress = native_mmap(mFD, length, PROT_READ | PROT_WRITE);
+ mOwnsRegion = true;
+ }
+
+ /**
+ * Creates a reference to an existing memory file. Changes to the original file
+ * will be available through this reference.
+ * Calls to {@link #allowPurging(boolean)} on the returned MemoryFile will fail.
+ *
+ * @param fd File descriptor for an existing memory file, as returned by
+ * {@link #getFileDescriptor()}. This file descriptor will be closed
+ * by {@link #close()}.
+ * @param length Length of the memory file in bytes.
+ * @param mode File mode. Currently only "r" for read-only access is supported.
+ * @throws NullPointerException if fd is null.
+ * @throws IOException If fd does not refer to an existing memory file,
+ * or if the file mode of the existing memory file is more restrictive
+ * than mode.
+ *
+ * @hide
+ */
+ public MemoryFile(FileDescriptor fd, int length, String mode) throws IOException {
+ if (fd == null) {
+ throw new NullPointerException("File descriptor is null.");
+ }
+ if (!isMemoryFile(fd)) {
+ throw new IllegalArgumentException("Not a memory file.");
+ }
+ mLength = length;
+ mFD = fd;
+ mAddress = native_mmap(mFD, length, modeToProt(mode));
+ mOwnsRegion = false;
}
/**
- * Closes and releases all resources for the memory file.
+ * Closes the memory file. If there are no other open references to the memory
+ * file, it will be deleted.
*/
public void close() {
deactivate();
@@ -76,7 +115,14 @@ public class MemoryFile
}
}
- private void deactivate() {
+ /**
+ * Unmaps the memory file from the process's memory space, but does not close it.
+ * After this method has been called, read and write operations through this object
+ * will fail, but {@link #getFileDescriptor()} will still return a valid file descriptor.
+ *
+ * @hide
+ */
+ public void deactivate() {
if (!isDeactivated()) {
try {
native_munmap(mAddress, mLength);
@@ -135,6 +181,9 @@ public class MemoryFile
* @return previous value of allowPurging
*/
synchronized public boolean allowPurging(boolean allowPurging) throws IOException {
+ if (!mOwnsRegion) {
+ throw new IOException("Only the owner can make ashmem regions purgable.");
+ }
boolean oldValue = mAllowPurging;
if (oldValue != allowPurging) {
native_pin(mFD, !allowPurging);
@@ -210,6 +259,64 @@ public class MemoryFile
native_write(mFD, mAddress, buffer, srcOffset, destOffset, count, mAllowPurging);
}
+ /**
+ * Gets a ParcelFileDescriptor for the memory file. See {@link #getFileDescriptor()}
+ * for caveats. This must be here to allow classes outside android.osfd is not a valid file descriptor.
+ *
+ * @hide
+ */
+ public static boolean isMemoryFile(FileDescriptor fd) throws IOException {
+ return native_is_ashmem_region(fd);
+ }
+
+ /**
+ * Converts a file mode string to a prot value as expected by
+ * native_mmap().
+ *
+ * @throws IllegalArgumentException if the file mode is invalid.
+ */
+ private static int modeToProt(String mode) {
+ if ("r".equals(mode)) {
+ return PROT_READ;
+ } else {
+ throw new IllegalArgumentException("Unsupported file mode: '" + mode + "'");
+ }
+ }
+
private class MemoryInputStream extends InputStream {
private int mMark = 0;
diff --git a/core/jni/android_os_MemoryFile.cpp b/core/jni/android_os_MemoryFile.cpp
index 6c16150a4cc8..8643393f7e8a 100644
--- a/core/jni/android_os_MemoryFile.cpp
+++ b/core/jni/android_os_MemoryFile.cpp
@@ -39,17 +39,17 @@ static jobject android_os_MemoryFile_open(JNIEnv* env, jobject clazz, jstring na
if (result < 0) {
jniThrowException(env, "java/io/IOException", "ashmem_create_region failed");
- return NULL;
+ return NULL;
}
return jniCreateFileDescriptor(env, result);
}
static jint android_os_MemoryFile_mmap(JNIEnv* env, jobject clazz, jobject fileDescriptor,
- jint length)
+ jint length, jint prot)
{
int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
- jint result = (jint)mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+ jint result = (jint)mmap(NULL, length, prot, MAP_SHARED, fd, 0);
if (!result)
jniThrowException(env, "java/io/IOException", "mmap failed");
return result;
@@ -118,14 +118,36 @@ static void android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jobject fileDe
}
}
+static jboolean android_os_MemoryFile_is_ashmem_region(JNIEnv* env, jobject clazz,
+ jobject fileDescriptor) {
+ int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
+ // Use ASHMEM_GET_SIZE to find out if the fd refers to an ashmem region.
+ // ASHMEM_GET_SIZE should succeed for all ashmem regions, and the kernel
+ // should return ENOTTY for all other valid file descriptors
+ int result = ashmem_get_size_region(fd);
+ if (result < 0) {
+ if (errno == ENOTTY) {
+ // ENOTTY means that the ioctl does not apply to this object,
+ // i.e., it is not an ashmem region.
+ return JNI_FALSE;
+ }
+ // Some other error, throw exception
+ jniThrowIOException(env, errno);
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
static const JNINativeMethod methods[] = {
{"native_open", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_MemoryFile_open},
- {"native_mmap", "(Ljava/io/FileDescriptor;I)I", (void*)android_os_MemoryFile_mmap},
+ {"native_mmap", "(Ljava/io/FileDescriptor;II)I", (void*)android_os_MemoryFile_mmap},
{"native_munmap", "(II)V", (void*)android_os_MemoryFile_munmap},
{"native_close", "(Ljava/io/FileDescriptor;)V", (void*)android_os_MemoryFile_close},
{"native_read", "(Ljava/io/FileDescriptor;I[BIIIZ)I", (void*)android_os_MemoryFile_read},
{"native_write", "(Ljava/io/FileDescriptor;I[BIIIZ)V", (void*)android_os_MemoryFile_write},
{"native_pin", "(Ljava/io/FileDescriptor;Z)V", (void*)android_os_MemoryFile_pin},
+ {"native_is_ashmem_region", "(Ljava/io/FileDescriptor;)Z",
+ (void*)android_os_MemoryFile_is_ashmem_region}
};
static const char* const kClassPathName = "android/os/MemoryFile";
diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml
index 843d844379ae..fd6e6d8ec3d6 100644
--- a/tests/AndroidTests/AndroidManifest.xml
+++ b/tests/AndroidTests/AndroidManifest.xml
@@ -206,6 +206,12 @@
+
+
+
+
Date: Mon, 1 Jun 2009 10:53:06 +0100
Subject: Handle EOF correctly in MemoryFile input stream.
Before, the variants of MemoryFile.MemoryInputStream.read() would throw
IOException or IndexOutOfBoundsException if EOF was encountered
before the requested number of bytes was read. This violates
the contract of InputStream.read().
This patch makes read() return the number of bytes available, if any.
If already at EOF, -1 is returned. The patch also adds new tests,
which checks cases where MemoryFile.MemoryInputStream.read()
should throw IndexOutOfBoundsException or return -1. several of these
tests failed with the old code and pass now.
This fixes http://b/issue?id=1881894
---
core/java/android/os/MemoryFile.java | 11 +++-
.../unit_tests/content/MemoryFileProviderTest.java | 3 +-
.../com/android/unit_tests/os/MemoryFileTest.java | 69 ++++++++++++++++++++++
3 files changed, 80 insertions(+), 3 deletions(-)
diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java
index 7e4cf8ae3284..c14925cd3839 100644
--- a/core/java/android/os/MemoryFile.java
+++ b/core/java/android/os/MemoryFile.java
@@ -353,13 +353,22 @@ public class MemoryFile
}
int result = read(mSingleByte, 0, 1);
if (result != 1) {
- throw new IOException("read() failed");
+ return -1;
}
return mSingleByte[0];
}
@Override
public int read(byte buffer[], int offset, int count) throws IOException {
+ if (offset < 0 || count < 0 || offset + count > buffer.length) {
+ // readBytes() also does this check, but we need to do it before
+ // changing count.
+ throw new IndexOutOfBoundsException();
+ }
+ count = Math.min(count, available());
+ if (count < 1) {
+ return -1;
+ }
int result = readBytes(buffer, mOffset, offset, count);
if (result > 0) {
mOffset += result;
diff --git a/tests/AndroidTests/src/com/android/unit_tests/content/MemoryFileProviderTest.java b/tests/AndroidTests/src/com/android/unit_tests/content/MemoryFileProviderTest.java
index 2d8190a71b29..f88a9dac3962 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/content/MemoryFileProviderTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/content/MemoryFileProviderTest.java
@@ -40,8 +40,7 @@ public class MemoryFileProviderTest extends AndroidTestCase {
assertNotNull(in);
int count = in.read(buf);
assertEquals(buf.length, count);
- // TODO: MemoryFile throws IndexOutOfBoundsException for this, http://b/issue?id=1881894
- //assertEquals(-1, in.read());
+ assertEquals(-1, in.read());
in.close();
assertTrue(Arrays.equals(MemoryFileProvider.TEST_BLOB, buf));
}
diff --git a/tests/AndroidTests/src/com/android/unit_tests/os/MemoryFileTest.java b/tests/AndroidTests/src/com/android/unit_tests/os/MemoryFileTest.java
index 66f2b508716c..18b3d6321e83 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/os/MemoryFileTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/os/MemoryFileTest.java
@@ -97,6 +97,74 @@ public class MemoryFileTest extends AndroidTestCase {
file.close();
}
+ // Tests for the IndexOutOfBoundsException cases in read().
+
+ private void readIndexOutOfBoundsException(int offset, int count, String msg)
+ throws Exception {
+ MemoryFile file = new MemoryFile("MemoryFileTest", testString.length);
+ try {
+ file.writeBytes(testString, 0, 0, testString.length);
+ InputStream is = file.getInputStream();
+ byte[] buffer = new byte[testString.length + 10];
+ try {
+ is.read(buffer, offset, count);
+ fail(msg);
+ } catch (IndexOutOfBoundsException ex) {
+ // this is what should happen
+ } finally {
+ is.close();
+ }
+ } finally {
+ file.close();
+ }
+ }
+
+ @SmallTest
+ public void testReadNegativeOffset() throws Exception {
+ readIndexOutOfBoundsException(-1, 5,
+ "read() with negative offset should throw IndexOutOfBoundsException");
+ }
+
+ @SmallTest
+ public void testReadNegativeCount() throws Exception {
+ readIndexOutOfBoundsException(5, -1,
+ "read() with negative length should throw IndexOutOfBoundsException");
+ }
+
+ @SmallTest
+ public void testReadOffsetOverflow() throws Exception {
+ readIndexOutOfBoundsException(testString.length + 10, 5,
+ "read() with offset outside buffer should throw IndexOutOfBoundsException");
+ }
+
+ @SmallTest
+ public void testReadOffsetCountOverflow() throws Exception {
+ readIndexOutOfBoundsException(testString.length, 11,
+ "read() with offset + count outside buffer should throw IndexOutOfBoundsException");
+ }
+
+ // Test behavior of read() at end of file
+ @SmallTest
+ public void testReadEOF() throws Exception {
+ MemoryFile file = new MemoryFile("MemoryFileTest", testString.length);
+ try {
+ file.writeBytes(testString, 0, 0, testString.length);
+ InputStream is = file.getInputStream();
+ try {
+ byte[] buffer = new byte[testString.length + 10];
+ // read() with count larger than data should succeed, and return # of bytes read
+ assertEquals(testString.length, is.read(buffer));
+ compareBuffers(testString, buffer, testString.length);
+ // Read at EOF should return -1
+ assertEquals(-1, is.read());
+ } finally {
+ is.close();
+ }
+ } finally {
+ file.close();
+ }
+ }
+
// Tests that close() is idempotent
@SmallTest
public void testCloseClose() throws Exception {
@@ -194,6 +262,7 @@ public class MemoryFileTest extends AndroidTestCase {
}
}
+ @SmallTest
public void testFileDescriptor() throws Exception {
MemoryFile file = new MemoryFile("MemoryFileTest", 1000000);
MemoryFile ref = new MemoryFile(file.getFileDescriptor(), file.length(), "r");
--
cgit v1.2.3-59-g8ed1b
From c55e08e3fc14475b3b15b4c87aaef7e47ec8a172 Mon Sep 17 00:00:00 2001
From: Bjorn Bringert
Date: Mon, 1 Jun 2009 21:50:09 +0100
Subject: Allow making AssetFileDescriptors from SQLite blobs.
This change adds a new class SQLiteContentHelper, which contains
a static method for creating an AssetFileDescriptor from
an SQLite query that returns a blob.
Internally, this uses a file descriptor for a MemoryFile.
The implementation is temporary. Ideally, the data should be copied
directly from SQLite to the MemoryFile ashmem region, using
sqlite3_blob_read().
This is part of the implementation of http://b/issue?id=1871731
---
.../database/sqlite/SQLiteContentHelper.java | 92 ++++++++++++++++++++++
1 file changed, 92 insertions(+)
create mode 100644 core/java/android/database/sqlite/SQLiteContentHelper.java
diff --git a/core/java/android/database/sqlite/SQLiteContentHelper.java b/core/java/android/database/sqlite/SQLiteContentHelper.java
new file mode 100644
index 000000000000..2800d86279b1
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteContentHelper.java
@@ -0,0 +1,92 @@
+/*
+ * 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.database.sqlite;
+
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.os.MemoryFile;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+/**
+ * Some helper functions for using SQLite database to implement content providers.
+ *
+ * @hide
+ */
+public class SQLiteContentHelper {
+
+ /**
+ * Runs an SQLite query and returns an AssetFileDescriptor for the
+ * blob in column 0 of the first row. If the first column does
+ * not contain a blob, an unspecified exception is thrown.
+ *
+ * @param db Handle to a readable database.
+ * @param sql SQL query, possibly with query arguments.
+ * @param selectionArgs Query argument values, or {@code null} for no argument.
+ * @return If no exception is thrown, a non-null AssetFileDescriptor is returned.
+ * @throws FileNotFoundException If the query returns no results or the
+ * value of column 0 is NULL, or if there is an error creating the
+ * asset file descriptor.
+ */
+ public static AssetFileDescriptor getBlobColumnAsAssetFile(SQLiteDatabase db, String sql,
+ String[] selectionArgs) throws FileNotFoundException {
+ try {
+ MemoryFile file = simpleQueryForBlobMemoryFile(db, sql, selectionArgs);
+ if (file == null) {
+ throw new FileNotFoundException("No results.");
+ }
+ return AssetFileDescriptor.fromMemoryFile(file);
+ } catch (IOException ex) {
+ throw new FileNotFoundException(ex.toString());
+ }
+ }
+
+ /**
+ * Runs an SQLite query and returns a MemoryFile for the
+ * blob in column 0 of the first row. If the first column does
+ * not contain a blob, an unspecified exception is thrown.
+ *
+ * @return A memory file, or {@code null} if the query returns no results
+ * or the value column 0 is NULL.
+ * @throws IOException If there is an error creating the memory file.
+ */
+ // TODO: make this native and use the SQLite blob API to reduce copying
+ private static MemoryFile simpleQueryForBlobMemoryFile(SQLiteDatabase db, String sql,
+ String[] selectionArgs) throws IOException {
+ Cursor cursor = db.rawQuery(sql, selectionArgs);
+ if (cursor == null) {
+ return null;
+ }
+ try {
+ if (!cursor.moveToFirst()) {
+ return null;
+ }
+ byte[] bytes = cursor.getBlob(0);
+ if (bytes == null) {
+ return null;
+ }
+ MemoryFile file = new MemoryFile(null, bytes.length);
+ file.writeBytes(bytes, 0, 0, bytes.length);
+ file.deactivate();
+ return file;
+ } finally {
+ cursor.close();
+ }
+ }
+
+}
--
cgit v1.2.3-59-g8ed1b
From 33a22dc9c84ef12006b0c12f6d169d2a74c15284 Mon Sep 17 00:00:00 2001
From: Bjorn Bringert
Date: Tue, 2 Jun 2009 16:59:54 +0100
Subject: Close icon input stream in SuggestionsAdapter.
Before, SuggestionsAdapter would not close input streams after
reading icons from them. This leaks file descriptors and,
in the case of MemoryFiles, virtual address space.
---
core/java/android/app/SuggestionsAdapter.java | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java
index 451697a43723..747bec9944dc 100644
--- a/core/java/android/app/SuggestionsAdapter.java
+++ b/core/java/android/app/SuggestionsAdapter.java
@@ -32,12 +32,13 @@ import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.CursorAdapter;
import android.widget.ImageView;
import android.widget.ResourceCursorAdapter;
import android.widget.TextView;
import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.WeakHashMap;
/**
@@ -376,9 +377,18 @@ class SuggestionsAdapter extends ResourceCursorAdapter {
// Let the ContentResolver handle content, android.resource and file URIs.
try {
Uri uri = Uri.parse(drawableId);
- drawable = Drawable.createFromStream(
- mProviderContext.getContentResolver().openInputStream(uri),
- null);
+ InputStream stream = mProviderContext.getContentResolver().openInputStream(uri);
+ if (stream != null) {
+ try {
+ drawable = Drawable.createFromStream(stream, null);
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException ex) {
+ Log.e(LOG_TAG, "Error closing icon stream for " + uri, ex);
+ }
+ }
+ }
if (DBG) Log.d(LOG_TAG, "Opened icon input stream: " + drawableId);
} catch (FileNotFoundException fnfe) {
if (DBG) Log.d(LOG_TAG, "Icon stream not found: " + drawableId);
--
cgit v1.2.3-59-g8ed1b
From 2e5c150e746647a1ce5c10e1708debbf06c45ea7 Mon Sep 17 00:00:00 2001
From: Derek Sollenberger
Date: Wed, 3 Jun 2009 10:44:42 -0400
Subject: Centralized debug flags and enabled more granular control of debug
settings.
---
core/java/android/webkit/BrowserFrame.java | 8 +--
core/java/android/webkit/CacheManager.java | 4 +-
core/java/android/webkit/CallbackProxy.java | 2 +-
core/java/android/webkit/CookieManager.java | 12 ++--
core/java/android/webkit/CookieSyncManager.java | 4 +-
core/java/android/webkit/DebugFlags.java | 48 ++++++++++++++++
core/java/android/webkit/FrameLoader.java | 8 +--
core/java/android/webkit/JWebCoreJavaBridge.java | 4 +-
core/java/android/webkit/LoadListener.java | 36 ++++++------
core/java/android/webkit/Network.java | 14 ++---
core/java/android/webkit/SslErrorHandler.java | 12 ++--
core/java/android/webkit/StreamLoader.java | 2 +-
core/java/android/webkit/URLUtil.java | 2 +-
core/java/android/webkit/WebBackForwardList.java | 2 +-
core/java/android/webkit/WebSettings.java | 2 +-
core/java/android/webkit/WebSyncManager.java | 10 ++--
core/java/android/webkit/WebView.java | 70 ++++++++++++------------
core/java/android/webkit/WebViewCore.java | 22 ++++----
18 files changed, 153 insertions(+), 109 deletions(-)
create mode 100644 core/java/android/webkit/DebugFlags.java
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 08ca209fa9e2..e04ae720a6da 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -121,7 +121,7 @@ class BrowserFrame extends Handler {
mDatabase = WebViewDatabase.getInstance(context);
mWebViewCore = w;
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.BROWSER_FRAME) {
Log.v(LOGTAG, "BrowserFrame constructor: this=" + this);
}
}
@@ -343,7 +343,7 @@ class BrowserFrame extends Handler {
switch (msg.what) {
case FRAME_COMPLETED: {
if (mSettings.getSavePassword() && hasPasswordField()) {
- if (WebView.DEBUG) {
+ if (DebugFlags.BROWSER_FRAME) {
Assert.assertNotNull(mCallbackProxy.getBackForwardList()
.getCurrentItem());
}
@@ -492,7 +492,7 @@ class BrowserFrame extends Handler {
}
if (mSettings.getSavePassword() && hasPasswordField()) {
try {
- if (WebView.DEBUG) {
+ if (DebugFlags.BROWSER_FRAME) {
Assert.assertNotNull(mCallbackProxy.getBackForwardList()
.getCurrentItem());
}
@@ -540,7 +540,7 @@ class BrowserFrame extends Handler {
// is this resource the main-frame top-level page?
boolean isMainFramePage = mIsMainFrame;
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.BROWSER_FRAME) {
Log.v(LOGTAG, "startLoadingResource: url=" + url + ", method="
+ method + ", postData=" + postData + ", isHighPriority="
+ isHighPriority + ", isMainFramePage=" + isMainFramePage);
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index 0095b91f250b..4d471f7f6789 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -320,7 +320,7 @@ public final class CacheManager {
}
}
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.CACHE_MANAGER) {
Log.v(LOGTAG, "getCacheFile for url " + url);
}
@@ -422,7 +422,7 @@ public final class CacheManager {
mDataBase.addCache(url, cacheRet);
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.CACHE_MANAGER) {
Log.v(LOGTAG, "saveCacheFile for url " + url);
}
}
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 667cb2c61e07..2fb696456ced 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -852,7 +852,7 @@ class CallbackProxy extends Handler {
String password, Message resumeMsg) {
// resumeMsg should be null at this point because we want to create it
// within the CallbackProxy.
- if (WebView.DEBUG) {
+ if (DebugFlags.CALLBACK_PROXY) {
junit.framework.Assert.assertNull(resumeMsg);
}
resumeMsg = obtainMessage(NOTIFY);
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index e8c22798bd55..7b9172487876 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -262,7 +262,7 @@ public final class CookieManager {
if (!mAcceptCookie || uri == null) {
return;
}
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.COOKIE_MANAGER) {
Log.v(LOGTAG, "setCookie: uri: " + uri + " value: " + value);
}
@@ -427,12 +427,12 @@ public final class CookieManager {
}
}
if (ret.length() > 0) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.COOKIE_MANAGER) {
Log.v(LOGTAG, "getCookie: uri: " + uri + " value: " + ret);
}
return ret.toString();
} else {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.COOKIE_MANAGER) {
Log.v(LOGTAG, "getCookie: uri: " + uri
+ " But can't find cookie.");
}
@@ -588,7 +588,7 @@ public final class CookieManager {
Iterator> listIter = cookieLists.iterator();
while (listIter.hasNext() && count < MAX_RAM_COOKIES_COUNT) {
ArrayList list = listIter.next();
- if (WebView.DEBUG) {
+ if (DebugFlags.COOKIE_MANAGER) {
Iterator iter = list.iterator();
while (iter.hasNext() && count < MAX_RAM_COOKIES_COUNT) {
Cookie cookie = iter.next();
@@ -608,7 +608,7 @@ public final class CookieManager {
ArrayList retlist = new ArrayList();
if (mapSize >= MAX_RAM_DOMAIN_COUNT || count >= MAX_RAM_COOKIES_COUNT) {
- if (WebView.DEBUG) {
+ if (DebugFlags.COOKIE_MANAGER) {
Log.v(LOGTAG, count + " cookies used " + byteCount
+ " bytes with " + mapSize + " domains");
}
@@ -616,7 +616,7 @@ public final class CookieManager {
int toGo = mapSize / 10 + 1;
while (toGo-- > 0){
String domain = domains[toGo].toString();
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.COOKIE_MANAGER) {
Log.v(LOGTAG, "delete domain: " + domain
+ " from RAM cache");
}
diff --git a/core/java/android/webkit/CookieSyncManager.java b/core/java/android/webkit/CookieSyncManager.java
index aa6c76b432b0..14375d2b1322 100644
--- a/core/java/android/webkit/CookieSyncManager.java
+++ b/core/java/android/webkit/CookieSyncManager.java
@@ -170,7 +170,7 @@ public final class CookieSyncManager extends WebSyncManager {
}
protected void syncFromRamToFlash() {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.COOKIE_SYNC_MANAGER) {
Log.v(LOGTAG, "CookieSyncManager::syncFromRamToFlash STARTS");
}
@@ -187,7 +187,7 @@ public final class CookieSyncManager extends WebSyncManager {
CookieManager.getInstance().deleteLRUDomain();
syncFromRamToFlash(lruList);
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.COOKIE_SYNC_MANAGER) {
Log.v(LOGTAG, "CookieSyncManager::syncFromRamToFlash DONE");
}
}
diff --git a/core/java/android/webkit/DebugFlags.java b/core/java/android/webkit/DebugFlags.java
new file mode 100644
index 000000000000..89cb606df165
--- /dev/null
+++ b/core/java/android/webkit/DebugFlags.java
@@ -0,0 +1,48 @@
+/*
+ * 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.webkit;
+
+/**
+ * This class is a container for all of the debug flags used in the Java
+ * components of webkit. These flags must be final in order to ensure that
+ * the compiler optimizes the code that uses them out of the final executable.
+ *
+ * The name of each flags maps directly to the name of the class in which that
+ * flag is used.
+ *
+ */
+class DebugFlags {
+
+ public static final boolean BROWSER_FRAME = false;
+ public static final boolean CACHE_MANAGER = false;
+ public static final boolean CALLBACK_PROXY = false;
+ public static final boolean COOKIE_MANAGER = false;
+ public static final boolean COOKIE_SYNC_MANAGER = false;
+ public static final boolean FRAME_LOADER = false;
+ public static final boolean J_WEB_CORE_JAVA_BRIDGE = false;// HIGHLY VERBOSE
+ public static final boolean LOAD_LISTENER = false;
+ public static final boolean NETWORK = false;
+ public static final boolean SSL_ERROR_HANDLER = false;
+ public static final boolean STREAM_LOADER = false;
+ public static final boolean URL_UTIL = false;
+ public static final boolean WEB_BACK_FORWARD_LIST = false;
+ public static final boolean WEB_SETTINGS = false;
+ public static final boolean WEB_SYNC_MANAGER = false;
+ public static final boolean WEB_VIEW = false;
+ public static final boolean WEB_VIEW_CORE = false;
+
+}
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java
index 6f1b16047950..c33744e57388 100644
--- a/core/java/android/webkit/FrameLoader.java
+++ b/core/java/android/webkit/FrameLoader.java
@@ -120,7 +120,7 @@ class FrameLoader {
} else if (handleLocalFile(url, mListener, mSettings)) {
return true;
}
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.FRAME_LOADER) {
Log.v(LOGTAG, "FrameLoader.executeLoad: url protocol not supported:"
+ mListener.url());
}
@@ -180,7 +180,7 @@ class FrameLoader {
return true;
}
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.FRAME_LOADER) {
Log.v(LOGTAG, "FrameLoader: http " + mMethod + " load for: "
+ mListener.url());
}
@@ -211,7 +211,7 @@ class FrameLoader {
* setup a load from the byte stream in a CacheResult.
*/
private void startCacheLoad(CacheResult result) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.FRAME_LOADER) {
Log.v(LOGTAG, "FrameLoader: loading from cache: "
+ mListener.url());
}
@@ -285,7 +285,7 @@ class FrameLoader {
// of it's state. If it is not in the cache, then go to the
// network.
case WebSettings.LOAD_CACHE_ELSE_NETWORK: {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.FRAME_LOADER) {
Log.v(LOGTAG, "FrameLoader: checking cache: "
+ mListener.url());
}
diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java
index bf055189d6cb..878b690a7cf6 100644
--- a/core/java/android/webkit/JWebCoreJavaBridge.java
+++ b/core/java/android/webkit/JWebCoreJavaBridge.java
@@ -190,7 +190,7 @@ final class JWebCoreJavaBridge extends Handler {
* @param timemillis The relative time when the timer should fire
*/
private void setSharedTimer(long timemillis) {
- if (WebView.LOGV_ENABLED) Log.v(LOGTAG, "setSharedTimer " + timemillis);
+ if (DebugFlags.J_WEB_CORE_JAVA_BRIDGE) Log.v(LOGTAG, "setSharedTimer " + timemillis);
if (timemillis <= 0) {
// we don't accumulate the sharedTimer unless it is a delayed
@@ -214,7 +214,7 @@ final class JWebCoreJavaBridge extends Handler {
* Stop the shared timer.
*/
private void stopSharedTimer() {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.J_WEB_CORE_JAVA_BRIDGE) {
Log.v(LOGTAG, "stopSharedTimer removing all timers");
}
removeMessages(TIMER_MESSAGE);
diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java
index d3e26bd9d166..1ebdb79789d7 100644
--- a/core/java/android/webkit/LoadListener.java
+++ b/core/java/android/webkit/LoadListener.java
@@ -131,7 +131,7 @@ class LoadListener extends Handler implements EventHandler {
LoadListener(Context context, BrowserFrame frame, String url,
int nativeLoader, boolean synchronous, boolean isMainPageLoader) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener constructor url=" + url);
}
mContext = context;
@@ -280,7 +280,7 @@ class LoadListener extends Handler implements EventHandler {
* directly
*/
public void headers(Headers headers) {
- if (WebView.LOGV_ENABLED) Log.v(LOGTAG, "LoadListener.headers");
+ if (DebugFlags.LOAD_LISTENER) Log.v(LOGTAG, "LoadListener.headers");
sendMessageInternal(obtainMessage(MSG_CONTENT_HEADERS, headers));
}
@@ -422,7 +422,7 @@ class LoadListener extends Handler implements EventHandler {
*/
public void status(int majorVersion, int minorVersion,
int code, /* Status-Code value */ String reasonPhrase) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener: from: " + mUrl
+ " major: " + majorVersion
+ " minor: " + minorVersion
@@ -482,7 +482,7 @@ class LoadListener extends Handler implements EventHandler {
* directly
*/
public void error(int id, String description) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener.error url:" +
url() + " id:" + id + " description:" + description);
}
@@ -510,7 +510,7 @@ class LoadListener extends Handler implements EventHandler {
* mDataBuilder is a thread-safe structure.
*/
public void data(byte[] data, int length) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener.data(): url: " + url());
}
@@ -535,7 +535,7 @@ class LoadListener extends Handler implements EventHandler {
* directly
*/
public void endData() {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener.endData(): url: " + url());
}
sendMessageInternal(obtainMessage(MSG_CONTENT_FINISHED));
@@ -588,7 +588,7 @@ class LoadListener extends Handler implements EventHandler {
// before calling it.
if (mCacheLoader != null) {
mCacheLoader.load();
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener cache load url=" + url());
}
return;
@@ -638,7 +638,7 @@ class LoadListener extends Handler implements EventHandler {
CacheManager.HEADER_KEY_IFNONEMATCH) &&
!headers.containsKey(
CacheManager.HEADER_KEY_IFMODIFIEDSINCE)) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "FrameLoader: HTTP URL in cache " +
"and usable: " + url());
}
@@ -657,7 +657,7 @@ class LoadListener extends Handler implements EventHandler {
* directly
*/
public void handleSslErrorRequest(SslError error) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG,
"LoadListener.handleSslErrorRequest(): url:" + url() +
" primary error: " + error.getPrimaryError() +
@@ -723,7 +723,7 @@ class LoadListener extends Handler implements EventHandler {
* are null, cancel the request.
*/
void handleAuthResponse(String username, String password) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener.handleAuthResponse: url: " + mUrl
+ " username: " + username
+ " password: " + password);
@@ -820,7 +820,7 @@ class LoadListener extends Handler implements EventHandler {
}
void attachRequestHandle(RequestHandle requestHandle) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener.attachRequestHandle(): " +
"requestHandle: " + requestHandle);
}
@@ -828,7 +828,7 @@ class LoadListener extends Handler implements EventHandler {
}
void detachRequestHandle() {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener.detachRequestHandle(): " +
"requestHandle: " + mRequestHandle);
}
@@ -867,7 +867,7 @@ class LoadListener extends Handler implements EventHandler {
*/
static boolean willLoadFromCache(String url) {
boolean inCache = CacheManager.getCacheFile(url, null) != null;
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "willLoadFromCache: " + url + " in cache: " +
inCache);
}
@@ -1041,7 +1041,7 @@ class LoadListener extends Handler implements EventHandler {
* EventHandler's method call.
*/
public void cancel() {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
if (mRequestHandle == null) {
Log.v(LOGTAG, "LoadListener.cancel(): no requestHandle");
} else {
@@ -1173,7 +1173,7 @@ class LoadListener extends Handler implements EventHandler {
tearDown();
}
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener.onRedirect(): redirect to: " +
redirectTo);
}
@@ -1187,7 +1187,7 @@ class LoadListener extends Handler implements EventHandler {
Pattern.compile("^((?:[xX]-)?[a-zA-Z\\*]+/[\\w\\+\\*-]+[\\.[\\w\\+-]+]*)$");
/* package */ void parseContentTypeHeader(String contentType) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "LoadListener.parseContentTypeHeader: " +
"contentType: " + contentType);
}
@@ -1377,7 +1377,7 @@ class LoadListener extends Handler implements EventHandler {
*/
private String guessMimeTypeFromExtension() {
// PENDING: need to normalize url
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.LOAD_LISTENER) {
Log.v(LOGTAG, "guessMimeTypeFromExtension: mURL = " + mUrl);
}
@@ -1401,7 +1401,7 @@ class LoadListener extends Handler implements EventHandler {
* Cycle through our messages for synchronous loads.
*/
/* package */ void loadSynchronousMessages() {
- if (WebView.DEBUG && !mSynchronous) {
+ if (DebugFlags.LOAD_LISTENER && !mSynchronous) {
throw new AssertionError();
}
// Note: this can be called twice if it is a synchronous network load,
diff --git a/core/java/android/webkit/Network.java b/core/java/android/webkit/Network.java
index c9b80ce77df6..8c2b09b1a7a1 100644
--- a/core/java/android/webkit/Network.java
+++ b/core/java/android/webkit/Network.java
@@ -132,7 +132,7 @@ class Network {
* XXX: Must be created in the same thread as WebCore!!!!!
*/
private Network(Context context) {
- if (WebView.DEBUG) {
+ if (DebugFlags.NETWORK) {
Assert.assertTrue(Thread.currentThread().
getName().equals(WebViewCore.THREAD_NAME));
}
@@ -232,7 +232,7 @@ class Network {
* connecting through the proxy.
*/
public synchronized void setProxyUsername(String proxyUsername) {
- if (WebView.DEBUG) {
+ if (DebugFlags.NETWORK) {
Assert.assertTrue(isValidProxySet());
}
@@ -252,7 +252,7 @@ class Network {
* connecting through the proxy.
*/
public synchronized void setProxyPassword(String proxyPassword) {
- if (WebView.DEBUG) {
+ if (DebugFlags.NETWORK) {
Assert.assertTrue(isValidProxySet());
}
@@ -266,7 +266,7 @@ class Network {
* @return True iff succeeds.
*/
public boolean saveState(Bundle outState) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.NETWORK) {
Log.v(LOGTAG, "Network.saveState()");
}
@@ -280,7 +280,7 @@ class Network {
* @return True iff succeeds.
*/
public boolean restoreState(Bundle inState) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.NETWORK) {
Log.v(LOGTAG, "Network.restoreState()");
}
@@ -300,7 +300,7 @@ class Network {
* @param loader The loader that resulted in SSL errors.
*/
public void handleSslErrorRequest(LoadListener loader) {
- if (WebView.DEBUG) Assert.assertNotNull(loader);
+ if (DebugFlags.NETWORK) Assert.assertNotNull(loader);
if (loader != null) {
mSslErrorHandler.handleSslErrorRequest(loader);
}
@@ -313,7 +313,7 @@ class Network {
* authentication request.
*/
public void handleAuthRequest(LoadListener loader) {
- if (WebView.DEBUG) Assert.assertNotNull(loader);
+ if (DebugFlags.NETWORK) Assert.assertNotNull(loader);
if (loader != null) {
mHttpAuthHandler.handleAuthRequest(loader);
}
diff --git a/core/java/android/webkit/SslErrorHandler.java b/core/java/android/webkit/SslErrorHandler.java
index 617a3145ee51..cc1e7501b1ad 100644
--- a/core/java/android/webkit/SslErrorHandler.java
+++ b/core/java/android/webkit/SslErrorHandler.java
@@ -120,7 +120,7 @@ public class SslErrorHandler extends Handler {
* Handles SSL error(s) on the way up to the user.
*/
/* package */ synchronized void handleSslErrorRequest(LoadListener loader) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.SSL_ERROR_HANDLER) {
Log.v(LOGTAG, "SslErrorHandler.handleSslErrorRequest(): " +
"url=" + loader.url());
}
@@ -157,14 +157,14 @@ public class SslErrorHandler extends Handler {
SslError error = loader.sslError();
- if (WebView.DEBUG) {
+ if (DebugFlags.SSL_ERROR_HANDLER) {
Assert.assertNotNull(error);
}
int primary = error.getPrimaryError();
String host = loader.host();
- if (WebView.DEBUG) {
+ if (DebugFlags.SSL_ERROR_HANDLER) {
Assert.assertTrue(host != null && primary != 0);
}
@@ -205,11 +205,11 @@ public class SslErrorHandler extends Handler {
*/
/* package */ synchronized void handleSslErrorResponse(boolean proceed) {
LoadListener loader = mLoaderQueue.poll();
- if (WebView.DEBUG) {
+ if (DebugFlags.SSL_ERROR_HANDLER) {
Assert.assertNotNull(loader);
}
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.SSL_ERROR_HANDLER) {
Log.v(LOGTAG, "SslErrorHandler.handleSslErrorResponse():"
+ " proceed: " + proceed
+ " url:" + loader.url());
@@ -221,7 +221,7 @@ public class SslErrorHandler extends Handler {
int primary = loader.sslError().getPrimaryError();
String host = loader.host();
- if (WebView.DEBUG) {
+ if (DebugFlags.SSL_ERROR_HANDLER) {
Assert.assertTrue(host != null && primary != 0);
}
boolean hasKey = mSslPrefTable.containsKey(host);
diff --git a/core/java/android/webkit/StreamLoader.java b/core/java/android/webkit/StreamLoader.java
index 7d6d7cc5d3fc..eab3350d7265 100644
--- a/core/java/android/webkit/StreamLoader.java
+++ b/core/java/android/webkit/StreamLoader.java
@@ -113,7 +113,7 @@ abstract class StreamLoader extends Handler {
* @see android.os.Handler#handleMessage(android.os.Message)
*/
public void handleMessage(Message msg) {
- if (WebView.DEBUG && mHandler.isSynchronous()) {
+ if (DebugFlags.STREAM_LOADER && mHandler.isSynchronous()) {
throw new AssertionError();
}
if (mHandler.cancelled()) {
diff --git a/core/java/android/webkit/URLUtil.java b/core/java/android/webkit/URLUtil.java
index d6ac3e9093bf..de70fc2d2b65 100644
--- a/core/java/android/webkit/URLUtil.java
+++ b/core/java/android/webkit/URLUtil.java
@@ -61,7 +61,7 @@ public final class URLUtil {
webAddress = new WebAddress(inUrl);
} catch (ParseException ex) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.URL_UTIL) {
Log.v(LOGTAG, "smartUrlFilter: failed to parse url = " + inUrl);
}
return retVal;
diff --git a/core/java/android/webkit/WebBackForwardList.java b/core/java/android/webkit/WebBackForwardList.java
index ffd6a118d9e6..62a55318d306 100644
--- a/core/java/android/webkit/WebBackForwardList.java
+++ b/core/java/android/webkit/WebBackForwardList.java
@@ -137,7 +137,7 @@ public class WebBackForwardList implements Cloneable, Serializable {
// when removing the first item, we can assert that the index is 0.
// This lets us change the current index without having to query the
// native BackForwardList.
- if (WebView.DEBUG && (index != 0)) {
+ if (DebugFlags.WEB_BACK_FORWARD_LIST && (index != 0)) {
throw new AssertionError();
}
final WebHistoryItem h = mArray.remove(index);
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index e7352928b06a..ea186fd5ff3d 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1190,7 +1190,7 @@ public class WebSettings {
/*package*/
synchronized void syncSettingsAndCreateHandler(BrowserFrame frame) {
mBrowserFrame = frame;
- if (WebView.DEBUG) {
+ if (DebugFlags.WEB_SETTINGS) {
junit.framework.Assert.assertTrue(frame.mNativeFrame != 0);
}
nativeSync(frame.mNativeFrame);
diff --git a/core/java/android/webkit/WebSyncManager.java b/core/java/android/webkit/WebSyncManager.java
index ded17ed518c7..d3ec6031ace4 100644
--- a/core/java/android/webkit/WebSyncManager.java
+++ b/core/java/android/webkit/WebSyncManager.java
@@ -47,7 +47,7 @@ abstract class WebSyncManager implements Runnable {
@Override
public void handleMessage(Message msg) {
if (msg.what == SYNC_MESSAGE) {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.WEB_SYNC_MANAGER) {
Log.v(LOGTAG, "*** WebSyncManager sync ***");
}
syncFromRamToFlash();
@@ -94,7 +94,7 @@ abstract class WebSyncManager implements Runnable {
* sync() forces sync manager to sync now
*/
public void sync() {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.WEB_SYNC_MANAGER) {
Log.v(LOGTAG, "*** WebSyncManager sync ***");
}
if (mHandler == null) {
@@ -109,7 +109,7 @@ abstract class WebSyncManager implements Runnable {
* resetSync() resets sync manager's timer
*/
public void resetSync() {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.WEB_SYNC_MANAGER) {
Log.v(LOGTAG, "*** WebSyncManager resetSync ***");
}
if (mHandler == null) {
@@ -124,7 +124,7 @@ abstract class WebSyncManager implements Runnable {
* startSync() requests sync manager to start sync
*/
public void startSync() {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.WEB_SYNC_MANAGER) {
Log.v(LOGTAG, "*** WebSyncManager startSync ***, Ref count:" +
mStartSyncRefCount);
}
@@ -142,7 +142,7 @@ abstract class WebSyncManager implements Runnable {
* the queue to break the sync loop
*/
public void stopSync() {
- if (WebView.LOGV_ENABLED) {
+ if (DebugFlags.WEB_SYNC_MANAGER) {
Log.v(LOGTAG, "*** WebSyncManager stopSync ***, Ref count:" +
mStartSyncRefCount);
}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 4072c224e553..e549d5fd0d5a 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -207,8 +207,6 @@ public class WebView extends AbsoluteLayout
// keep debugging parameters near the top of the file
static final String LOGTAG = "webview";
- static final boolean DEBUG = false;
- static final boolean LOGV_ENABLED = DEBUG;
private static class ExtendedZoomControls extends FrameLayout {
public ExtendedZoomControls(Context context, AttributeSet attrs) {
@@ -2643,7 +2641,7 @@ public class WebView extends AbsoluteLayout
}
float newX = scrollZoomX(scale);
float newY = scrollZoomY(scale);
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "scrollZoomDraw scale=" + scale + " + (" + newX
+ ", " + newY + ") mZoomScroll=(" + mZoomScrollX + ", "
+ mZoomScrollY + ")" + " invScale=" + invScale + " scale="
@@ -2690,7 +2688,7 @@ public class WebView extends AbsoluteLayout
}
canvas.scale(halfScale, halfScale, mZoomScrollX + width * halfX
, mZoomScrollY + height * halfY);
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "scrollZoomDraw halfScale=" + halfScale + " w/h=("
+ width + ", " + height + ") half=(" + halfX + ", "
+ halfY + ")");
@@ -2719,7 +2717,7 @@ public class WebView extends AbsoluteLayout
, Math.max(0, (int) ((x - left) / scale)));
mZoomScrollY = Math.min(mContentHeight - height
, Math.max(0, (int) ((y - top) / scale)));
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "zoomScrollTap scale=" + scale + " + (" + left
+ ", " + top + ") mZoomScroll=(" + mZoomScrollX + ", "
+ mZoomScrollY + ")" + " x=" + x + " y=" + y);
@@ -2736,7 +2734,7 @@ public class WebView extends AbsoluteLayout
float y = (float) height / (float) mContentHeight;
mZoomScrollLimit = Math.max(DEFAULT_MIN_ZOOM_SCALE, Math.min(x, y));
mZoomScrollInvLimit = 1.0f / mZoomScrollLimit;
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "canZoomScrollOut"
+ " mInvActualScale=" + mInvActualScale
+ " mZoomScrollLimit=" + mZoomScrollLimit
@@ -2783,7 +2781,7 @@ public class WebView extends AbsoluteLayout
- (zoomFrame.height() >> 1));
scrollTo(0, 0); // triggers inval, starts animation
clearTextEntry();
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "startZoomScrollOut mZoomScroll=("
+ mZoomScrollX + ", " + mZoomScrollY +")");
}
@@ -2814,7 +2812,7 @@ public class WebView extends AbsoluteLayout
if (maxZoomX > 0) {
int maxScreenX = width - (int) Math.ceil(width
* mZoomScrollLimit) - SCROLL_ZOOM_FINGER_BUFFER;
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "moveZoomScrollWindow-X"
+ " maxScreenX=" + maxScreenX + " width=" + width
+ " mZoomScrollLimit=" + mZoomScrollLimit + " x=" + x);
@@ -2827,7 +2825,7 @@ public class WebView extends AbsoluteLayout
if (maxZoomY > 0) {
int maxScreenY = height - (int) Math.ceil(height
* mZoomScrollLimit) - SCROLL_ZOOM_FINGER_BUFFER;
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "moveZoomScrollWindow-Y"
+ " maxScreenY=" + maxScreenY + " height=" + height
+ " mZoomScrollLimit=" + mZoomScrollLimit + " y=" + y);
@@ -2839,7 +2837,7 @@ public class WebView extends AbsoluteLayout
if (oldX != mZoomScrollX || oldY != mZoomScrollY) {
invalidate();
}
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "moveZoomScrollWindow"
+ " scrollTo=(" + mZoomScrollX + ", " + mZoomScrollY + ")"
+ " mLastTouch=(" + mLastTouchX + ", " + mLastTouchY + ")"
@@ -3107,7 +3105,7 @@ public class WebView extends AbsoluteLayout
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
+ ", " + event);
}
@@ -3232,7 +3230,7 @@ public class WebView extends AbsoluteLayout
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
+ ", " + event);
}
@@ -3299,7 +3297,7 @@ public class WebView extends AbsoluteLayout
if (mTouchMode == TOUCH_DOUBLECLICK_MODE) {
zoomScrollOut();
} else {
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "TOUCH_DOUBLECLICK_MODE");
}
mPrivateHandler.sendMessageDelayed(mPrivateHandler
@@ -3478,7 +3476,7 @@ public class WebView extends AbsoluteLayout
@Override
protected void onFocusChanged(boolean focused, int direction,
Rect previouslyFocusedRect) {
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
}
if (focused) {
@@ -3586,7 +3584,7 @@ public class WebView extends AbsoluteLayout
return false;
}
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, ev + " at " + ev.getEventTime() + " mTouchMode="
+ mTouchMode);
}
@@ -3642,7 +3640,7 @@ public class WebView extends AbsoluteLayout
mSelectX = mScrollX + (int) x;
mSelectY = mScrollY + (int) y;
mTouchMode = TOUCH_SELECT_MODE;
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "select=" + mSelectX + "," + mSelectY);
}
nativeMoveSelection(viewToContent(mSelectX)
@@ -3687,7 +3685,7 @@ public class WebView extends AbsoluteLayout
if (mTouchMode == TOUCH_SELECT_MODE) {
mSelectX = mScrollX + (int) x;
mSelectY = mScrollY + (int) y;
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "xtend=" + mSelectX + "," + mSelectY);
}
nativeMoveSelection(viewToContent(mSelectX)
@@ -3839,7 +3837,7 @@ public class WebView extends AbsoluteLayout
// no action during scroll animation
break;
case SCROLL_ZOOM_OUT:
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "ACTION_UP SCROLL_ZOOM_OUT"
+ " eventTime - mLastTouchTime="
+ (eventTime - mLastTouchTime));
@@ -3961,7 +3959,7 @@ public class WebView extends AbsoluteLayout
&& !mLastFocusBounds.equals(nativeGetCursorRingBounds())) {
nativeSelectBestAt(mLastFocusBounds);
}
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
+ " time=" + time
+ " mLastFocusTime=" + mLastFocusTime);
@@ -3981,7 +3979,7 @@ public class WebView extends AbsoluteLayout
mExtendSelection = true;
}
}
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
+ " time=" + time
);
@@ -3989,26 +3987,26 @@ public class WebView extends AbsoluteLayout
return false; // let common code in onKeyUp at it
}
if (mMapTrackballToArrowKeys && mShiftIsPressed == false) {
- if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent gmail quit");
+ if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
return false;
}
// no move if we're still waiting on SWITCH_TO_CLICK timeout
if (mTouchMode == TOUCH_DOUBLECLICK_MODE) {
- if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent 2 click quit");
+ if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent 2 click quit");
return true;
}
if (mTrackballDown) {
- if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent down quit");
+ if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit");
return true; // discard move if trackball is down
}
if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
- if (LOGV_ENABLED) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
+ if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
return true;
}
// TODO: alternatively we can do panning as touch does
switchOutDrawHistory();
if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "onTrackballEvent time="
+ time + " last=" + mTrackballLastTime);
}
@@ -4016,7 +4014,7 @@ public class WebView extends AbsoluteLayout
mTrackballXMove = mTrackballYMove = 0;
}
mTrackballLastTime = time;
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
}
mTrackballRemainsX += ev.getX();
@@ -4038,7 +4036,7 @@ public class WebView extends AbsoluteLayout
, mSelectX));
mSelectY = Math.min(maxY, Math.max(mScrollY - SELECT_CURSOR_OFFSET
, mSelectY));
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "moveSelection"
+ " mSelectX=" + mSelectX
+ " mSelectY=" + mSelectY
@@ -4121,7 +4119,7 @@ public class WebView extends AbsoluteLayout
float ax = Math.abs(xRate);
float ay = Math.abs(yRate);
float maxA = Math.max(ax, ay);
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
+ " xRate=" + xRate
+ " yRate=" + yRate
@@ -4138,7 +4136,7 @@ public class WebView extends AbsoluteLayout
int maxWH = Math.max(width, height);
mZoomScrollX += scaleTrackballX(xRate, maxWH);
mZoomScrollY += scaleTrackballY(yRate, maxWH);
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "doTrackball SCROLL_ZOOM_OUT"
+ " mZoomScrollX=" + mZoomScrollX
+ " mZoomScrollY=" + mZoomScrollY);
@@ -4163,7 +4161,7 @@ public class WebView extends AbsoluteLayout
mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
KeyEvent.KEYCODE_DPAD_RIGHT;
count = Math.min(count, TRACKBALL_MOVE_COUNT);
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
+ " count=" + count
+ " mTrackballRemainsX=" + mTrackballRemainsX
@@ -4177,7 +4175,7 @@ public class WebView extends AbsoluteLayout
if (count >= TRACKBALL_SCROLL_COUNT) {
int xMove = scaleTrackballX(xRate, width);
int yMove = scaleTrackballY(yRate, height);
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "doTrackball pinScrollBy"
+ " count=" + count
+ " xMove=" + xMove + " yMove=" + yMove
@@ -4601,7 +4599,7 @@ public class WebView extends AbsoluteLayout
class PrivateHandler extends Handler {
@Override
public void handleMessage(Message msg) {
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD || msg.what
> INVAL_RECT_MSG_ID ? Integer.toString(msg.what)
: HandlerDebugString[msg.what - REMEMBER_PASSWORD]);
@@ -4701,7 +4699,7 @@ public class WebView extends AbsoluteLayout
&& viewSize.y == mLastHeightSent;
recordNewContentSize(draw.mWidthHeight.x,
draw.mWidthHeight.y, updateLayout);
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Rect b = draw.mInvalRegion.getBounds();
Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
b.left+","+b.top+","+b.right+","+b.bottom+"}");
@@ -4833,7 +4831,7 @@ public class WebView extends AbsoluteLayout
break;
case UPDATE_CLIPBOARD:
String str = (String) msg.obj;
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "UPDATE_CLIPBOARD " + str);
}
try {
@@ -5196,7 +5194,7 @@ public class WebView extends AbsoluteLayout
mLastFocusTime = time;
mLastFocusBounds = nativeGetCursorRingBounds();
boolean keyHandled = nativeMoveFocus(keyCode, count, noScroll) == false;
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "navHandledKey mLastFocusBounds=" + mLastFocusBounds
+ " mLastFocusTime=" + mLastFocusTime
+ " handled=" + keyHandled);
@@ -5228,7 +5226,7 @@ public class WebView extends AbsoluteLayout
}
if (mLastFocusBounds.isEmpty()) return keyHandled;
if (mLastFocusBounds.equals(contentFocus)) return keyHandled;
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, "navHandledKey contentFocus=" + contentFocus);
}
requestRectangleOnScreen(viewFocus);
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 1d91f52e00e5..614323c61611 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -41,8 +41,6 @@ import junit.framework.Assert;
final class WebViewCore {
private static final String LOGTAG = "webcore";
- static final boolean DEBUG = false;
- static final boolean LOGV_ENABLED = DEBUG;
static {
// Load libwebcore during static initialization. This happens in the
@@ -694,7 +692,7 @@ final class WebViewCore {
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW_CORE) {
Log.v(LOGTAG, msg.what < LOAD_URL || msg.what
> SET_ACTIVE ? Integer.toString(msg.what)
: HandlerDebugString[msg.what - LOAD_URL]);
@@ -1164,7 +1162,7 @@ final class WebViewCore {
//-------------------------------------------------------------------------
void stopLoading() {
- if (LOGV_ENABLED) Log.v(LOGTAG, "CORE stopLoading");
+ if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE stopLoading");
if (mBrowserFrame != null) {
mBrowserFrame.stopLoading();
}
@@ -1240,12 +1238,12 @@ final class WebViewCore {
//-------------------------------------------------------------------------
private void loadUrl(String url) {
- if (LOGV_ENABLED) Log.v(LOGTAG, " CORE loadUrl " + url);
+ if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url);
mBrowserFrame.loadUrl(url);
}
private void key(KeyEvent evt, boolean isDown) {
- if (LOGV_ENABLED) {
+ if (DebugFlags.WEB_VIEW_CORE) {
Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
+ evt);
}
@@ -1263,7 +1261,7 @@ final class WebViewCore {
// notify webkit that our virtual view size changed size (after inv-zoom)
private void viewSizeChanged(int w, int h, float scale) {
- if (LOGV_ENABLED) Log.v(LOGTAG, "CORE onSizeChanged");
+ if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "CORE onSizeChanged");
if (w == 0) {
Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
return;
@@ -1303,7 +1301,7 @@ final class WebViewCore {
if (needInvalidate) {
// ensure {@link #webkitDraw} is called as we were blocking in
// {@link #contentDraw} when mCurrentViewWidth is 0
- if (LOGV_ENABLED) Log.v(LOGTAG, "viewSizeChanged");
+ if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "viewSizeChanged");
contentDraw();
}
mEventHub.sendMessage(Message.obtain(null,
@@ -1342,17 +1340,17 @@ final class WebViewCore {
private void webkitDraw() {
mDrawIsScheduled = false;
DrawData draw = new DrawData();
- if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw start");
+ if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
if (nativeRecordContent(draw.mInvalRegion, draw.mWidthHeight)
== false) {
- if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw abort");
+ if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
return;
}
if (mWebView != null) {
// Send the native view size that was used during the most recent
// layout.
draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
- if (LOGV_ENABLED) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
+ if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
Message.obtain(mWebView.mPrivateHandler,
WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
if (mWebkitScrollX != 0 || mWebkitScrollY != 0) {
@@ -1439,7 +1437,7 @@ final class WebViewCore {
synchronized (core) {
core.mDrawIsScheduled = false;
core.mDrawIsPaused = false;
- if (LOGV_ENABLED) Log.v(LOGTAG, "resumeUpdate");
+ if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "resumeUpdate");
core.contentDraw();
}
}
--
cgit v1.2.3-59-g8ed1b
From 2f1d60cd55933f444842991d3bf05a8552cdd30d Mon Sep 17 00:00:00 2001
From: Cary Clark
Date: Wed, 3 Jun 2009 08:05:53 -0400
Subject: remove unused text parameters from WebView
The additional parameters are no longer used.
---
core/java/android/webkit/WebView.java | 17 ++++++-----------
core/java/android/webkit/WebViewCore.java | 31 +++++++++----------------------
2 files changed, 15 insertions(+), 33 deletions(-)
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index e549d5fd0d5a..9437236e71b2 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -205,7 +205,6 @@ public class WebView extends AbsoluteLayout
// true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK
private boolean mAutoRedraw;
- // keep debugging parameters near the top of the file
static final String LOGTAG = "webview";
private static class ExtendedZoomControls extends FrameLayout {
@@ -2923,8 +2922,7 @@ public class WebView extends AbsoluteLayout
*/
/* package */ void deleteSelection(int start, int end) {
mTextGeneration++;
- mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, start, end,
- cursorData());
+ mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, start, end);
}
/**
@@ -2934,8 +2932,7 @@ public class WebView extends AbsoluteLayout
* @param end End of selection.
*/
/* package */ void setSelection(int start, int end) {
- mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end,
- cursorData());
+ mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
}
// Called by JNI when a touch event puts a textfield into focus.
@@ -3133,7 +3130,7 @@ public class WebView extends AbsoluteLayout
return false;
}
- if (mShiftIsPressed == false && nativeFocusNodeWantsKeyEvents() == false
+ if (mShiftIsPressed == false && nativeCursorWantsKeyEvents() == false
&& (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
|| keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT)) {
mExtendSelection = false;
@@ -3217,7 +3214,7 @@ public class WebView extends AbsoluteLayout
}
// TODO: should we pass all the keys to DOM or check the meta tag
- if (nativeFocusNodeWantsKeyEvents() || true) {
+ if (nativeCursorWantsKeyEvents() || true) {
// pass the key to DOM
mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
// return true as DOM handles the key
@@ -3326,7 +3323,7 @@ public class WebView extends AbsoluteLayout
}
// TODO: should we pass all the keys to DOM or check the meta tag
- if (nativeFocusNodeWantsKeyEvents() || true) {
+ if (nativeCursorWantsKeyEvents() || true) {
// pass the key to DOM
mWebViewCore.sendMessage(EventHub.KEY_UP, event);
// return true as DOM handles the key
@@ -4559,7 +4556,6 @@ public class WebView extends AbsoluteLayout
/* package */ void replaceTextfieldText(int oldStart, int oldEnd,
String replace, int newStart, int newEnd) {
HashMap arg = new HashMap();
- arg.put("focusData", cursorData());
arg.put("replace", replace);
arg.put("start", Integer.valueOf(newStart));
arg.put("end", Integer.valueOf(newEnd));
@@ -4569,7 +4565,6 @@ public class WebView extends AbsoluteLayout
/* package */ void passToJavaScript(String currentText, KeyEvent event) {
HashMap arg = new HashMap();
- arg.put("focusData", cursorData());
arg.put("event", event);
arg.put("currentText", currentText);
// Increase our text generation number, and pass it to webcore thread
@@ -5269,6 +5264,7 @@ public class WebView extends AbsoluteLayout
private native boolean nativeCursorIsAnchor();
private native boolean nativeCursorIsTextInput();
private native String nativeCursorText();
+ private native boolean nativeCursorWantsKeyEvents();
private native void nativeDebugDump();
private native void nativeDestroy();
private native void nativeDrawCursorRing(Canvas content);
@@ -5293,7 +5289,6 @@ public class WebView extends AbsoluteLayout
* Returns true if the native focus nodes says it wants to handle key events
* (ala plugins). This can only be called if mNativeClass is non-zero!
*/
- private native boolean nativeFocusNodeWantsKeyEvents();
private native Rect nativeGetCursorRingBounds();
private native Region nativeGetSelection();
private native boolean nativeHasCursorNode();
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 614323c61611..706e6284e779 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -359,11 +359,10 @@ final class WebViewCore {
private native int nativeGetContentMinPrefWidth();
// Start: functions that deal with text editing
- private native void nativeReplaceTextfieldText(int frame, int node, int x,
- int y, int oldStart, int oldEnd, String replace, int newStart,
- int newEnd);
+ private native void nativeReplaceTextfieldText(
+ int oldStart, int oldEnd, String replace, int newStart, int newEnd);
- private native void passToJs(int frame, int node, int x, int y, int gen,
+ private native void passToJs(int gen,
String currentText, int keyCode, int keyValue, boolean down,
boolean cap, boolean fn, boolean sym);
@@ -409,8 +408,7 @@ final class WebViewCore {
* @param start Beginning of selection to delete.
* @param end End of selection to delete.
*/
- private native void nativeDeleteSelection(int frame, int node, int x, int y,
- int start, int end);
+ private native void nativeDeleteSelection(int start, int end);
/**
* Set the selection to (start, end) in the focused textfield. If start and
@@ -418,8 +416,7 @@ final class WebViewCore {
* @param start Beginning of selection.
* @param end End of selection.
*/
- private native void nativeSetSelection(int frame, int node, int x, int y,
- int start, int end);
+ private native void nativeSetSelection(int start, int end);
private native String nativeGetSelection(Region sel);
@@ -871,26 +868,22 @@ final class WebViewCore {
case REPLACE_TEXT:
HashMap jMap = (HashMap) msg.obj;
- CursorData fData = (CursorData) jMap.get("focusData");
String replace = (String) jMap.get("replace");
int newStart =
((Integer) jMap.get("start")).intValue();
int newEnd =
((Integer) jMap.get("end")).intValue();
- nativeReplaceTextfieldText(fData.mFrame,
- fData.mNode, fData.mX, fData.mY, msg.arg1,
+ nativeReplaceTextfieldText(msg.arg1,
msg.arg2, replace, newStart, newEnd);
break;
case PASS_TO_JS: {
HashMap jsMap = (HashMap) msg.obj;
- CursorData fDat = (CursorData) jsMap.get("focusData");
KeyEvent evt = (KeyEvent) jsMap.get("event");
int keyCode = evt.getKeyCode();
int keyValue = evt.getUnicodeChar();
int generation = msg.arg1;
- passToJs(fDat.mFrame, fDat.mNode, fDat.mX, fDat.mY,
- generation,
+ passToJs(generation,
(String) jsMap.get("currentText"),
keyCode,
keyValue,
@@ -999,17 +992,11 @@ final class WebViewCore {
break;
case DELETE_SELECTION:
- CursorData delData = (CursorData) msg.obj;
- nativeDeleteSelection(delData.mFrame,
- delData.mNode, delData.mX,
- delData.mY, msg.arg1, msg.arg2);
+ nativeDeleteSelection(msg.arg1, msg.arg2);
break;
case SET_SELECTION:
- CursorData selData = (CursorData) msg.obj;
- nativeSetSelection(selData.mFrame,
- selData.mNode, selData.mX,
- selData.mY, msg.arg1, msg.arg2);
+ nativeSetSelection(msg.arg1, msg.arg2);
break;
case LISTBOX_CHOICES:
--
cgit v1.2.3-59-g8ed1b
From 8aeac9408ff74126a1b3e9bb58b86056350b708d Mon Sep 17 00:00:00 2001
From: Jean-Michel Trivi
Date: Wed, 3 Jun 2009 09:43:14 -0700
Subject: Cleaning up makefile for libttssynthproxy for the simulator.
---
tts/jni/Android.mk | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/tts/jni/Android.mk b/tts/jni/Android.mk
index bb76583c52d6..665d6d20fc41 100755
--- a/tts/jni/Android.mk
+++ b/tts/jni/Android.mk
@@ -14,13 +14,10 @@ LOCAL_SHARED_LIBRARIES := \
libutils \
libcutils
-ifneq ($(TARGET_SIMULATOR),true)
-LOCAL_SHARED_LIBRARIES += \
- libdl
-endif
-
-ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
-LOCAL_LDLIBS += -ldl
+ifeq ($(TARGET_SIMULATOR),true)
+ LOCAL_LDLIBS += -ldl
+else
+ LOCAL_SHARED_LIBRARIES += libdl
endif
--
cgit v1.2.3-59-g8ed1b
From 82aa2f5eecddf723af2e4e134de15bbf5c6b32f4 Mon Sep 17 00:00:00 2001
From: Leon Scroggins
Date: Wed, 3 Jun 2009 13:01:32 -0400
Subject: Remove obsolete flags from WebViewCore and WebView
Remove NO_FOCUS_CHANGE_BLOCK and BLOCK_FOCUS_CHANGE_UNTIL_KEY_UP, which were
part of SET_FINAL_FOCUS (which is no longer used) from WebViewCore. In WebView,
do not pass BLOCK_FOCUS_CHANGE_UNTIL_KEY_UP with SET_MOVE_MOUSE, since the
receiver does not care about it.
---
core/java/android/webkit/WebView.java | 4 +---
core/java/android/webkit/WebViewCore.java | 4 ----
2 files changed, 1 insertion(+), 7 deletions(-)
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 9437236e71b2..032c0737d605 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3310,9 +3310,7 @@ public class WebView extends AbsoluteLayout
// coordinates should be in content coordinates.
if (nativeCursorIntersects(visibleRect)) {
nativeSetFollowedLink(true);
- mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE,
- EventHub.BLOCK_FOCUS_CHANGE_UNTIL_KEY_UP, 0,
- cursorData());
+ mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, cursorData());
playSoundEffect(SoundEffectConstants.CLICK);
return true;
} else if (nativeHasCursorNode()) {
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 706e6284e779..78d0b4de2996 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -658,10 +658,6 @@ final class WebViewCore {
// private message ids
private static final int DESTROY = 200;
- // flag values passed to message SET_FINAL_FOCUS
- static final int NO_FOCUS_CHANGE_BLOCK = 0;
- static final int BLOCK_FOCUS_CHANGE_UNTIL_KEY_UP = 1;
-
// Private handler for WebCore messages.
private Handler mHandler;
// Message queue for containing messages before the WebCore thread is
--
cgit v1.2.3-59-g8ed1b
From ceaafa5f1f0504ec85a7d6cdf45381cf748f54aa Mon Sep 17 00:00:00 2001
From: Dmitri Plotnikov
Date: Wed, 3 Jun 2009 10:46:44 -0700
Subject: Adding a new type: aggregation_exception.
---
core/java/android/provider/ContactsContract.java | 43 ++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 3763f9ae64f9..835a5f646e51 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -706,4 +706,47 @@ public final class ContactsContract {
}
}
+ /**
+ * Constants for the contact aggregation exceptions table, which contains
+ * aggregation rules overriding those used by automatic aggregation.
+ */
+ public static final class AggregationExceptions {
+ /**
+ * This utility class cannot be instantiated
+ */
+ private AggregationExceptions() {}
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI =
+ Uri.withAppendedPath(AUTHORITY_URI, "aggregation_exception");
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} providing a directory of data.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/aggregation_exception";
+
+ /**
+ * The type of exception: {@link #TYPE_NEVER_MATCH} or {@link #TYPE_ALWAYS_MATCH}.
+ *
+ * Type: INTEGER
+ */
+ public static final String TYPE = "type";
+
+ public static final int TYPE_NEVER_MATCH = 0;
+ public static final int TYPE_ALWAYS_MATCH = 1;
+
+ /**
+ * A reference to the {@link android.provider.ContactsContract.Contacts#_ID} of one of
+ * the contacts that the rule applies to.
+ */
+ public static final String CONTACT_ID1 = "contact_id1";
+
+ /**
+ * A reference to the {@link android.provider.ContactsContract.Contacts#_ID} of the other
+ * contact that the rule applies to.
+ */
+ public static final String CONTACT_ID2 = "contact_id2";
+ }
}
--
cgit v1.2.3-59-g8ed1b
From 3e8950c0c73f9c1574ce3388c754009edf6bc930 Mon Sep 17 00:00:00 2001
From: Guang Zhu
Date: Wed, 3 Jun 2009 12:23:09 -0700
Subject: Added new parameter to enable a manual pause between pages
---
.../DumpRenderTree/assets/run_reliability_tests.py | 22 +++++++++++++++-------
.../dumprendertree/LayoutTestsAutoRunner.java | 18 ++++++++++++------
.../android/dumprendertree/ReliabilityTest.java | 5 +++--
.../dumprendertree/ReliabilityTestActivity.java | 16 +++++++++++++---
4 files changed, 43 insertions(+), 18 deletions(-)
diff --git a/tests/DumpRenderTree/assets/run_reliability_tests.py b/tests/DumpRenderTree/assets/run_reliability_tests.py
index 84b9501620da..6aab00929a1c 100755
--- a/tests/DumpRenderTree/assets/run_reliability_tests.py
+++ b/tests/DumpRenderTree/assets/run_reliability_tests.py
@@ -75,6 +75,11 @@ def main(options, args):
else:
timedout_file = options.timeout_file
+ if not options.delay:
+ manual_delay = 0
+ else:
+ manual_delay = options.delay
+
adb_cmd = "adb "
if options.adb_options:
adb_cmd += options.adb_options + " "
@@ -110,8 +115,8 @@ def main(options, args):
# Call ReliabilityTestsAutoTest#startReliabilityTests
test_cmd = (test_cmd_prefix + " -e class "
"com.android.dumprendertree.ReliabilityTest#"
- "runReliabilityTest -e timeout %s %s" %
- (str(timeout_ms), test_cmd_postfix))
+ "runReliabilityTest -e timeout %s -e delay %s %s" %
+ (str(timeout_ms), str(manual_delay), test_cmd_postfix))
adb_output = subprocess.Popen(test_cmd, shell=True,
stdout=subprocess.PIPE,
@@ -153,20 +158,23 @@ def main(options, args):
if "__main__" == __name__:
option_parser = optparse.OptionParser()
- option_parser.add_option("", "--time-out-ms",
+ option_parser.add_option("-t", "--time-out-ms",
default=60000,
help="set the timeout for each test")
- option_parser.add_option("", "--verbose", action="store_true",
+ option_parser.add_option("-v", "--verbose", action="store_true",
default=False,
help="include debug-level logging")
- option_parser.add_option("", "--adb-options",
+ option_parser.add_option("-a", "--adb-options",
default=None,
help="pass options to adb, such as -d -e, etc")
- option_parser.add_option("", "--crash-file",
+ option_parser.add_option("-c", "--crash-file",
default="reliability_crashed_sites.txt",
help="the list of sites that cause browser to crash")
- option_parser.add_option("", "--timeout-file",
+ option_parser.add_option("-f", "--timeout-file",
default="reliability_timedout_sites.txt",
help="the list of sites that timedout during test.")
+ option_parser.add_option("-d", "--delay",
+ default=0,
+ help="add a manual delay between pages (in ms)")
opts, arguments = option_parser.parse_args()
main(opts, arguments)
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java
index ebdc9c72305c..57e06a1cbcde 100755
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoRunner.java
@@ -16,14 +16,11 @@
package com.android.dumprendertree;
-import junit.framework.TestSuite;
-import com.android.dumprendertree.LayoutTestsAutoTest;
-
+import android.os.Bundle;
import android.test.InstrumentationTestRunner;
import android.test.InstrumentationTestSuite;
-import android.util.Log;
-import android.content.Intent;
-import android.os.Bundle;
+
+import junit.framework.TestSuite;
/**
@@ -61,6 +58,14 @@ public class LayoutTestsAutoRunner extends InstrumentationTestRunner {
}
}
+ String delay_str = (String) icicle.get("delay");
+ if(delay_str != null) {
+ try {
+ this.mDelay = Integer.parseInt(delay_str);
+ } catch (Exception e) {
+ }
+ }
+
String r = (String)icicle.get("rebaseline");
this.mRebaseline = (r != null && r.toLowerCase().equals("true"));
super.onCreate(icicle);
@@ -68,6 +73,7 @@ public class LayoutTestsAutoRunner extends InstrumentationTestRunner {
public String mTestPath = null;
public int mTimeoutInMillis = 0;
+ public int mDelay = 0;
public boolean mRebaseline = false;
}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
index aa3940ed9b04..e63aa95b558f 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
@@ -25,7 +25,7 @@ public class ReliabilityTest extends ActivityInstrumentationTestCase2
Date: Wed, 3 Jun 2009 15:53:13 -0400
Subject: Remove handling of ENTER key from WebView.
WebView was (incorrectly) handling the ENTER key as if it could start
a long press like CENTER key does. Separate the two behaviors. Now,
the ENTER key does not do anything unless it is part of a textfield or
the DOM otherwise handles it.
As a result, I was able to remove some orphaned code. Before this
change, pressing ENTER would play a click noise, and animate the
focus ring as if its link were being followed, but do nothing. In
WebViewCore, do not pass the ENTER key back to the Activity.
---
core/java/android/webkit/WebView.java | 79 ++++++++++++-------------------
core/java/android/webkit/WebViewCore.java | 7 ++-
2 files changed, 35 insertions(+), 51 deletions(-)
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 032c0737d605..2940983dcd2c 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -451,7 +451,7 @@ public class WebView extends AbsoluteLayout
static final int MARK_NODE_INVALID_ID = 21;
static final int UPDATE_CLIPBOARD = 22;
- static final int LONG_PRESS_ENTER = 23;
+ static final int LONG_PRESS_CENTER = 23;
static final int PREVENT_TOUCH_ID = 24;
static final int WEBCORE_NEED_TOUCH_EVENTS = 25;
// obj=Rect in doc coordinates
@@ -480,7 +480,7 @@ public class WebView extends AbsoluteLayout
"20",
"MARK_NODE_INVALID_ID", // = 21;
"UPDATE_CLIPBOARD", // = 22;
- "LONG_PRESS_ENTER", // = 23;
+ "LONG_PRESS_CENTER", // = 23;
"PREVENT_TOUCH_ID", // = 24;
"WEBCORE_NEED_TOUCH_EVENTS", // = 25;
"INVAL_RECT_MSG_ID" // = 26;
@@ -2411,7 +2411,7 @@ public class WebView extends AbsoluteLayout
// need to check it again.
nativeRecordButtons(hasFocus() && hasWindowFocus(),
mTouchMode == TOUCH_SHORTPRESS_START_MODE
- || mTrackballDown || mGotEnterDown, false);
+ || mTrackballDown || mGotCenterDown, false);
drawCoreAndCursorRing(canvas, mBackgroundColor, mDrawCursorRing);
}
canvas.restoreToCount(sc);
@@ -3096,9 +3096,9 @@ public class WebView extends AbsoluteLayout
}
}
- // This is used to determine long press with the enter key, or
- // a center key. Does not affect long press with the trackball/touch.
- private boolean mGotEnterDown = false;
+ // This is used to determine long press with the center key. Does not
+ // affect long press with the trackball/touch.
+ private boolean mGotCenterDown = false;
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
@@ -3160,13 +3160,12 @@ public class WebView extends AbsoluteLayout
return false;
}
- if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER
- || keyCode == KeyEvent.KEYCODE_ENTER) {
+ if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
switchOutDrawHistory();
if (event.getRepeatCount() == 0) {
- mGotEnterDown = true;
+ mGotCenterDown = true;
mPrivateHandler.sendMessageDelayed(mPrivateHandler
- .obtainMessage(LONG_PRESS_ENTER), LONG_PRESS_TIMEOUT);
+ .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT);
// Already checked mNativeClass, so we do not need to check it
// again.
nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
@@ -3280,44 +3279,26 @@ public class WebView extends AbsoluteLayout
return false;
}
- if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER
- || keyCode == KeyEvent.KEYCODE_ENTER) {
+ if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
// remove the long press message first
- mPrivateHandler.removeMessages(LONG_PRESS_ENTER);
- mGotEnterDown = false;
+ mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
+ mGotCenterDown = false;
- if (KeyEvent.KEYCODE_DPAD_CENTER == keyCode) {
- if (mShiftIsPressed) {
- return false;
- }
- if (getSettings().supportZoom()) {
- if (mTouchMode == TOUCH_DOUBLECLICK_MODE) {
- zoomScrollOut();
- } else {
- if (DebugFlags.WEB_VIEW) {
- Log.v(LOGTAG, "TOUCH_DOUBLECLICK_MODE");
- }
- mPrivateHandler.sendMessageDelayed(mPrivateHandler
- .obtainMessage(SWITCH_TO_CLICK), TAP_TIMEOUT);
- mTouchMode = TOUCH_DOUBLECLICK_MODE;
- }
- return true;
- }
+ if (mShiftIsPressed) {
+ return false;
}
-
- Rect visibleRect = sendOurVisibleRect();
- // Note that sendOurVisibleRect calls viewToContent, so the
- // coordinates should be in content coordinates.
- if (nativeCursorIntersects(visibleRect)) {
- nativeSetFollowedLink(true);
- mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, cursorData());
- playSoundEffect(SoundEffectConstants.CLICK);
- return true;
- } else if (nativeHasCursorNode()) {
- return true;
+ if (getSettings().supportZoom()
+ && mTouchMode == TOUCH_DOUBLECLICK_MODE) {
+ zoomScrollOut();
+ } else {
+ mPrivateHandler.sendMessageDelayed(mPrivateHandler
+ .obtainMessage(SWITCH_TO_CLICK), TAP_TIMEOUT);
+ if (DebugFlags.WEB_VIEW) {
+ Log.v(LOGTAG, "TOUCH_DOUBLECLICK_MODE");
+ }
+ mTouchMode = TOUCH_DOUBLECLICK_MODE;
}
- // Bubble up the key event as WebView doesn't handle it
- return false;
+ return true;
}
// TODO: should we pass all the keys to DOM or check the meta tag
@@ -3963,8 +3944,8 @@ public class WebView extends AbsoluteLayout
return false; // let common code in onKeyDown at it
}
if (ev.getAction() == MotionEvent.ACTION_UP) {
- // LONG_PRESS_ENTER is set in common onKeyDown
- mPrivateHandler.removeMessages(LONG_PRESS_ENTER);
+ // LONG_PRESS_CENTER is set in common onKeyDown
+ mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
mTrackballDown = false;
mTrackballUpTime = time;
if (mShiftIsPressed) {
@@ -4839,12 +4820,12 @@ public class WebView extends AbsoluteLayout
WebViewCore.resumeUpdate(mWebViewCore);
break;
- case LONG_PRESS_ENTER:
+ case LONG_PRESS_CENTER:
// as this is shared by keydown and trackballdown, reset all
// the states
- mGotEnterDown = false;
+ mGotCenterDown = false;
mTrackballDown = false;
- // LONG_PRESS_ENTER is sent as a delayed message. If we
+ // LONG_PRESS_CENTER is sent as a delayed message. If we
// switch to windows overview, the WebView will be
// temporarily removed from the view system. In that case,
// do nothing.
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 78d0b4de2996..de142ae5565a 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1230,10 +1230,13 @@ final class WebViewCore {
Log.v(LOGTAG, "CORE key at " + System.currentTimeMillis() + ", "
+ evt);
}
- if (!nativeKey(evt.getKeyCode(), evt.getUnicodeChar(),
+ int keyCode = evt.getKeyCode();
+ if (!nativeKey(keyCode, evt.getUnicodeChar(),
evt.getRepeatCount(), evt.isShiftPressed(), evt.isAltPressed(),
- isDown)) {
+ isDown) && keyCode != KeyEvent.KEYCODE_ENTER) {
// bubble up the event handling
+ // but do not bubble up the ENTER key, which would open the search
+ // bar without any text.
mCallbackProxy.onUnhandledKeyEvent(evt);
}
}
--
cgit v1.2.3-59-g8ed1b
From b9a39cd300998a1a4577ac7eb87f9b505b8621dc Mon Sep 17 00:00:00 2001
From: Guang Zhu
Date: Wed, 3 Jun 2009 14:14:27 -0700
Subject: Adding missing callback onJsConfirm to dismiss any confirmation
dialogs
---
.../src/com/android/dumprendertree/ReliabilityTestActivity.java | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTestActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTestActivity.java
index a374a414c85d..cbec104d1d61 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTestActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTestActivity.java
@@ -214,6 +214,12 @@ public class ReliabilityTestActivity extends Activity {
return true;
}
+ @Override
+ public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
+ result.confirm();
+ return true;
+ }
+
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue,
JsPromptResult result) {
--
cgit v1.2.3-59-g8ed1b
From 2a2c5cd74128a7750f05683614c9824c9262addc Mon Sep 17 00:00:00 2001
From: Wei-Ta Chen
Date: Wed, 3 Jun 2009 14:08:04 -0700
Subject: Modify the decoding logic in the FD case when a purgeable flag is
set, and lower the threshold of bitmap size for using ashmem().
For the decoding logic, we now go through the "non-purgeable" path if isShareable is false,
irrespective of the value of the purgeable flag.
---
core/jni/android/graphics/BitmapFactory.cpp | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 1fd15d687baf..137707fa93bf 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -311,7 +311,7 @@ static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
int sampleSize) {
SkPixelRef* pr;
// only use ashmem for large images, since mmaps come at a price
- if (bitmap->getSize() >= 32 * 65536) {
+ if (bitmap->getSize() >= 32 * 1024) {
pr = new SkImageRef_ashmem(stream, bitmap->config(), sampleSize);
} else {
pr = new SkImageRef_GlobalPool(stream, bitmap->config(), sampleSize);
@@ -520,7 +520,10 @@ static jobject nativeDecodeFileDescriptor(JNIEnv* env, jobject clazz,
*/
AutoFDSeek as(descriptor);
- return doDecode(env, stream, padding, bitmapFactoryOptions, true);
+ /* Allow purgeable iff we own the FD, i.e., in the puregeable and
+ shareable case.
+ */
+ return doDecode(env, stream, padding, bitmapFactoryOptions, weOwnTheFD);
}
/* make a deep copy of the asset, and return it as a stream, or NULL if there
--
cgit v1.2.3-59-g8ed1b
From cede1ed3e1721dc4a697a540388ef0f4b51c60eb Mon Sep 17 00:00:00 2001
From: Mathias Agopian
Date: Tue, 2 Jun 2009 22:05:04 -0700
Subject: fix [1610840] Positional light doesn't work correctly on emulator
This bug was introduced when lighting computations was changed from eye-space to object-space.
The light position need to be transformed back to object-space each time the modelview matrix changes which requires us to compute the inverse of the modelview matrix. This computation was done with the assumption that normals where transformed (which was the case when the computation was made in eye-space), however, normals only require the inverse of the upper 3x3 matrix while transforming positions requires the inverse of the whole matrix.
This caused the interesting behavior that lights were more-or-less transformed properly, but not translated at all, which caused improper lighting with directional lights in particular.
There was also another smaller bug affecting directional lights: when vertices are read, only the active component are read, the other ones are ignored, later, the transformation operations are set up to ignore the unset values, howver, in the case of lighting, we use the vertex in object space (that is, before it is transformed), and therefore were using uninitalized values; in particular w.
---
opengl/libagl/array.cpp | 4 +++
opengl/libagl/light.cpp | 30 ++++++++---------
opengl/libagl/matrix.cpp | 83 ++++++++++++------------------------------------
3 files changed, 39 insertions(+), 78 deletions(-)
diff --git a/opengl/libagl/array.cpp b/opengl/libagl/array.cpp
index 8fa7566aacc0..3e9c6a542709 100644
--- a/opengl/libagl/array.cpp
+++ b/opengl/libagl/array.cpp
@@ -951,6 +951,8 @@ void compileElement__generic(ogles_context_t* c,
v->index = first;
first &= vertex_cache_t::INDEX_MASK;
const GLubyte* vp = c->arrays.vertex.element(first);
+ v->obj.z = 0;
+ v->obj.w = 0x10000;
c->arrays.vertex.fetch(c, v->obj.v, vp);
c->arrays.mvp_transform(&c->transforms.mvp, &v->clip, &v->obj);
c->arrays.perspective(c, v);
@@ -966,6 +968,8 @@ void compileElements__generic(ogles_context_t* c,
do {
v->flags = 0;
v->index = first++;
+ v->obj.z = 0;
+ v->obj.w = 0x10000;
c->arrays.vertex.fetch(c, v->obj.v, vp);
c->arrays.mvp_transform(mvp, &v->clip, &v->obj);
c->arrays.perspective(c, v);
diff --git a/opengl/libagl/light.cpp b/opengl/libagl/light.cpp
index bc9449c0bdef..8ae32cc0f0e1 100644
--- a/opengl/libagl/light.cpp
+++ b/opengl/libagl/light.cpp
@@ -38,13 +38,14 @@ static void lightVertex(ogles_context_t* c, vertex_t* v);
static void lightVertexMaterial(ogles_context_t* c, vertex_t* v);
static inline void vscale3(GLfixed* d, const GLfixed* m, GLfixed s);
-static inline void vsub3w(GLfixed* d, const GLfixed* a, const GLfixed* b);
static __attribute__((noinline))
void vnorm3(GLfixed* d, const GLfixed* a);
static inline void vsa3(GLfixed* d,
const GLfixed* m, GLfixed s, const GLfixed* a);
+static inline void vss3(GLfixed* d,
+ const GLfixed* m, GLfixed s, const GLfixed* a);
static inline void vmla3(GLfixed* d,
const GLfixed* m0, const GLfixed* m1, const GLfixed* a);
static inline void vmul3(GLfixed* d,
@@ -151,18 +152,10 @@ void vsa3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a) {
}
static inline
-void vsub3w(GLfixed* d, const GLfixed* a, const GLfixed* b) {
- const GLfixed wa = a[3];
- const GLfixed wb = b[3];
- if (ggl_likely(wa == wb)) {
- d[0] = a[0] - b[0];
- d[1] = a[1] - b[1];
- d[2] = a[2] - b[2];
- } else {
- d[0] = gglMulSubx(a[0], wb, gglMulx(b[0], wa));
- d[1] = gglMulSubx(a[1], wb, gglMulx(b[1], wa));
- d[2] = gglMulSubx(a[2], wb, gglMulx(b[2], wa));
- }
+void vss3(GLfixed* d, const GLfixed* m, GLfixed s, const GLfixed* a) {
+ d[0] = gglMulSubx(m[0], s, a[0]);
+ d[1] = gglMulSubx(m[1], s, a[1]);
+ d[2] = gglMulSubx(m[2], s, a[2]);
}
static inline
@@ -227,7 +220,7 @@ static inline void validate_light_mvi(ogles_context_t* c)
const int i = 31 - gglClz(en);
en &= ~(1<lighting.lights[i];
- c->transforms.mvui.point3(&c->transforms.mvui,
+ c->transforms.mvui.point4(&c->transforms.mvui,
&l.objPosition, &l.position);
vnorm3(l.normalizedObjPosition.v, l.objPosition.v);
}
@@ -348,7 +341,11 @@ void lightVertex(ogles_context_t* c, vertex_t* v)
vec4_t n;
c->arrays.normal.fetch(c, n.v,
c->arrays.normal.element(v->index & vertex_cache_t::INDEX_MASK));
- if (c->transforms.rescaleNormals == GL_NORMALIZE)
+
+ // TODO: right now we handle GL_RESCALE_NORMALS as if ti were
+ // GL_NORMALIZE. We could optimize this by scaling mvui
+ // appropriately instead.
+ if (c->transforms.rescaleNormals)
vnorm3(n.v, n.v);
const material_t& material = c->lighting.front;
@@ -365,7 +362,8 @@ void lightVertex(ogles_context_t* c, vertex_t* v)
// compute vertex-to-light vector
if (ggl_unlikely(l.position.w)) {
- vsub3w(d.v, l.objPosition.v, v->obj.v);
+ // lightPos/1.0 - vertex/vertex.w == lightPos*vertex.w - vertex
+ vss3(d.v, l.objPosition.v, v->obj.w, v->obj.v);
sqDist = dot3(d.v, d.v);
vscale3(d.v, d.v, gglSqrtRecipx(sqDist));
} else {
diff --git a/opengl/libagl/matrix.cpp b/opengl/libagl/matrix.cpp
index f175cdad6e6a..0b68dc06f5e3 100644
--- a/opengl/libagl/matrix.cpp
+++ b/opengl/libagl/matrix.cpp
@@ -55,7 +55,7 @@ static void normal__nop(transform_t const*, vec4_t* c, vec4_t const* o);
static void point2__generic(transform_t const*, vec4_t* c, vec4_t const* o);
static void point3__generic(transform_t const*, vec4_t* c, vec4_t const* o);
static void point4__generic(transform_t const*, vec4_t* c, vec4_t const* o);
-static void normal__generic(transform_t const*, vec4_t* c, vec4_t const* o);
+static void point4__mvui(transform_t const*, vec4_t* c, vec4_t const* o);
// ----------------------------------------------------------------------------
#if 0
@@ -209,7 +209,8 @@ void mvui_transform_t::picker()
{
flags = 0;
ops = OP_ALL;
- point3 = normal__generic;
+ point3 = point4__mvui;
+ point4 = point4__mvui;
}
void transform_t::dump(const char* what)
@@ -596,66 +597,19 @@ void transform_state_t::update_mvit()
void transform_state_t::update_mvui()
{
+ GLfloat r[16];
const GLfloat* const mv = modelview.top().elements();
-
- /*
- When transforming normals, we can use the upper 3x3 matrix, see:
- http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node26.html
- */
- // Also note that:
- // l(obj) = tr(M).l(eye) for infinite light
- // l(obj) = inv(M).l(eye) for local light
-
- const uint32_t ops = modelview.top_ops() & ~OP_TRANSLATE;
- if (ggl_likely((!(ops & ~OP_ROTATE)) ||
- (rescaleNormals && modelview.isRigidBody()))) {
- // if the modelview matrix is a rigid body transformation
- // (translation, rotation, uniform scaling), then we can bypass
- // the inverse by transposing the matrix.
- GLfloat rescale = 1.0f;
- if (rescaleNormals == GL_RESCALE_NORMAL) {
- if (!(ops & ~OP_UNIFORM_SCALE)) {
- rescale = reciprocalf(mv[I(0,0)]);
- } else {
- rescale = rsqrtf(
- sqrf(mv[I(2,0)]) + sqrf(mv[I(2,1)]) + sqrf(mv[I(2,2)]));
- }
- }
- GLfixed* const x = mvui.matrix.m;
- for (int i=0 ; i<3 ; i++) {
- x[I(i,0)] = gglFloatToFixed(mv[I(0,i)] * rescale);
- x[I(i,1)] = gglFloatToFixed(mv[I(1,i)] * rescale);
- x[I(i,2)] = gglFloatToFixed(mv[I(2,i)] * rescale);
- }
- mvui.picker();
- return;
- }
-
- GLfloat r[3][3];
- r[0][0] = det22(mv[I(1,1)], mv[I(2,1)], mv[I(1,2)], mv[I(2,2)]);
- r[0][1] =ndet22(mv[I(0,1)], mv[I(2,1)], mv[I(0,2)], mv[I(2,2)]);
- r[0][2] = det22(mv[I(0,1)], mv[I(1,1)], mv[I(0,2)], mv[I(1,2)]);
- r[1][0] =ndet22(mv[I(1,0)], mv[I(2,0)], mv[I(1,2)], mv[I(2,2)]);
- r[1][1] = det22(mv[I(0,0)], mv[I(2,0)], mv[I(0,2)], mv[I(2,2)]);
- r[1][2] =ndet22(mv[I(0,0)], mv[I(1,0)], mv[I(0,2)], mv[I(1,2)]);
- r[2][0] = det22(mv[I(1,0)], mv[I(2,0)], mv[I(1,1)], mv[I(2,1)]);
- r[2][1] =ndet22(mv[I(0,0)], mv[I(2,0)], mv[I(0,1)], mv[I(2,1)]);
- r[2][2] = det22(mv[I(0,0)], mv[I(1,0)], mv[I(0,1)], mv[I(1,1)]);
-
- GLfloat rdet;
- if (rescaleNormals == GL_RESCALE_NORMAL) {
- rdet = rsqrtf(sqrf(r[0][2]) + sqrf(r[1][2]) + sqrf(r[2][2]));
- } else {
- rdet = reciprocalf(
- r[0][0]*mv[I(0,0)] + r[0][1]*mv[I(1,0)] + r[0][2]*mv[I(2,0)]);
- }
+ // TODO: we need a faster invert, especially for when the modelview
+ // is a rigid-body matrix
+ invert(r, mv);
GLfixed* const x = mvui.matrix.m;
- for (int i=0 ; i<3 ; i++) {
- x[I(i,0)] = gglFloatToFixed(r[i][0] * rdet);
- x[I(i,1)] = gglFloatToFixed(r[i][1] * rdet);
- x[I(i,2)] = gglFloatToFixed(r[i][2] * rdet);
+ for (int i=0 ; i<4 ; i++) {
+ x[I(i,0)] = gglFloatToFixed(r[I(i,0)]);
+ x[I(i,1)] = gglFloatToFixed(r[I(i,1)]);
+ x[I(i,2)] = gglFloatToFixed(r[I(i,2)]);
+ x[I(i,4)] = gglFloatToFixed(r[I(i,3)]);
}
mvui.picker();
}
@@ -783,14 +737,19 @@ void point4__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
lhs->w = mla4(rx, m[ 3], ry, m[ 7], rz, m[11], rw, m[15]);
}
-void normal__generic(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
+void point4__mvui(transform_t const* mx, vec4_t* lhs, vec4_t const* rhs) {
+ // this used for transforming light positions back to object space.
+ // Lights have 3 components positions, so w is always 1.
+ // however, it is used as a switch for directional lights, so we need
+ // to preserve it.
const GLfixed* const m = mx->matrix.m;
const GLfixed rx = rhs->x;
const GLfixed ry = rhs->y;
const GLfixed rz = rhs->z;
- lhs->x = mla3(rx, m[ 0], ry, m[ 4], rz, m[ 8]);
- lhs->y = mla3(rx, m[ 1], ry, m[ 5], rz, m[ 9]);
- lhs->z = mla3(rx, m[ 2], ry, m[ 6], rz, m[10]);
+ lhs->x = mla3a(rx, m[ 0], ry, m[ 4], rz, m[ 8], m[12]);
+ lhs->y = mla3a(rx, m[ 1], ry, m[ 5], rz, m[ 9], m[13]);
+ lhs->z = mla3a(rx, m[ 2], ry, m[ 6], rz, m[10], m[14]);
+ lhs->w = rhs->w;
}
--
cgit v1.2.3-59-g8ed1b
From ab5742dd63f4e62ee0f55f786854c024ef8c5bb4 Mon Sep 17 00:00:00 2001
From: Evan Millar
Date: Tue, 2 Jun 2009 16:21:45 -0700
Subject: Adds "is_primary" and "is_super_primary" columns to DataColumns.
Replaces the "primary" values stored in generic data fields with
standard "is_primary" and "is_super_primary" fields in the DataColumns
class. This makes it much easier to watch for changes to these fields
and enforce the uniqueness of primary fields.
---
core/java/android/provider/ContactsContract.java | 25 ++++++++++++------------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 835a5f646e51..62e95e8ab749 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -290,6 +290,19 @@ public final class ContactsContract {
*/
public static final String CONTACT_ID = "contact_id";
+ /**
+ * Whether this is the primary entry of its kind for the contact it belongs to
+ * Type: INTEGER (if set, non-0 means true)
+ */
+ public static final String IS_PRIMARY = "is_primary";
+
+ /**
+ * Whether this is the primary entry of its kind for the aggregate it belongs to. Any data
+ * record that is "super primary" must also be "primary".
+ * Type: INTEGER (if set, non-0 means true)
+ */
+ public static final String IS_SUPER_PRIMARY = "is_super_primary";
+
/** Generic data column, the meaning is {@link #MIMETYPE} specific */
public static final String DATA1 = "data1";
/** Generic data column, the meaning is {@link #MIMETYPE} specific */
@@ -409,12 +422,6 @@ public final class ContactsContract {
* Type: TEXT
*/
public static final String LABEL = "data3";
-
- /**
- * Whether this is the primary entry of its kind for the contact it belongs to
- * Type: INTEGER (if set, non-0 means true)
- */
- public static final String ISPRIMARY = "data4";
}
/**
@@ -642,12 +649,6 @@ public final class ContactsContract {
* Type: TEXT
*/
public static final String TITLE = "data4";
-
- /**
- * Whether this is the primary organization
- * Type: INTEGER (if set, non-0 means true)
- */
- public static final String ISPRIMARY = "data5";
}
/**
--
cgit v1.2.3-59-g8ed1b
From eaeb663bcd7a82b654954b42663232cbd7bef7e7 Mon Sep 17 00:00:00 2001
From: Amith Yamasani
Date: Wed, 3 Jun 2009 15:16:10 -0700
Subject: Track activity foreground CPU usage for battery stats.
Track the foreground CPU time of an activity so that we can tell if apps are
spending more time in the background compared to foreground.
Update power profile values for screen backlight and GPS.
Fix some javadoc bugs (milliseconds vs. microseconds).
---
core/java/android/os/BatteryStats.java | 23 +++++---
.../com/android/internal/os/BatteryStatsImpl.java | 63 ++++++++++++++++++++--
core/res/res/xml/power_profile_default.xml | 3 +-
services/java/com/android/server/ProcessStats.java | 16 +++++-
.../android/server/am/ActivityManagerService.java | 33 +++++++++++-
.../java/com/android/server/am/HistoryRecord.java | 1 +
6 files changed, 123 insertions(+), 16 deletions(-)
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 358a5463b04b..528def5c4011 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -307,6 +307,13 @@ public abstract class BatteryStats implements Parcelable {
* @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT.
*/
public abstract int getStarts(int which);
+
+ /**
+ * Returns the cpu time spent in microseconds while the process was in the foreground.
+ * @param which one of STATS_TOTAL, STATS_LAST, STATS_CURRENT or STATS_UNPLUGGED
+ * @return foreground cpu time in microseconds
+ */
+ public abstract long getForegroundTime(int which);
}
/**
@@ -364,7 +371,7 @@ public abstract class BatteryStats implements Parcelable {
public abstract int getStartCount();
/**
- * Returns the time in milliseconds that the screen has been on while the device was
+ * Returns the time in microseconds that the screen has been on while the device was
* running on battery.
*
* {@hide}
@@ -384,7 +391,7 @@ public abstract class BatteryStats implements Parcelable {
public static final int NUM_SCREEN_BRIGHTNESS_BINS = 5;
/**
- * Returns the time in milliseconds that the screen has been on with
+ * Returns the time in microseconds that the screen has been on with
* the given brightness
*
* {@hide}
@@ -395,7 +402,7 @@ public abstract class BatteryStats implements Parcelable {
public abstract int getInputEventCount(int which);
/**
- * Returns the time in milliseconds that the phone has been on while the device was
+ * Returns the time in microseconds that the phone has been on while the device was
* running on battery.
*
* {@hide}
@@ -415,7 +422,7 @@ public abstract class BatteryStats implements Parcelable {
public static final int NUM_SIGNAL_STRENGTH_BINS = 5;
/**
- * Returns the time in milliseconds that the phone has been running with
+ * Returns the time in microseconds that the phone has been running with
* the given signal strength.
*
* {@hide}
@@ -443,7 +450,7 @@ public abstract class BatteryStats implements Parcelable {
public static final int NUM_DATA_CONNECTION_TYPES = 5;
/**
- * Returns the time in milliseconds that the phone has been running with
+ * Returns the time in microseconds that the phone has been running with
* the given data connection.
*
* {@hide}
@@ -460,7 +467,7 @@ public abstract class BatteryStats implements Parcelable {
public abstract int getPhoneDataConnectionCount(int dataType, int which);
/**
- * Returns the time in milliseconds that wifi has been on while the device was
+ * Returns the time in microseconds that wifi has been on while the device was
* running on battery.
*
* {@hide}
@@ -468,7 +475,7 @@ public abstract class BatteryStats implements Parcelable {
public abstract long getWifiOnTime(long batteryRealtime, int which);
/**
- * Returns the time in milliseconds that wifi has been on and the driver has
+ * Returns the time in microseconds that wifi has been on and the driver has
* been in the running state while the device was running on battery.
*
* {@hide}
@@ -476,7 +483,7 @@ public abstract class BatteryStats implements Parcelable {
public abstract long getWifiRunningTime(long batteryRealtime, int which);
/**
- * Returns the time in milliseconds that bluetooth has been on while the device was
+ * Returns the time in microseconds that bluetooth has been on while the device was
* running on battery.
*
* {@hide}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 51f3b025f84d..99a381c5ae13 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -53,7 +53,7 @@ public final class BatteryStatsImpl extends BatteryStats {
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 37;
+ private static final int VERSION = 38;
private final File mFile;
private final File mBackupFile;
@@ -1342,11 +1342,13 @@ public final class BatteryStatsImpl extends BatteryStats {
public Map getPackageStats() {
return mPackageStats;
}
-
+
+ @Override
public int getUid() {
return mUid;
}
-
+
+ @Override
public long getTcpBytesReceived(int which) {
if (which == STATS_LAST) {
return mLoadedTcpBytesReceived;
@@ -1365,7 +1367,8 @@ public final class BatteryStatsImpl extends BatteryStats {
return mCurrentTcpBytesReceived + (mStartedTcpBytesReceived >= 0
? (NetStat.getUidRxBytes(mUid) - mStartedTcpBytesReceived) : 0);
}
-
+
+ @Override
public long getTcpBytesSent(int which) {
if (which == STATS_LAST) {
return mLoadedTcpBytesSent;
@@ -1754,7 +1757,8 @@ public final class BatteryStatsImpl extends BatteryStats {
public Timer getSensorTime() {
return mTimer;
}
-
+
+ @Override
public int getHandle() {
return mHandle;
}
@@ -1779,6 +1783,11 @@ public final class BatteryStatsImpl extends BatteryStats {
*/
int mStarts;
+ /**
+ * Amount of time the process was running in the foreground.
+ */
+ long mForegroundTime;
+
/**
* The amount of user time loaded from a previous save.
*/
@@ -1794,6 +1803,11 @@ public final class BatteryStatsImpl extends BatteryStats {
*/
int mLoadedStarts;
+ /**
+ * The amount of foreground time loaded from a previous save.
+ */
+ long mLoadedForegroundTime;
+
/**
* The amount of user time loaded from the previous run.
*/
@@ -1809,6 +1823,11 @@ public final class BatteryStatsImpl extends BatteryStats {
*/
int mLastStarts;
+ /**
+ * The amount of foreground time loaded from the previous run
+ */
+ long mLastForegroundTime;
+
/**
* The amount of user time when last unplugged.
*/
@@ -1824,6 +1843,11 @@ public final class BatteryStatsImpl extends BatteryStats {
*/
int mUnpluggedStarts;
+ /**
+ * The amount of foreground time since unplugged.
+ */
+ long mUnpluggedForegroundTime;
+
Proc() {
mUnpluggables.add(this);
}
@@ -1832,6 +1856,7 @@ public final class BatteryStatsImpl extends BatteryStats {
mUnpluggedUserTime = mUserTime;
mUnpluggedSystemTime = mSystemTime;
mUnpluggedStarts = mStarts;
+ mUnpluggedForegroundTime = mForegroundTime;
}
public void plug(long batteryUptime, long batteryRealtime) {
@@ -1843,30 +1868,38 @@ public final class BatteryStatsImpl extends BatteryStats {
out.writeLong(mUserTime);
out.writeLong(mSystemTime);
+ out.writeLong(mForegroundTime);
out.writeInt(mStarts);
out.writeLong(mLoadedUserTime);
out.writeLong(mLoadedSystemTime);
+ out.writeLong(mLoadedForegroundTime);
out.writeInt(mLoadedStarts);
out.writeLong(mLastUserTime);
out.writeLong(mLastSystemTime);
+ out.writeLong(mLastForegroundTime);
out.writeInt(mLastStarts);
out.writeLong(mUnpluggedUserTime);
out.writeLong(mUnpluggedSystemTime);
+ out.writeLong(mUnpluggedForegroundTime);
out.writeInt(mUnpluggedStarts);
}
void readFromParcelLocked(Parcel in) {
mUserTime = in.readLong();
mSystemTime = in.readLong();
+ mForegroundTime = in.readLong();
mStarts = in.readInt();
mLoadedUserTime = in.readLong();
mLoadedSystemTime = in.readLong();
+ mLoadedForegroundTime = in.readLong();
mLoadedStarts = in.readInt();
mLastUserTime = in.readLong();
mLastSystemTime = in.readLong();
+ mLastForegroundTime = in.readLong();
mLastStarts = in.readInt();
mUnpluggedUserTime = in.readLong();
mUnpluggedSystemTime = in.readLong();
+ mUnpluggedForegroundTime = in.readLong();
mUnpluggedStarts = in.readInt();
}
@@ -1879,6 +1912,10 @@ public final class BatteryStatsImpl extends BatteryStats {
mSystemTime += stime;
}
+ public void addForegroundTimeLocked(long ttime) {
+ mForegroundTime += ttime;
+ }
+
public void incStartsLocked() {
mStarts++;
}
@@ -1915,6 +1952,22 @@ public final class BatteryStatsImpl extends BatteryStats {
return val;
}
+ @Override
+ public long getForegroundTime(int which) {
+ long val;
+ if (which == STATS_LAST) {
+ val = mLastForegroundTime;
+ } else {
+ val = mForegroundTime;
+ if (which == STATS_CURRENT) {
+ val -= mLoadedForegroundTime;
+ } else if (which == STATS_UNPLUGGED) {
+ val -= mUnpluggedForegroundTime;
+ }
+ }
+ return val;
+ }
+
@Override
public int getStarts(int which) {
int val;
diff --git a/core/res/res/xml/power_profile_default.xml b/core/res/res/xml/power_profile_default.xml
index d265b46ef62c..ceecb1a7e790 100644
--- a/core/res/res/xml/power_profile_default.xml
+++ b/core/res/res/xml/power_profile_default.xml
@@ -22,7 +22,7 @@
- 30
- 103
- 5
- - 144
+ - 114
- 23
- 200
- 200
@@ -33,4 +33,5 @@
- 100
- 3
- 175
+ - 120
diff --git a/services/java/com/android/server/ProcessStats.java b/services/java/com/android/server/ProcessStats.java
index 55adabbfa7a0..58f8980c004d 100644
--- a/services/java/com/android/server/ProcessStats.java
+++ b/services/java/com/android/server/ProcessStats.java
@@ -54,7 +54,10 @@ public class ProcessStats {
PROC_SPACE_TERM|PROC_OUT_LONG // 14: stime
};
+ /** Stores user time and system time in 100ths of a second. */
private final long[] mProcessStatsData = new long[2];
+ /** Stores user time and system time in 100ths of a second. */
+ private final long[] mSinglePidStatsData = new long[2];
private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] {
PROC_SPACE_TERM,
@@ -418,7 +421,18 @@ public class ProcessStats {
return pids;
}
-
+
+ public long getCpuTimeForPid(int pid) {
+ final String statFile = "/proc/" + pid + "/stat";
+ final long[] statsData = mSinglePidStatsData;
+ if (Process.readProcFile(statFile, PROCESS_STATS_FORMAT,
+ null, statsData, null)) {
+ long time = statsData[0] + statsData[1];
+ return time;
+ }
+ return 0;
+ }
+
final public int getLastUserTime() {
return mRelUserTime;
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 3b26cb78d721..965079067c32 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -62,6 +62,7 @@ import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.net.Uri;
+import android.os.BatteryStats;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
@@ -1438,7 +1439,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
synchronized (mProcessStatsThread) {
final long now = SystemClock.uptimeMillis();
boolean haveNewCpuStats = false;
-
+
if (MONITOR_CPU_USAGE &&
mLastCpuTime < (now-MONITOR_CPU_MIN_TIME)) {
mLastCpuTime = now;
@@ -2063,6 +2064,25 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
if (prev != null) {
prev.resumeKeyDispatchingLocked();
}
+
+ if (prev.app != null && prev.cpuTimeAtResume > 0 && mBatteryStatsService.isOnBattery()) {
+ long diff = 0;
+ synchronized (mProcessStatsThread) {
+ diff = mProcessStats.getCpuTimeForPid(prev.app.pid) - prev.cpuTimeAtResume;
+ }
+ if (diff > 0) {
+ BatteryStatsImpl bsi = mBatteryStatsService.getActiveStatistics();
+ synchronized (bsi) {
+ BatteryStatsImpl.Uid.Proc ps =
+ bsi.getProcessStatsLocked(prev.info.applicationInfo.uid,
+ prev.info.packageName);
+ if (ps != null) {
+ ps.addForegroundTimeLocked(diff);
+ }
+ }
+ }
+ }
+ prev.cpuTimeAtResume = 0; // reset it
}
/**
@@ -2095,6 +2115,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
next.resumeKeyDispatchingLocked();
ensureActivitiesVisibleLocked(null, 0);
mWindowManager.executeAppTransition();
+
+ // Mark the point when the activity is resuming
+ // TODO: To be more accurate, the mark should be before the onCreate,
+ // not after the onResume. But for subsequent starts, onResume is fine.
+ if (next.app != null) {
+ synchronized (mProcessStatsThread) {
+ next.cpuTimeAtResume = mProcessStats.getCpuTimeForPid(next.app.pid);
+ }
+ } else {
+ next.cpuTimeAtResume = 0; // Couldn't get the cpu time of process
+ }
}
/**
diff --git a/services/java/com/android/server/am/HistoryRecord.java b/services/java/com/android/server/am/HistoryRecord.java
index 1789687973fa..944ea02dbf79 100644
--- a/services/java/com/android/server/am/HistoryRecord.java
+++ b/services/java/com/android/server/am/HistoryRecord.java
@@ -66,6 +66,7 @@ class HistoryRecord extends IApplicationToken.Stub {
int theme; // resource identifier of activity's theme.
TaskRecord task; // the task this is in.
long startTime; // when we starting launching this activity
+ long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity
Configuration configuration; // configuration activity was last running in
HistoryRecord resultTo; // who started this entry, so will get our reply
final String resultWho; // additional identifier for use by resultTo.
--
cgit v1.2.3-59-g8ed1b
From 701f5164c1230cc1416b1a1f3b0091ca68f6caec Mon Sep 17 00:00:00 2001
From: Suchi Amalapurapu
Date: Wed, 3 Jun 2009 15:47:55 -0700
Subject: Grant permissions to older package when deleting an updated system
application. When a system app gets updated, the permissions are granted to
the new pkg. Similary when this updated pkg(from data partition) gets
removed, the older pkg from system partition is restored. but the permissions
are'nt being granted explicitly and so the restore fails. This fix addresses
specific bugs related to uninstall of updated system apps. These code paths
will be revisited later but this fix is needed for OTA's that might fall back
to older versions of system apps.
---
services/java/com/android/server/PackageManagerService.java | 1 +
1 file changed, 1 insertion(+)
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 8da40acf9372..6a2e62fd1259 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -4147,6 +4147,7 @@ class PackageManagerService extends IPackageManager.Stub {
return false;
}
synchronized (mPackages) {
+ grantPermissionsLP(newPkg, true);
mSettings.writeLP();
}
return true;
--
cgit v1.2.3-59-g8ed1b
From 53003de64e1d2b8a4ed4433d5192b540653d79ec Mon Sep 17 00:00:00 2001
From: Guang Zhu
Date: Wed, 3 Jun 2009 16:01:58 -0700
Subject: Skip empty lines in test url list.
---
.../DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java | 3 +++
1 file changed, 3 insertions(+)
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
index e63aa95b558f..22c458cf926a 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/ReliabilityTest.java
@@ -55,6 +55,9 @@ public class ReliabilityTest extends ActivityInstrumentationTestCase2
Date: Wed, 3 Jun 2009 16:04:54 -0700
Subject: Generalize bitmap support and add remaining GL formats. Fix bug in
command fifo looping case.
---
libs/rs/rsAllocation.cpp | 98 +++++++++++++++++++++++++++++++++---------
libs/rs/rsElement.cpp | 104 +++++++++++++++++++++++++++++++++++++++++++++
libs/rs/rsElement.h | 2 +
libs/rs/rsLocklessFifo.cpp | 10 +++--
libs/rs/rsUtils.h | 12 ++++--
5 files changed, 198 insertions(+), 28 deletions(-)
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index 7b8bc8034ff2..3a01a7531782 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -47,6 +47,7 @@ Allocation::Allocation(const Type *type)
Allocation::~Allocation()
{
+ LOGE("Allocation %p destryed", this);
}
void Allocation::setCpuWritable(bool)
@@ -77,6 +78,13 @@ void Allocation::uploadToTexture(uint32_t lodOffset)
//LOGE("uploadToTexture %i, lod %i", mTextureID, lodOffset);
+ GLenum type = mType->getElement()->getGLType();
+ GLenum format = mType->getElement()->getGLFormat();
+
+ if (!type || !format) {
+ return;
+ }
+
if (!mTextureID) {
glGenTextures(1, &mTextureID);
}
@@ -87,9 +95,9 @@ void Allocation::uploadToTexture(uint32_t lodOffset)
adapt.setLOD(lod+lodOffset);
uint16_t * ptr = static_cast(adapt.getElement(0,0));
- glTexImage2D(GL_TEXTURE_2D, lod, GL_RGB,
- adapt.getDimX(), adapt.getDimY(),
- 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, ptr);
+ glTexImage2D(GL_TEXTURE_2D, lod, format,
+ adapt.getDimX(), adapt.getDimY(),
+ 0, format, type, ptr);
}
}
@@ -121,7 +129,7 @@ void Allocation::subData(uint32_t xoff, uint32_t count, const void *data)
memcpy(ptr, data, count * eSize);
}
-void Allocation::subData(uint32_t xoff, uint32_t yoff,
+void Allocation::subData(uint32_t xoff, uint32_t yoff,
uint32_t w, uint32_t h, const void *data)
{
uint32_t eSize = mType->getElementSizeBytes();
@@ -147,7 +155,7 @@ void Allocation::subData(uint32_t xoff, uint32_t yoff, uint32_t zoff,
/////////////////
-//
+//
namespace android {
@@ -192,7 +200,7 @@ void rsi_AllocationDestroy(Context *rsc, RsAllocation)
{
}
-static void mip(const Adapter2D &out, const Adapter2D &in)
+static void mip565(const Adapter2D &out, const Adapter2D &in)
{
uint32_t w = out.getDimX();
uint32_t h = out.getDimY();
@@ -203,7 +211,26 @@ static void mip(const Adapter2D &out, const Adapter2D &in)
const uint16_t *i2 = static_cast(in.getElement(0, y*2+1));
for (uint32_t x=0; x < h; x++) {
- *oPtr = rsBoxFilter565(i1[0], i1[2], i2[0], i2[1]);
+ *oPtr = rsBoxFilter565(i1[0], i1[1], i2[0], i2[1]);
+ oPtr ++;
+ i1 += 2;
+ i2 += 2;
+ }
+ }
+}
+
+static void mip8888(const Adapter2D &out, const Adapter2D &in)
+{
+ uint32_t w = out.getDimX();
+ uint32_t h = out.getDimY();
+
+ for (uint32_t y=0; y < w; y++) {
+ uint32_t *oPtr = static_cast(out.getElement(0, y));
+ const uint32_t *i1 = static_cast(in.getElement(0, y*2));
+ const uint32_t *i2 = static_cast(in.getElement(0, y*2+1));
+
+ for (uint32_t x=0; x < h; x++) {
+ *oPtr = rsBoxFilter8888(i1[0], i1[1], i2[0], i2[1]);
oPtr ++;
i1 += 2;
i2 += 2;
@@ -255,21 +282,25 @@ static void elementConverter_8888_to_565(void *dst, const void *src, uint32_t co
static ElementConverter_t pickConverter(RsElementPredefined dstFmt, RsElementPredefined srcFmt)
{
- if ((dstFmt == RS_ELEMENT_RGB_565) &&
+ if ((dstFmt == RS_ELEMENT_RGB_565) &&
(srcFmt == RS_ELEMENT_RGB_565)) {
return elementConverter_cpy_16;
}
- if ((dstFmt == RS_ELEMENT_RGB_565) &&
+ if ((dstFmt == RS_ELEMENT_RGB_565) &&
(srcFmt == RS_ELEMENT_RGB_888)) {
return elementConverter_888_to_565;
}
- if ((dstFmt == RS_ELEMENT_RGB_565) &&
+ if ((dstFmt == RS_ELEMENT_RGB_565) &&
(srcFmt == RS_ELEMENT_RGBA_8888)) {
return elementConverter_8888_to_565;
}
+ if ((dstFmt == RS_ELEMENT_RGBA_8888) &&
+ (srcFmt == RS_ELEMENT_RGBA_8888)) {
+ return elementConverter_cpy_32;
+ }
LOGE("pickConverter, unsuported combo");
return 0;
@@ -303,7 +334,7 @@ RsAllocation rsi_AllocationCreateFromBitmap(Context *rsc, uint32_t w, uint32_t h
for(uint32_t lod=0; lod < (texAlloc->getType()->getLODCount() -1); lod++) {
adapt.setLOD(lod);
adapt2.setLOD(lod + 1);
- mip(adapt2, adapt);
+ mip565(adapt2, adapt);
}
}
@@ -312,6 +343,8 @@ RsAllocation rsi_AllocationCreateFromBitmap(Context *rsc, uint32_t w, uint32_t h
RsAllocation rsi_AllocationCreateFromFile(Context *rsc, const char *file, bool genMips)
{
+ bool use32bpp = false;
+
typedef struct _Win3xBitmapHeader
{
uint16_t type;
@@ -351,7 +384,11 @@ RsAllocation rsi_AllocationCreateFromFile(Context *rsc, const char *file, bool g
int32_t texWidth = rsHigherPow2(hdr.width);
int32_t texHeight = rsHigherPow2(hdr.height);
- rsi_TypeBegin(rsc, rsi_ElementGetPredefined(rsc, RS_ELEMENT_RGB_565));
+ if (use32bpp) {
+ rsi_TypeBegin(rsc, rsi_ElementGetPredefined(rsc, RS_ELEMENT_RGBA_8888));
+ } else {
+ rsi_TypeBegin(rsc, rsi_ElementGetPredefined(rsc, RS_ELEMENT_RGB_565));
+ }
rsi_TypeAdd(rsc, RS_DIMENSION_X, texWidth);
rsi_TypeAdd(rsc, RS_DIMENSION_Y, texHeight);
if (genMips) {
@@ -372,14 +409,29 @@ RsAllocation rsi_AllocationCreateFromFile(Context *rsc, const char *file, bool g
Adapter2D adapt(texAlloc);
uint8_t * fileInBuf = new uint8_t[texWidth * 3];
uint32_t yOffset = (hdr.width - hdr.height) / 2;
- uint16_t *tmp = static_cast(adapt.getElement(0, yOffset));
-
- for (int y=0; y < hdr.height; y++) {
- fseek(f, hdr.offset + (y*hdr.width*3), SEEK_SET);
- fread(fileInBuf, 1, hdr.width * 3, f);
- for(int x=0; x < hdr.width; x++) {
- *tmp = rs888to565(fileInBuf[x*3], fileInBuf[x*3 + 1], fileInBuf[x*3 + 2]);
- tmp++;
+
+ if (use32bpp) {
+ uint8_t *tmp = static_cast(adapt.getElement(0, yOffset));
+ for (int y=0; y < hdr.height; y++) {
+ fseek(f, hdr.offset + (y*hdr.width*3), SEEK_SET);
+ fread(fileInBuf, 1, hdr.width * 3, f);
+ for(int x=0; x < hdr.width; x++) {
+ tmp[0] = fileInBuf[x*3 + 2];
+ tmp[1] = fileInBuf[x*3 + 1];
+ tmp[2] = fileInBuf[x*3];
+ tmp[3] = 0xff;
+ tmp += 4;
+ }
+ }
+ } else {
+ uint16_t *tmp = static_cast(adapt.getElement(0, yOffset));
+ for (int y=0; y < hdr.height; y++) {
+ fseek(f, hdr.offset + (y*hdr.width*3), SEEK_SET);
+ fread(fileInBuf, 1, hdr.width * 3, f);
+ for(int x=0; x < hdr.width; x++) {
+ *tmp = rs888to565(fileInBuf[x*3 + 2], fileInBuf[x*3 + 1], fileInBuf[x*3]);
+ tmp++;
+ }
}
}
@@ -391,7 +443,11 @@ RsAllocation rsi_AllocationCreateFromFile(Context *rsc, const char *file, bool g
for(uint32_t lod=0; lod < (texAlloc->getType()->getLODCount() -1); lod++) {
adapt.setLOD(lod);
adapt2.setLOD(lod + 1);
- mip(adapt2, adapt);
+ if (use32bpp) {
+ mip8888(adapt2, adapt);
+ } else {
+ mip565(adapt2, adapt);
+ }
}
}
diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp
index bd11f725d535..5a44f474a986 100644
--- a/libs/rs/rsElement.cpp
+++ b/libs/rs/rsElement.cpp
@@ -16,6 +16,8 @@
#include "rsContext.h"
+#include
+
using namespace android;
using namespace android::renderscript;
@@ -235,6 +237,108 @@ size_t Element::getComponentOffsetBits(uint32_t componentNumber) const
return offset;
}
+uint32_t Element::getGLType() const
+{
+ int bits[4];
+
+ if (mComponentCount > 4) {
+ return 0;
+ }
+
+ for (uint32_t ct=0; ct < mComponentCount; ct++) {
+ bits[ct] = mComponents[ct]->getBits();
+ if (mComponents[ct]->getType() != Component::UNSIGNED) {
+ return 0;
+ }
+ if (!mComponents[ct]->getIsNormalized()) {
+ return 0;
+ }
+ }
+
+ switch(mComponentCount) {
+ case 1:
+ if (bits[0] == 8) {
+ return GL_UNSIGNED_BYTE;
+ }
+ return 0;
+ case 2:
+ if ((bits[0] == 8) &&
+ (bits[1] == 8)) {
+ return GL_UNSIGNED_BYTE;
+ }
+ return 0;
+ case 3:
+ if ((bits[0] == 8) &&
+ (bits[1] == 8) &&
+ (bits[2] == 8)) {
+ return GL_UNSIGNED_BYTE;
+ }
+ if ((bits[0] == 5) &&
+ (bits[1] == 6) &&
+ (bits[2] == 5)) {
+ return GL_UNSIGNED_SHORT_5_6_5;
+ }
+ return 0;
+ case 4:
+ if ((bits[0] == 8) &&
+ (bits[1] == 8) &&
+ (bits[2] == 8) &&
+ (bits[3] == 8)) {
+ return GL_UNSIGNED_BYTE;
+ }
+ if ((bits[0] == 4) &&
+ (bits[1] == 4) &&
+ (bits[2] == 4) &&
+ (bits[3] == 4)) {
+ return GL_UNSIGNED_SHORT_4_4_4_4;
+ }
+ if ((bits[0] == 5) &&
+ (bits[1] == 5) &&
+ (bits[2] == 5) &&
+ (bits[3] == 1)) {
+ return GL_UNSIGNED_SHORT_5_5_5_1;
+ }
+ }
+ return 0;
+}
+
+uint32_t Element::getGLFormat() const
+{
+ switch(mComponentCount) {
+ case 1:
+ if (mComponents[0]->getKind() == Component::ALPHA) {
+ return GL_ALPHA;
+ }
+ if (mComponents[0]->getKind() == Component::LUMINANCE) {
+ return GL_LUMINANCE;
+ }
+ break;
+ case 2:
+ if ((mComponents[0]->getKind() == Component::LUMINANCE) &&
+ (mComponents[1]->getKind() == Component::ALPHA)) {
+ return GL_LUMINANCE_ALPHA;
+ }
+ break;
+ case 3:
+ if ((mComponents[0]->getKind() == Component::RED) &&
+ (mComponents[1]->getKind() == Component::GREEN) &&
+ (mComponents[2]->getKind() == Component::BLUE)) {
+ return GL_RGB;
+ }
+ break;
+ case 4:
+ if ((mComponents[0]->getKind() == Component::RED) &&
+ (mComponents[1]->getKind() == Component::GREEN) &&
+ (mComponents[2]->getKind() == Component::BLUE) &&
+ (mComponents[3]->getKind() == Component::ALPHA)) {
+ return GL_RGBA;
+ }
+ break;
+ }
+ return 0;
+}
+
+
ElementState::ElementState()
{
}
diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h
index 7852ffcb33ef..2434977dbc2a 100644
--- a/libs/rs/rsElement.h
+++ b/libs/rs/rsElement.h
@@ -36,6 +36,8 @@ public:
void setComponent(uint32_t idx, Component *c);
+ uint32_t getGLType() const;
+ uint32_t getGLFormat() const;
size_t getSizeBits() const;
diff --git a/libs/rs/rsLocklessFifo.cpp b/libs/rs/rsLocklessFifo.cpp
index 3f51e04c2c16..67ab434cf4df 100644
--- a/libs/rs/rsLocklessFifo.cpp
+++ b/libs/rs/rsLocklessFifo.cpp
@@ -74,6 +74,7 @@ uint32_t LocklessCommandFifo::getFreeSpace() const
freeSpace = 0;
}
+ //LOGE("free %i", freeSpace);
return freeSpace;
}
@@ -85,8 +86,8 @@ bool LocklessCommandFifo::isEmpty() const
void * LocklessCommandFifo::reserve(uint32_t sizeInBytes)
{
- // Add space for command header;
- sizeInBytes += 4;
+ // Add space for command header and loop token;
+ sizeInBytes += 8;
//dumpState("reserve");
if (getFreeSpace() < sizeInBytes) {
@@ -153,16 +154,17 @@ void LocklessCommandFifo::next()
void LocklessCommandFifo::makeSpace(uint32_t bytes)
{
+ //dumpState("make space");
if ((mPut+bytes) > mEnd) {
// Need to loop regardless of where get is.
- while((mGet > mPut) && (mPut+4 >= mGet)) {
+ while((mGet > mPut) && (mBuffer+4 >= mGet)) {
sleep(1);
}
// Toss in a reset then the normal wait for space will do the rest.
reinterpret_cast(mPut)[0] = 0;
reinterpret_cast(mPut)[1] = 0;
- mPut += 4;
+ mPut = mBuffer;
}
// it will fit here so we just need to wait for space.
diff --git a/libs/rs/rsUtils.h b/libs/rs/rsUtils.h
index f40e2ced0129..5a43fb3f8e57 100644
--- a/libs/rs/rsUtils.h
+++ b/libs/rs/rsUtils.h
@@ -96,13 +96,19 @@ static inline uint16_t rs888to565(uint32_t r, uint32_t g, uint32_t b)
static inline uint16_t rsBoxFilter565(uint16_t i1, uint16_t i2, uint16_t i3, uint16_t i4)
{
uint32_t r = ((i1 & 0x1f) + (i2 & 0x1f) + (i3 & 0x1f) + (i4 & 0x1f));
- uint32_t g = ((i1 >> 5) & 0x3f) + ((i2 >> 5) & 0x3f) + ((i3 >> 5) & 0x3f) + ((i1 >> 5) & 0x3f);
+ uint32_t g = ((i1 >> 5) & 0x3f) + ((i2 >> 5) & 0x3f) + ((i3 >> 5) & 0x3f) + ((i4 >> 5) & 0x3f);
uint32_t b = ((i1 >> 11) + (i2 >> 11) + (i3 >> 11) + (i4 >> 11));
return (r >> 2) | ((g >> 2) << 5) | ((b >> 2) << 11);
}
-
-
+static inline uint32_t rsBoxFilter8888(uint32_t i1, uint32_t i2, uint32_t i3, uint32_t i4)
+{
+ uint32_t r = (i1 & 0xff) + (i2 & 0xff) + (i3 & 0xff) + (i4 & 0xff);
+ uint32_t g = ((i1 >> 8) & 0xff) + ((i2 >> 8) & 0xff) + ((i3 >> 8) & 0xff) + ((i4 >> 8) & 0xff);
+ uint32_t b = ((i1 >> 16) & 0xff) + ((i2 >> 16) & 0xff) + ((i3 >> 16) & 0xff) + ((i4 >> 16) & 0xff);
+ uint32_t a = ((i1 >> 24) & 0xff) + ((i2 >> 24) & 0xff) + ((i3 >> 24) & 0xff) + ((i4 >> 24) & 0xff);
+ return (r >> 2) | ((g >> 2) << 8) | ((b >> 2) << 16) | ((a >> 2) << 24);
+}
--
cgit v1.2.3-59-g8ed1b
From e87b2f02761744520c841f536d3f2d7be97fcc91 Mon Sep 17 00:00:00 2001
From: Wei Huang
Date: Tue, 2 Jun 2009 15:16:04 -0700
Subject: add Gservices settings for adaptive heartbeat parameters and wifi
heartbeat interval.
---
core/java/android/provider/Settings.java | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b4338596ef62..68e4329d988f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2566,6 +2566,32 @@ public final class Settings {
public static final String GTALK_SERVICE_NOSYNC_HEARTBEAT_INTERVAL_MS =
"gtalk_nosync_heartbeat_ping_interval_ms";
+ /**
+ * The maximum heartbeat interval used while on the WIFI network.
+ */
+ public static final String GTALK_SERVICE_WIFI_MAX_HEARTBEAT_INTERVAL_MS =
+ "gtalk_wifi_max_heartbeat_ping_interval_ms";
+
+ /**
+ * The minimum interval for how frequently we send heartbeat pings to the GTalk server.
+ */
+ public static final String GTALK_SERVICE_MIN_HEARTBEAT_INTERVAL_MS =
+ "gtalk_min_heartbeat_ping_interval_ms";
+
+ /**
+ * The scale down factor used by adaptive heartbeat logic (to scale down the heartbeat
+ * interval) when the previous interval fails to get a response from the server.
+ */
+ public static final String GTALK_SERVICE_ADAPTIVE_HEARTBEAT_SCALER =
+ "gtalk_adaptive_heartbeat_scaler";
+
+ /**
+ * The trigger for adaptively scaling down the heartbeat interval. This is the number of
+ * consecutive times we failed to get a server response for sending the heartbeat ping.
+ */
+ public static final String GTALK_SERVICE_ADAPTIVE_HEARTBEAT_TRIGGER =
+ "gtalk_adaptive_heartbeat_trigger";
+
/**
* How long we wait to receive a heartbeat ping acknowledgement (or another packet)
* from the GTalk server, before deeming the connection dead.
--
cgit v1.2.3-59-g8ed1b
From 9189cabb0b6c6c28232fe6f412b7ba7a37352a6a Mon Sep 17 00:00:00 2001
From: Mitsuru Oshima
Date: Wed, 3 Jun 2009 11:19:12 -0700
Subject: * Moved supports-density tag under manifest * Refactored
Compatibility code * Added CompatibilityInfo class * Removed
getApplicationScale from Context * Added Resources#getCompatibilityInfo so
that RootView can get the compatibility info w/o going through Context *
Expandable support * Added expandable tag under manifest * Old
application w/o expandable is given the default screen size ([320, 480] x
density). * The non-expandable window is centered.
---
api/current.xml | 21 +++
core/java/android/app/ActivityThread.java | 79 ++--------
core/java/android/app/ApplicationContext.java | 16 +-
core/java/android/content/Context.java | 10 --
core/java/android/content/ContextWrapper.java | 8 -
core/java/android/content/pm/ApplicationInfo.java | 11 +-
core/java/android/content/pm/PackageManager.java | 6 +
core/java/android/content/pm/PackageParser.java | 49 +++---
.../android/content/res/CompatibilityInfo.java | 102 +++++++++++++
core/java/android/content/res/Resources.java | 44 +++++-
core/java/android/util/DisplayMetrics.java | 51 +++++--
core/java/android/view/SurfaceView.java | 66 ++++----
core/java/android/view/ViewRoot.java | 166 +++++++++++++--------
core/res/res/values/attrs_manifest.xml | 11 +-
.../android/server/am/ActivityManagerService.java | 2 +-
test-runner/android/test/mock/MockContext.java | 8 -
.../android/layoutlib/bridge/BridgeContext.java | 8 -
17 files changed, 412 insertions(+), 246 deletions(-)
create mode 100644 core/java/android/content/res/CompatibilityInfo.java
diff --git a/api/current.xml b/api/current.xml
index 9e9eaad45e0e..41f8c2ce5030 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -34943,6 +34943,16 @@
visibility="public"
>
+
+
+
+
wr = mActiveResources.get(appDir);
@@ -181,23 +181,17 @@ public final class ActivityThread {
if (assets.addAssetPath(appDir) == 0) {
return null;
}
- DisplayMetrics metrics = getDisplayMetricsLocked(false);
- // density used to load resources
- // scaledDensity is calculated in Resources constructor
- //
- boolean usePreloaded = true;
-
- // TODO: use explicit flag to indicate the compatibility mode.
- if (applicationScale != 1.0f) {
- usePreloaded = false;
- DisplayMetrics newMetrics = new DisplayMetrics();
- newMetrics.setTo(metrics);
- float newDensity = metrics.density / applicationScale;
- newMetrics.updateDensity(newDensity);
- metrics = newMetrics;
+ ApplicationInfo appInfo;
+ try {
+ appInfo = getPackageManager().getApplicationInfo(
+ pkgInfo.getPackageName(),
+ PackageManager.GET_SUPPORTS_DENSITIES | PackageManager.GET_EXPANDABLE);
+ } catch (RemoteException e) {
+ throw new AssertionError(e);
}
//Log.i(TAG, "Resource:" + appDir + ", display metrics=" + metrics);
- r = new Resources(assets, metrics, getConfiguration(), usePreloaded);
+ DisplayMetrics metrics = getDisplayMetricsLocked(false);
+ r = new Resources(assets, metrics, getConfiguration(), appInfo);
//Log.i(TAG, "Created app resources " + r + ": " + r.getConfiguration());
// XXX need to remove entries when weak references go away
mActiveResources.put(appDir, new WeakReference(r));
@@ -225,7 +219,6 @@ public final class ActivityThread {
private Resources mResources;
private ClassLoader mClassLoader;
private Application mApplication;
- private float mApplicationScale;
private final HashMap> mReceivers
= new HashMap>();
@@ -268,8 +261,6 @@ public final class ActivityThread {
mClassLoader = mSystemContext.getClassLoader();
mResources = mSystemContext.getResources();
}
-
- mApplicationScale = -1.0f;
}
public PackageInfo(ActivityThread activityThread, String name,
@@ -288,7 +279,6 @@ public final class ActivityThread {
mIncludeCode = true;
mClassLoader = systemContext.getClassLoader();
mResources = systemContext.getResources();
- mApplicationScale = systemContext.getApplicationScale();
}
public String getPackageName() {
@@ -299,45 +289,6 @@ public final class ActivityThread {
return mSecurityViolation;
}
- public float getApplicationScale() {
- if (mApplicationScale > 0.0f) {
- return mApplicationScale;
- }
- DisplayMetrics metrics = mActivityThread.getDisplayMetricsLocked(false);
- // Find out the density scale (relative to 160) of the supported density that
- // is closest to the system's density.
- try {
- ApplicationInfo ai = getPackageManager().getApplicationInfo(
- mPackageName, PackageManager.GET_SUPPORTS_DENSITIES);
-
- float appScale = -1.0f;
- if (ai.supportsDensities != null) {
- int minDiff = Integer.MAX_VALUE;
- for (int density : ai.supportsDensities) {
- int tmpDiff = (int) Math.abs(DisplayMetrics.DEVICE_DENSITY - density);
- if (tmpDiff == 0) {
- appScale = 1.0f;
- break;
- }
- // prefer higher density (appScale>1.0), unless that's only option.
- if (tmpDiff < minDiff && appScale < 1.0f) {
- appScale = DisplayMetrics.DEVICE_DENSITY / density;
- minDiff = tmpDiff;
- }
- }
- }
- if (appScale < 0.0f) {
- mApplicationScale = metrics.density;
- } else {
- mApplicationScale = appScale;
- }
- } catch (RemoteException e) {
- throw new AssertionError(e);
- }
- if (localLOGV) Log.v(TAG, "appScale=" + mApplicationScale + ", pkg=" + mPackageName);
- return mApplicationScale;
- }
-
/**
* Gets the array of shared libraries that are listed as
* used by the given package.
@@ -495,7 +446,7 @@ public final class ActivityThread {
public Resources getResources(ActivityThread mainThread) {
if (mResources == null) {
- mResources = mainThread.getTopLevelResources(mResDir, getApplicationScale());
+ mResources = mainThread.getTopLevelResources(mResDir, this);
}
return mResources;
}
@@ -3606,8 +3557,6 @@ public final class ActivityThread {
}
mConfiguration.updateFrom(config);
DisplayMetrics dm = getDisplayMetricsLocked(true);
- DisplayMetrics appDm = new DisplayMetrics();
- appDm.setTo(dm);
// set it for java, this also affects newly created Resources
if (config.locale != null) {
@@ -3627,11 +3576,7 @@ public final class ActivityThread {
WeakReference v = it.next();
Resources r = v.get();
if (r != null) {
- // keep the original density based on application cale.
- appDm.updateDensity(r.getDisplayMetrics().density);
- r.updateConfiguration(config, appDm);
- // reset
- appDm.setTo(dm);
+ r.updateConfiguration(config, dm);
//Log.i(TAG, "Updated app resources " + v.getKey()
// + " " + r + ": " + r.getConfiguration());
} else {
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
index 2d6381a632df..98bbf7b82ddf 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -551,19 +551,6 @@ class ApplicationContext extends Context {
}
}
- /**
- * @hide
- */
- @Override
- public float getApplicationScale() {
- if (mPackageInfo != null) {
- return mPackageInfo.getApplicationScale();
- } else {
- // same as system density
- return 1.0f;
- }
- }
-
@Override
public void setWallpaper(Bitmap bitmap) throws IOException {
try {
@@ -2028,8 +2015,7 @@ class ApplicationContext extends Context {
ActivityThread.PackageInfo pi = mContext.mMainThread.getPackageInfoNoCheck(app);
Resources r = mContext.mMainThread.getTopLevelResources(
app.uid == Process.myUid() ? app.sourceDir
- : app.publicSourceDir,
- pi.getApplicationScale());
+ : app.publicSourceDir, pi);
if (r != null) {
return r;
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c328d161499d..ec847a472d5f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -526,16 +526,6 @@ public abstract class Context {
*/
public abstract int getWallpaperDesiredMinimumHeight();
- /**
- * Returns the scale in which the application will be drawn on the
- * screen. This is usually 1.0f if the application supports the device's
- * resolution/density. This will be 1.5f, for example, if the application
- * that supports only 160 density runs on 240 density screen.
- *
- * @hide
- */
- public abstract float getApplicationScale();
-
/**
* Change the current system wallpaper to a bitmap. The given bitmap is
* converted to a PNG and stored as the wallpaper. On success, the intent
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 25b2caeb7279..36e1c340d1d9 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -419,12 +419,4 @@ public class ContextWrapper extends Context {
throws PackageManager.NameNotFoundException {
return mBase.createPackageContext(packageName, flags);
}
-
- /**
- * @hide
- */
- @Override
- public float getApplicationScale() {
- return mBase.getApplicationScale();
- }
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index f16eb74b6ff1..f3dfc5af938b 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -186,7 +186,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
*/
public int uid;
-
/**
* The list of densities in DPI that application supprots. This
* field is only set if the {@link PackageManager#GET_SUPPORTS_DENSITIES} flag was
@@ -194,6 +193,12 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
*/
public int[] supportsDensities;
+ /**
+ * True when the application's window can be expanded over default window
+ * size in target density (320x480 for 1.0 density, 480x720 for 1.5 density etc)
+ */
+ public boolean expandable = false;
+
/**
* The minimum SDK version this application targets. It may run on earilier
* versions, but it knows how to work with any new behavior added at this
@@ -228,6 +233,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
pw.println(prefix + "manageSpaceActivityName="+manageSpaceActivityName);
pw.println(prefix + "description=0x"+Integer.toHexString(descriptionRes));
pw.println(prefix + "supportsDensities=" + supportsDensities);
+ pw.println(prefix + "expandable=" + expandable);
super.dumpBack(pw, prefix);
}
@@ -275,6 +281,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
manageSpaceActivityName = orig.manageSpaceActivityName;
descriptionRes = orig.descriptionRes;
supportsDensities = orig.supportsDensities;
+ expandable = orig.expandable;
}
@@ -307,6 +314,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
dest.writeString(backupAgentName);
dest.writeInt(descriptionRes);
dest.writeIntArray(supportsDensities);
+ dest.writeInt(expandable ? 1 : 0);
}
public static final Parcelable.Creator CREATOR
@@ -338,6 +346,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
backupAgentName = source.readString();
descriptionRes = source.readInt();
supportsDensities = source.createIntArray();
+ expandable = source.readInt() != 0;
}
/**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a2c82e890e41..65783917f909 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -179,6 +179,12 @@ public abstract class PackageManager {
*/
public static final int MATCH_DEFAULT_ONLY = 0x00010000;
+ /**
+ * {@link ApplicationInfo} flag: return the
+ * {link ApplicationInfo#expandable} boolean flag of the package.
+ */
+ public static final int GET_EXPANDABLE = 0x00020000;
+
/**
* Permission check result: this is returned by {@link #checkPermission}
* if the permission has been granted to the given package.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 212b59017160..e2c0fe69f345 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -835,6 +835,26 @@ public class PackageParser {
+ parser.getName();
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
+
+
+ } else if (tagName.equals("supports-density")) {
+ sa = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.AndroidManifestSupportsDensity);
+
+ int density = sa.getInteger(
+ com.android.internal.R.styleable.AndroidManifestSupportsDensity_density, -1);
+
+ sa.recycle();
+
+ if (density != -1 && !pkg.supportsDensityList.contains(density)) {
+ pkg.supportsDensityList.add(density);
+ }
+
+ XmlUtils.skipCurrentTag(parser);
+
+ } else if (tagName.equals("expandable")) {
+ pkg.expandable = true;
+ XmlUtils.skipCurrentTag(parser);
} else {
Log.w(TAG, "Bad element under : "
+ parser.getName());
@@ -866,7 +886,8 @@ public class PackageParser {
pkg.usesLibraryFiles = new String[pkg.usesLibraries.size()];
pkg.usesLibraries.toArray(pkg.usesLibraryFiles);
}
-
+ // TODO: enable all density & expandable if target sdk is higher than donut
+
int size = pkg.supportsDensityList.size();
if (size > 0) {
int densities[] = pkg.supportsDensities = new int[size];
@@ -1345,21 +1366,6 @@ public class PackageParser {
XmlUtils.skipCurrentTag(parser);
- } else if (tagName.equals("supports-density")) {
- sa = res.obtainAttributes(attrs,
- com.android.internal.R.styleable.AndroidManifestSupportsDensity);
-
- int density = sa.getInteger(
- com.android.internal.R.styleable.AndroidManifestSupportsDensity_density, -1);
-
- sa.recycle();
-
- if (density != -1 && !owner.supportsDensityList.contains(density)) {
- owner.supportsDensityList.add(density);
- }
-
- XmlUtils.skipCurrentTag(parser);
-
} else {
if (!RIGID_PARSER) {
Log.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
@@ -2244,6 +2250,9 @@ public class PackageParser {
public final ArrayList supportsDensityList = new ArrayList();
public int[] supportsDensities = null;
+ // If the application's window is expandable.
+ public boolean expandable;
+
// If this is a 3rd party app, this is the path of the zip file.
public String mPath;
@@ -2415,7 +2424,10 @@ public class PackageParser {
return true;
}
if ((flags & PackageManager.GET_SUPPORTS_DENSITIES) != 0
- && p.supportsDensities != null) {
+ && p.supportsDensities != null) {
+ return true;
+ }
+ if ((flags & PackageManager.GET_EXPANDABLE) != 0) {
return true;
}
return false;
@@ -2438,6 +2450,9 @@ public class PackageParser {
if ((flags & PackageManager.GET_SUPPORTS_DENSITIES) != 0) {
ai.supportsDensities = p.supportsDensities;
}
+ if ((flags & PackageManager.GET_EXPANDABLE) != 0) {
+ ai.expandable = p.expandable;
+ }
return ai;
}
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
new file mode 100644
index 000000000000..8a6a6f0390d2
--- /dev/null
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res;
+
+import android.content.pm.ApplicationInfo;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+
+/**
+ * CompatibilityInfo class keeps the information about compatibility mode that the application is
+ * running under.
+ *
+ * {@hide}
+ */
+public class CompatibilityInfo {
+ /** default compatibility info object for compatible applications */
+ public static final CompatibilityInfo DEFAULT_COMPATIBILITY_INFO = new CompatibilityInfo();
+
+ /**
+ * The default width of the screen in portrait mode.
+ */
+ public static final int DEFAULT_PORTRAIT_WIDTH = 320;
+
+ /**
+ * The default height of the screen in portrait mode.
+ */
+ public static final int DEFAULT_PORTRAIT_HEIGHT = 480;
+
+ /**
+ * Application's scale.
+ */
+ public final float mApplicationScale;
+
+ /**
+ * Application's inverted scale.
+ */
+ public final float mApplicationInvertedScale;
+
+ /**
+ *
+ * A boolean flag to indicates that the application can expand over the original size.
+ */
+ public final boolean mExpandable;
+
+ /**
+ * A boolean flag to tell if the application needs scaling (when mApplicationScale != 1.0f)
+ */
+ public final boolean mScalingRequired;
+
+ public CompatibilityInfo(ApplicationInfo appInfo) {
+ mExpandable = appInfo.expandable;
+ float packageDensityScale = -1.0f;
+ if (appInfo.supportsDensities != null) {
+ int minDiff = Integer.MAX_VALUE;
+ for (int density : appInfo.supportsDensities) {
+ int tmpDiff = Math.abs(DisplayMetrics.DEVICE_DENSITY - density);
+ if (tmpDiff == 0) {
+ packageDensityScale = 1.0f;
+ break;
+ }
+ // prefer higher density (appScale>1.0), unless that's only option.
+ if (tmpDiff < minDiff && packageDensityScale < 1.0f) {
+ packageDensityScale = DisplayMetrics.DEVICE_DENSITY / (float) density;
+ minDiff = tmpDiff;
+ }
+ }
+ }
+ if (packageDensityScale > 0.0f) {
+ mApplicationScale = packageDensityScale;
+ } else {
+ mApplicationScale = DisplayMetrics.DEVICE_DENSITY / (float) DisplayMetrics.DEFAULT_DENSITY;
+ }
+ mApplicationInvertedScale = 1.0f / mApplicationScale;
+ mScalingRequired = mApplicationScale != 1.0f;
+ }
+
+ private CompatibilityInfo() {
+ mApplicationScale = mApplicationInvertedScale = 1.0f;
+ mExpandable = true;
+ mScalingRequired = false;
+ }
+
+ @Override
+ public String toString() {
+ return "CompatibilityInfo{scale=" + mApplicationScale +
+ ", expandable=" + mExpandable + "}";
+ }
+}
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 665e40ca362f..976b6189310f 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -22,6 +22,8 @@ import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.app.ActivityThread.PackageInfo;
+import android.content.pm.ApplicationInfo;
import android.graphics.Movie;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.BitmapDrawable;
@@ -84,7 +86,9 @@ public class Resources {
private final Configuration mConfiguration = new Configuration();
/*package*/ final DisplayMetrics mMetrics = new DisplayMetrics();
PluralRules mPluralRule;
-
+
+ private final CompatibilityInfo mCompatibilityInfo;
+
private static final SparseArray EMPTY_ARRAY = new SparseArray() {
@Override
public void put(int k, Object o) {
@@ -126,23 +130,36 @@ public class Resources {
*/
public Resources(AssetManager assets, DisplayMetrics metrics,
Configuration config) {
- this(assets, metrics, config, true);
+ this(assets, metrics, config, null);
}
/**
- * Create a resource with an additional flag for preloaded
- * drawable cache. Used by {@link ActivityThread}.
- *
+ * Creates a new Resources object with ApplicationInfo.
+ *
+ * @param assets Previously created AssetManager.
+ * @param metrics Current display metrics to consider when
+ * selecting/computing resource values.
+ * @param config Desired device configuration to consider when
+ * selecting/computing resource values (optional).
+ * @param appInfo this resource's application info.
* @hide
*/
public Resources(AssetManager assets, DisplayMetrics metrics,
- Configuration config, boolean usePreloadedCache) {
+ Configuration config, ApplicationInfo appInfo) {
mAssets = assets;
mConfiguration.setToDefaults();
mMetrics.setToDefaults();
+ if (appInfo != null) {
+ mCompatibilityInfo = new CompatibilityInfo(appInfo);
+ if (DEBUG_CONFIG) {
+ Log.d(TAG, "compatibility for " + appInfo.packageName + " : " + mCompatibilityInfo);
+ }
+ } else {
+ mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
+ }
updateConfiguration(config, metrics);
assets.ensureStringBlocks();
- if (usePreloadedCache) {
+ if (!mCompatibilityInfo.mScalingRequired) {
mPreloadedDrawables = sPreloadedDrawables;
} else {
mPreloadedDrawables = emptySparseArray();
@@ -1251,6 +1268,7 @@ public class Resources {
}
if (metrics != null) {
mMetrics.setTo(metrics);
+ mMetrics.updateMetrics(mCompatibilityInfo, mConfiguration);
}
mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
@@ -1356,6 +1374,17 @@ public class Resources {
public Configuration getConfiguration() {
return mConfiguration;
}
+
+ /**
+ * Return the compatibility mode information for the application.
+ * The returned object should be treated as read-only.
+ *
+ * @return compatibility info. null if the app does not require compatibility mode.
+ * @hide
+ */
+ public CompatibilityInfo getCompatibilityInfo() {
+ return mCompatibilityInfo;
+ }
/**
* Return a resource identifier for the given resource name. A fully
@@ -1920,5 +1949,6 @@ public class Resources {
updateConfiguration(null, null);
mAssets.ensureStringBlocks();
mPreloadedDrawables = sPreloadedDrawables;
+ mCompatibilityInfo = CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
}
}
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index e4dd020e1a81..987be2b3ce2f 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -16,6 +16,8 @@
package android.util;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
import android.os.*;
@@ -101,17 +103,46 @@ public class DisplayMetrics {
}
/**
- * Set the display metrics' density and update parameters depend on it.
- * @hide
+ * Update the display metrics based on the compatibility info and configuration.
+ * {@hide}
*/
- public void updateDensity(float newDensity) {
- float ratio = newDensity / density;
- density = newDensity;
- scaledDensity = density;
- widthPixels *= ratio;
- heightPixels *= ratio;
- xdpi *= ratio;
- ydpi *= ratio;
+ public void updateMetrics(CompatibilityInfo compatibilityInfo, Configuration configuration) {
+ if (compatibilityInfo.mScalingRequired) {
+ float invertedRatio = compatibilityInfo.mApplicationInvertedScale;
+ density *= invertedRatio;
+ scaledDensity *= invertedRatio;
+ xdpi *= invertedRatio;
+ ydpi *= invertedRatio;
+ widthPixels *= invertedRatio;
+ heightPixels *= invertedRatio;
+ }
+ if (!compatibilityInfo.mExpandable) {
+ // Note: this assume that configuration is updated before calling
+ // updateMetrics method.
+ int defaultWidth;
+ int defaultHeight;
+ switch (configuration.orientation) {
+ case Configuration.ORIENTATION_LANDSCAPE: {
+ defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density);
+ defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density);
+ break;
+ }
+ case Configuration.ORIENTATION_UNDEFINED:
+ case Configuration.ORIENTATION_PORTRAIT:
+ case Configuration.ORIENTATION_SQUARE:
+ default: {
+ defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density);
+ defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density);
+ }
+ }
+ // adjust the size only when the device's screen is bigger.
+ if (defaultWidth < widthPixels) {
+ widthPixels = defaultWidth;
+ }
+ if (defaultHeight < heightPixels) {
+ heightPixels = defaultHeight;
+ }
+ }
}
public String toString() {
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 0dc257027065..082cca24e15f 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -17,6 +17,7 @@
package android.view;
import android.content.Context;
+import android.content.res.CompatibilityInfo;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
@@ -137,28 +138,24 @@ public class SurfaceView extends View {
int mFormat = -1;
int mType = -1;
final Rect mSurfaceFrame = new Rect();
- private final float mAppScale;
- private final float mAppScaleInverted;
+ private final CompatibilityInfo mCompatibilityInfo;
public SurfaceView(Context context) {
super(context);
setWillNotDraw(true);
- mAppScale = context.getApplicationScale();
- mAppScaleInverted = 1.0f / mAppScale;
+ mCompatibilityInfo = context.getResources().getCompatibilityInfo();
}
public SurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
setWillNotDraw(true);
- mAppScale = context.getApplicationScale();
- mAppScaleInverted = 1.0f / mAppScale;
+ mCompatibilityInfo = context.getResources().getCompatibilityInfo();
}
public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setWillNotDraw(true);
- mAppScale = context.getApplicationScale();
- mAppScaleInverted = 1.0f / mAppScale;
+ mCompatibilityInfo = context.getResources().getCompatibilityInfo();
}
/**
@@ -261,9 +258,9 @@ public class SurfaceView extends View {
public boolean dispatchTouchEvent(MotionEvent event) {
// SurfaceView uses pre-scaled size unless fixed size is requested. This hook
// scales the event back to the pre-scaled coordinates for such surface.
- if (mRequestedWidth < 0 && mAppScale != 1.0f) {
+ if (mRequestedWidth < 0 && mCompatibilityInfo.mScalingRequired) {
MotionEvent scaledBack = MotionEvent.obtain(event);
- scaledBack.scale(mAppScale);
+ scaledBack.scale(mCompatibilityInfo.mApplicationScale);
try {
return super.dispatchTouchEvent(scaledBack);
} finally {
@@ -300,6 +297,7 @@ public class SurfaceView extends View {
if (!mHaveFrame) {
return;
}
+ float appScale = mCompatibilityInfo.mApplicationScale;
int myWidth = mRequestedWidth;
if (myWidth <= 0) myWidth = getWidth();
@@ -307,9 +305,9 @@ public class SurfaceView extends View {
if (myHeight <= 0) myHeight = getHeight();
// Use original size for surface unless fixed size is requested.
- if (mRequestedWidth <= 0) {
- myWidth *= mAppScale;
- myHeight *= mAppScale;
+ if (mRequestedWidth <= 0 && mCompatibilityInfo.mScalingRequired) {
+ myWidth *= appScale;
+ myHeight *= appScale;
}
getLocationInWindow(mLocation);
@@ -337,11 +335,11 @@ public class SurfaceView extends View {
mFormat = mRequestedFormat;
mType = mRequestedType;
- // Scaling window's layout here beause mLayout is not used elsewhere.
- mLayout.x = (int) (mLeft * mAppScale);
- mLayout.y = (int) (mTop * mAppScale);
- mLayout.width = (int) (getWidth() * mAppScale);
- mLayout.height = (int) (getHeight() * mAppScale);
+ // Scaling window's layout here because mLayout is not used elsewhere.
+ mLayout.x = (int) (mLeft * appScale);
+ mLayout.y = (int) (mTop * appScale);
+ mLayout.width = (int) (getWidth() * appScale);
+ mLayout.height = (int) (getHeight() * appScale);
mLayout.format = mRequestedFormat;
mLayout.flags |=WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
| WindowManager.LayoutParams.FLAG_SCALED
@@ -367,14 +365,18 @@ public class SurfaceView extends View {
mSurfaceLock.lock();
mDrawingStopped = !visible;
+
final int relayoutResult = mSession.relayout(
mWindow, mLayout, mWidth, mHeight,
visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets,
mVisibleInsets, mSurface);
- mContentInsets.scale(mAppScaleInverted);
- mVisibleInsets.scale(mAppScaleInverted);
- mWinFrame.scale(mAppScaleInverted);
+ if (mCompatibilityInfo.mScalingRequired) {
+ float invertedScale = mCompatibilityInfo.mApplicationInvertedScale;
+ mContentInsets.scale(invertedScale);
+ mVisibleInsets.scale(invertedScale);
+ mWinFrame.scale(invertedScale);
+ }
if (localLOGV) Log.i(TAG, "New surface: " + mSurface
+ ", vis=" + visible + ", frame=" + mWinFrame);
@@ -444,23 +446,23 @@ public class SurfaceView extends View {
private static class MyWindow extends IWindow.Stub {
private final WeakReference mSurfaceView;
- private final float mAppScale;
- private final float mAppScaleInverted;
+ private final CompatibilityInfo mCompatibilityInfo;
public MyWindow(SurfaceView surfaceView) {
mSurfaceView = new WeakReference(surfaceView);
- mAppScale = surfaceView.getContext().getApplicationScale();
- mAppScaleInverted = 1.0f / mAppScale;
+ mCompatibilityInfo = surfaceView.getContext().getResources().getCompatibilityInfo();
}
public void resized(int w, int h, Rect coveredInsets,
Rect visibleInsets, boolean reportDraw) {
SurfaceView surfaceView = mSurfaceView.get();
- float scale = mAppScaleInverted;
- w *= scale;
- h *= scale;
- coveredInsets.scale(scale);
- visibleInsets.scale(scale);
+ if (mCompatibilityInfo.mScalingRequired) {
+ float scale = mCompatibilityInfo.mApplicationInvertedScale;
+ w *= scale;
+ h *= scale;
+ coveredInsets.scale(scale);
+ visibleInsets.scale(scale);
+ }
if (surfaceView != null) {
if (localLOGV) Log.v(
@@ -624,7 +626,9 @@ public class SurfaceView extends View {
Canvas c = null;
if (!mDrawingStopped && mWindow != null) {
Rect frame = dirty != null ? dirty : mSurfaceFrame;
- frame.scale(mAppScale);
+ if (mCompatibilityInfo.mScalingRequired) {
+ frame.scale(mCompatibilityInfo.mApplicationScale);
+ }
try {
c = mSurface.lockCanvas(frame);
} catch (Exception e) {
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 7cd65e229d65..d8bab56a79d0 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -30,6 +30,7 @@ import android.os.Process;
import android.os.SystemProperties;
import android.util.AndroidRuntimeException;
import android.util.Config;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.util.EventLog;
import android.util.SparseArray;
@@ -40,6 +41,7 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
import android.content.pm.PackageManager;
+import android.content.res.CompatibilityInfo;
import android.content.Context;
import android.app.ActivityManagerNative;
import android.Manifest;
@@ -125,9 +127,8 @@ public final class ViewRoot extends Handler implements ViewParent,
int mHeight;
Rect mDirty; // will be a graphics.Region soon
boolean mIsAnimating;
- // TODO: change these to scalar class.
- private float mAppScale;
- private float mAppScaleInverted; // = 1.0f / mAppScale
+
+ private CompatibilityInfo mCompatibilityInfo;
private int[] mWindowLayoutParamsBackup = null;
final View.AttachInfo mAttachInfo;
@@ -386,12 +387,15 @@ public final class ViewRoot extends Handler implements ViewParent,
synchronized (this) {
if (mView == null) {
mView = view;
- mAppScale = mView.getContext().getApplicationScale();
- if (mAppScale != 1.0f) {
+ mWindowAttributes.copyFrom(attrs);
+ mCompatibilityInfo =
+ mView.getContext().getResources().getCompatibilityInfo();
+ if (mCompatibilityInfo.mScalingRequired) {
mWindowLayoutParamsBackup = new int[4];
}
- mAppScaleInverted = 1.0f / mAppScale;
- mWindowAttributes.copyFrom(attrs);
+ if (!mCompatibilityInfo.mExpandable) {
+ adjustWindowAttributesForCompatibleMode(mWindowAttributes);
+ }
mSoftInputMode = attrs.softInputMode;
mWindowAttributesChanged = true;
mAttachInfo.mRootView = view;
@@ -406,9 +410,8 @@ public final class ViewRoot extends Handler implements ViewParent,
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
-
try {
- res = sWindowSession.add(mWindow, attrs,
+ res = sWindowSession.add(mWindow, mWindowAttributes,
getHostVisibility(), mAttachInfo.mContentInsets);
} catch (RemoteException e) {
mAdded = false;
@@ -417,7 +420,10 @@ public final class ViewRoot extends Handler implements ViewParent,
unscheduleTraversals();
throw new RuntimeException("Adding window failed", e);
}
- mAttachInfo.mContentInsets.scale(mAppScaleInverted);
+ if (mCompatibilityInfo.mScalingRequired) {
+ mAttachInfo.mContentInsets.scale(
+ mCompatibilityInfo.mApplicationInvertedScale);
+ }
mPendingContentInsets.set(mAttachInfo.mContentInsets);
mPendingVisibleInsets.set(0, 0, 0, 0);
if (Config.LOGV) Log.v("ViewRoot", "Added window " + mWindow);
@@ -529,13 +535,13 @@ public final class ViewRoot extends Handler implements ViewParent,
public void invalidateChild(View child, Rect dirty) {
checkThread();
if (LOCAL_LOGV) Log.v(TAG, "Invalidate child: " + dirty);
- if (mCurScrollY != 0 || mAppScale != 1.0f) {
+ if (mCurScrollY != 0 || mCompatibilityInfo.mScalingRequired) {
mTempRect.set(dirty);
if (mCurScrollY != 0) {
mTempRect.offset(0, -mCurScrollY);
}
- if (mAppScale != 1.0f) {
- mTempRect.scale(mAppScale);
+ if (mCompatibilityInfo.mScalingRequired) {
+ mTempRect.scale(mCompatibilityInfo.mApplicationScale);
}
dirty = mTempRect;
}
@@ -615,6 +621,8 @@ public final class ViewRoot extends Handler implements ViewParent,
boolean viewVisibilityChanged = mViewVisibility != viewVisibility
|| mNewSurfaceNeeded;
+ float appScale = mCompatibilityInfo.mApplicationScale;
+
WindowManager.LayoutParams params = null;
if (mWindowAttributesChanged) {
mWindowAttributesChanged = false;
@@ -625,9 +633,10 @@ public final class ViewRoot extends Handler implements ViewParent,
fullRedrawNeeded = true;
mLayoutRequested = true;
- Display d = new Display(0);
- desiredWindowWidth = (int) (d.getWidth() * mAppScaleInverted);
- desiredWindowHeight = (int) (d.getHeight() * mAppScaleInverted);
+ DisplayMetrics packageMetrics =
+ mView.getContext().getResources().getDisplayMetrics();
+ desiredWindowWidth = packageMetrics.widthPixels;
+ desiredWindowHeight = packageMetrics.heightPixels;
// For the very first time, tell the view hierarchy that it
// is attached to the window. Note that at this point the surface
@@ -696,9 +705,10 @@ public final class ViewRoot extends Handler implements ViewParent,
|| lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
windowResizesToFitContent = true;
- Display d = new Display(0);
- desiredWindowWidth = (int) (d.getWidth() * mAppScaleInverted);
- desiredWindowHeight = (int) (d.getHeight() * mAppScaleInverted);
+ DisplayMetrics packageMetrics =
+ mView.getContext().getResources().getDisplayMetrics();
+ desiredWindowWidth = packageMetrics.widthPixels;
+ desiredWindowHeight = packageMetrics.heightPixels;
}
}
@@ -878,7 +888,7 @@ public final class ViewRoot extends Handler implements ViewParent,
mHeight = frame.height();
if (initialized) {
- mGlCanvas.setViewport((int) (mWidth * mAppScale), (int) (mHeight * mAppScale));
+ mGlCanvas.setViewport((int) (mWidth * appScale), (int) (mHeight * appScale));
}
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
@@ -968,11 +978,7 @@ public final class ViewRoot extends Handler implements ViewParent,
mTmpLocation[1] + host.mBottom - host.mTop);
host.gatherTransparentRegion(mTransparentRegion);
-
- // TODO: scale the region, like:
- // Region uses native methods. We probabl should have ScalableRegion class.
-
- // Region does not have equals method ?
+ mTransparentRegion.scale(appScale);
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
// reconfigure window manager
@@ -983,7 +989,6 @@ public final class ViewRoot extends Handler implements ViewParent,
}
}
-
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals -- after setFrame");
@@ -1003,10 +1008,11 @@ public final class ViewRoot extends Handler implements ViewParent,
givenContent.left = givenContent.top = givenContent.right
= givenContent.bottom = givenVisible.left = givenVisible.top
= givenVisible.right = givenVisible.bottom = 0;
- insets.contentInsets.scale(mAppScale);
- insets.visibleInsets.scale(mAppScale);
-
attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
+ if (mCompatibilityInfo.mScalingRequired) {
+ insets.contentInsets.scale(appScale);
+ insets.visibleInsets.scale(appScale);
+ }
if (insetsPending || !mLastGivenInsets.equals(insets)) {
mLastGivenInsets.set(insets);
try {
@@ -1154,6 +1160,8 @@ public final class ViewRoot extends Handler implements ViewParent,
mCurScrollY = yoff;
fullRedrawNeeded = true;
}
+ float appScale = mCompatibilityInfo.mApplicationScale;
+ boolean scalingRequired = mCompatibilityInfo.mScalingRequired;
Rect dirty = mDirty;
if (mUseGL) {
@@ -1169,12 +1177,11 @@ public final class ViewRoot extends Handler implements ViewParent,
mAttachInfo.mIgnoreDirtyState = true;
mView.mPrivateFlags |= View.DRAWN;
- float scale = mAppScale;
int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
try {
canvas.translate(0, -yoff);
- if (scale != 1.0f) {
- canvas.scale(scale, scale);
+ if (scalingRequired) {
+ canvas.scale(appScale, appScale);
}
mView.draw(canvas);
if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
@@ -1206,8 +1213,8 @@ public final class ViewRoot extends Handler implements ViewParent,
}
if (fullRedrawNeeded) {
- mAttachInfo.mIgnoreDirtyState = true;
- dirty.union(0, 0, (int) (mWidth * mAppScale), (int) (mHeight * mAppScale));
+ mAttachInfo.mIgnoreDirtyState = true;
+ dirty.union(0, 0, (int) (mWidth * appScale), (int) (mHeight * appScale));
}
if (DEBUG_ORIENTATION || DEBUG_DRAW) {
@@ -1215,7 +1222,8 @@ public final class ViewRoot extends Handler implements ViewParent,
+ mWindowAttributes.getTitle()
+ ": dirty={" + dirty.left + "," + dirty.top
+ "," + dirty.right + "," + dirty.bottom + "} surface="
- + surface + " surface.isValid()=" + surface.isValid());
+ + surface + " surface.isValid()=" + surface.isValid() + ", appScale:" +
+ appScale + ", width=" + mWidth + ", height=" + mHeight);
}
Canvas canvas;
@@ -1272,18 +1280,16 @@ public final class ViewRoot extends Handler implements ViewParent,
mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
mView.mPrivateFlags |= View.DRAWN;
- float scale = mAppScale;
if (DEBUG_DRAW) {
Context cxt = mView.getContext();
Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
- ", appScale=" + mAppScale);
+ ", metrics=" + mView.getContext().getResources().getDisplayMetrics());
}
- int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ int saveCount = canvas.save(Canvas.MATRIX_SAVE_FLAG);
try {
canvas.translate(0, -yoff);
- if (scale != 1.0f) {
- // re-scale this
- canvas.scale(scale, scale);
+ if (scalingRequired) {
+ canvas.scale(appScale, appScale);
}
mView.draw(canvas);
} finally {
@@ -1586,8 +1592,8 @@ public final class ViewRoot extends Handler implements ViewParent,
} else {
didFinish = event.getAction() == MotionEvent.ACTION_OUTSIDE;
}
- if (event != null) {
- event.scale(mAppScaleInverted);
+ if (event != null && mCompatibilityInfo.mScalingRequired) {
+ event.scale(mCompatibilityInfo.mApplicationInvertedScale);
}
try {
@@ -1709,8 +1715,9 @@ public final class ViewRoot extends Handler implements ViewParent,
if (mGlWanted && !mUseGL) {
initializeGL();
if (mGlCanvas != null) {
- mGlCanvas.setViewport((int) (mWidth * mAppScale),
- (int) (mHeight * mAppScale));
+ float appScale = mCompatibilityInfo.mApplicationScale;
+ mGlCanvas.setViewport(
+ (int) (mWidth * appScale), (int) (mHeight * appScale));
}
}
}
@@ -1914,8 +1921,8 @@ public final class ViewRoot extends Handler implements ViewParent,
} else {
didFinish = false;
}
- if (event != null) {
- event.scale(mAppScaleInverted);
+ if (event != null && mCompatibilityInfo.mScalingRequired) {
+ event.scale(mCompatibilityInfo.mApplicationInvertedScale);
}
if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event);
@@ -2345,27 +2352,59 @@ public final class ViewRoot extends Handler implements ViewParent,
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
-
boolean restore = false;
- if (params != null && mAppScale != 1.0f) {
+ float appScale = mCompatibilityInfo.mApplicationScale;
+ boolean scalingRequired = mCompatibilityInfo.mScalingRequired;
+
+ if (params != null && !mCompatibilityInfo.mExpandable) {
+ adjustWindowAttributesForCompatibleMode(params);
+ }
+ if (params != null && scalingRequired) {
restore = true;
- params.scale(mAppScale, mWindowLayoutParamsBackup);
+ params.scale(appScale, mWindowLayoutParamsBackup);
}
int relayoutResult = sWindowSession.relayout(
mWindow, params,
- (int) (mView.mMeasuredWidth * mAppScale),
- (int) (mView.mMeasuredHeight * mAppScale),
+ (int) (mView.mMeasuredWidth * appScale),
+ (int) (mView.mMeasuredHeight * appScale),
viewVisibility, insetsPending, mWinFrame,
mPendingContentInsets, mPendingVisibleInsets, mSurface);
if (restore) {
params.restore(mWindowLayoutParamsBackup);
}
-
- mPendingContentInsets.scale(mAppScaleInverted);
- mPendingVisibleInsets.scale(mAppScaleInverted);
- mWinFrame.scale(mAppScaleInverted);
+ if (scalingRequired) {
+ float invertedScale = mCompatibilityInfo.mApplicationInvertedScale;
+ mPendingContentInsets.scale(invertedScale);
+ mPendingVisibleInsets.scale(invertedScale);
+ mWinFrame.scale(invertedScale);
+ }
return relayoutResult;
}
+
+ /**
+ * Adjust the window's layout parameter for compatibility mode. It replaces FILL_PARENT
+ * with the default window size, and centers if the window wanted to fill
+ * horizontally.
+ *
+ * @param attrs the window's layout params to adjust
+ */
+ private void adjustWindowAttributesForCompatibleMode(WindowManager.LayoutParams attrs) {
+ // fix app windows only
+ if (attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
+ DisplayMetrics metrics = mView.getContext().getResources().getDisplayMetrics();
+ // TODO: improve gravity logic
+ if (attrs.width == ViewGroup.LayoutParams.FILL_PARENT) {
+ attrs.width = metrics.widthPixels;
+ attrs.gravity |= Gravity.CENTER_HORIZONTAL;
+ }
+ if (attrs.height == ViewGroup.LayoutParams.FILL_PARENT) {
+ attrs.height = metrics.heightPixels;
+ }
+ if (DEBUG_LAYOUT) {
+ Log.d(TAG, "Attributes fixed for compatibility : " + attrs);
+ }
+ }
+ }
/**
* {@inheritDoc}
@@ -2470,11 +2509,16 @@ public final class ViewRoot extends Handler implements ViewParent,
+ " visibleInsets=" + visibleInsets.toShortString()
+ " reportDraw=" + reportDraw);
Message msg = obtainMessage(reportDraw ? RESIZED_REPORT :RESIZED);
-
- coveredInsets.scale(mAppScaleInverted);
- visibleInsets.scale(mAppScaleInverted);
- msg.arg1 = (int) (w * mAppScaleInverted);
- msg.arg2 = (int) (h * mAppScaleInverted);
+ if (mCompatibilityInfo.mScalingRequired) {
+ float invertedScale = mCompatibilityInfo.mApplicationInvertedScale;
+ coveredInsets.scale(invertedScale);
+ visibleInsets.scale(invertedScale);
+ msg.arg1 = (int) (w * invertedScale);
+ msg.arg2 = (int) (h * invertedScale);
+ } else {
+ msg.arg1 = w;
+ msg.arg2 = h;
+ }
msg.obj = new Rect[] { new Rect(coveredInsets), new Rect(visibleInsets) };
sendMessage(msg);
}
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 817a56637ea4..0a2d2088992b 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -810,12 +810,19 @@
-
+ {@link #AndroidManifest manifest} tag. -->
+
+
+
+
+
+
+ - com.google.android.providers.genie/.GenieLauncher
+ - com.android.googlesearch/.GoogleSearch
+ - com.android.websearch/.Search.1
+
diff --git a/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java b/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java
index bdf67ba86ac7..a46f07dbb875 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java
@@ -361,7 +361,8 @@ public class SearchablesTest extends AndroidTestCase {
@Override
public List queryIntentActivities(Intent intent, int flags) {
assertNotNull(intent);
- assertEquals(intent.getAction(), Intent.ACTION_SEARCH);
+ assertTrue(intent.getAction().equals(Intent.ACTION_SEARCH)
+ || intent.getAction().equals(Intent.ACTION_WEB_SEARCH));
switch (mSearchablesMode) {
case SEARCHABLES_PASSTHROUGH:
return mRealPackageManager.queryIntentActivities(intent, flags);
--
cgit v1.2.3-59-g8ed1b
From 6262ae5c9df44c0673cebaeaf7f655094f5b5485 Mon Sep 17 00:00:00 2001
From: Ben Murdoch
Date: Fri, 17 Apr 2009 13:21:53 +0100
Subject: Implement handling of console messages from WebCore. Default
implementation in WebChromeClient is to do nothing.
---
core/java/android/webkit/CallbackProxy.java | 30 +++++++++++++++++++++++++++
core/java/android/webkit/WebChromeClient.java | 11 ++++++++++
core/java/android/webkit/WebViewCore.java | 10 +++++++++
3 files changed, 51 insertions(+)
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 2fb696456ced..c40704410155 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -100,6 +100,7 @@ class CallbackProxy extends Handler {
private static final int SWITCH_OUT_HISTORY = 125;
private static final int EXCEEDED_DATABASE_QUOTA = 126;
private static final int JS_TIMEOUT = 127;
+ private static final int ADD_MESSAGE_TO_CONSOLE = 128;
// Message triggered by the client to resume execution
private static final int NOTIFY = 200;
@@ -581,6 +582,13 @@ class CallbackProxy extends Handler {
case SWITCH_OUT_HISTORY:
mWebView.switchOutDrawHistory();
break;
+
+ case ADD_MESSAGE_TO_CONSOLE:
+ String message = msg.getData().getString("message");
+ String sourceID = msg.getData().getString("sourceID");
+ int lineNumber = msg.getData().getInt("lineNumber");
+ mWebChromeClient.addMessageToConsole(message, lineNumber, sourceID);
+ break;
}
}
@@ -1086,6 +1094,28 @@ class CallbackProxy extends Handler {
sendMessage(exceededQuota);
}
+ /**
+ * Called by WebViewCore when we have a message to be added to the JavaScript
+ * error console. Sends a message to the Java side with the details.
+ * @param message The message to add to the console.
+ * @param lineNumber The lineNumber of the source file on which the error
+ * occurred.
+ * @param sourceID The filename of the source file in which the error
+ * occurred.
+ * @hide pending API counsel.
+ */
+ public void addMessageToConsole(String message, int lineNumber, String sourceID) {
+ if (mWebChromeClient == null) {
+ return;
+ }
+
+ Message msg = obtainMessage(ADD_MESSAGE_TO_CONSOLE);
+ msg.getData().putString("message", message);
+ msg.getData().putString("sourceID", sourceID);
+ msg.getData().putInt("lineNumber", lineNumber);
+ sendMessage(msg);
+ }
+
/**
* @hide pending API council approval
*/
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index bd64f897fe15..754b1d90adf4 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -189,4 +189,15 @@ public class WebChromeClient {
public boolean onJsTimeout() {
return true;
}
+
+ /**
+ * Add a JavaScript error message to the console. Clients should override
+ * this to process the log message as they see fit.
+ * @param message The error message to report.
+ * @param lineNumber The line number of the error.
+ * @param sourceID The name of the source file that caused the error.
+ * @hide pending API council.
+ */
+ public void addMessageToConsole(String message, int lineNumber, String sourceID) {
+ }
}
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index de142ae5565a..f4b99b9e3bc7 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -226,6 +226,16 @@ final class WebViewCore {
return mSettings;
}
+ /**
+ * Add an error message to the client's console.
+ * @param message The message to add
+ * @param lineNumber the line on which the error occurred
+ * @param sourceID the filename of the source that caused the error.
+ */
+ protected void addMessageToConsole(String message, int lineNumber, String sourceID) {
+ mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID);
+ }
+
/**
* Invoke a javascript alert.
* @param message The message displayed in the alert.
--
cgit v1.2.3-59-g8ed1b
From 0e74aa0f7ed90d46e0bdde02bf9b7b29c6b95bd8 Mon Sep 17 00:00:00 2001
From: Satish Sampath
Date: Fri, 5 Jun 2009 15:04:32 +0100
Subject: Fix broken Searchables unit tests.
The newly added code was using methods which were not overridden by the unit test, fixed now.
---
.../com/android/unit_tests/SearchablesTest.java | 35 +++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)
diff --git a/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java b/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java
index a46f07dbb875..6b56e6ce4b62 100644
--- a/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java
+++ b/tests/AndroidTests/src/com/android/unit_tests/SearchablesTest.java
@@ -20,6 +20,7 @@ import android.app.SearchManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
@@ -306,6 +307,14 @@ public class SearchablesTest extends AndroidTestCase {
throws PackageManager.NameNotFoundException {
return mRealContext.createPackageContext(packageName, flags);
}
+
+ /**
+ * Message broadcast. Pass through for now.
+ */
+ @Override
+ public void sendBroadcast(Intent intent) {
+ mRealContext.sendBroadcast(intent);
+ }
}
/**
@@ -376,7 +385,8 @@ public class SearchablesTest extends AndroidTestCase {
@Override
public ResolveInfo resolveActivity(Intent intent, int flags) {
assertNotNull(intent);
- assertEquals(intent.getAction(), SearchManager.INTENT_ACTION_GLOBAL_SEARCH);
+ assertTrue(intent.getAction().equals(Intent.ACTION_WEB_SEARCH)
+ || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH));
switch (mSearchablesMode) {
case SEARCHABLES_PASSTHROUGH:
return mRealPackageManager.resolveActivity(intent, flags);
@@ -439,6 +449,29 @@ public class SearchablesTest extends AndroidTestCase {
throw new UnsupportedOperationException();
}
}
+
+ /**
+ * Get the activity information for a particular activity.
+ *
+ * @param name The name of the activity to find.
+ * @param flags Additional option flags.
+ *
+ * @return ActivityInfo Information about the activity, if found, else null.
+ */
+ @Override
+ public ActivityInfo getActivityInfo(ComponentName name, int flags)
+ throws NameNotFoundException {
+ assertNotNull(name);
+ MoreAsserts.assertNotEqual(name, "");
+ switch (mSearchablesMode) {
+ case SEARCHABLES_PASSTHROUGH:
+ return mRealPackageManager.getActivityInfo(name, flags);
+ case SEARCHABLES_MOCK_ZERO:
+ throw new NameNotFoundException();
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
}
}
--
cgit v1.2.3-59-g8ed1b
From 55e3d60da5626752ffe1d15150d35ccb8fa644e7 Mon Sep 17 00:00:00 2001
From: Mathias Agopian
Date: Fri, 5 Jun 2009 14:56:35 -0700
Subject: break dependency on utils/ZipEntry.h and utils/ZipFile.h, get rid of
inet_address.h and Socket.h which were not used
---
include/utils/Socket.h | 80 ---
include/utils/ZipEntry.h | 345 -----------
include/utils/ZipFile.h | 269 ---------
include/utils/inet_address.h | 103 ----
libs/utils/Android.mk | 23 +-
libs/utils/InetAddress.cpp | 236 --------
libs/utils/Socket.cpp | 388 -------------
libs/utils/ZipEntry.cpp | 696 -----------------------
libs/utils/ZipFile.cpp | 1296 -----------------------------------------
tools/aapt/AaptAssets.h | 2 +-
tools/aapt/Android.mk | 5 +-
tools/aapt/Command.cpp | 1 -
tools/aapt/Main.cpp | 1 -
tools/aapt/Main.h | 2 +-
tools/aapt/Package.cpp | 1 -
tools/aapt/ZipEntry.cpp | 696 +++++++++++++++++++++++
tools/aapt/ZipEntry.h | 345 +++++++++++
tools/aapt/ZipFile.cpp | 1297 ++++++++++++++++++++++++++++++++++++++++++
tools/aapt/ZipFile.h | 270 +++++++++
19 files changed, 2615 insertions(+), 3441 deletions(-)
delete mode 100644 include/utils/Socket.h
delete mode 100644 include/utils/ZipEntry.h
delete mode 100644 include/utils/ZipFile.h
delete mode 100644 include/utils/inet_address.h
delete mode 100644 libs/utils/InetAddress.cpp
delete mode 100644 libs/utils/Socket.cpp
delete mode 100644 libs/utils/ZipEntry.cpp
delete mode 100644 libs/utils/ZipFile.cpp
create mode 100644 tools/aapt/ZipEntry.cpp
create mode 100644 tools/aapt/ZipEntry.h
create mode 100644 tools/aapt/ZipFile.cpp
create mode 100644 tools/aapt/ZipFile.h
diff --git a/include/utils/Socket.h b/include/utils/Socket.h
deleted file mode 100644
index 8b7f40617959..000000000000
--- a/include/utils/Socket.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-// Socket class. Modeled after Java classes.
-//
-#ifndef _RUNTIME_SOCKET_H
-#define _RUNTIME_SOCKET_H
-
-#include
-#include
-
-namespace android {
-
-/*
- * Basic socket class, needed to abstract away the differences between
- * BSD sockets and WinSock. This establishes a streaming network
- * connection (TCP/IP) to somebody.
- */
-class Socket {
-public:
- Socket(void);
- ~Socket(void);
-
- // Create a connection to somewhere.
- // Return 0 on success.
- int connect(const char* host, int port);
- int connect(const InetAddress* addr, int port);
-
-
- // Close the socket. Don't try to use this object again after
- // calling this. Returns false on failure.
- bool close(void);
-
- // If we created the socket without an address, we can use these
- // to finish the connection. Returns 0 on success.
- int bind(const SocketAddress& bindPoint);
- int connect(const SocketAddress& endPoint);
-
- // Here we deviate from the traditional object-oriented fanciness
- // and just provide read/write operators instead of getters for
- // objects that abstract a stream.
- //
- // Standard read/write semantics.
- int read(void* buf, ssize_t len) const;
- int write(const void* buf, ssize_t len) const;
-
- // This must be called once, at program startup.
- static bool bootInit(void);
- static void finalShutdown(void);
-
-private:
- // Internal function that establishes a connection.
- int doConnect(const InetSocketAddress& addr);
-
- unsigned long mSock; // holds SOCKET or int
-
- static bool mBootInitialized;
-};
-
-
-// debug -- unit tests
-void TestSockets(void);
-
-}; // namespace android
-
-#endif // _RUNTIME_SOCKET_H
diff --git a/include/utils/ZipEntry.h b/include/utils/ZipEntry.h
deleted file mode 100644
index e4698dfbbc47..000000000000
--- a/include/utils/ZipEntry.h
+++ /dev/null
@@ -1,345 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Zip archive entries.
-//
-// The ZipEntry class is tightly meshed with the ZipFile class.
-//
-#ifndef __LIBS_ZIPENTRY_H
-#define __LIBS_ZIPENTRY_H
-
-#include "Errors.h"
-
-#include
-#include
-
-namespace android {
-
-class ZipFile;
-
-/*
- * ZipEntry objects represent a single entry in a Zip archive.
- *
- * You can use one of these to get or set information about an entry, but
- * there are no functions here for accessing the data itself. (We could
- * tuck a pointer to the ZipFile in here for convenience, but that raises
- * the likelihood of using ZipEntry objects after discarding the ZipFile.)
- *
- * File information is stored in two places: next to the file data (the Local
- * File Header, and possibly a Data Descriptor), and at the end of the file
- * (the Central Directory Entry). The two must be kept in sync.
- */
-class ZipEntry {
-public:
- friend class ZipFile;
-
- ZipEntry(void)
- : mDeleted(false), mMarked(false)
- {}
- ~ZipEntry(void) {}
-
- /*
- * Returns "true" if the data is compressed.
- */
- bool isCompressed(void) const {
- return mCDE.mCompressionMethod != kCompressStored;
- }
- int getCompressionMethod(void) const { return mCDE.mCompressionMethod; }
-
- /*
- * Return the uncompressed length.
- */
- off_t getUncompressedLen(void) const { return mCDE.mUncompressedSize; }
-
- /*
- * Return the compressed length. For uncompressed data, this returns
- * the same thing as getUncompresesdLen().
- */
- off_t getCompressedLen(void) const { return mCDE.mCompressedSize; }
-
- /*
- * Return the absolute file offset of the start of the compressed or
- * uncompressed data.
- */
- off_t getFileOffset(void) const {
- return mCDE.mLocalHeaderRelOffset +
- LocalFileHeader::kLFHLen +
- mLFH.mFileNameLength +
- mLFH.mExtraFieldLength;
- }
-
- /*
- * Return the data CRC.
- */
- unsigned long getCRC32(void) const { return mCDE.mCRC32; }
-
- /*
- * Return file modification time in UNIX seconds-since-epoch.
- */
- time_t getModWhen(void) const;
-
- /*
- * Return the archived file name.
- */
- const char* getFileName(void) const { return (const char*) mCDE.mFileName; }
-
- /*
- * Application-defined "mark". Can be useful when synchronizing the
- * contents of an archive with contents on disk.
- */
- bool getMarked(void) const { return mMarked; }
- void setMarked(bool val) { mMarked = val; }
-
- /*
- * Some basic functions for raw data manipulation. "LE" means
- * Little Endian.
- */
- static inline unsigned short getShortLE(const unsigned char* buf) {
- return buf[0] | (buf[1] << 8);
- }
- static inline unsigned long getLongLE(const unsigned char* buf) {
- return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
- }
- static inline void putShortLE(unsigned char* buf, short val) {
- buf[0] = (unsigned char) val;
- buf[1] = (unsigned char) (val >> 8);
- }
- static inline void putLongLE(unsigned char* buf, long val) {
- buf[0] = (unsigned char) val;
- buf[1] = (unsigned char) (val >> 8);
- buf[2] = (unsigned char) (val >> 16);
- buf[3] = (unsigned char) (val >> 24);
- }
-
- /* defined for Zip archives */
- enum {
- kCompressStored = 0, // no compression
- // shrunk = 1,
- // reduced 1 = 2,
- // reduced 2 = 3,
- // reduced 3 = 4,
- // reduced 4 = 5,
- // imploded = 6,
- // tokenized = 7,
- kCompressDeflated = 8, // standard deflate
- // Deflate64 = 9,
- // lib imploded = 10,
- // reserved = 11,
- // bzip2 = 12,
- };
-
- /*
- * Deletion flag. If set, the entry will be removed on the next
- * call to "flush".
- */
- bool getDeleted(void) const { return mDeleted; }
-
-protected:
- /*
- * Initialize the structure from the file, which is pointing at
- * our Central Directory entry.
- */
- status_t initFromCDE(FILE* fp);
-
- /*
- * Initialize the structure for a new file. We need the filename
- * and comment so that we can properly size the LFH area. The
- * filename is mandatory, the comment is optional.
- */
- void initNew(const char* fileName, const char* comment);
-
- /*
- * Initialize the structure with the contents of a ZipEntry from
- * another file.
- */
- status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry);
-
- /*
- * Add some pad bytes to the LFH. We do this by adding or resizing
- * the "extra" field.
- */
- status_t addPadding(int padding);
-
- /*
- * Set information about the data for this entry.
- */
- void setDataInfo(long uncompLen, long compLen, unsigned long crc32,
- int compressionMethod);
-
- /*
- * Set the modification date.
- */
- void setModWhen(time_t when);
-
- /*
- * Return the offset of the local file header.
- */
- off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; }
-
- /*
- * Set the offset of the local file header, relative to the start of
- * the current file.
- */
- void setLFHOffset(off_t offset) {
- mCDE.mLocalHeaderRelOffset = (long) offset;
- }
-
- /* mark for deletion; used by ZipFile::remove() */
- void setDeleted(void) { mDeleted = true; }
-
-private:
- /* these are private and not defined */
- ZipEntry(const ZipEntry& src);
- ZipEntry& operator=(const ZipEntry& src);
-
- /* returns "true" if the CDE and the LFH agree */
- bool compareHeaders(void) const;
- void copyCDEtoLFH(void);
-
- bool mDeleted; // set if entry is pending deletion
- bool mMarked; // app-defined marker
-
- /*
- * Every entry in the Zip archive starts off with one of these.
- */
- class LocalFileHeader {
- public:
- LocalFileHeader(void) :
- mVersionToExtract(0),
- mGPBitFlag(0),
- mCompressionMethod(0),
- mLastModFileTime(0),
- mLastModFileDate(0),
- mCRC32(0),
- mCompressedSize(0),
- mUncompressedSize(0),
- mFileNameLength(0),
- mExtraFieldLength(0),
- mFileName(NULL),
- mExtraField(NULL)
- {}
- virtual ~LocalFileHeader(void) {
- delete[] mFileName;
- delete[] mExtraField;
- }
-
- status_t read(FILE* fp);
- status_t write(FILE* fp);
-
- // unsigned long mSignature;
- unsigned short mVersionToExtract;
- unsigned short mGPBitFlag;
- unsigned short mCompressionMethod;
- unsigned short mLastModFileTime;
- unsigned short mLastModFileDate;
- unsigned long mCRC32;
- unsigned long mCompressedSize;
- unsigned long mUncompressedSize;
- unsigned short mFileNameLength;
- unsigned short mExtraFieldLength;
- unsigned char* mFileName;
- unsigned char* mExtraField;
-
- enum {
- kSignature = 0x04034b50,
- kLFHLen = 30, // LocalFileHdr len, excl. var fields
- };
-
- void dump(void) const;
- };
-
- /*
- * Every entry in the Zip archive has one of these in the "central
- * directory" at the end of the file.
- */
- class CentralDirEntry {
- public:
- CentralDirEntry(void) :
- mVersionMadeBy(0),
- mVersionToExtract(0),
- mGPBitFlag(0),
- mCompressionMethod(0),
- mLastModFileTime(0),
- mLastModFileDate(0),
- mCRC32(0),
- mCompressedSize(0),
- mUncompressedSize(0),
- mFileNameLength(0),
- mExtraFieldLength(0),
- mFileCommentLength(0),
- mDiskNumberStart(0),
- mInternalAttrs(0),
- mExternalAttrs(0),
- mLocalHeaderRelOffset(0),
- mFileName(NULL),
- mExtraField(NULL),
- mFileComment(NULL)
- {}
- virtual ~CentralDirEntry(void) {
- delete[] mFileName;
- delete[] mExtraField;
- delete[] mFileComment;
- }
-
- status_t read(FILE* fp);
- status_t write(FILE* fp);
-
- // unsigned long mSignature;
- unsigned short mVersionMadeBy;
- unsigned short mVersionToExtract;
- unsigned short mGPBitFlag;
- unsigned short mCompressionMethod;
- unsigned short mLastModFileTime;
- unsigned short mLastModFileDate;
- unsigned long mCRC32;
- unsigned long mCompressedSize;
- unsigned long mUncompressedSize;
- unsigned short mFileNameLength;
- unsigned short mExtraFieldLength;
- unsigned short mFileCommentLength;
- unsigned short mDiskNumberStart;
- unsigned short mInternalAttrs;
- unsigned long mExternalAttrs;
- unsigned long mLocalHeaderRelOffset;
- unsigned char* mFileName;
- unsigned char* mExtraField;
- unsigned char* mFileComment;
-
- void dump(void) const;
-
- enum {
- kSignature = 0x02014b50,
- kCDELen = 46, // CentralDirEnt len, excl. var fields
- };
- };
-
- enum {
- //kDataDescriptorSignature = 0x08074b50, // currently unused
- kDataDescriptorLen = 16, // four 32-bit fields
-
- kDefaultVersion = 20, // need deflate, nothing much else
- kDefaultMadeBy = 0x0317, // 03=UNIX, 17=spec v2.3
- kUsesDataDescr = 0x0008, // GPBitFlag bit 3
- };
-
- LocalFileHeader mLFH;
- CentralDirEntry mCDE;
-};
-
-}; // namespace android
-
-#endif // __LIBS_ZIPENTRY_H
diff --git a/include/utils/ZipFile.h b/include/utils/ZipFile.h
deleted file mode 100644
index 44df5bbaa405..000000000000
--- a/include/utils/ZipFile.h
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// General-purpose Zip archive access. This class allows both reading and
-// writing to Zip archives, including deletion of existing entries.
-//
-#ifndef __LIBS_ZIPFILE_H
-#define __LIBS_ZIPFILE_H
-
-#include "ZipEntry.h"
-#include "Vector.h"
-#include "Errors.h"
-#include
-
-namespace android {
-
-/*
- * Manipulate a Zip archive.
- *
- * Some changes will not be visible in the until until "flush" is called.
- *
- * The correct way to update a file archive is to make all changes to a
- * copy of the archive in a temporary file, and then unlink/rename over
- * the original after everything completes. Because we're only interested
- * in using this for packaging, we don't worry about such things. Crashing
- * after making changes and before flush() completes could leave us with
- * an unusable Zip archive.
- */
-class ZipFile {
-public:
- ZipFile(void)
- : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false)
- {}
- ~ZipFile(void) {
- if (!mReadOnly)
- flush();
- if (mZipFp != NULL)
- fclose(mZipFp);
- discardEntries();
- }
-
- /*
- * Open a new or existing archive.
- */
- typedef enum {
- kOpenReadOnly = 0x01,
- kOpenReadWrite = 0x02,
- kOpenCreate = 0x04, // create if it doesn't exist
- kOpenTruncate = 0x08, // if it exists, empty it
- };
- status_t open(const char* zipFileName, int flags);
-
- /*
- * Add a file to the end of the archive. Specify whether you want the
- * library to try to store it compressed.
- *
- * If "storageName" is specified, the archive will use that instead
- * of "fileName".
- *
- * If there is already an entry with the same name, the call fails.
- * Existing entries with the same name must be removed first.
- *
- * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
- */
- status_t add(const char* fileName, int compressionMethod,
- ZipEntry** ppEntry)
- {
- return add(fileName, fileName, compressionMethod, ppEntry);
- }
- status_t add(const char* fileName, const char* storageName,
- int compressionMethod, ZipEntry** ppEntry)
- {
- return addCommon(fileName, NULL, 0, storageName,
- ZipEntry::kCompressStored,
- compressionMethod, ppEntry);
- }
-
- /*
- * Add a file that is already compressed with gzip.
- *
- * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
- */
- status_t addGzip(const char* fileName, const char* storageName,
- ZipEntry** ppEntry)
- {
- return addCommon(fileName, NULL, 0, storageName,
- ZipEntry::kCompressDeflated,
- ZipEntry::kCompressDeflated, ppEntry);
- }
-
- /*
- * Add a file from an in-memory data buffer.
- *
- * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
- */
- status_t add(const void* data, size_t size, const char* storageName,
- int compressionMethod, ZipEntry** ppEntry)
- {
- return addCommon(NULL, data, size, storageName,
- ZipEntry::kCompressStored,
- compressionMethod, ppEntry);
- }
-
- /*
- * Add an entry by copying it from another zip file. If "padding" is
- * nonzero, the specified number of bytes will be added to the "extra"
- * field in the header.
- *
- * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
- */
- status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
- int padding, ZipEntry** ppEntry);
-
- /*
- * Mark an entry as having been removed. It is not actually deleted
- * from the archive or our internal data structures until flush() is
- * called.
- */
- status_t remove(ZipEntry* pEntry);
-
- /*
- * Flush changes. If mNeedCDRewrite is set, this writes the central dir.
- */
- status_t flush(void);
-
- /*
- * Expand the data into the buffer provided. The buffer must hold
- * at least bytes. Variation expands directly
- * to a file.
- *
- * Returns "false" if an error was encountered in the compressed data.
- */
- //bool uncompress(const ZipEntry* pEntry, void* buf) const;
- //bool uncompress(const ZipEntry* pEntry, FILE* fp) const;
- void* uncompress(const ZipEntry* pEntry);
-
- /*
- * Get an entry, by name. Returns NULL if not found.
- *
- * Does not return entries pending deletion.
- */
- ZipEntry* getEntryByName(const char* fileName) const;
-
- /*
- * Get the Nth entry in the archive.
- *
- * This will return an entry that is pending deletion.
- */
- int getNumEntries(void) const { return mEntries.size(); }
- ZipEntry* getEntryByIndex(int idx) const;
-
-private:
- /* these are private and not defined */
- ZipFile(const ZipFile& src);
- ZipFile& operator=(const ZipFile& src);
-
- class EndOfCentralDir {
- public:
- EndOfCentralDir(void) :
- mDiskNumber(0),
- mDiskWithCentralDir(0),
- mNumEntries(0),
- mTotalNumEntries(0),
- mCentralDirSize(0),
- mCentralDirOffset(0),
- mCommentLen(0),
- mComment(NULL)
- {}
- virtual ~EndOfCentralDir(void) {
- delete[] mComment;
- }
-
- status_t readBuf(const unsigned char* buf, int len);
- status_t write(FILE* fp);
-
- //unsigned long mSignature;
- unsigned short mDiskNumber;
- unsigned short mDiskWithCentralDir;
- unsigned short mNumEntries;
- unsigned short mTotalNumEntries;
- unsigned long mCentralDirSize;
- unsigned long mCentralDirOffset; // offset from first disk
- unsigned short mCommentLen;
- unsigned char* mComment;
-
- enum {
- kSignature = 0x06054b50,
- kEOCDLen = 22, // EndOfCentralDir len, excl. comment
-
- kMaxCommentLen = 65535, // longest possible in ushort
- kMaxEOCDSearch = kMaxCommentLen + EndOfCentralDir::kEOCDLen,
-
- };
-
- void dump(void) const;
- };
-
-
- /* read all entries in the central dir */
- status_t readCentralDir(void);
-
- /* crunch deleted entries out */
- status_t crunchArchive(void);
-
- /* clean up mEntries */
- void discardEntries(void);
-
- /* common handler for all "add" functions */
- status_t addCommon(const char* fileName, const void* data, size_t size,
- const char* storageName, int sourceType, int compressionMethod,
- ZipEntry** ppEntry);
-
- /* copy all of "srcFp" into "dstFp" */
- status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32);
- /* copy all of "data" into "dstFp" */
- status_t copyDataToFp(FILE* dstFp,
- const void* data, size_t size, unsigned long* pCRC32);
- /* copy some of "srcFp" into "dstFp" */
- status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
- unsigned long* pCRC32);
- /* like memmove(), but on parts of a single file */
- status_t filemove(FILE* fp, off_t dest, off_t src, size_t n);
- /* compress all of "srcFp" into "dstFp", using Deflate */
- status_t compressFpToFp(FILE* dstFp, FILE* srcFp,
- const void* data, size_t size, unsigned long* pCRC32);
-
- /* get modification date from a file descriptor */
- time_t getModTime(int fd);
-
- /*
- * We use stdio FILE*, which gives us buffering but makes dealing
- * with files >2GB awkward. Until we support Zip64, we're fine.
- */
- FILE* mZipFp; // Zip file pointer
-
- /* one of these per file */
- EndOfCentralDir mEOCD;
-
- /* did we open this read-only? */
- bool mReadOnly;
-
- /* set this when we trash the central dir */
- bool mNeedCDRewrite;
-
- /*
- * One ZipEntry per entry in the zip file. I'm using pointers instead
- * of objects because it's easier than making operator= work for the
- * classes and sub-classes.
- */
- Vector mEntries;
-};
-
-}; // namespace android
-
-#endif // __LIBS_ZIPFILE_H
diff --git a/include/utils/inet_address.h b/include/utils/inet_address.h
deleted file mode 100644
index dbd8672e0b0b..000000000000
--- a/include/utils/inet_address.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-// Internet address classes. Modeled after Java classes.
-//
-#ifndef _RUNTIME_INET_ADDRESS_H
-#define _RUNTIME_INET_ADDRESS_H
-
-#ifdef HAVE_ANDROID_OS
-#error DO NOT USE THIS FILE IN THE DEVICE BUILD
-#endif
-
-
-namespace android {
-
-/*
- * This class holds Internet addresses. Perhaps more useful is its
- * ability to look up addresses by name.
- *
- * Invoke one of the static factory methods to create a new object.
- */
-class InetAddress {
-public:
- virtual ~InetAddress(void);
-
- // create from w.x.y.z or foo.bar.com notation
- static InetAddress* getByName(const char* host);
-
- // copy-construction
- InetAddress(const InetAddress& orig);
-
- const void* getAddress(void) const { return mAddress; }
- int getAddressLength(void) const { return mLength; }
- const char* getHostName(void) const { return mName; }
-
-private:
- InetAddress(void);
- // assignment (private)
- InetAddress& operator=(const InetAddress& addr);
-
- // use a void* here so we don't have to expose actual socket headers
- void* mAddress; // this is really a ptr to sockaddr_in
- int mLength;
- char* mName;
-};
-
-
-/*
- * Base class for socket addresses.
- */
-class SocketAddress {
-public:
- SocketAddress() {}
- virtual ~SocketAddress() {}
-};
-
-
-/*
- * Internet address class. This combines an InetAddress with a port.
- */
-class InetSocketAddress : public SocketAddress {
-public:
- InetSocketAddress() :
- mAddress(0), mPort(-1)
- {}
- ~InetSocketAddress(void) {
- delete mAddress;
- }
-
- // Create an address with a host wildcard (useful for servers).
- bool create(int port);
- // Create an address with the specified host and port.
- bool create(const InetAddress* addr, int port);
- // Create an address with the specified host and port. Does the
- // hostname lookup.
- bool create(const char* host, int port);
-
- const InetAddress* getAddress(void) const { return mAddress; }
- const int getPort(void) const { return mPort; }
- const char* getHostName(void) const { return mAddress->getHostName(); }
-
-private:
- InetAddress* mAddress;
- int mPort;
-};
-
-}; // namespace android
-
-#endif // _RUNTIME_INET_ADDRESS_H
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 6605c9fde0ea..70d440797305 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -44,26 +44,13 @@ commonSources:= \
misc.cpp \
LogSocket.cpp
-#
-# The cpp files listed here do not belong in the device
-# build. Consult with the swetland before even thinking about
-# putting them in commonSources.
-#
-# They're used by the simulator runtime and by host-side tools like
-# aapt and the simulator front-end.
-#
-hostSources:= \
- InetAddress.cpp \
- Socket.cpp \
- ZipEntry.cpp \
- ZipFile.cpp
# For the host
# =====================================================
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= $(commonSources) $(hostSources)
+LOCAL_SRC_FILES:= $(commonSources)
ifeq ($(HOST_OS),linux)
# Use the futex based mutex and condition variable
@@ -100,10 +87,6 @@ LOCAL_SRC_FILES:= \
BackupData.cpp \
BackupHelpers.cpp
-ifeq ($(TARGET_SIMULATOR),true)
-LOCAL_SRC_FILES += $(hostSources)
-endif
-
ifeq ($(TARGET_OS),linux)
# Use the futex based mutex and condition variable
# implementation from android-arm because it's shared mem safe
@@ -130,9 +113,5 @@ endif # linux-x86
endif # sim
LOCAL_MODULE:= libutils
-
-#LOCAL_CFLAGS+=
-#LOCAL_LDFLAGS:=
-
include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/utils/InetAddress.cpp b/libs/utils/InetAddress.cpp
deleted file mode 100644
index 39a0a6839a53..000000000000
--- a/libs/utils/InetAddress.cpp
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-// Internet address class.
-//
-#ifdef HAVE_WINSOCK
-# include
-#else
-# include
-# include
-# include
-//# include
-# include
-#endif
-
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-
-using namespace android;
-
-
-/*
- * ===========================================================================
- * InetAddress
- * ===========================================================================
- */
-
-// lock for the next couple of functions; could tuck into InetAddress
-static Mutex* gGHBNLock;
-
-/*
- * Lock/unlock access to the hostent struct returned by gethostbyname().
- */
-static inline void lock_gethostbyname(void)
-{
- if (gGHBNLock == NULL)
- gGHBNLock = new Mutex;
- gGHBNLock->lock();
-}
-static inline void unlock_gethostbyname(void)
-{
- assert(gGHBNLock != NULL);
- gGHBNLock->unlock();
-}
-
-
-/*
- * Constructor -- just init members. This is private so that callers
- * are required to use getByName().
- */
-InetAddress::InetAddress(void)
- : mAddress(NULL), mLength(-1), mName(NULL)
-{
-}
-
-/*
- * Destructor -- free address storage.
- */
-InetAddress::~InetAddress(void)
-{
- delete[] (char*) mAddress;
- delete[] mName;
-}
-
-/*
- * Copy constructor.
- */
-InetAddress::InetAddress(const InetAddress& orig)
-{
- *this = orig; // use assignment code
-}
-
-/*
- * Assignment operator.
- */
-InetAddress& InetAddress::operator=(const InetAddress& addr)
-{
- // handle self-assignment
- if (this == &addr)
- return *this;
- // copy mLength and mAddress
- mLength = addr.mLength;
- if (mLength > 0) {
- mAddress = new char[mLength];
- memcpy(mAddress, addr.mAddress, mLength);
- LOG(LOG_DEBUG, "socket",
- "HEY: copied %d bytes in assignment operator\n", mLength);
- } else {
- mAddress = NULL;
- }
- // copy mName
- mName = new char[strlen(addr.mName)+1];
- strcpy(mName, addr.mName);
-
- return *this;
-}
-
-/*
- * Create a new object from a name or a dotted-number IP notation.
- *
- * Returns NULL on failure.
- */
-InetAddress*
-InetAddress::getByName(const char* host)
-{
- InetAddress* newAddr = NULL;
- struct sockaddr_in addr;
- struct hostent* he;
- DurationTimer hostTimer, lockTimer;
-
- // gethostbyname() isn't reentrant, so we need to lock things until
- // we can copy the data out.
- lockTimer.start();
- lock_gethostbyname();
- hostTimer.start();
-
- he = gethostbyname(host);
- if (he == NULL) {
- LOG(LOG_WARN, "socket", "WARNING: cannot resolve host %s\n", host);
- unlock_gethostbyname();
- return NULL;
- }
-
- memcpy(&addr.sin_addr, he->h_addr, he->h_length);
- addr.sin_family = he->h_addrtype;
- addr.sin_port = 0;
-
- // got it, unlock us
- hostTimer.stop();
- he = NULL;
- unlock_gethostbyname();
-
- lockTimer.stop();
- if ((long) lockTimer.durationUsecs() > 100000) {
- long lockTime = (long) lockTimer.durationUsecs();
- long hostTime = (long) hostTimer.durationUsecs();
- LOG(LOG_DEBUG, "socket",
- "Lookup of %s took %.3fs (gethostbyname=%.3fs lock=%.3fs)\n",
- host, lockTime / 1000000.0, hostTime / 1000000.0,
- (lockTime - hostTime) / 1000000.0);
- }
-
- // Alloc storage and copy it over.
- newAddr = new InetAddress();
- if (newAddr == NULL)
- return NULL;
-
- newAddr->mLength = sizeof(struct sockaddr_in);
- newAddr->mAddress = new char[sizeof(struct sockaddr_in)];
- if (newAddr->mAddress == NULL) {
- delete newAddr;
- return NULL;
- }
- memcpy(newAddr->mAddress, &addr, newAddr->mLength);
-
- // Keep this for debug messages.
- newAddr->mName = new char[strlen(host)+1];
- if (newAddr->mName == NULL) {
- delete newAddr;
- return NULL;
- }
- strcpy(newAddr->mName, host);
-
- return newAddr;
-}
-
-
-/*
- * ===========================================================================
- * InetSocketAddress
- * ===========================================================================
- */
-
-/*
- * Create an address with the host wildcard (INADDR_ANY).
- */
-bool InetSocketAddress::create(int port)
-{
- assert(mAddress == NULL);
-
- mAddress = InetAddress::getByName("0.0.0.0");
- if (mAddress == NULL)
- return false;
- mPort = port;
- return true;
-}
-
-/*
- * Create address with host and port specified.
- */
-bool InetSocketAddress::create(const InetAddress* addr, int port)
-{
- assert(mAddress == NULL);
-
- mAddress = new InetAddress(*addr); // make a copy
- if (mAddress == NULL)
- return false;
- mPort = port;
- return true;
-}
-
-/*
- * Create address with host and port specified.
- */
-bool InetSocketAddress::create(const char* host, int port)
-{
- assert(mAddress == NULL);
-
- mAddress = InetAddress::getByName(host);
- if (mAddress == NULL)
- return false;
- mPort = port;
- return true;
-}
-
diff --git a/libs/utils/Socket.cpp b/libs/utils/Socket.cpp
deleted file mode 100644
index 51509a30480d..000000000000
--- a/libs/utils/Socket.cpp
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Copyright (C) 2005 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.
- */
-
-//
-// Internet address class.
-//
-
-#ifdef HAVE_WINSOCK
-// This needs to come first, or Cygwin gets concerned about a potential
-// clash between WinSock and .
-# include
-#endif
-
-#include
-#include
-#include
-#include
-
-#ifndef HAVE_WINSOCK
-# include
-# include
-# include
-# include
-#endif
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-using namespace android;
-
-
-/*
- * ===========================================================================
- * Socket
- * ===========================================================================
- */
-
-#ifndef INVALID_SOCKET
-# define INVALID_SOCKET (-1)
-#endif
-#define UNDEF_SOCKET ((unsigned long) INVALID_SOCKET)
-
-/*static*/ bool Socket::mBootInitialized = false;
-
-/*
- * Extract system-dependent error code.
- */
-static inline int getSocketError(void) {
-#ifdef HAVE_WINSOCK
- return WSAGetLastError();
-#else
- return errno;
-#endif
-}
-
-/*
- * One-time initialization for socket code.
- */
-/*static*/ bool Socket::bootInit(void)
-{
-#ifdef HAVE_WINSOCK
- WSADATA wsaData;
- int err;
-
- err = WSAStartup(MAKEWORD(2, 0), &wsaData);
- if (err != 0) {
- LOG(LOG_ERROR, "socket", "Unable to start WinSock\n");
- return false;
- }
-
- LOG(LOG_INFO, "socket", "Using WinSock v%d.%d\n",
- LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion));
-#endif
-
- mBootInitialized = true;
- return true;
-}
-
-/*
- * One-time shutdown for socket code.
- */
-/*static*/ void Socket::finalShutdown(void)
-{
-#ifdef HAVE_WINSOCK
- WSACleanup();
-#endif
- mBootInitialized = false;
-}
-
-
-/*
- * Simple constructor. Allow the application to create us and then make
- * bind/connect calls.
- */
-Socket::Socket(void)
- : mSock(UNDEF_SOCKET)
-{
- if (!mBootInitialized)
- LOG(LOG_WARN, "socket", "WARNING: sockets not initialized\n");
-}
-
-/*
- * Destructor. Closes the socket and resets our storage.
- */
-Socket::~Socket(void)
-{
- close();
-}
-
-
-/*
- * Create a socket and connect to the specified host and port.
- */
-int Socket::connect(const char* host, int port)
-{
- if (mSock != UNDEF_SOCKET) {
- LOG(LOG_WARN, "socket", "Socket already connected\n");
- return -1;
- }
-
- InetSocketAddress sockAddr;
- if (!sockAddr.create(host, port))
- return -1;
-
- //return doConnect(sockAddr);
- int foo;
- foo = doConnect(sockAddr);
- return foo;
-}
-
-/*
- * Create a socket and connect to the specified host and port.
- */
-int Socket::connect(const InetAddress* addr, int port)
-{
- if (mSock != UNDEF_SOCKET) {
- LOG(LOG_WARN, "socket", "Socket already connected\n");
- return -1;
- }
-
- InetSocketAddress sockAddr;
- if (!sockAddr.create(addr, port))
- return -1;
-
- return doConnect(sockAddr);
-}
-
-/*
- * Finish creating a socket by connecting to the remote host.
- *
- * Returns 0 on success.
- */
-int Socket::doConnect(const InetSocketAddress& sockAddr)
-{
-#ifdef HAVE_WINSOCK
- SOCKET sock;
-#else
- int sock;
-#endif
- const InetAddress* addr = sockAddr.getAddress();
- int port = sockAddr.getPort();
- struct sockaddr_in inaddr;
- DurationTimer connectTimer;
-
- assert(sizeof(struct sockaddr_in) == addr->getAddressLength());
- memcpy(&inaddr, addr->getAddress(), addr->getAddressLength());
- inaddr.sin_port = htons(port);
-
- //fprintf(stderr, "--- connecting to %s:%d\n",
- // sockAddr.getHostName(), port);
-
- sock = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (sock == INVALID_SOCKET) {
- int err = getSocketError();
- LOG(LOG_ERROR, "socket", "Unable to create socket (err=%d)\n", err);
- return (err != 0) ? err : -1;
- }
-
- connectTimer.start();
-
- if (::connect(sock, (struct sockaddr*) &inaddr, sizeof(inaddr)) != 0) {
- int err = getSocketError();
- LOG(LOG_WARN, "socket", "Connect to %s:%d failed: %d\n",
- sockAddr.getHostName(), port, err);
- return (err != 0) ? err : -1;
- }
-
- connectTimer.stop();
- if ((long) connectTimer.durationUsecs() > 100000) {
- LOG(LOG_INFO, "socket",
- "Connect to %s:%d took %.3fs\n", sockAddr.getHostName(),
- port, ((long) connectTimer.durationUsecs()) / 1000000.0);
- }
-
- mSock = (unsigned long) sock;
- LOG(LOG_VERBOSE, "socket",
- "--- connected to %s:%d\n", sockAddr.getHostName(), port);
- return 0;
-}
-
-
-/*
- * Close the socket if it needs closing.
- */
-bool Socket::close(void)
-{
- if (mSock != UNDEF_SOCKET) {
- //fprintf(stderr, "--- closing socket %lu\n", mSock);
-#ifdef HAVE_WINSOCK
- if (::closesocket((SOCKET) mSock) != 0)
- return false;
-#else
- if (::close((int) mSock) != 0)
- return false;
-#endif
- }
-
- mSock = UNDEF_SOCKET;
-
- return true;
-}
-
-/*
- * Read data from socket.
- *
- * Standard semantics: read up to "len" bytes into "buf". Returns the
- * number of bytes read, or less than zero on error.
- */
-int Socket::read(void* buf, ssize_t len) const
-{
- if (mSock == UNDEF_SOCKET) {
- LOG(LOG_ERROR, "socket", "ERROR: read on invalid socket\n");
- return -500;
- }
-
-#ifdef HAVE_WINSOCK
- SOCKET sock = (SOCKET) mSock;
-#else
- int sock = (int) mSock;
-#endif
- int cc;
-
- cc = recv(sock, (char*)buf, len, 0);
- if (cc < 0) {
- int err = getSocketError();
- return (err > 0) ? -err : -1;
- }
-
- return cc;
-}
-
-/*
- * Write data to a socket.
- *
- * Standard semantics: write up to "len" bytes into "buf". Returns the
- * number of bytes written, or less than zero on error.
- */
-int Socket::write(const void* buf, ssize_t len) const
-{
- if (mSock == UNDEF_SOCKET) {
- LOG(LOG_ERROR, "socket", "ERROR: write on invalid socket\n");
- return -500;
- }
-
-#ifdef HAVE_WINSOCK
- SOCKET sock = (SOCKET) mSock;
-#else
- int sock = (int) mSock;
-#endif
- int cc;
-
- cc = send(sock, (const char*)buf, len, 0);
- if (cc < 0) {
- int err = getSocketError();
- return (err > 0) ? -err : -1;
- }
-
- return cc;
-}
-
-
-/*
- * ===========================================================================
- * Socket tests
- * ===========================================================================
- */
-
-/*
- * Read all data from the socket. The data is read into a buffer that
- * expands as needed.
- *
- * On exit, the buffer is returned, and the length of the data is stored
- * in "*sz". A null byte is added to the end, but is not included in
- * the length.
- */
-static char* socketReadAll(const Socket& s, int *sz)
-{
- int max, r;
- char *data, *ptr, *tmp;
-
- data = (char*) malloc(max = 32768);
- if (data == NULL)
- return NULL;
-
- ptr = data;
-
- for (;;) {
- if ((ptr - data) == max) {
- tmp = (char*) realloc(data, max *= 2);
- if(tmp == 0) {
- free(data);
- return 0;
- }
- }
- r = s.read(ptr, max - (ptr - data));
- if (r == 0)
- break;
- if (r < 0) {
- LOG(LOG_WARN, "socket", "WARNING: socket read failed (res=%d)\n",r);
- break;
- }
- ptr += r;
- }
-
- if ((ptr - data) == max) {
- tmp = (char*) realloc(data, max + 1);
- if (tmp == NULL) {
- free(data);
- return NULL;
- }
- }
- *ptr = '\0';
- *sz = (ptr - data);
- return data;
-}
-
-/*
- * Exercise the Socket class.
- */
-void android::TestSockets(void)
-{
- printf("----- SOCKET TEST ------\n");
- Socket::bootInit();
-
- char* buf = NULL;
- int len, cc;
- const char* kTestStr =
- "GET / HTTP/1.0\n"
- "Connection: close\n"
- "\n";
-
- Socket sock;
- if (sock.connect("www.google.com", 80) != 0) {
- fprintf(stderr, "socket connected failed\n");
- goto bail;
- }
-
- cc = sock.write(kTestStr, strlen(kTestStr));
- if (cc != (int) strlen(kTestStr)) {
- fprintf(stderr, "write failed, res=%d\n", cc);
- goto bail;
- }
- buf = socketReadAll(sock, &len);
-
- printf("GOT '%s'\n", buf);
-
-bail:
- sock.close();
- free(buf);
-}
-
diff --git a/libs/utils/ZipEntry.cpp b/libs/utils/ZipEntry.cpp
deleted file mode 100644
index 96f9fc4d692f..000000000000
--- a/libs/utils/ZipEntry.cpp
+++ /dev/null
@@ -1,696 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Access to entries in a Zip archive.
-//
-
-#define LOG_TAG "zip"
-
-#include
-#include
-
-#include
-#include
-#include
-
-using namespace android;
-
-/*
- * Initialize a new ZipEntry structure from a FILE* positioned at a
- * CentralDirectoryEntry.
- *
- * On exit, the file pointer will be at the start of the next CDE or
- * at the EOCD.
- */
-status_t ZipEntry::initFromCDE(FILE* fp)
-{
- status_t result;
- long posn;
- bool hasDD;
-
- //LOGV("initFromCDE ---\n");
-
- /* read the CDE */
- result = mCDE.read(fp);
- if (result != NO_ERROR) {
- LOGD("mCDE.read failed\n");
- return result;
- }
-
- //mCDE.dump();
-
- /* using the info in the CDE, go load up the LFH */
- posn = ftell(fp);
- if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
- LOGD("local header seek failed (%ld)\n",
- mCDE.mLocalHeaderRelOffset);
- return UNKNOWN_ERROR;
- }
-
- result = mLFH.read(fp);
- if (result != NO_ERROR) {
- LOGD("mLFH.read failed\n");
- return result;
- }
-
- if (fseek(fp, posn, SEEK_SET) != 0)
- return UNKNOWN_ERROR;
-
- //mLFH.dump();
-
- /*
- * We *might* need to read the Data Descriptor at this point and
- * integrate it into the LFH. If this bit is set, the CRC-32,
- * compressed size, and uncompressed size will be zero. In practice
- * these seem to be rare.
- */
- hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;
- if (hasDD) {
- // do something clever
- //LOGD("+++ has data descriptor\n");
- }
-
- /*
- * Sanity-check the LFH. Note that this will fail if the "kUsesDataDescr"
- * flag is set, because the LFH is incomplete. (Not a problem, since we
- * prefer the CDE values.)
- */
- if (!hasDD && !compareHeaders()) {
- LOGW("WARNING: header mismatch\n");
- // keep going?
- }
-
- /*
- * If the mVersionToExtract is greater than 20, we may have an
- * issue unpacking the record -- could be encrypted, compressed
- * with something we don't support, or use Zip64 extensions. We
- * can defer worrying about that to when we're extracting data.
- */
-
- return NO_ERROR;
-}
-
-/*
- * Initialize a new entry. Pass in the file name and an optional comment.
- *
- * Initializes the CDE and the LFH.
- */
-void ZipEntry::initNew(const char* fileName, const char* comment)
-{
- assert(fileName != NULL && *fileName != '\0'); // name required
-
- /* most fields are properly initialized by constructor */
- mCDE.mVersionMadeBy = kDefaultMadeBy;
- mCDE.mVersionToExtract = kDefaultVersion;
- mCDE.mCompressionMethod = kCompressStored;
- mCDE.mFileNameLength = strlen(fileName);
- if (comment != NULL)
- mCDE.mFileCommentLength = strlen(comment);
- mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does
-
- if (mCDE.mFileNameLength > 0) {
- mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
- strcpy((char*) mCDE.mFileName, fileName);
- }
- if (mCDE.mFileCommentLength > 0) {
- /* TODO: stop assuming null-terminated ASCII here? */
- mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
- strcpy((char*) mCDE.mFileComment, comment);
- }
-
- copyCDEtoLFH();
-}
-
-/*
- * Initialize a new entry, starting with the ZipEntry from a different
- * archive.
- *
- * Initializes the CDE and the LFH.
- */
-status_t ZipEntry::initFromExternal(const ZipFile* pZipFile,
- const ZipEntry* pEntry)
-{
- /*
- * Copy everything in the CDE over, then fix up the hairy bits.
- */
- memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE));
-
- if (mCDE.mFileNameLength > 0) {
- mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
- if (mCDE.mFileName == NULL)
- return NO_MEMORY;
- strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName);
- }
- if (mCDE.mFileCommentLength > 0) {
- mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
- if (mCDE.mFileComment == NULL)
- return NO_MEMORY;
- strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment);
- }
- if (mCDE.mExtraFieldLength > 0) {
- /* we null-terminate this, though it may not be a string */
- mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1];
- if (mCDE.mExtraField == NULL)
- return NO_MEMORY;
- memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField,
- mCDE.mExtraFieldLength+1);
- }
-
- /* construct the LFH from the CDE */
- copyCDEtoLFH();
-
- /*
- * The LFH "extra" field is independent of the CDE "extra", so we
- * handle it here.
- */
- assert(mLFH.mExtraField == NULL);
- mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
- if (mLFH.mExtraFieldLength > 0) {
- mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1];
- if (mLFH.mExtraField == NULL)
- return NO_MEMORY;
- memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
- mLFH.mExtraFieldLength+1);
- }
-
- return NO_ERROR;
-}
-
-/*
- * Insert pad bytes in the LFH by tweaking the "extra" field. This will
- * potentially confuse something that put "extra" data in here earlier,
- * but I can't find an actual problem.
- */
-status_t ZipEntry::addPadding(int padding)
-{
- if (padding <= 0)
- return INVALID_OPERATION;
-
- //LOGI("HEY: adding %d pad bytes to existing %d in %s\n",
- // padding, mLFH.mExtraFieldLength, mCDE.mFileName);
-
- if (mLFH.mExtraFieldLength > 0) {
- /* extend existing field */
- unsigned char* newExtra;
-
- newExtra = new unsigned char[mLFH.mExtraFieldLength + padding];
- if (newExtra == NULL)
- return NO_MEMORY;
- memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
- memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
-
- delete[] mLFH.mExtraField;
- mLFH.mExtraField = newExtra;
- mLFH.mExtraFieldLength += padding;
- } else {
- /* create new field */
- mLFH.mExtraField = new unsigned char[padding];
- memset(mLFH.mExtraField, 0, padding);
- mLFH.mExtraFieldLength = padding;
- }
-
- return NO_ERROR;
-}
-
-/*
- * Set the fields in the LFH equal to the corresponding fields in the CDE.
- *
- * This does not touch the LFH "extra" field.
- */
-void ZipEntry::copyCDEtoLFH(void)
-{
- mLFH.mVersionToExtract = mCDE.mVersionToExtract;
- mLFH.mGPBitFlag = mCDE.mGPBitFlag;
- mLFH.mCompressionMethod = mCDE.mCompressionMethod;
- mLFH.mLastModFileTime = mCDE.mLastModFileTime;
- mLFH.mLastModFileDate = mCDE.mLastModFileDate;
- mLFH.mCRC32 = mCDE.mCRC32;
- mLFH.mCompressedSize = mCDE.mCompressedSize;
- mLFH.mUncompressedSize = mCDE.mUncompressedSize;
- mLFH.mFileNameLength = mCDE.mFileNameLength;
- // the "extra field" is independent
-
- delete[] mLFH.mFileName;
- if (mLFH.mFileNameLength > 0) {
- mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1];
- strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
- } else {
- mLFH.mFileName = NULL;
- }
-}
-
-/*
- * Set some information about a file after we add it.
- */
-void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32,
- int compressionMethod)
-{
- mCDE.mCompressionMethod = compressionMethod;
- mCDE.mCRC32 = crc32;
- mCDE.mCompressedSize = compLen;
- mCDE.mUncompressedSize = uncompLen;
- mCDE.mCompressionMethod = compressionMethod;
- if (compressionMethod == kCompressDeflated) {
- mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used
- }
- copyCDEtoLFH();
-}
-
-/*
- * See if the data in mCDE and mLFH match up. This is mostly useful for
- * debugging these classes, but it can be used to identify damaged
- * archives.
- *
- * Returns "false" if they differ.
- */
-bool ZipEntry::compareHeaders(void) const
-{
- if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
- LOGV("cmp: VersionToExtract\n");
- return false;
- }
- if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
- LOGV("cmp: GPBitFlag\n");
- return false;
- }
- if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
- LOGV("cmp: CompressionMethod\n");
- return false;
- }
- if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
- LOGV("cmp: LastModFileTime\n");
- return false;
- }
- if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
- LOGV("cmp: LastModFileDate\n");
- return false;
- }
- if (mCDE.mCRC32 != mLFH.mCRC32) {
- LOGV("cmp: CRC32\n");
- return false;
- }
- if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
- LOGV("cmp: CompressedSize\n");
- return false;
- }
- if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
- LOGV("cmp: UncompressedSize\n");
- return false;
- }
- if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
- LOGV("cmp: FileNameLength\n");
- return false;
- }
-#if 0 // this seems to be used for padding, not real data
- if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
- LOGV("cmp: ExtraFieldLength\n");
- return false;
- }
-#endif
- if (mCDE.mFileName != NULL) {
- if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
- LOGV("cmp: FileName\n");
- return false;
- }
- }
-
- return true;
-}
-
-
-/*
- * Convert the DOS date/time stamp into a UNIX time stamp.
- */
-time_t ZipEntry::getModWhen(void) const
-{
- struct tm parts;
-
- parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
- parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
- parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
- parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
- parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
- parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
- parts.tm_wday = parts.tm_yday = 0;
- parts.tm_isdst = -1; // DST info "not available"
-
- return mktime(&parts);
-}
-
-/*
- * Set the CDE/LFH timestamp from UNIX time.
- */
-void ZipEntry::setModWhen(time_t when)
-{
-#ifdef HAVE_LOCALTIME_R
- struct tm tmResult;
-#endif
- time_t even;
- unsigned short zdate, ztime;
-
- struct tm* ptm;
-
- /* round up to an even number of seconds */
- even = (time_t)(((unsigned long)(when) + 1) & (~1));
-
- /* expand */
-#ifdef HAVE_LOCALTIME_R
- ptm = localtime_r(&even, &tmResult);
-#else
- ptm = localtime(&even);
-#endif
-
- int year;
- year = ptm->tm_year;
- if (year < 80)
- year = 80;
-
- zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
- ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
-
- mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
- mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
-}
-
-
-/*
- * ===========================================================================
- * ZipEntry::LocalFileHeader
- * ===========================================================================
- */
-
-/*
- * Read a local file header.
- *
- * On entry, "fp" points to the signature at the start of the header.
- * On exit, "fp" points to the start of data.
- */
-status_t ZipEntry::LocalFileHeader::read(FILE* fp)
-{
- status_t result = NO_ERROR;
- unsigned char buf[kLFHLen];
-
- assert(mFileName == NULL);
- assert(mExtraField == NULL);
-
- if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
- LOGD("whoops: didn't find expected signature\n");
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
- mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
- mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
- mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
- mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
- mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
- mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
- mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
- mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
- mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
-
- // TODO: validate sizes
-
- /* grab filename */
- if (mFileNameLength != 0) {
- mFileName = new unsigned char[mFileNameLength+1];
- if (mFileName == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
- if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
- mFileName[mFileNameLength] = '\0';
- }
-
- /* grab extra field */
- if (mExtraFieldLength != 0) {
- mExtraField = new unsigned char[mExtraFieldLength+1];
- if (mExtraField == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
- if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
- mExtraField[mExtraFieldLength] = '\0';
- }
-
-bail:
- return result;
-}
-
-/*
- * Write a local file header.
- */
-status_t ZipEntry::LocalFileHeader::write(FILE* fp)
-{
- unsigned char buf[kLFHLen];
-
- ZipEntry::putLongLE(&buf[0x00], kSignature);
- ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
- ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
- ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
- ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
- ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
- ZipEntry::putLongLE(&buf[0x0e], mCRC32);
- ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
- ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
- ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
- ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
-
- if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
- return UNKNOWN_ERROR;
-
- /* write filename */
- if (mFileNameLength != 0) {
- if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
- return UNKNOWN_ERROR;
- }
-
- /* write "extra field" */
- if (mExtraFieldLength != 0) {
- if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
- return UNKNOWN_ERROR;
- }
-
- return NO_ERROR;
-}
-
-
-/*
- * Dump the contents of a LocalFileHeader object.
- */
-void ZipEntry::LocalFileHeader::dump(void) const
-{
- LOGD(" LocalFileHeader contents:\n");
- LOGD(" versToExt=%u gpBits=0x%04x compression=%u\n",
- mVersionToExtract, mGPBitFlag, mCompressionMethod);
- LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
- mLastModFileTime, mLastModFileDate, mCRC32);
- LOGD(" compressedSize=%lu uncompressedSize=%lu\n",
- mCompressedSize, mUncompressedSize);
- LOGD(" filenameLen=%u extraLen=%u\n",
- mFileNameLength, mExtraFieldLength);
- if (mFileName != NULL)
- LOGD(" filename: '%s'\n", mFileName);
-}
-
-
-/*
- * ===========================================================================
- * ZipEntry::CentralDirEntry
- * ===========================================================================
- */
-
-/*
- * Read the central dir entry that appears next in the file.
- *
- * On entry, "fp" should be positioned on the signature bytes for the
- * entry. On exit, "fp" will point at the signature word for the next
- * entry or for the EOCD.
- */
-status_t ZipEntry::CentralDirEntry::read(FILE* fp)
-{
- status_t result = NO_ERROR;
- unsigned char buf[kCDELen];
-
- /* no re-use */
- assert(mFileName == NULL);
- assert(mExtraField == NULL);
- assert(mFileComment == NULL);
-
- if (fread(buf, 1, kCDELen, fp) != kCDELen) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
- LOGD("Whoops: didn't find expected signature\n");
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
- mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
- mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
- mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
- mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
- mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
- mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
- mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
- mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
- mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
- mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
- mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
- mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
- mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
- mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
- mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
-
- // TODO: validate sizes and offsets
-
- /* grab filename */
- if (mFileNameLength != 0) {
- mFileName = new unsigned char[mFileNameLength+1];
- if (mFileName == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
- if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
- mFileName[mFileNameLength] = '\0';
- }
-
- /* read "extra field" */
- if (mExtraFieldLength != 0) {
- mExtraField = new unsigned char[mExtraFieldLength+1];
- if (mExtraField == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
- if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
- mExtraField[mExtraFieldLength] = '\0';
- }
-
-
- /* grab comment, if any */
- if (mFileCommentLength != 0) {
- mFileComment = new unsigned char[mFileCommentLength+1];
- if (mFileComment == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
- if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
- {
- result = UNKNOWN_ERROR;
- goto bail;
- }
- mFileComment[mFileCommentLength] = '\0';
- }
-
-bail:
- return result;
-}
-
-/*
- * Write a central dir entry.
- */
-status_t ZipEntry::CentralDirEntry::write(FILE* fp)
-{
- unsigned char buf[kCDELen];
-
- ZipEntry::putLongLE(&buf[0x00], kSignature);
- ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
- ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
- ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
- ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
- ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
- ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
- ZipEntry::putLongLE(&buf[0x10], mCRC32);
- ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
- ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
- ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
- ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
- ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
- ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
- ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
- ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
- ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
-
- if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
- return UNKNOWN_ERROR;
-
- /* write filename */
- if (mFileNameLength != 0) {
- if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
- return UNKNOWN_ERROR;
- }
-
- /* write "extra field" */
- if (mExtraFieldLength != 0) {
- if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
- return UNKNOWN_ERROR;
- }
-
- /* write comment */
- if (mFileCommentLength != 0) {
- if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
- return UNKNOWN_ERROR;
- }
-
- return NO_ERROR;
-}
-
-/*
- * Dump the contents of a CentralDirEntry object.
- */
-void ZipEntry::CentralDirEntry::dump(void) const
-{
- LOGD(" CentralDirEntry contents:\n");
- LOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n",
- mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
- LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
- mLastModFileTime, mLastModFileDate, mCRC32);
- LOGD(" compressedSize=%lu uncompressedSize=%lu\n",
- mCompressedSize, mUncompressedSize);
- LOGD(" filenameLen=%u extraLen=%u commentLen=%u\n",
- mFileNameLength, mExtraFieldLength, mFileCommentLength);
- LOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n",
- mDiskNumberStart, mInternalAttrs, mExternalAttrs,
- mLocalHeaderRelOffset);
-
- if (mFileName != NULL)
- LOGD(" filename: '%s'\n", mFileName);
- if (mFileComment != NULL)
- LOGD(" comment: '%s'\n", mFileComment);
-}
-
diff --git a/libs/utils/ZipFile.cpp b/libs/utils/ZipFile.cpp
deleted file mode 100644
index eaa0b208cbfd..000000000000
--- a/libs/utils/ZipFile.cpp
+++ /dev/null
@@ -1,1296 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//
-// Access to Zip archives.
-//
-
-#define LOG_TAG "zip"
-
-#include
-#include
-#include
-
-#include
-#define DEF_MEM_LEVEL 8 // normally in zutil.h?
-
-#include
-#include
-#include
-#include
-
-using namespace android;
-
-/*
- * Some environments require the "b", some choke on it.
- */
-#define FILE_OPEN_RO "rb"
-#define FILE_OPEN_RW "r+b"
-#define FILE_OPEN_RW_CREATE "w+b"
-
-/* should live somewhere else? */
-static status_t errnoToStatus(int err)
-{
- if (err == ENOENT)
- return NAME_NOT_FOUND;
- else if (err == EACCES)
- return PERMISSION_DENIED;
- else
- return UNKNOWN_ERROR;
-}
-
-/*
- * Open a file and parse its guts.
- */
-status_t ZipFile::open(const char* zipFileName, int flags)
-{
- bool newArchive = false;
-
- assert(mZipFp == NULL); // no reopen
-
- if ((flags & kOpenTruncate))
- flags |= kOpenCreate; // trunc implies create
-
- if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite))
- return INVALID_OPERATION; // not both
- if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite)))
- return INVALID_OPERATION; // not neither
- if ((flags & kOpenCreate) && !(flags & kOpenReadWrite))
- return INVALID_OPERATION; // create requires write
-
- if (flags & kOpenTruncate) {
- newArchive = true;
- } else {
- newArchive = (access(zipFileName, F_OK) != 0);
- if (!(flags & kOpenCreate) && newArchive) {
- /* not creating, must already exist */
- LOGD("File %s does not exist", zipFileName);
- return NAME_NOT_FOUND;
- }
- }
-
- /* open the file */
- const char* openflags;
- if (flags & kOpenReadWrite) {
- if (newArchive)
- openflags = FILE_OPEN_RW_CREATE;
- else
- openflags = FILE_OPEN_RW;
- } else {
- openflags = FILE_OPEN_RO;
- }
- mZipFp = fopen(zipFileName, openflags);
- if (mZipFp == NULL) {
- int err = errno;
- LOGD("fopen failed: %d\n", err);
- return errnoToStatus(err);
- }
-
- status_t result;
- if (!newArchive) {
- /*
- * Load the central directory. If that fails, then this probably
- * isn't a Zip archive.
- */
- result = readCentralDir();
- } else {
- /*
- * Newly-created. The EndOfCentralDir constructor actually
- * sets everything to be the way we want it (all zeroes). We
- * set mNeedCDRewrite so that we create *something* if the
- * caller doesn't add any files. (We could also just unlink
- * the file if it's brand new and nothing was added, but that's
- * probably doing more than we really should -- the user might
- * have a need for empty zip files.)
- */
- mNeedCDRewrite = true;
- result = NO_ERROR;
- }
-
- if (flags & kOpenReadOnly)
- mReadOnly = true;
- else
- assert(!mReadOnly);
-
- return result;
-}
-
-/*
- * Return the Nth entry in the archive.
- */
-ZipEntry* ZipFile::getEntryByIndex(int idx) const
-{
- if (idx < 0 || idx >= (int) mEntries.size())
- return NULL;
-
- return mEntries[idx];
-}
-
-/*
- * Find an entry by name.
- */
-ZipEntry* ZipFile::getEntryByName(const char* fileName) const
-{
- /*
- * Do a stupid linear string-compare search.
- *
- * There are various ways to speed this up, especially since it's rare
- * to intermingle changes to the archive with "get by name" calls. We
- * don't want to sort the mEntries vector itself, however, because
- * it's used to recreate the Central Directory.
- *
- * (Hash table works, parallel list of pointers in sorted order is good.)
- */
- int idx;
-
- for (idx = mEntries.size()-1; idx >= 0; idx--) {
- ZipEntry* pEntry = mEntries[idx];
- if (!pEntry->getDeleted() &&
- strcmp(fileName, pEntry->getFileName()) == 0)
- {
- return pEntry;
- }
- }
-
- return NULL;
-}
-
-/*
- * Empty the mEntries vector.
- */
-void ZipFile::discardEntries(void)
-{
- int count = mEntries.size();
-
- while (--count >= 0)
- delete mEntries[count];
-
- mEntries.clear();
-}
-
-
-/*
- * Find the central directory and read the contents.
- *
- * The fun thing about ZIP archives is that they may or may not be
- * readable from start to end. In some cases, notably for archives
- * that were written to stdout, the only length information is in the
- * central directory at the end of the file.
- *
- * Of course, the central directory can be followed by a variable-length
- * comment field, so we have to scan through it backwards. The comment
- * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff
- * itself, plus apparently sometimes people throw random junk on the end
- * just for the fun of it.
- *
- * This is all a little wobbly. If the wrong value ends up in the EOCD
- * area, we're hosed. This appears to be the way that everbody handles
- * it though, so we're in pretty good company if this fails.
- */
-status_t ZipFile::readCentralDir(void)
-{
- status_t result = NO_ERROR;
- unsigned char* buf = NULL;
- off_t fileLength, seekStart;
- long readAmount;
- int i;
-
- fseek(mZipFp, 0, SEEK_END);
- fileLength = ftell(mZipFp);
- rewind(mZipFp);
-
- /* too small to be a ZIP archive? */
- if (fileLength < EndOfCentralDir::kEOCDLen) {
- LOGD("Length is %ld -- too small\n", (long)fileLength);
- result = INVALID_OPERATION;
- goto bail;
- }
-
- buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch];
- if (buf == NULL) {
- LOGD("Failure allocating %d bytes for EOCD search",
- EndOfCentralDir::kMaxEOCDSearch);
- result = NO_MEMORY;
- goto bail;
- }
-
- if (fileLength > EndOfCentralDir::kMaxEOCDSearch) {
- seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch;
- readAmount = EndOfCentralDir::kMaxEOCDSearch;
- } else {
- seekStart = 0;
- readAmount = (long) fileLength;
- }
- if (fseek(mZipFp, seekStart, SEEK_SET) != 0) {
- LOGD("Failure seeking to end of zip at %ld", (long) seekStart);
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- /* read the last part of the file into the buffer */
- if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) {
- LOGD("short file? wanted %ld\n", readAmount);
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- /* find the end-of-central-dir magic */
- for (i = readAmount - 4; i >= 0; i--) {
- if (buf[i] == 0x50 &&
- ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature)
- {
- LOGV("+++ Found EOCD at buf+%d\n", i);
- break;
- }
- }
- if (i < 0) {
- LOGD("EOCD not found, not Zip\n");
- result = INVALID_OPERATION;
- goto bail;
- }
-
- /* extract eocd values */
- result = mEOCD.readBuf(buf + i, readAmount - i);
- if (result != NO_ERROR) {
- LOGD("Failure reading %ld bytes of EOCD values", readAmount - i);
- goto bail;
- }
- //mEOCD.dump();
-
- if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 ||
- mEOCD.mNumEntries != mEOCD.mTotalNumEntries)
- {
- LOGD("Archive spanning not supported\n");
- result = INVALID_OPERATION;
- goto bail;
- }
-
- /*
- * So far so good. "mCentralDirSize" is the size in bytes of the
- * central directory, so we can just seek back that far to find it.
- * We can also seek forward mCentralDirOffset bytes from the
- * start of the file.
- *
- * We're not guaranteed to have the rest of the central dir in the
- * buffer, nor are we guaranteed that the central dir will have any
- * sort of convenient size. We need to skip to the start of it and
- * read the header, then the other goodies.
- *
- * The only thing we really need right now is the file comment, which
- * we're hoping to preserve.
- */
- if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
- LOGD("Failure seeking to central dir offset %ld\n",
- mEOCD.mCentralDirOffset);
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- /*
- * Loop through and read the central dir entries.
- */
- LOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries);
- int entry;
- for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) {
- ZipEntry* pEntry = new ZipEntry;
-
- result = pEntry->initFromCDE(mZipFp);
- if (result != NO_ERROR) {
- LOGD("initFromCDE failed\n");
- delete pEntry;
- goto bail;
- }
-
- mEntries.add(pEntry);
- }
-
-
- /*
- * If all went well, we should now be back at the EOCD.
- */
- {
- unsigned char checkBuf[4];
- if (fread(checkBuf, 1, 4, mZipFp) != 4) {
- LOGD("EOCD check read failed\n");
- result = INVALID_OPERATION;
- goto bail;
- }
- if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) {
- LOGD("EOCD read check failed\n");
- result = UNKNOWN_ERROR;
- goto bail;
- }
- LOGV("+++ EOCD read check passed\n");
- }
-
-bail:
- delete[] buf;
- return result;
-}
-
-
-/*
- * Add a new file to the archive.
- *
- * This requires creating and populating a ZipEntry structure, and copying
- * the data into the file at the appropriate position. The "appropriate
- * position" is the current location of the central directory, which we
- * casually overwrite (we can put it back later).
- *
- * If we were concerned about safety, we would want to make all changes
- * in a temp file and then overwrite the original after everything was
- * safely written. Not really a concern for us.
- */
-status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size,
- const char* storageName, int sourceType, int compressionMethod,
- ZipEntry** ppEntry)
-{
- ZipEntry* pEntry = NULL;
- status_t result = NO_ERROR;
- long lfhPosn, startPosn, endPosn, uncompressedLen;
- FILE* inputFp = NULL;
- unsigned long crc;
- time_t modWhen;
-
- if (mReadOnly)
- return INVALID_OPERATION;
-
- assert(compressionMethod == ZipEntry::kCompressDeflated ||
- compressionMethod == ZipEntry::kCompressStored);
-
- /* make sure we're in a reasonable state */
- assert(mZipFp != NULL);
- assert(mEntries.size() == mEOCD.mTotalNumEntries);
-
- /* make sure it doesn't already exist */
- if (getEntryByName(storageName) != NULL)
- return ALREADY_EXISTS;
-
- if (!data) {
- inputFp = fopen(fileName, FILE_OPEN_RO);
- if (inputFp == NULL)
- return errnoToStatus(errno);
- }
-
- if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- pEntry = new ZipEntry;
- pEntry->initNew(storageName, NULL);
-
- /*
- * From here on out, failures are more interesting.
- */
- mNeedCDRewrite = true;
-
- /*
- * Write the LFH, even though it's still mostly blank. We need it
- * as a place-holder. In theory the LFH isn't necessary, but in
- * practice some utilities demand it.
- */
- lfhPosn = ftell(mZipFp);
- pEntry->mLFH.write(mZipFp);
- startPosn = ftell(mZipFp);
-
- /*
- * Copy the data in, possibly compressing it as we go.
- */
- if (sourceType == ZipEntry::kCompressStored) {
- if (compressionMethod == ZipEntry::kCompressDeflated) {
- bool failed = false;
- result = compressFpToFp(mZipFp, inputFp, data, size, &crc);
- if (result != NO_ERROR) {
- LOGD("compression failed, storing\n");
- failed = true;
- } else {
- /*
- * Make sure it has compressed "enough". This probably ought
- * to be set through an API call, but I don't expect our
- * criteria to change over time.
- */
- long src = inputFp ? ftell(inputFp) : size;
- long dst = ftell(mZipFp) - startPosn;
- if (dst + (dst / 10) > src) {
- LOGD("insufficient compression (src=%ld dst=%ld), storing\n",
- src, dst);
- failed = true;
- }
- }
-
- if (failed) {
- compressionMethod = ZipEntry::kCompressStored;
- if (inputFp) rewind(inputFp);
- fseek(mZipFp, startPosn, SEEK_SET);
- /* fall through to kCompressStored case */
- }
- }
- /* handle "no compression" request, or failed compression from above */
- if (compressionMethod == ZipEntry::kCompressStored) {
- if (inputFp) {
- result = copyFpToFp(mZipFp, inputFp, &crc);
- } else {
- result = copyDataToFp(mZipFp, data, size, &crc);
- }
- if (result != NO_ERROR) {
- // don't need to truncate; happens in CDE rewrite
- LOGD("failed copying data in\n");
- goto bail;
- }
- }
-
- // currently seeked to end of file
- uncompressedLen = inputFp ? ftell(inputFp) : size;
- } else if (sourceType == ZipEntry::kCompressDeflated) {
- /* we should support uncompressed-from-compressed, but it's not
- * important right now */
- assert(compressionMethod == ZipEntry::kCompressDeflated);
-
- bool scanResult;
- int method;
- long compressedLen;
-
- scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen,
- &compressedLen, &crc);
- if (!scanResult || method != ZipEntry::kCompressDeflated) {
- LOGD("this isn't a deflated gzip file?");
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL);
- if (result != NO_ERROR) {
- LOGD("failed copying gzip data in\n");
- goto bail;
- }
- } else {
- assert(false);
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- /*
- * We could write the "Data Descriptor", but there doesn't seem to
- * be any point since we're going to go back and write the LFH.
- *
- * Update file offsets.
- */
- endPosn = ftell(mZipFp); // seeked to end of compressed data
-
- /*
- * Success! Fill out new values.
- */
- pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc,
- compressionMethod);
- modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp));
- pEntry->setModWhen(modWhen);
- pEntry->setLFHOffset(lfhPosn);
- mEOCD.mNumEntries++;
- mEOCD.mTotalNumEntries++;
- mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
- mEOCD.mCentralDirOffset = endPosn;
-
- /*
- * Go back and write the LFH.
- */
- if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
- pEntry->mLFH.write(mZipFp);
-
- /*
- * Add pEntry to the list.
- */
- mEntries.add(pEntry);
- if (ppEntry != NULL)
- *ppEntry = pEntry;
- pEntry = NULL;
-
-bail:
- if (inputFp != NULL)
- fclose(inputFp);
- delete pEntry;
- return result;
-}
-
-/*
- * Add an entry by copying it from another zip file. If "padding" is
- * nonzero, the specified number of bytes will be added to the "extra"
- * field in the header.
- *
- * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
- */
-status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
- int padding, ZipEntry** ppEntry)
-{
- ZipEntry* pEntry = NULL;
- status_t result;
- long lfhPosn, endPosn;
-
- if (mReadOnly)
- return INVALID_OPERATION;
-
- /* make sure we're in a reasonable state */
- assert(mZipFp != NULL);
- assert(mEntries.size() == mEOCD.mTotalNumEntries);
-
- if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- pEntry = new ZipEntry;
- if (pEntry == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
-
- result = pEntry->initFromExternal(pSourceZip, pSourceEntry);
- if (result != NO_ERROR)
- goto bail;
- if (padding != 0) {
- result = pEntry->addPadding(padding);
- if (result != NO_ERROR)
- goto bail;
- }
-
- /*
- * From here on out, failures are more interesting.
- */
- mNeedCDRewrite = true;
-
- /*
- * Write the LFH. Since we're not recompressing the data, we already
- * have all of the fields filled out.
- */
- lfhPosn = ftell(mZipFp);
- pEntry->mLFH.write(mZipFp);
-
- /*
- * Copy the data over.
- *
- * If the "has data descriptor" flag is set, we want to copy the DD
- * fields as well. This is a fixed-size area immediately following
- * the data.
- */
- if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0)
- {
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- off_t copyLen;
- copyLen = pSourceEntry->getCompressedLen();
- if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0)
- copyLen += ZipEntry::kDataDescriptorLen;
-
- if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL)
- != NO_ERROR)
- {
- LOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName);
- result = UNKNOWN_ERROR;
- goto bail;
- }
-
- /*
- * Update file offsets.
- */
- endPosn = ftell(mZipFp);
-
- /*
- * Success! Fill out new values.
- */
- pEntry->setLFHOffset(lfhPosn); // sets mCDE.mLocalHeaderRelOffset
- mEOCD.mNumEntries++;
- mEOCD.mTotalNumEntries++;
- mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
- mEOCD.mCentralDirOffset = endPosn;
-
- /*
- * Add pEntry to the list.
- */
- mEntries.add(pEntry);
- if (ppEntry != NULL)
- *ppEntry = pEntry;
- pEntry = NULL;
-
- result = NO_ERROR;
-
-bail:
- delete pEntry;
- return result;
-}
-
-/*
- * Copy all of the bytes in "src" to "dst".
- *
- * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
- * will be seeked immediately past the data.
- */
-status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32)
-{
- unsigned char tmpBuf[32768];
- size_t count;
-
- *pCRC32 = crc32(0L, Z_NULL, 0);
-
- while (1) {
- count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp);
- if (ferror(srcFp) || ferror(dstFp))
- return errnoToStatus(errno);
- if (count == 0)
- break;
-
- *pCRC32 = crc32(*pCRC32, tmpBuf, count);
-
- if (fwrite(tmpBuf, 1, count, dstFp) != count) {
- LOGD("fwrite %d bytes failed\n", (int) count);
- return UNKNOWN_ERROR;
- }
- }
-
- return NO_ERROR;
-}
-
-/*
- * Copy all of the bytes in "src" to "dst".
- *
- * On exit, "dstFp" will be seeked immediately past the data.
- */
-status_t ZipFile::copyDataToFp(FILE* dstFp,
- const void* data, size_t size, unsigned long* pCRC32)
-{
- size_t count;
-
- *pCRC32 = crc32(0L, Z_NULL, 0);
- if (size > 0) {
- *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size);
- if (fwrite(data, 1, size, dstFp) != size) {
- LOGD("fwrite %d bytes failed\n", (int) size);
- return UNKNOWN_ERROR;
- }
- }
-
- return NO_ERROR;
-}
-
-/*
- * Copy some of the bytes in "src" to "dst".
- *
- * If "pCRC32" is NULL, the CRC will not be computed.
- *
- * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
- * will be seeked immediately past the data just written.
- */
-status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
- unsigned long* pCRC32)
-{
- unsigned char tmpBuf[32768];
- size_t count;
-
- if (pCRC32 != NULL)
- *pCRC32 = crc32(0L, Z_NULL, 0);
-
- while (length) {
- long readSize;
-
- readSize = sizeof(tmpBuf);
- if (readSize > length)
- readSize = length;
-
- count = fread(tmpBuf, 1, readSize, srcFp);
- if ((long) count != readSize) { // error or unexpected EOF
- LOGD("fread %d bytes failed\n", (int) readSize);
- return UNKNOWN_ERROR;
- }
-
- if (pCRC32 != NULL)
- *pCRC32 = crc32(*pCRC32, tmpBuf, count);
-
- if (fwrite(tmpBuf, 1, count, dstFp) != count) {
- LOGD("fwrite %d bytes failed\n", (int) count);
- return UNKNOWN_ERROR;
- }
-
- length -= readSize;
- }
-
- return NO_ERROR;
-}
-
-/*
- * Compress all of the data in "srcFp" and write it to "dstFp".
- *
- * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
- * will be seeked immediately past the compressed data.
- */
-status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp,
- const void* data, size_t size, unsigned long* pCRC32)
-{
- status_t result = NO_ERROR;
- const size_t kBufSize = 32768;
- unsigned char* inBuf = NULL;
- unsigned char* outBuf = NULL;
- z_stream zstream;
- bool atEof = false; // no feof() aviailable yet
- unsigned long crc;
- int zerr;
-
- /*
- * Create an input buffer and an output buffer.
- */
- inBuf = new unsigned char[kBufSize];
- outBuf = new unsigned char[kBufSize];
- if (inBuf == NULL || outBuf == NULL) {
- result = NO_MEMORY;
- goto bail;
- }
-
- /*
- * Initialize the zlib stream.
- */
- memset(&zstream, 0, sizeof(zstream));
- zstream.zalloc = Z_NULL;
- zstream.zfree = Z_NULL;
- zstream.opaque = Z_NULL;
- zstream.next_in = NULL;
- zstream.avail_in = 0;
- zstream.next_out = outBuf;
- zstream.avail_out = kBufSize;
- zstream.data_type = Z_UNKNOWN;
-
- zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION,
- Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
- if (zerr != Z_OK) {
- result = UNKNOWN_ERROR;
- if (zerr == Z_VERSION_ERROR) {
- LOGE("Installed zlib is not compatible with linked version (%s)\n",
- ZLIB_VERSION);
- } else {
- LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr);
- }
- goto bail;
- }
-
- crc = crc32(0L, Z_NULL, 0);
-
- /*
- * Loop while we have data.
- */
- do {
- size_t getSize;
- int flush;
-
- /* only read if the input buffer is empty */
- if (zstream.avail_in == 0 && !atEof) {
- LOGV("+++ reading %d bytes\n", (int)kBufSize);
- if (data) {
- getSize = size > kBufSize ? kBufSize : size;
- memcpy(inBuf, data, getSize);
- data = ((const char*)data) + getSize;
- size -= getSize;
- } else {
- getSize = fread(inBuf, 1, kBufSize, srcFp);
- if (ferror(srcFp)) {
- LOGD("deflate read failed (errno=%d)\n", errno);
- goto z_bail;
- }
- }
- if (getSize < kBufSize) {
- LOGV("+++ got %d bytes, EOF reached\n",
- (int)getSize);
- atEof = true;
- }
-
- crc = crc32(crc, inBuf, getSize);
-
- zstream.next_in = inBuf;
- zstream.avail_in = getSize;
- }
-
- if (atEof)
- flush = Z_FINISH; /* tell zlib that we're done */
- else
- flush = Z_NO_FLUSH; /* more to come! */
-
- zerr = deflate(&zstream, flush);
- if (zerr != Z_OK && zerr != Z_STREAM_END) {
- LOGD("zlib deflate call failed (zerr=%d)\n", zerr);
- result = UNKNOWN_ERROR;
- goto z_bail;
- }
-
- /* write when we're full or when we're done */
- if (zstream.avail_out == 0 ||
- (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize))
- {
- LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf));
- if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) !=
- (size_t)(zstream.next_out - outBuf))
- {
- LOGD("write %d failed in deflate\n",
- (int) (zstream.next_out - outBuf));
- goto z_bail;
- }
-
- zstream.next_out = outBuf;
- zstream.avail_out = kBufSize;
- }
- } while (zerr == Z_OK);
-
- assert(zerr == Z_STREAM_END); /* other errors should've been caught */
-
- *pCRC32 = crc;
-
-z_bail:
- deflateEnd(&zstream); /* free up any allocated structures */
-
-bail:
- delete[] inBuf;
- delete[] outBuf;
-
- return result;
-}
-
-/*
- * Mark an entry as deleted.
- *
- * We will eventually need to crunch the file down, but if several files
- * are being removed (perhaps as part of an "update" process) we can make
- * things considerably faster by deferring the removal to "flush" time.
- */
-status_t ZipFile::remove(ZipEntry* pEntry)
-{
- /*
- * Should verify that pEntry is actually part of this archive, and
- * not some stray ZipEntry from a different file.
- */
-
- /* mark entry as deleted, and mark archive as dirty */
- pEntry->setDeleted();
- mNeedCDRewrite = true;
- return NO_ERROR;
-}
-
-/*
- * Flush any pending writes.
- *
- * In particular, this will crunch out deleted entries, and write the
- * Central Directory and EOCD if we have stomped on them.
- */
-status_t ZipFile::flush(void)
-{
- status_t result = NO_ERROR;
- long eocdPosn;
- int i, count;
-
- if (mReadOnly)
- return INVALID_OPERATION;
- if (!mNeedCDRewrite)
- return NO_ERROR;
-
- assert(mZipFp != NULL);
-
- result = crunchArchive();
- if (result != NO_ERROR)
- return result;
-
- if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0)
- return UNKNOWN_ERROR;
-
- count = mEntries.size();
- for (i = 0; i < count; i++) {
- ZipEntry* pEntry = mEntries[i];
- pEntry->mCDE.write(mZipFp);
- }
-
- eocdPosn = ftell(mZipFp);
- mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset;
-
- mEOCD.write(mZipFp);
-
- /*
- * If we had some stuff bloat up during compression and get replaced
- * with plain files, or if we deleted some entries, there's a lot
- * of wasted space at the end of the file. Remove it now.
- */
- if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) {
- LOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno));
- // not fatal
- }
-
- /* should we clear the "newly added" flag in all entries now? */
-
- mNeedCDRewrite = false;
- return NO_ERROR;
-}
-
-/*
- * Crunch deleted files out of an archive by shifting the later files down.
- *
- * Because we're not using a temp file, we do the operation inside the
- * current file.
- */
-status_t ZipFile::crunchArchive(void)
-{
- status_t result = NO_ERROR;
- int i, count;
- long delCount, adjust;
-
-#if 0
- printf("CONTENTS:\n");
- for (i = 0; i < (int) mEntries.size(); i++) {
- printf(" %d: lfhOff=%ld del=%d\n",
- i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted());
- }
- printf(" END is %ld\n", (long) mEOCD.mCentralDirOffset);
-#endif
-
- /*
- * Roll through the set of files, shifting them as appropriate. We
- * could probably get a slight performance improvement by sliding
- * multiple files down at once (because we could use larger reads
- * when operating on batches of small files), but it's not that useful.
- */
- count = mEntries.size();
- delCount = adjust = 0;
- for (i = 0; i < count; i++) {
- ZipEntry* pEntry = mEntries[i];
- long span;
-
- if (pEntry->getLFHOffset() != 0) {
- long nextOffset;
-
- /* Get the length of this entry by finding the offset
- * of the next entry. Directory entries don't have
- * file offsets, so we need to find the next non-directory
- * entry.
- */
- nextOffset = 0;
- for (int ii = i+1; nextOffset == 0 && ii < count; ii++)
- nextOffset = mEntries[ii]->getLFHOffset();
- if (nextOffset == 0)
- nextOffset = mEOCD.mCentralDirOffset;
- span = nextOffset - pEntry->getLFHOffset();
-
- assert(span >= ZipEntry::LocalFileHeader::kLFHLen);
- } else {
- /* This is a directory entry. It doesn't have
- * any actual file contents, so there's no need to
- * move anything.
- */
- span = 0;
- }
-
- //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n",
- // i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count);
-
- if (pEntry->getDeleted()) {
- adjust += span;
- delCount++;
-
- delete pEntry;
- mEntries.removeAt(i);
-
- /* adjust loop control */
- count--;
- i--;
- } else if (span != 0 && adjust > 0) {
- /* shuffle this entry back */
- //printf("+++ Shuffling '%s' back %ld\n",
- // pEntry->getFileName(), adjust);
- result = filemove(mZipFp, pEntry->getLFHOffset() - adjust,
- pEntry->getLFHOffset(), span);
- if (result != NO_ERROR) {
- /* this is why you use a temp file */
- LOGE("error during crunch - archive is toast\n");
- return result;
- }
-
- pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust);
- }
- }
-
- /*
- * Fix EOCD info. We have to wait until the end to do some of this
- * because we use mCentralDirOffset to determine "span" for the
- * last entry.
- */
- mEOCD.mCentralDirOffset -= adjust;
- mEOCD.mNumEntries -= delCount;
- mEOCD.mTotalNumEntries -= delCount;
- mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
-
- assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries);
- assert(mEOCD.mNumEntries == count);
-
- return result;
-}
-
-/*
- * Works like memmove(), but on pieces of a file.
- */
-status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n)
-{
- if (dst == src || n <= 0)
- return NO_ERROR;
-
- unsigned char readBuf[32768];
-
- if (dst < src) {
- /* shift stuff toward start of file; must read from start */
- while (n != 0) {
- size_t getSize = sizeof(readBuf);
- if (getSize > n)
- getSize = n;
-
- if (fseek(fp, (long) src, SEEK_SET) != 0) {
- LOGD("filemove src seek %ld failed\n", (long) src);
- return UNKNOWN_ERROR;
- }
-
- if (fread(readBuf, 1, getSize, fp) != getSize) {
- LOGD("filemove read %ld off=%ld failed\n",
- (long) getSize, (long) src);
- return UNKNOWN_ERROR;
- }
-
- if (fseek(fp, (long) dst, SEEK_SET) != 0) {
- LOGD("filemove dst seek %ld failed\n", (long) dst);
- return UNKNOWN_ERROR;
- }
-
- if (fwrite(readBuf, 1, getSize, fp) != getSize) {
- LOGD("filemove write %ld off=%ld failed\n",
- (long) getSize, (long) dst);
- return UNKNOWN_ERROR;
- }
-
- src += getSize;
- dst += getSize;
- n -= getSize;
- }
- } else {
- /* shift stuff toward end of file; must read from end */
- assert(false); // write this someday, maybe
- return UNKNOWN_ERROR;
- }
-
- return NO_ERROR;
-}
-
-
-/*
- * Get the modification time from a file descriptor.
- */
-time_t ZipFile::getModTime(int fd)
-{
- struct stat sb;
-
- if (fstat(fd, &sb) < 0) {
- LOGD("HEY: fstat on fd %d failed\n", fd);
- return (time_t) -1;
- }
-
- return sb.st_mtime;
-}
-
-
-#if 0 /* this is a bad idea */
-/*
- * Get a copy of the Zip file descriptor.
- *
- * We don't allow this if the file was opened read-write because we tend
- * to leave the file contents in an uncertain state between calls to
- * flush(). The duplicated file descriptor should only be valid for reads.
- */
-int ZipFile::getZipFd(void) const
-{
- if (!mReadOnly)
- return INVALID_OPERATION;
- assert(mZipFp != NULL);
-
- int fd;
- fd = dup(fileno(mZipFp));
- if (fd < 0) {
- LOGD("didn't work, errno=%d\n", errno);
- }
-
- return fd;
-}
-#endif
-
-
-#if 0
-/*
- * Expand data.
- */
-bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const
-{
- return false;
-}
-#endif
-
-// free the memory when you're done
-void* ZipFile::uncompress(const ZipEntry* entry)
-{
- size_t unlen = entry->getUncompressedLen();
- size_t clen = entry->getCompressedLen();
-
- void* buf = malloc(unlen);
- if (buf == NULL) {
- return NULL;
- }
-
- fseek(mZipFp, 0, SEEK_SET);
-
- off_t offset = entry->getFileOffset();
- if (fseek(mZipFp, offset, SEEK_SET) != 0) {
- goto bail;
- }
-
- switch (entry->getCompressionMethod())
- {
- case ZipEntry::kCompressStored: {
- ssize_t amt = fread(buf, 1, unlen, mZipFp);
- if (amt != (ssize_t)unlen) {
- goto bail;
- }
-#if 0
- printf("data...\n");
- const unsigned char* p = (unsigned char*)buf;
- const unsigned char* end = p+unlen;
- for (int i=0; i<32 && p < end; i++) {
- printf("0x%08x ", (int)(offset+(i*0x10)));
- for (int j=0; j<0x10 && p < end; j++) {
- printf(" %02x", *p);
- p++;
- }
- printf("\n");
- }
-#endif
-
- }
- break;
- case ZipEntry::kCompressDeflated: {
- if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) {
- goto bail;
- }
- }
- break;
- default:
- goto bail;
- }
- return buf;
-
-bail:
- free(buf);
- return NULL;
-}
-
-
-/*
- * ===========================================================================
- * ZipFile::EndOfCentralDir
- * ===========================================================================
- */
-
-/*
- * Read the end-of-central-dir fields.
- *
- * "buf" should be positioned at the EOCD signature, and should contain
- * the entire EOCD area including the comment.
- */
-status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len)
-{
- /* don't allow re-use */
- assert(mComment == NULL);
-
- if (len < kEOCDLen) {
- /* looks like ZIP file got truncated */
- LOGD(" Zip EOCD: expected >= %d bytes, found %d\n",
- kEOCDLen, len);
- return INVALID_OPERATION;
- }
-
- /* this should probably be an assert() */
- if (ZipEntry::getLongLE(&buf[0x00]) != kSignature)
- return UNKNOWN_ERROR;
-
- mDiskNumber = ZipEntry::getShortLE(&buf[0x04]);
- mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]);
- mNumEntries = ZipEntry::getShortLE(&buf[0x08]);
- mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]);
- mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]);
- mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]);
- mCommentLen = ZipEntry::getShortLE(&buf[0x14]);
-
- // TODO: validate mCentralDirOffset
-
- if (mCommentLen > 0) {
- if (kEOCDLen + mCommentLen > len) {
- LOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n",
- kEOCDLen, mCommentLen, len);
- return UNKNOWN_ERROR;
- }
- mComment = new unsigned char[mCommentLen];
- memcpy(mComment, buf + kEOCDLen, mCommentLen);
- }
-
- return NO_ERROR;
-}
-
-/*
- * Write an end-of-central-directory section.
- */
-status_t ZipFile::EndOfCentralDir::write(FILE* fp)
-{
- unsigned char buf[kEOCDLen];
-
- ZipEntry::putLongLE(&buf[0x00], kSignature);
- ZipEntry::putShortLE(&buf[0x04], mDiskNumber);
- ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir);
- ZipEntry::putShortLE(&buf[0x08], mNumEntries);
- ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries);
- ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize);
- ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset);
- ZipEntry::putShortLE(&buf[0x14], mCommentLen);
-
- if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen)
- return UNKNOWN_ERROR;
- if (mCommentLen > 0) {
- assert(mComment != NULL);
- if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen)
- return UNKNOWN_ERROR;
- }
-
- return NO_ERROR;
-}
-
-/*
- * Dump the contents of an EndOfCentralDir object.
- */
-void ZipFile::EndOfCentralDir::dump(void) const
-{
- LOGD(" EndOfCentralDir contents:\n");
- LOGD(" diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n",
- mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries);
- LOGD(" centDirSize=%lu centDirOff=%lu commentLen=%u\n",
- mCentralDirSize, mCentralDirOffset, mCommentLen);
-}
-
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 2ef0347c2d45..3f370696289a 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -15,7 +15,7 @@
#include
#include
#include
-#include
+#include "ZipFile.h"
#include "Bundle.h"
#include "SourcePos.h"
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index fdc859c01c4d..2d8973d89b72 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -17,7 +17,10 @@ LOCAL_SRC_FILES := \
ResourceTable.cpp \
Images.cpp \
Resource.cpp \
- SourcePos.cpp
+ SourcePos.cpp \
+ ZipEntry.cpp \
+ ZipFile.cpp
+
LOCAL_CFLAGS += -Wno-format-y2k
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 32df6317e288..0e889f5d1a1e 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -12,7 +12,6 @@
#include
#include
#include
-#include
#include
#include
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 6ff82690105d..a33b4d7113dd 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -10,7 +10,6 @@
#include
#include
#include
-#include
#include
#include
diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h
index b5c334c7700a..34ca5e5e4d09 100644
--- a/tools/aapt/Main.h
+++ b/tools/aapt/Main.h
@@ -12,7 +12,7 @@
#include
#include "Bundle.h"
#include "AaptAssets.h"
-#include
+#include "ZipFile.h"
extern int doVersion(Bundle* bundle);
extern int doList(Bundle* bundle);
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index 890052972d9d..84241699db3f 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -11,7 +11,6 @@
#include
#include
#include
-#include
#include
#include
diff --git a/tools/aapt/ZipEntry.cpp b/tools/aapt/ZipEntry.cpp
new file mode 100644
index 000000000000..bed033361c58
--- /dev/null
+++ b/tools/aapt/ZipEntry.cpp
@@ -0,0 +1,696 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Access to entries in a Zip archive.
+//
+
+#define LOG_TAG "zip"
+
+#include "ZipEntry.h"
+#include
+
+#include
+#include
+#include
+
+using namespace android;
+
+/*
+ * Initialize a new ZipEntry structure from a FILE* positioned at a
+ * CentralDirectoryEntry.
+ *
+ * On exit, the file pointer will be at the start of the next CDE or
+ * at the EOCD.
+ */
+status_t ZipEntry::initFromCDE(FILE* fp)
+{
+ status_t result;
+ long posn;
+ bool hasDD;
+
+ //LOGV("initFromCDE ---\n");
+
+ /* read the CDE */
+ result = mCDE.read(fp);
+ if (result != NO_ERROR) {
+ LOGD("mCDE.read failed\n");
+ return result;
+ }
+
+ //mCDE.dump();
+
+ /* using the info in the CDE, go load up the LFH */
+ posn = ftell(fp);
+ if (fseek(fp, mCDE.mLocalHeaderRelOffset, SEEK_SET) != 0) {
+ LOGD("local header seek failed (%ld)\n",
+ mCDE.mLocalHeaderRelOffset);
+ return UNKNOWN_ERROR;
+ }
+
+ result = mLFH.read(fp);
+ if (result != NO_ERROR) {
+ LOGD("mLFH.read failed\n");
+ return result;
+ }
+
+ if (fseek(fp, posn, SEEK_SET) != 0)
+ return UNKNOWN_ERROR;
+
+ //mLFH.dump();
+
+ /*
+ * We *might* need to read the Data Descriptor at this point and
+ * integrate it into the LFH. If this bit is set, the CRC-32,
+ * compressed size, and uncompressed size will be zero. In practice
+ * these seem to be rare.
+ */
+ hasDD = (mLFH.mGPBitFlag & kUsesDataDescr) != 0;
+ if (hasDD) {
+ // do something clever
+ //LOGD("+++ has data descriptor\n");
+ }
+
+ /*
+ * Sanity-check the LFH. Note that this will fail if the "kUsesDataDescr"
+ * flag is set, because the LFH is incomplete. (Not a problem, since we
+ * prefer the CDE values.)
+ */
+ if (!hasDD && !compareHeaders()) {
+ LOGW("WARNING: header mismatch\n");
+ // keep going?
+ }
+
+ /*
+ * If the mVersionToExtract is greater than 20, we may have an
+ * issue unpacking the record -- could be encrypted, compressed
+ * with something we don't support, or use Zip64 extensions. We
+ * can defer worrying about that to when we're extracting data.
+ */
+
+ return NO_ERROR;
+}
+
+/*
+ * Initialize a new entry. Pass in the file name and an optional comment.
+ *
+ * Initializes the CDE and the LFH.
+ */
+void ZipEntry::initNew(const char* fileName, const char* comment)
+{
+ assert(fileName != NULL && *fileName != '\0'); // name required
+
+ /* most fields are properly initialized by constructor */
+ mCDE.mVersionMadeBy = kDefaultMadeBy;
+ mCDE.mVersionToExtract = kDefaultVersion;
+ mCDE.mCompressionMethod = kCompressStored;
+ mCDE.mFileNameLength = strlen(fileName);
+ if (comment != NULL)
+ mCDE.mFileCommentLength = strlen(comment);
+ mCDE.mExternalAttrs = 0x81b60020; // matches what WinZip does
+
+ if (mCDE.mFileNameLength > 0) {
+ mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
+ strcpy((char*) mCDE.mFileName, fileName);
+ }
+ if (mCDE.mFileCommentLength > 0) {
+ /* TODO: stop assuming null-terminated ASCII here? */
+ mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
+ strcpy((char*) mCDE.mFileComment, comment);
+ }
+
+ copyCDEtoLFH();
+}
+
+/*
+ * Initialize a new entry, starting with the ZipEntry from a different
+ * archive.
+ *
+ * Initializes the CDE and the LFH.
+ */
+status_t ZipEntry::initFromExternal(const ZipFile* pZipFile,
+ const ZipEntry* pEntry)
+{
+ /*
+ * Copy everything in the CDE over, then fix up the hairy bits.
+ */
+ memcpy(&mCDE, &pEntry->mCDE, sizeof(mCDE));
+
+ if (mCDE.mFileNameLength > 0) {
+ mCDE.mFileName = new unsigned char[mCDE.mFileNameLength+1];
+ if (mCDE.mFileName == NULL)
+ return NO_MEMORY;
+ strcpy((char*) mCDE.mFileName, (char*)pEntry->mCDE.mFileName);
+ }
+ if (mCDE.mFileCommentLength > 0) {
+ mCDE.mFileComment = new unsigned char[mCDE.mFileCommentLength+1];
+ if (mCDE.mFileComment == NULL)
+ return NO_MEMORY;
+ strcpy((char*) mCDE.mFileComment, (char*)pEntry->mCDE.mFileComment);
+ }
+ if (mCDE.mExtraFieldLength > 0) {
+ /* we null-terminate this, though it may not be a string */
+ mCDE.mExtraField = new unsigned char[mCDE.mExtraFieldLength+1];
+ if (mCDE.mExtraField == NULL)
+ return NO_MEMORY;
+ memcpy(mCDE.mExtraField, pEntry->mCDE.mExtraField,
+ mCDE.mExtraFieldLength+1);
+ }
+
+ /* construct the LFH from the CDE */
+ copyCDEtoLFH();
+
+ /*
+ * The LFH "extra" field is independent of the CDE "extra", so we
+ * handle it here.
+ */
+ assert(mLFH.mExtraField == NULL);
+ mLFH.mExtraFieldLength = pEntry->mLFH.mExtraFieldLength;
+ if (mLFH.mExtraFieldLength > 0) {
+ mLFH.mExtraField = new unsigned char[mLFH.mExtraFieldLength+1];
+ if (mLFH.mExtraField == NULL)
+ return NO_MEMORY;
+ memcpy(mLFH.mExtraField, pEntry->mLFH.mExtraField,
+ mLFH.mExtraFieldLength+1);
+ }
+
+ return NO_ERROR;
+}
+
+/*
+ * Insert pad bytes in the LFH by tweaking the "extra" field. This will
+ * potentially confuse something that put "extra" data in here earlier,
+ * but I can't find an actual problem.
+ */
+status_t ZipEntry::addPadding(int padding)
+{
+ if (padding <= 0)
+ return INVALID_OPERATION;
+
+ //LOGI("HEY: adding %d pad bytes to existing %d in %s\n",
+ // padding, mLFH.mExtraFieldLength, mCDE.mFileName);
+
+ if (mLFH.mExtraFieldLength > 0) {
+ /* extend existing field */
+ unsigned char* newExtra;
+
+ newExtra = new unsigned char[mLFH.mExtraFieldLength + padding];
+ if (newExtra == NULL)
+ return NO_MEMORY;
+ memset(newExtra + mLFH.mExtraFieldLength, 0, padding);
+ memcpy(newExtra, mLFH.mExtraField, mLFH.mExtraFieldLength);
+
+ delete[] mLFH.mExtraField;
+ mLFH.mExtraField = newExtra;
+ mLFH.mExtraFieldLength += padding;
+ } else {
+ /* create new field */
+ mLFH.mExtraField = new unsigned char[padding];
+ memset(mLFH.mExtraField, 0, padding);
+ mLFH.mExtraFieldLength = padding;
+ }
+
+ return NO_ERROR;
+}
+
+/*
+ * Set the fields in the LFH equal to the corresponding fields in the CDE.
+ *
+ * This does not touch the LFH "extra" field.
+ */
+void ZipEntry::copyCDEtoLFH(void)
+{
+ mLFH.mVersionToExtract = mCDE.mVersionToExtract;
+ mLFH.mGPBitFlag = mCDE.mGPBitFlag;
+ mLFH.mCompressionMethod = mCDE.mCompressionMethod;
+ mLFH.mLastModFileTime = mCDE.mLastModFileTime;
+ mLFH.mLastModFileDate = mCDE.mLastModFileDate;
+ mLFH.mCRC32 = mCDE.mCRC32;
+ mLFH.mCompressedSize = mCDE.mCompressedSize;
+ mLFH.mUncompressedSize = mCDE.mUncompressedSize;
+ mLFH.mFileNameLength = mCDE.mFileNameLength;
+ // the "extra field" is independent
+
+ delete[] mLFH.mFileName;
+ if (mLFH.mFileNameLength > 0) {
+ mLFH.mFileName = new unsigned char[mLFH.mFileNameLength+1];
+ strcpy((char*) mLFH.mFileName, (const char*) mCDE.mFileName);
+ } else {
+ mLFH.mFileName = NULL;
+ }
+}
+
+/*
+ * Set some information about a file after we add it.
+ */
+void ZipEntry::setDataInfo(long uncompLen, long compLen, unsigned long crc32,
+ int compressionMethod)
+{
+ mCDE.mCompressionMethod = compressionMethod;
+ mCDE.mCRC32 = crc32;
+ mCDE.mCompressedSize = compLen;
+ mCDE.mUncompressedSize = uncompLen;
+ mCDE.mCompressionMethod = compressionMethod;
+ if (compressionMethod == kCompressDeflated) {
+ mCDE.mGPBitFlag |= 0x0002; // indicates maximum compression used
+ }
+ copyCDEtoLFH();
+}
+
+/*
+ * See if the data in mCDE and mLFH match up. This is mostly useful for
+ * debugging these classes, but it can be used to identify damaged
+ * archives.
+ *
+ * Returns "false" if they differ.
+ */
+bool ZipEntry::compareHeaders(void) const
+{
+ if (mCDE.mVersionToExtract != mLFH.mVersionToExtract) {
+ LOGV("cmp: VersionToExtract\n");
+ return false;
+ }
+ if (mCDE.mGPBitFlag != mLFH.mGPBitFlag) {
+ LOGV("cmp: GPBitFlag\n");
+ return false;
+ }
+ if (mCDE.mCompressionMethod != mLFH.mCompressionMethod) {
+ LOGV("cmp: CompressionMethod\n");
+ return false;
+ }
+ if (mCDE.mLastModFileTime != mLFH.mLastModFileTime) {
+ LOGV("cmp: LastModFileTime\n");
+ return false;
+ }
+ if (mCDE.mLastModFileDate != mLFH.mLastModFileDate) {
+ LOGV("cmp: LastModFileDate\n");
+ return false;
+ }
+ if (mCDE.mCRC32 != mLFH.mCRC32) {
+ LOGV("cmp: CRC32\n");
+ return false;
+ }
+ if (mCDE.mCompressedSize != mLFH.mCompressedSize) {
+ LOGV("cmp: CompressedSize\n");
+ return false;
+ }
+ if (mCDE.mUncompressedSize != mLFH.mUncompressedSize) {
+ LOGV("cmp: UncompressedSize\n");
+ return false;
+ }
+ if (mCDE.mFileNameLength != mLFH.mFileNameLength) {
+ LOGV("cmp: FileNameLength\n");
+ return false;
+ }
+#if 0 // this seems to be used for padding, not real data
+ if (mCDE.mExtraFieldLength != mLFH.mExtraFieldLength) {
+ LOGV("cmp: ExtraFieldLength\n");
+ return false;
+ }
+#endif
+ if (mCDE.mFileName != NULL) {
+ if (strcmp((char*) mCDE.mFileName, (char*) mLFH.mFileName) != 0) {
+ LOGV("cmp: FileName\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/*
+ * Convert the DOS date/time stamp into a UNIX time stamp.
+ */
+time_t ZipEntry::getModWhen(void) const
+{
+ struct tm parts;
+
+ parts.tm_sec = (mCDE.mLastModFileTime & 0x001f) << 1;
+ parts.tm_min = (mCDE.mLastModFileTime & 0x07e0) >> 5;
+ parts.tm_hour = (mCDE.mLastModFileTime & 0xf800) >> 11;
+ parts.tm_mday = (mCDE.mLastModFileDate & 0x001f);
+ parts.tm_mon = ((mCDE.mLastModFileDate & 0x01e0) >> 5) -1;
+ parts.tm_year = ((mCDE.mLastModFileDate & 0xfe00) >> 9) + 80;
+ parts.tm_wday = parts.tm_yday = 0;
+ parts.tm_isdst = -1; // DST info "not available"
+
+ return mktime(&parts);
+}
+
+/*
+ * Set the CDE/LFH timestamp from UNIX time.
+ */
+void ZipEntry::setModWhen(time_t when)
+{
+#ifdef HAVE_LOCALTIME_R
+ struct tm tmResult;
+#endif
+ time_t even;
+ unsigned short zdate, ztime;
+
+ struct tm* ptm;
+
+ /* round up to an even number of seconds */
+ even = (time_t)(((unsigned long)(when) + 1) & (~1));
+
+ /* expand */
+#ifdef HAVE_LOCALTIME_R
+ ptm = localtime_r(&even, &tmResult);
+#else
+ ptm = localtime(&even);
+#endif
+
+ int year;
+ year = ptm->tm_year;
+ if (year < 80)
+ year = 80;
+
+ zdate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday;
+ ztime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
+
+ mCDE.mLastModFileTime = mLFH.mLastModFileTime = ztime;
+ mCDE.mLastModFileDate = mLFH.mLastModFileDate = zdate;
+}
+
+
+/*
+ * ===========================================================================
+ * ZipEntry::LocalFileHeader
+ * ===========================================================================
+ */
+
+/*
+ * Read a local file header.
+ *
+ * On entry, "fp" points to the signature at the start of the header.
+ * On exit, "fp" points to the start of data.
+ */
+status_t ZipEntry::LocalFileHeader::read(FILE* fp)
+{
+ status_t result = NO_ERROR;
+ unsigned char buf[kLFHLen];
+
+ assert(mFileName == NULL);
+ assert(mExtraField == NULL);
+
+ if (fread(buf, 1, kLFHLen, fp) != kLFHLen) {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
+ LOGD("whoops: didn't find expected signature\n");
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ mVersionToExtract = ZipEntry::getShortLE(&buf[0x04]);
+ mGPBitFlag = ZipEntry::getShortLE(&buf[0x06]);
+ mCompressionMethod = ZipEntry::getShortLE(&buf[0x08]);
+ mLastModFileTime = ZipEntry::getShortLE(&buf[0x0a]);
+ mLastModFileDate = ZipEntry::getShortLE(&buf[0x0c]);
+ mCRC32 = ZipEntry::getLongLE(&buf[0x0e]);
+ mCompressedSize = ZipEntry::getLongLE(&buf[0x12]);
+ mUncompressedSize = ZipEntry::getLongLE(&buf[0x16]);
+ mFileNameLength = ZipEntry::getShortLE(&buf[0x1a]);
+ mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1c]);
+
+ // TODO: validate sizes
+
+ /* grab filename */
+ if (mFileNameLength != 0) {
+ mFileName = new unsigned char[mFileNameLength+1];
+ if (mFileName == NULL) {
+ result = NO_MEMORY;
+ goto bail;
+ }
+ if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+ mFileName[mFileNameLength] = '\0';
+ }
+
+ /* grab extra field */
+ if (mExtraFieldLength != 0) {
+ mExtraField = new unsigned char[mExtraFieldLength+1];
+ if (mExtraField == NULL) {
+ result = NO_MEMORY;
+ goto bail;
+ }
+ if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+ mExtraField[mExtraFieldLength] = '\0';
+ }
+
+bail:
+ return result;
+}
+
+/*
+ * Write a local file header.
+ */
+status_t ZipEntry::LocalFileHeader::write(FILE* fp)
+{
+ unsigned char buf[kLFHLen];
+
+ ZipEntry::putLongLE(&buf[0x00], kSignature);
+ ZipEntry::putShortLE(&buf[0x04], mVersionToExtract);
+ ZipEntry::putShortLE(&buf[0x06], mGPBitFlag);
+ ZipEntry::putShortLE(&buf[0x08], mCompressionMethod);
+ ZipEntry::putShortLE(&buf[0x0a], mLastModFileTime);
+ ZipEntry::putShortLE(&buf[0x0c], mLastModFileDate);
+ ZipEntry::putLongLE(&buf[0x0e], mCRC32);
+ ZipEntry::putLongLE(&buf[0x12], mCompressedSize);
+ ZipEntry::putLongLE(&buf[0x16], mUncompressedSize);
+ ZipEntry::putShortLE(&buf[0x1a], mFileNameLength);
+ ZipEntry::putShortLE(&buf[0x1c], mExtraFieldLength);
+
+ if (fwrite(buf, 1, kLFHLen, fp) != kLFHLen)
+ return UNKNOWN_ERROR;
+
+ /* write filename */
+ if (mFileNameLength != 0) {
+ if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
+ return UNKNOWN_ERROR;
+ }
+
+ /* write "extra field" */
+ if (mExtraFieldLength != 0) {
+ if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
+ return UNKNOWN_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+
+/*
+ * Dump the contents of a LocalFileHeader object.
+ */
+void ZipEntry::LocalFileHeader::dump(void) const
+{
+ LOGD(" LocalFileHeader contents:\n");
+ LOGD(" versToExt=%u gpBits=0x%04x compression=%u\n",
+ mVersionToExtract, mGPBitFlag, mCompressionMethod);
+ LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
+ mLastModFileTime, mLastModFileDate, mCRC32);
+ LOGD(" compressedSize=%lu uncompressedSize=%lu\n",
+ mCompressedSize, mUncompressedSize);
+ LOGD(" filenameLen=%u extraLen=%u\n",
+ mFileNameLength, mExtraFieldLength);
+ if (mFileName != NULL)
+ LOGD(" filename: '%s'\n", mFileName);
+}
+
+
+/*
+ * ===========================================================================
+ * ZipEntry::CentralDirEntry
+ * ===========================================================================
+ */
+
+/*
+ * Read the central dir entry that appears next in the file.
+ *
+ * On entry, "fp" should be positioned on the signature bytes for the
+ * entry. On exit, "fp" will point at the signature word for the next
+ * entry or for the EOCD.
+ */
+status_t ZipEntry::CentralDirEntry::read(FILE* fp)
+{
+ status_t result = NO_ERROR;
+ unsigned char buf[kCDELen];
+
+ /* no re-use */
+ assert(mFileName == NULL);
+ assert(mExtraField == NULL);
+ assert(mFileComment == NULL);
+
+ if (fread(buf, 1, kCDELen, fp) != kCDELen) {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ if (ZipEntry::getLongLE(&buf[0x00]) != kSignature) {
+ LOGD("Whoops: didn't find expected signature\n");
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ mVersionMadeBy = ZipEntry::getShortLE(&buf[0x04]);
+ mVersionToExtract = ZipEntry::getShortLE(&buf[0x06]);
+ mGPBitFlag = ZipEntry::getShortLE(&buf[0x08]);
+ mCompressionMethod = ZipEntry::getShortLE(&buf[0x0a]);
+ mLastModFileTime = ZipEntry::getShortLE(&buf[0x0c]);
+ mLastModFileDate = ZipEntry::getShortLE(&buf[0x0e]);
+ mCRC32 = ZipEntry::getLongLE(&buf[0x10]);
+ mCompressedSize = ZipEntry::getLongLE(&buf[0x14]);
+ mUncompressedSize = ZipEntry::getLongLE(&buf[0x18]);
+ mFileNameLength = ZipEntry::getShortLE(&buf[0x1c]);
+ mExtraFieldLength = ZipEntry::getShortLE(&buf[0x1e]);
+ mFileCommentLength = ZipEntry::getShortLE(&buf[0x20]);
+ mDiskNumberStart = ZipEntry::getShortLE(&buf[0x22]);
+ mInternalAttrs = ZipEntry::getShortLE(&buf[0x24]);
+ mExternalAttrs = ZipEntry::getLongLE(&buf[0x26]);
+ mLocalHeaderRelOffset = ZipEntry::getLongLE(&buf[0x2a]);
+
+ // TODO: validate sizes and offsets
+
+ /* grab filename */
+ if (mFileNameLength != 0) {
+ mFileName = new unsigned char[mFileNameLength+1];
+ if (mFileName == NULL) {
+ result = NO_MEMORY;
+ goto bail;
+ }
+ if (fread(mFileName, 1, mFileNameLength, fp) != mFileNameLength) {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+ mFileName[mFileNameLength] = '\0';
+ }
+
+ /* read "extra field" */
+ if (mExtraFieldLength != 0) {
+ mExtraField = new unsigned char[mExtraFieldLength+1];
+ if (mExtraField == NULL) {
+ result = NO_MEMORY;
+ goto bail;
+ }
+ if (fread(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength) {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+ mExtraField[mExtraFieldLength] = '\0';
+ }
+
+
+ /* grab comment, if any */
+ if (mFileCommentLength != 0) {
+ mFileComment = new unsigned char[mFileCommentLength+1];
+ if (mFileComment == NULL) {
+ result = NO_MEMORY;
+ goto bail;
+ }
+ if (fread(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
+ {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+ mFileComment[mFileCommentLength] = '\0';
+ }
+
+bail:
+ return result;
+}
+
+/*
+ * Write a central dir entry.
+ */
+status_t ZipEntry::CentralDirEntry::write(FILE* fp)
+{
+ unsigned char buf[kCDELen];
+
+ ZipEntry::putLongLE(&buf[0x00], kSignature);
+ ZipEntry::putShortLE(&buf[0x04], mVersionMadeBy);
+ ZipEntry::putShortLE(&buf[0x06], mVersionToExtract);
+ ZipEntry::putShortLE(&buf[0x08], mGPBitFlag);
+ ZipEntry::putShortLE(&buf[0x0a], mCompressionMethod);
+ ZipEntry::putShortLE(&buf[0x0c], mLastModFileTime);
+ ZipEntry::putShortLE(&buf[0x0e], mLastModFileDate);
+ ZipEntry::putLongLE(&buf[0x10], mCRC32);
+ ZipEntry::putLongLE(&buf[0x14], mCompressedSize);
+ ZipEntry::putLongLE(&buf[0x18], mUncompressedSize);
+ ZipEntry::putShortLE(&buf[0x1c], mFileNameLength);
+ ZipEntry::putShortLE(&buf[0x1e], mExtraFieldLength);
+ ZipEntry::putShortLE(&buf[0x20], mFileCommentLength);
+ ZipEntry::putShortLE(&buf[0x22], mDiskNumberStart);
+ ZipEntry::putShortLE(&buf[0x24], mInternalAttrs);
+ ZipEntry::putLongLE(&buf[0x26], mExternalAttrs);
+ ZipEntry::putLongLE(&buf[0x2a], mLocalHeaderRelOffset);
+
+ if (fwrite(buf, 1, kCDELen, fp) != kCDELen)
+ return UNKNOWN_ERROR;
+
+ /* write filename */
+ if (mFileNameLength != 0) {
+ if (fwrite(mFileName, 1, mFileNameLength, fp) != mFileNameLength)
+ return UNKNOWN_ERROR;
+ }
+
+ /* write "extra field" */
+ if (mExtraFieldLength != 0) {
+ if (fwrite(mExtraField, 1, mExtraFieldLength, fp) != mExtraFieldLength)
+ return UNKNOWN_ERROR;
+ }
+
+ /* write comment */
+ if (mFileCommentLength != 0) {
+ if (fwrite(mFileComment, 1, mFileCommentLength, fp) != mFileCommentLength)
+ return UNKNOWN_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+/*
+ * Dump the contents of a CentralDirEntry object.
+ */
+void ZipEntry::CentralDirEntry::dump(void) const
+{
+ LOGD(" CentralDirEntry contents:\n");
+ LOGD(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u\n",
+ mVersionMadeBy, mVersionToExtract, mGPBitFlag, mCompressionMethod);
+ LOGD(" modTime=0x%04x modDate=0x%04x crc32=0x%08lx\n",
+ mLastModFileTime, mLastModFileDate, mCRC32);
+ LOGD(" compressedSize=%lu uncompressedSize=%lu\n",
+ mCompressedSize, mUncompressedSize);
+ LOGD(" filenameLen=%u extraLen=%u commentLen=%u\n",
+ mFileNameLength, mExtraFieldLength, mFileCommentLength);
+ LOGD(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08lx relOffset=%lu\n",
+ mDiskNumberStart, mInternalAttrs, mExternalAttrs,
+ mLocalHeaderRelOffset);
+
+ if (mFileName != NULL)
+ LOGD(" filename: '%s'\n", mFileName);
+ if (mFileComment != NULL)
+ LOGD(" comment: '%s'\n", mFileComment);
+}
+
diff --git a/tools/aapt/ZipEntry.h b/tools/aapt/ZipEntry.h
new file mode 100644
index 000000000000..7f721b46321a
--- /dev/null
+++ b/tools/aapt/ZipEntry.h
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Zip archive entries.
+//
+// The ZipEntry class is tightly meshed with the ZipFile class.
+//
+#ifndef __LIBS_ZIPENTRY_H
+#define __LIBS_ZIPENTRY_H
+
+#include
+
+#include
+#include
+
+namespace android {
+
+class ZipFile;
+
+/*
+ * ZipEntry objects represent a single entry in a Zip archive.
+ *
+ * You can use one of these to get or set information about an entry, but
+ * there are no functions here for accessing the data itself. (We could
+ * tuck a pointer to the ZipFile in here for convenience, but that raises
+ * the likelihood of using ZipEntry objects after discarding the ZipFile.)
+ *
+ * File information is stored in two places: next to the file data (the Local
+ * File Header, and possibly a Data Descriptor), and at the end of the file
+ * (the Central Directory Entry). The two must be kept in sync.
+ */
+class ZipEntry {
+public:
+ friend class ZipFile;
+
+ ZipEntry(void)
+ : mDeleted(false), mMarked(false)
+ {}
+ ~ZipEntry(void) {}
+
+ /*
+ * Returns "true" if the data is compressed.
+ */
+ bool isCompressed(void) const {
+ return mCDE.mCompressionMethod != kCompressStored;
+ }
+ int getCompressionMethod(void) const { return mCDE.mCompressionMethod; }
+
+ /*
+ * Return the uncompressed length.
+ */
+ off_t getUncompressedLen(void) const { return mCDE.mUncompressedSize; }
+
+ /*
+ * Return the compressed length. For uncompressed data, this returns
+ * the same thing as getUncompresesdLen().
+ */
+ off_t getCompressedLen(void) const { return mCDE.mCompressedSize; }
+
+ /*
+ * Return the absolute file offset of the start of the compressed or
+ * uncompressed data.
+ */
+ off_t getFileOffset(void) const {
+ return mCDE.mLocalHeaderRelOffset +
+ LocalFileHeader::kLFHLen +
+ mLFH.mFileNameLength +
+ mLFH.mExtraFieldLength;
+ }
+
+ /*
+ * Return the data CRC.
+ */
+ unsigned long getCRC32(void) const { return mCDE.mCRC32; }
+
+ /*
+ * Return file modification time in UNIX seconds-since-epoch.
+ */
+ time_t getModWhen(void) const;
+
+ /*
+ * Return the archived file name.
+ */
+ const char* getFileName(void) const { return (const char*) mCDE.mFileName; }
+
+ /*
+ * Application-defined "mark". Can be useful when synchronizing the
+ * contents of an archive with contents on disk.
+ */
+ bool getMarked(void) const { return mMarked; }
+ void setMarked(bool val) { mMarked = val; }
+
+ /*
+ * Some basic functions for raw data manipulation. "LE" means
+ * Little Endian.
+ */
+ static inline unsigned short getShortLE(const unsigned char* buf) {
+ return buf[0] | (buf[1] << 8);
+ }
+ static inline unsigned long getLongLE(const unsigned char* buf) {
+ return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+ }
+ static inline void putShortLE(unsigned char* buf, short val) {
+ buf[0] = (unsigned char) val;
+ buf[1] = (unsigned char) (val >> 8);
+ }
+ static inline void putLongLE(unsigned char* buf, long val) {
+ buf[0] = (unsigned char) val;
+ buf[1] = (unsigned char) (val >> 8);
+ buf[2] = (unsigned char) (val >> 16);
+ buf[3] = (unsigned char) (val >> 24);
+ }
+
+ /* defined for Zip archives */
+ enum {
+ kCompressStored = 0, // no compression
+ // shrunk = 1,
+ // reduced 1 = 2,
+ // reduced 2 = 3,
+ // reduced 3 = 4,
+ // reduced 4 = 5,
+ // imploded = 6,
+ // tokenized = 7,
+ kCompressDeflated = 8, // standard deflate
+ // Deflate64 = 9,
+ // lib imploded = 10,
+ // reserved = 11,
+ // bzip2 = 12,
+ };
+
+ /*
+ * Deletion flag. If set, the entry will be removed on the next
+ * call to "flush".
+ */
+ bool getDeleted(void) const { return mDeleted; }
+
+protected:
+ /*
+ * Initialize the structure from the file, which is pointing at
+ * our Central Directory entry.
+ */
+ status_t initFromCDE(FILE* fp);
+
+ /*
+ * Initialize the structure for a new file. We need the filename
+ * and comment so that we can properly size the LFH area. The
+ * filename is mandatory, the comment is optional.
+ */
+ void initNew(const char* fileName, const char* comment);
+
+ /*
+ * Initialize the structure with the contents of a ZipEntry from
+ * another file.
+ */
+ status_t initFromExternal(const ZipFile* pZipFile, const ZipEntry* pEntry);
+
+ /*
+ * Add some pad bytes to the LFH. We do this by adding or resizing
+ * the "extra" field.
+ */
+ status_t addPadding(int padding);
+
+ /*
+ * Set information about the data for this entry.
+ */
+ void setDataInfo(long uncompLen, long compLen, unsigned long crc32,
+ int compressionMethod);
+
+ /*
+ * Set the modification date.
+ */
+ void setModWhen(time_t when);
+
+ /*
+ * Return the offset of the local file header.
+ */
+ off_t getLFHOffset(void) const { return mCDE.mLocalHeaderRelOffset; }
+
+ /*
+ * Set the offset of the local file header, relative to the start of
+ * the current file.
+ */
+ void setLFHOffset(off_t offset) {
+ mCDE.mLocalHeaderRelOffset = (long) offset;
+ }
+
+ /* mark for deletion; used by ZipFile::remove() */
+ void setDeleted(void) { mDeleted = true; }
+
+private:
+ /* these are private and not defined */
+ ZipEntry(const ZipEntry& src);
+ ZipEntry& operator=(const ZipEntry& src);
+
+ /* returns "true" if the CDE and the LFH agree */
+ bool compareHeaders(void) const;
+ void copyCDEtoLFH(void);
+
+ bool mDeleted; // set if entry is pending deletion
+ bool mMarked; // app-defined marker
+
+ /*
+ * Every entry in the Zip archive starts off with one of these.
+ */
+ class LocalFileHeader {
+ public:
+ LocalFileHeader(void) :
+ mVersionToExtract(0),
+ mGPBitFlag(0),
+ mCompressionMethod(0),
+ mLastModFileTime(0),
+ mLastModFileDate(0),
+ mCRC32(0),
+ mCompressedSize(0),
+ mUncompressedSize(0),
+ mFileNameLength(0),
+ mExtraFieldLength(0),
+ mFileName(NULL),
+ mExtraField(NULL)
+ {}
+ virtual ~LocalFileHeader(void) {
+ delete[] mFileName;
+ delete[] mExtraField;
+ }
+
+ status_t read(FILE* fp);
+ status_t write(FILE* fp);
+
+ // unsigned long mSignature;
+ unsigned short mVersionToExtract;
+ unsigned short mGPBitFlag;
+ unsigned short mCompressionMethod;
+ unsigned short mLastModFileTime;
+ unsigned short mLastModFileDate;
+ unsigned long mCRC32;
+ unsigned long mCompressedSize;
+ unsigned long mUncompressedSize;
+ unsigned short mFileNameLength;
+ unsigned short mExtraFieldLength;
+ unsigned char* mFileName;
+ unsigned char* mExtraField;
+
+ enum {
+ kSignature = 0x04034b50,
+ kLFHLen = 30, // LocalFileHdr len, excl. var fields
+ };
+
+ void dump(void) const;
+ };
+
+ /*
+ * Every entry in the Zip archive has one of these in the "central
+ * directory" at the end of the file.
+ */
+ class CentralDirEntry {
+ public:
+ CentralDirEntry(void) :
+ mVersionMadeBy(0),
+ mVersionToExtract(0),
+ mGPBitFlag(0),
+ mCompressionMethod(0),
+ mLastModFileTime(0),
+ mLastModFileDate(0),
+ mCRC32(0),
+ mCompressedSize(0),
+ mUncompressedSize(0),
+ mFileNameLength(0),
+ mExtraFieldLength(0),
+ mFileCommentLength(0),
+ mDiskNumberStart(0),
+ mInternalAttrs(0),
+ mExternalAttrs(0),
+ mLocalHeaderRelOffset(0),
+ mFileName(NULL),
+ mExtraField(NULL),
+ mFileComment(NULL)
+ {}
+ virtual ~CentralDirEntry(void) {
+ delete[] mFileName;
+ delete[] mExtraField;
+ delete[] mFileComment;
+ }
+
+ status_t read(FILE* fp);
+ status_t write(FILE* fp);
+
+ // unsigned long mSignature;
+ unsigned short mVersionMadeBy;
+ unsigned short mVersionToExtract;
+ unsigned short mGPBitFlag;
+ unsigned short mCompressionMethod;
+ unsigned short mLastModFileTime;
+ unsigned short mLastModFileDate;
+ unsigned long mCRC32;
+ unsigned long mCompressedSize;
+ unsigned long mUncompressedSize;
+ unsigned short mFileNameLength;
+ unsigned short mExtraFieldLength;
+ unsigned short mFileCommentLength;
+ unsigned short mDiskNumberStart;
+ unsigned short mInternalAttrs;
+ unsigned long mExternalAttrs;
+ unsigned long mLocalHeaderRelOffset;
+ unsigned char* mFileName;
+ unsigned char* mExtraField;
+ unsigned char* mFileComment;
+
+ void dump(void) const;
+
+ enum {
+ kSignature = 0x02014b50,
+ kCDELen = 46, // CentralDirEnt len, excl. var fields
+ };
+ };
+
+ enum {
+ //kDataDescriptorSignature = 0x08074b50, // currently unused
+ kDataDescriptorLen = 16, // four 32-bit fields
+
+ kDefaultVersion = 20, // need deflate, nothing much else
+ kDefaultMadeBy = 0x0317, // 03=UNIX, 17=spec v2.3
+ kUsesDataDescr = 0x0008, // GPBitFlag bit 3
+ };
+
+ LocalFileHeader mLFH;
+ CentralDirEntry mCDE;
+};
+
+}; // namespace android
+
+#endif // __LIBS_ZIPENTRY_H
diff --git a/tools/aapt/ZipFile.cpp b/tools/aapt/ZipFile.cpp
new file mode 100644
index 000000000000..62c9383fc22a
--- /dev/null
+++ b/tools/aapt/ZipFile.cpp
@@ -0,0 +1,1297 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Access to Zip archives.
+//
+
+#define LOG_TAG "zip"
+
+#include
+#include
+
+#include "ZipFile.h"
+
+#include
+#define DEF_MEM_LEVEL 8 // normally in zutil.h?
+
+#include
+#include
+#include
+#include
+
+using namespace android;
+
+/*
+ * Some environments require the "b", some choke on it.
+ */
+#define FILE_OPEN_RO "rb"
+#define FILE_OPEN_RW "r+b"
+#define FILE_OPEN_RW_CREATE "w+b"
+
+/* should live somewhere else? */
+static status_t errnoToStatus(int err)
+{
+ if (err == ENOENT)
+ return NAME_NOT_FOUND;
+ else if (err == EACCES)
+ return PERMISSION_DENIED;
+ else
+ return UNKNOWN_ERROR;
+}
+
+/*
+ * Open a file and parse its guts.
+ */
+status_t ZipFile::open(const char* zipFileName, int flags)
+{
+ bool newArchive = false;
+
+ assert(mZipFp == NULL); // no reopen
+
+ if ((flags & kOpenTruncate))
+ flags |= kOpenCreate; // trunc implies create
+
+ if ((flags & kOpenReadOnly) && (flags & kOpenReadWrite))
+ return INVALID_OPERATION; // not both
+ if (!((flags & kOpenReadOnly) || (flags & kOpenReadWrite)))
+ return INVALID_OPERATION; // not neither
+ if ((flags & kOpenCreate) && !(flags & kOpenReadWrite))
+ return INVALID_OPERATION; // create requires write
+
+ if (flags & kOpenTruncate) {
+ newArchive = true;
+ } else {
+ newArchive = (access(zipFileName, F_OK) != 0);
+ if (!(flags & kOpenCreate) && newArchive) {
+ /* not creating, must already exist */
+ LOGD("File %s does not exist", zipFileName);
+ return NAME_NOT_FOUND;
+ }
+ }
+
+ /* open the file */
+ const char* openflags;
+ if (flags & kOpenReadWrite) {
+ if (newArchive)
+ openflags = FILE_OPEN_RW_CREATE;
+ else
+ openflags = FILE_OPEN_RW;
+ } else {
+ openflags = FILE_OPEN_RO;
+ }
+ mZipFp = fopen(zipFileName, openflags);
+ if (mZipFp == NULL) {
+ int err = errno;
+ LOGD("fopen failed: %d\n", err);
+ return errnoToStatus(err);
+ }
+
+ status_t result;
+ if (!newArchive) {
+ /*
+ * Load the central directory. If that fails, then this probably
+ * isn't a Zip archive.
+ */
+ result = readCentralDir();
+ } else {
+ /*
+ * Newly-created. The EndOfCentralDir constructor actually
+ * sets everything to be the way we want it (all zeroes). We
+ * set mNeedCDRewrite so that we create *something* if the
+ * caller doesn't add any files. (We could also just unlink
+ * the file if it's brand new and nothing was added, but that's
+ * probably doing more than we really should -- the user might
+ * have a need for empty zip files.)
+ */
+ mNeedCDRewrite = true;
+ result = NO_ERROR;
+ }
+
+ if (flags & kOpenReadOnly)
+ mReadOnly = true;
+ else
+ assert(!mReadOnly);
+
+ return result;
+}
+
+/*
+ * Return the Nth entry in the archive.
+ */
+ZipEntry* ZipFile::getEntryByIndex(int idx) const
+{
+ if (idx < 0 || idx >= (int) mEntries.size())
+ return NULL;
+
+ return mEntries[idx];
+}
+
+/*
+ * Find an entry by name.
+ */
+ZipEntry* ZipFile::getEntryByName(const char* fileName) const
+{
+ /*
+ * Do a stupid linear string-compare search.
+ *
+ * There are various ways to speed this up, especially since it's rare
+ * to intermingle changes to the archive with "get by name" calls. We
+ * don't want to sort the mEntries vector itself, however, because
+ * it's used to recreate the Central Directory.
+ *
+ * (Hash table works, parallel list of pointers in sorted order is good.)
+ */
+ int idx;
+
+ for (idx = mEntries.size()-1; idx >= 0; idx--) {
+ ZipEntry* pEntry = mEntries[idx];
+ if (!pEntry->getDeleted() &&
+ strcmp(fileName, pEntry->getFileName()) == 0)
+ {
+ return pEntry;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Empty the mEntries vector.
+ */
+void ZipFile::discardEntries(void)
+{
+ int count = mEntries.size();
+
+ while (--count >= 0)
+ delete mEntries[count];
+
+ mEntries.clear();
+}
+
+
+/*
+ * Find the central directory and read the contents.
+ *
+ * The fun thing about ZIP archives is that they may or may not be
+ * readable from start to end. In some cases, notably for archives
+ * that were written to stdout, the only length information is in the
+ * central directory at the end of the file.
+ *
+ * Of course, the central directory can be followed by a variable-length
+ * comment field, so we have to scan through it backwards. The comment
+ * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff
+ * itself, plus apparently sometimes people throw random junk on the end
+ * just for the fun of it.
+ *
+ * This is all a little wobbly. If the wrong value ends up in the EOCD
+ * area, we're hosed. This appears to be the way that everbody handles
+ * it though, so we're in pretty good company if this fails.
+ */
+status_t ZipFile::readCentralDir(void)
+{
+ status_t result = NO_ERROR;
+ unsigned char* buf = NULL;
+ off_t fileLength, seekStart;
+ long readAmount;
+ int i;
+
+ fseek(mZipFp, 0, SEEK_END);
+ fileLength = ftell(mZipFp);
+ rewind(mZipFp);
+
+ /* too small to be a ZIP archive? */
+ if (fileLength < EndOfCentralDir::kEOCDLen) {
+ LOGD("Length is %ld -- too small\n", (long)fileLength);
+ result = INVALID_OPERATION;
+ goto bail;
+ }
+
+ buf = new unsigned char[EndOfCentralDir::kMaxEOCDSearch];
+ if (buf == NULL) {
+ LOGD("Failure allocating %d bytes for EOCD search",
+ EndOfCentralDir::kMaxEOCDSearch);
+ result = NO_MEMORY;
+ goto bail;
+ }
+
+ if (fileLength > EndOfCentralDir::kMaxEOCDSearch) {
+ seekStart = fileLength - EndOfCentralDir::kMaxEOCDSearch;
+ readAmount = EndOfCentralDir::kMaxEOCDSearch;
+ } else {
+ seekStart = 0;
+ readAmount = (long) fileLength;
+ }
+ if (fseek(mZipFp, seekStart, SEEK_SET) != 0) {
+ LOGD("Failure seeking to end of zip at %ld", (long) seekStart);
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ /* read the last part of the file into the buffer */
+ if (fread(buf, 1, readAmount, mZipFp) != (size_t) readAmount) {
+ LOGD("short file? wanted %ld\n", readAmount);
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ /* find the end-of-central-dir magic */
+ for (i = readAmount - 4; i >= 0; i--) {
+ if (buf[i] == 0x50 &&
+ ZipEntry::getLongLE(&buf[i]) == EndOfCentralDir::kSignature)
+ {
+ LOGV("+++ Found EOCD at buf+%d\n", i);
+ break;
+ }
+ }
+ if (i < 0) {
+ LOGD("EOCD not found, not Zip\n");
+ result = INVALID_OPERATION;
+ goto bail;
+ }
+
+ /* extract eocd values */
+ result = mEOCD.readBuf(buf + i, readAmount - i);
+ if (result != NO_ERROR) {
+ LOGD("Failure reading %ld bytes of EOCD values", readAmount - i);
+ goto bail;
+ }
+ //mEOCD.dump();
+
+ if (mEOCD.mDiskNumber != 0 || mEOCD.mDiskWithCentralDir != 0 ||
+ mEOCD.mNumEntries != mEOCD.mTotalNumEntries)
+ {
+ LOGD("Archive spanning not supported\n");
+ result = INVALID_OPERATION;
+ goto bail;
+ }
+
+ /*
+ * So far so good. "mCentralDirSize" is the size in bytes of the
+ * central directory, so we can just seek back that far to find it.
+ * We can also seek forward mCentralDirOffset bytes from the
+ * start of the file.
+ *
+ * We're not guaranteed to have the rest of the central dir in the
+ * buffer, nor are we guaranteed that the central dir will have any
+ * sort of convenient size. We need to skip to the start of it and
+ * read the header, then the other goodies.
+ *
+ * The only thing we really need right now is the file comment, which
+ * we're hoping to preserve.
+ */
+ if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
+ LOGD("Failure seeking to central dir offset %ld\n",
+ mEOCD.mCentralDirOffset);
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ /*
+ * Loop through and read the central dir entries.
+ */
+ LOGV("Scanning %d entries...\n", mEOCD.mTotalNumEntries);
+ int entry;
+ for (entry = 0; entry < mEOCD.mTotalNumEntries; entry++) {
+ ZipEntry* pEntry = new ZipEntry;
+
+ result = pEntry->initFromCDE(mZipFp);
+ if (result != NO_ERROR) {
+ LOGD("initFromCDE failed\n");
+ delete pEntry;
+ goto bail;
+ }
+
+ mEntries.add(pEntry);
+ }
+
+
+ /*
+ * If all went well, we should now be back at the EOCD.
+ */
+ {
+ unsigned char checkBuf[4];
+ if (fread(checkBuf, 1, 4, mZipFp) != 4) {
+ LOGD("EOCD check read failed\n");
+ result = INVALID_OPERATION;
+ goto bail;
+ }
+ if (ZipEntry::getLongLE(checkBuf) != EndOfCentralDir::kSignature) {
+ LOGD("EOCD read check failed\n");
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+ LOGV("+++ EOCD read check passed\n");
+ }
+
+bail:
+ delete[] buf;
+ return result;
+}
+
+
+/*
+ * Add a new file to the archive.
+ *
+ * This requires creating and populating a ZipEntry structure, and copying
+ * the data into the file at the appropriate position. The "appropriate
+ * position" is the current location of the central directory, which we
+ * casually overwrite (we can put it back later).
+ *
+ * If we were concerned about safety, we would want to make all changes
+ * in a temp file and then overwrite the original after everything was
+ * safely written. Not really a concern for us.
+ */
+status_t ZipFile::addCommon(const char* fileName, const void* data, size_t size,
+ const char* storageName, int sourceType, int compressionMethod,
+ ZipEntry** ppEntry)
+{
+ ZipEntry* pEntry = NULL;
+ status_t result = NO_ERROR;
+ long lfhPosn, startPosn, endPosn, uncompressedLen;
+ FILE* inputFp = NULL;
+ unsigned long crc;
+ time_t modWhen;
+
+ if (mReadOnly)
+ return INVALID_OPERATION;
+
+ assert(compressionMethod == ZipEntry::kCompressDeflated ||
+ compressionMethod == ZipEntry::kCompressStored);
+
+ /* make sure we're in a reasonable state */
+ assert(mZipFp != NULL);
+ assert(mEntries.size() == mEOCD.mTotalNumEntries);
+
+ /* make sure it doesn't already exist */
+ if (getEntryByName(storageName) != NULL)
+ return ALREADY_EXISTS;
+
+ if (!data) {
+ inputFp = fopen(fileName, FILE_OPEN_RO);
+ if (inputFp == NULL)
+ return errnoToStatus(errno);
+ }
+
+ if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ pEntry = new ZipEntry;
+ pEntry->initNew(storageName, NULL);
+
+ /*
+ * From here on out, failures are more interesting.
+ */
+ mNeedCDRewrite = true;
+
+ /*
+ * Write the LFH, even though it's still mostly blank. We need it
+ * as a place-holder. In theory the LFH isn't necessary, but in
+ * practice some utilities demand it.
+ */
+ lfhPosn = ftell(mZipFp);
+ pEntry->mLFH.write(mZipFp);
+ startPosn = ftell(mZipFp);
+
+ /*
+ * Copy the data in, possibly compressing it as we go.
+ */
+ if (sourceType == ZipEntry::kCompressStored) {
+ if (compressionMethod == ZipEntry::kCompressDeflated) {
+ bool failed = false;
+ result = compressFpToFp(mZipFp, inputFp, data, size, &crc);
+ if (result != NO_ERROR) {
+ LOGD("compression failed, storing\n");
+ failed = true;
+ } else {
+ /*
+ * Make sure it has compressed "enough". This probably ought
+ * to be set through an API call, but I don't expect our
+ * criteria to change over time.
+ */
+ long src = inputFp ? ftell(inputFp) : size;
+ long dst = ftell(mZipFp) - startPosn;
+ if (dst + (dst / 10) > src) {
+ LOGD("insufficient compression (src=%ld dst=%ld), storing\n",
+ src, dst);
+ failed = true;
+ }
+ }
+
+ if (failed) {
+ compressionMethod = ZipEntry::kCompressStored;
+ if (inputFp) rewind(inputFp);
+ fseek(mZipFp, startPosn, SEEK_SET);
+ /* fall through to kCompressStored case */
+ }
+ }
+ /* handle "no compression" request, or failed compression from above */
+ if (compressionMethod == ZipEntry::kCompressStored) {
+ if (inputFp) {
+ result = copyFpToFp(mZipFp, inputFp, &crc);
+ } else {
+ result = copyDataToFp(mZipFp, data, size, &crc);
+ }
+ if (result != NO_ERROR) {
+ // don't need to truncate; happens in CDE rewrite
+ LOGD("failed copying data in\n");
+ goto bail;
+ }
+ }
+
+ // currently seeked to end of file
+ uncompressedLen = inputFp ? ftell(inputFp) : size;
+ } else if (sourceType == ZipEntry::kCompressDeflated) {
+ /* we should support uncompressed-from-compressed, but it's not
+ * important right now */
+ assert(compressionMethod == ZipEntry::kCompressDeflated);
+
+ bool scanResult;
+ int method;
+ long compressedLen;
+
+ scanResult = ZipUtils::examineGzip(inputFp, &method, &uncompressedLen,
+ &compressedLen, &crc);
+ if (!scanResult || method != ZipEntry::kCompressDeflated) {
+ LOGD("this isn't a deflated gzip file?");
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ result = copyPartialFpToFp(mZipFp, inputFp, compressedLen, NULL);
+ if (result != NO_ERROR) {
+ LOGD("failed copying gzip data in\n");
+ goto bail;
+ }
+ } else {
+ assert(false);
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ /*
+ * We could write the "Data Descriptor", but there doesn't seem to
+ * be any point since we're going to go back and write the LFH.
+ *
+ * Update file offsets.
+ */
+ endPosn = ftell(mZipFp); // seeked to end of compressed data
+
+ /*
+ * Success! Fill out new values.
+ */
+ pEntry->setDataInfo(uncompressedLen, endPosn - startPosn, crc,
+ compressionMethod);
+ modWhen = getModTime(inputFp ? fileno(inputFp) : fileno(mZipFp));
+ pEntry->setModWhen(modWhen);
+ pEntry->setLFHOffset(lfhPosn);
+ mEOCD.mNumEntries++;
+ mEOCD.mTotalNumEntries++;
+ mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
+ mEOCD.mCentralDirOffset = endPosn;
+
+ /*
+ * Go back and write the LFH.
+ */
+ if (fseek(mZipFp, lfhPosn, SEEK_SET) != 0) {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+ pEntry->mLFH.write(mZipFp);
+
+ /*
+ * Add pEntry to the list.
+ */
+ mEntries.add(pEntry);
+ if (ppEntry != NULL)
+ *ppEntry = pEntry;
+ pEntry = NULL;
+
+bail:
+ if (inputFp != NULL)
+ fclose(inputFp);
+ delete pEntry;
+ return result;
+}
+
+/*
+ * Add an entry by copying it from another zip file. If "padding" is
+ * nonzero, the specified number of bytes will be added to the "extra"
+ * field in the header.
+ *
+ * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+ */
+status_t ZipFile::add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
+ int padding, ZipEntry** ppEntry)
+{
+ ZipEntry* pEntry = NULL;
+ status_t result;
+ long lfhPosn, endPosn;
+
+ if (mReadOnly)
+ return INVALID_OPERATION;
+
+ /* make sure we're in a reasonable state */
+ assert(mZipFp != NULL);
+ assert(mEntries.size() == mEOCD.mTotalNumEntries);
+
+ if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0) {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ pEntry = new ZipEntry;
+ if (pEntry == NULL) {
+ result = NO_MEMORY;
+ goto bail;
+ }
+
+ result = pEntry->initFromExternal(pSourceZip, pSourceEntry);
+ if (result != NO_ERROR)
+ goto bail;
+ if (padding != 0) {
+ result = pEntry->addPadding(padding);
+ if (result != NO_ERROR)
+ goto bail;
+ }
+
+ /*
+ * From here on out, failures are more interesting.
+ */
+ mNeedCDRewrite = true;
+
+ /*
+ * Write the LFH. Since we're not recompressing the data, we already
+ * have all of the fields filled out.
+ */
+ lfhPosn = ftell(mZipFp);
+ pEntry->mLFH.write(mZipFp);
+
+ /*
+ * Copy the data over.
+ *
+ * If the "has data descriptor" flag is set, we want to copy the DD
+ * fields as well. This is a fixed-size area immediately following
+ * the data.
+ */
+ if (fseek(pSourceZip->mZipFp, pSourceEntry->getFileOffset(), SEEK_SET) != 0)
+ {
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ off_t copyLen;
+ copyLen = pSourceEntry->getCompressedLen();
+ if ((pSourceEntry->mLFH.mGPBitFlag & ZipEntry::kUsesDataDescr) != 0)
+ copyLen += ZipEntry::kDataDescriptorLen;
+
+ if (copyPartialFpToFp(mZipFp, pSourceZip->mZipFp, copyLen, NULL)
+ != NO_ERROR)
+ {
+ LOGW("copy of '%s' failed\n", pEntry->mCDE.mFileName);
+ result = UNKNOWN_ERROR;
+ goto bail;
+ }
+
+ /*
+ * Update file offsets.
+ */
+ endPosn = ftell(mZipFp);
+
+ /*
+ * Success! Fill out new values.
+ */
+ pEntry->setLFHOffset(lfhPosn); // sets mCDE.mLocalHeaderRelOffset
+ mEOCD.mNumEntries++;
+ mEOCD.mTotalNumEntries++;
+ mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
+ mEOCD.mCentralDirOffset = endPosn;
+
+ /*
+ * Add pEntry to the list.
+ */
+ mEntries.add(pEntry);
+ if (ppEntry != NULL)
+ *ppEntry = pEntry;
+ pEntry = NULL;
+
+ result = NO_ERROR;
+
+bail:
+ delete pEntry;
+ return result;
+}
+
+/*
+ * Copy all of the bytes in "src" to "dst".
+ *
+ * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
+ * will be seeked immediately past the data.
+ */
+status_t ZipFile::copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32)
+{
+ unsigned char tmpBuf[32768];
+ size_t count;
+
+ *pCRC32 = crc32(0L, Z_NULL, 0);
+
+ while (1) {
+ count = fread(tmpBuf, 1, sizeof(tmpBuf), srcFp);
+ if (ferror(srcFp) || ferror(dstFp))
+ return errnoToStatus(errno);
+ if (count == 0)
+ break;
+
+ *pCRC32 = crc32(*pCRC32, tmpBuf, count);
+
+ if (fwrite(tmpBuf, 1, count, dstFp) != count) {
+ LOGD("fwrite %d bytes failed\n", (int) count);
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+/*
+ * Copy all of the bytes in "src" to "dst".
+ *
+ * On exit, "dstFp" will be seeked immediately past the data.
+ */
+status_t ZipFile::copyDataToFp(FILE* dstFp,
+ const void* data, size_t size, unsigned long* pCRC32)
+{
+ size_t count;
+
+ *pCRC32 = crc32(0L, Z_NULL, 0);
+ if (size > 0) {
+ *pCRC32 = crc32(*pCRC32, (const unsigned char*)data, size);
+ if (fwrite(data, 1, size, dstFp) != size) {
+ LOGD("fwrite %d bytes failed\n", (int) size);
+ return UNKNOWN_ERROR;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+/*
+ * Copy some of the bytes in "src" to "dst".
+ *
+ * If "pCRC32" is NULL, the CRC will not be computed.
+ *
+ * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
+ * will be seeked immediately past the data just written.
+ */
+status_t ZipFile::copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
+ unsigned long* pCRC32)
+{
+ unsigned char tmpBuf[32768];
+ size_t count;
+
+ if (pCRC32 != NULL)
+ *pCRC32 = crc32(0L, Z_NULL, 0);
+
+ while (length) {
+ long readSize;
+
+ readSize = sizeof(tmpBuf);
+ if (readSize > length)
+ readSize = length;
+
+ count = fread(tmpBuf, 1, readSize, srcFp);
+ if ((long) count != readSize) { // error or unexpected EOF
+ LOGD("fread %d bytes failed\n", (int) readSize);
+ return UNKNOWN_ERROR;
+ }
+
+ if (pCRC32 != NULL)
+ *pCRC32 = crc32(*pCRC32, tmpBuf, count);
+
+ if (fwrite(tmpBuf, 1, count, dstFp) != count) {
+ LOGD("fwrite %d bytes failed\n", (int) count);
+ return UNKNOWN_ERROR;
+ }
+
+ length -= readSize;
+ }
+
+ return NO_ERROR;
+}
+
+/*
+ * Compress all of the data in "srcFp" and write it to "dstFp".
+ *
+ * On exit, "srcFp" will be seeked to the end of the file, and "dstFp"
+ * will be seeked immediately past the compressed data.
+ */
+status_t ZipFile::compressFpToFp(FILE* dstFp, FILE* srcFp,
+ const void* data, size_t size, unsigned long* pCRC32)
+{
+ status_t result = NO_ERROR;
+ const size_t kBufSize = 32768;
+ unsigned char* inBuf = NULL;
+ unsigned char* outBuf = NULL;
+ z_stream zstream;
+ bool atEof = false; // no feof() aviailable yet
+ unsigned long crc;
+ int zerr;
+
+ /*
+ * Create an input buffer and an output buffer.
+ */
+ inBuf = new unsigned char[kBufSize];
+ outBuf = new unsigned char[kBufSize];
+ if (inBuf == NULL || outBuf == NULL) {
+ result = NO_MEMORY;
+ goto bail;
+ }
+
+ /*
+ * Initialize the zlib stream.
+ */
+ memset(&zstream, 0, sizeof(zstream));
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ zstream.opaque = Z_NULL;
+ zstream.next_in = NULL;
+ zstream.avail_in = 0;
+ zstream.next_out = outBuf;
+ zstream.avail_out = kBufSize;
+ zstream.data_type = Z_UNKNOWN;
+
+ zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION,
+ Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+ if (zerr != Z_OK) {
+ result = UNKNOWN_ERROR;
+ if (zerr == Z_VERSION_ERROR) {
+ LOGE("Installed zlib is not compatible with linked version (%s)\n",
+ ZLIB_VERSION);
+ } else {
+ LOGD("Call to deflateInit2 failed (zerr=%d)\n", zerr);
+ }
+ goto bail;
+ }
+
+ crc = crc32(0L, Z_NULL, 0);
+
+ /*
+ * Loop while we have data.
+ */
+ do {
+ size_t getSize;
+ int flush;
+
+ /* only read if the input buffer is empty */
+ if (zstream.avail_in == 0 && !atEof) {
+ LOGV("+++ reading %d bytes\n", (int)kBufSize);
+ if (data) {
+ getSize = size > kBufSize ? kBufSize : size;
+ memcpy(inBuf, data, getSize);
+ data = ((const char*)data) + getSize;
+ size -= getSize;
+ } else {
+ getSize = fread(inBuf, 1, kBufSize, srcFp);
+ if (ferror(srcFp)) {
+ LOGD("deflate read failed (errno=%d)\n", errno);
+ goto z_bail;
+ }
+ }
+ if (getSize < kBufSize) {
+ LOGV("+++ got %d bytes, EOF reached\n",
+ (int)getSize);
+ atEof = true;
+ }
+
+ crc = crc32(crc, inBuf, getSize);
+
+ zstream.next_in = inBuf;
+ zstream.avail_in = getSize;
+ }
+
+ if (atEof)
+ flush = Z_FINISH; /* tell zlib that we're done */
+ else
+ flush = Z_NO_FLUSH; /* more to come! */
+
+ zerr = deflate(&zstream, flush);
+ if (zerr != Z_OK && zerr != Z_STREAM_END) {
+ LOGD("zlib deflate call failed (zerr=%d)\n", zerr);
+ result = UNKNOWN_ERROR;
+ goto z_bail;
+ }
+
+ /* write when we're full or when we're done */
+ if (zstream.avail_out == 0 ||
+ (zerr == Z_STREAM_END && zstream.avail_out != (uInt) kBufSize))
+ {
+ LOGV("+++ writing %d bytes\n", (int) (zstream.next_out - outBuf));
+ if (fwrite(outBuf, 1, zstream.next_out - outBuf, dstFp) !=
+ (size_t)(zstream.next_out - outBuf))
+ {
+ LOGD("write %d failed in deflate\n",
+ (int) (zstream.next_out - outBuf));
+ goto z_bail;
+ }
+
+ zstream.next_out = outBuf;
+ zstream.avail_out = kBufSize;
+ }
+ } while (zerr == Z_OK);
+
+ assert(zerr == Z_STREAM_END); /* other errors should've been caught */
+
+ *pCRC32 = crc;
+
+z_bail:
+ deflateEnd(&zstream); /* free up any allocated structures */
+
+bail:
+ delete[] inBuf;
+ delete[] outBuf;
+
+ return result;
+}
+
+/*
+ * Mark an entry as deleted.
+ *
+ * We will eventually need to crunch the file down, but if several files
+ * are being removed (perhaps as part of an "update" process) we can make
+ * things considerably faster by deferring the removal to "flush" time.
+ */
+status_t ZipFile::remove(ZipEntry* pEntry)
+{
+ /*
+ * Should verify that pEntry is actually part of this archive, and
+ * not some stray ZipEntry from a different file.
+ */
+
+ /* mark entry as deleted, and mark archive as dirty */
+ pEntry->setDeleted();
+ mNeedCDRewrite = true;
+ return NO_ERROR;
+}
+
+/*
+ * Flush any pending writes.
+ *
+ * In particular, this will crunch out deleted entries, and write the
+ * Central Directory and EOCD if we have stomped on them.
+ */
+status_t ZipFile::flush(void)
+{
+ status_t result = NO_ERROR;
+ long eocdPosn;
+ int i, count;
+
+ if (mReadOnly)
+ return INVALID_OPERATION;
+ if (!mNeedCDRewrite)
+ return NO_ERROR;
+
+ assert(mZipFp != NULL);
+
+ result = crunchArchive();
+ if (result != NO_ERROR)
+ return result;
+
+ if (fseek(mZipFp, mEOCD.mCentralDirOffset, SEEK_SET) != 0)
+ return UNKNOWN_ERROR;
+
+ count = mEntries.size();
+ for (i = 0; i < count; i++) {
+ ZipEntry* pEntry = mEntries[i];
+ pEntry->mCDE.write(mZipFp);
+ }
+
+ eocdPosn = ftell(mZipFp);
+ mEOCD.mCentralDirSize = eocdPosn - mEOCD.mCentralDirOffset;
+
+ mEOCD.write(mZipFp);
+
+ /*
+ * If we had some stuff bloat up during compression and get replaced
+ * with plain files, or if we deleted some entries, there's a lot
+ * of wasted space at the end of the file. Remove it now.
+ */
+ if (ftruncate(fileno(mZipFp), ftell(mZipFp)) != 0) {
+ LOGW("ftruncate failed %ld: %s\n", ftell(mZipFp), strerror(errno));
+ // not fatal
+ }
+
+ /* should we clear the "newly added" flag in all entries now? */
+
+ mNeedCDRewrite = false;
+ return NO_ERROR;
+}
+
+/*
+ * Crunch deleted files out of an archive by shifting the later files down.
+ *
+ * Because we're not using a temp file, we do the operation inside the
+ * current file.
+ */
+status_t ZipFile::crunchArchive(void)
+{
+ status_t result = NO_ERROR;
+ int i, count;
+ long delCount, adjust;
+
+#if 0
+ printf("CONTENTS:\n");
+ for (i = 0; i < (int) mEntries.size(); i++) {
+ printf(" %d: lfhOff=%ld del=%d\n",
+ i, mEntries[i]->getLFHOffset(), mEntries[i]->getDeleted());
+ }
+ printf(" END is %ld\n", (long) mEOCD.mCentralDirOffset);
+#endif
+
+ /*
+ * Roll through the set of files, shifting them as appropriate. We
+ * could probably get a slight performance improvement by sliding
+ * multiple files down at once (because we could use larger reads
+ * when operating on batches of small files), but it's not that useful.
+ */
+ count = mEntries.size();
+ delCount = adjust = 0;
+ for (i = 0; i < count; i++) {
+ ZipEntry* pEntry = mEntries[i];
+ long span;
+
+ if (pEntry->getLFHOffset() != 0) {
+ long nextOffset;
+
+ /* Get the length of this entry by finding the offset
+ * of the next entry. Directory entries don't have
+ * file offsets, so we need to find the next non-directory
+ * entry.
+ */
+ nextOffset = 0;
+ for (int ii = i+1; nextOffset == 0 && ii < count; ii++)
+ nextOffset = mEntries[ii]->getLFHOffset();
+ if (nextOffset == 0)
+ nextOffset = mEOCD.mCentralDirOffset;
+ span = nextOffset - pEntry->getLFHOffset();
+
+ assert(span >= ZipEntry::LocalFileHeader::kLFHLen);
+ } else {
+ /* This is a directory entry. It doesn't have
+ * any actual file contents, so there's no need to
+ * move anything.
+ */
+ span = 0;
+ }
+
+ //printf("+++ %d: off=%ld span=%ld del=%d [count=%d]\n",
+ // i, pEntry->getLFHOffset(), span, pEntry->getDeleted(), count);
+
+ if (pEntry->getDeleted()) {
+ adjust += span;
+ delCount++;
+
+ delete pEntry;
+ mEntries.removeAt(i);
+
+ /* adjust loop control */
+ count--;
+ i--;
+ } else if (span != 0 && adjust > 0) {
+ /* shuffle this entry back */
+ //printf("+++ Shuffling '%s' back %ld\n",
+ // pEntry->getFileName(), adjust);
+ result = filemove(mZipFp, pEntry->getLFHOffset() - adjust,
+ pEntry->getLFHOffset(), span);
+ if (result != NO_ERROR) {
+ /* this is why you use a temp file */
+ LOGE("error during crunch - archive is toast\n");
+ return result;
+ }
+
+ pEntry->setLFHOffset(pEntry->getLFHOffset() - adjust);
+ }
+ }
+
+ /*
+ * Fix EOCD info. We have to wait until the end to do some of this
+ * because we use mCentralDirOffset to determine "span" for the
+ * last entry.
+ */
+ mEOCD.mCentralDirOffset -= adjust;
+ mEOCD.mNumEntries -= delCount;
+ mEOCD.mTotalNumEntries -= delCount;
+ mEOCD.mCentralDirSize = 0; // mark invalid; set by flush()
+
+ assert(mEOCD.mNumEntries == mEOCD.mTotalNumEntries);
+ assert(mEOCD.mNumEntries == count);
+
+ return result;
+}
+
+/*
+ * Works like memmove(), but on pieces of a file.
+ */
+status_t ZipFile::filemove(FILE* fp, off_t dst, off_t src, size_t n)
+{
+ if (dst == src || n <= 0)
+ return NO_ERROR;
+
+ unsigned char readBuf[32768];
+
+ if (dst < src) {
+ /* shift stuff toward start of file; must read from start */
+ while (n != 0) {
+ size_t getSize = sizeof(readBuf);
+ if (getSize > n)
+ getSize = n;
+
+ if (fseek(fp, (long) src, SEEK_SET) != 0) {
+ LOGD("filemove src seek %ld failed\n", (long) src);
+ return UNKNOWN_ERROR;
+ }
+
+ if (fread(readBuf, 1, getSize, fp) != getSize) {
+ LOGD("filemove read %ld off=%ld failed\n",
+ (long) getSize, (long) src);
+ return UNKNOWN_ERROR;
+ }
+
+ if (fseek(fp, (long) dst, SEEK_SET) != 0) {
+ LOGD("filemove dst seek %ld failed\n", (long) dst);
+ return UNKNOWN_ERROR;
+ }
+
+ if (fwrite(readBuf, 1, getSize, fp) != getSize) {
+ LOGD("filemove write %ld off=%ld failed\n",
+ (long) getSize, (long) dst);
+ return UNKNOWN_ERROR;
+ }
+
+ src += getSize;
+ dst += getSize;
+ n -= getSize;
+ }
+ } else {
+ /* shift stuff toward end of file; must read from end */
+ assert(false); // write this someday, maybe
+ return UNKNOWN_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+
+/*
+ * Get the modification time from a file descriptor.
+ */
+time_t ZipFile::getModTime(int fd)
+{
+ struct stat sb;
+
+ if (fstat(fd, &sb) < 0) {
+ LOGD("HEY: fstat on fd %d failed\n", fd);
+ return (time_t) -1;
+ }
+
+ return sb.st_mtime;
+}
+
+
+#if 0 /* this is a bad idea */
+/*
+ * Get a copy of the Zip file descriptor.
+ *
+ * We don't allow this if the file was opened read-write because we tend
+ * to leave the file contents in an uncertain state between calls to
+ * flush(). The duplicated file descriptor should only be valid for reads.
+ */
+int ZipFile::getZipFd(void) const
+{
+ if (!mReadOnly)
+ return INVALID_OPERATION;
+ assert(mZipFp != NULL);
+
+ int fd;
+ fd = dup(fileno(mZipFp));
+ if (fd < 0) {
+ LOGD("didn't work, errno=%d\n", errno);
+ }
+
+ return fd;
+}
+#endif
+
+
+#if 0
+/*
+ * Expand data.
+ */
+bool ZipFile::uncompress(const ZipEntry* pEntry, void* buf) const
+{
+ return false;
+}
+#endif
+
+// free the memory when you're done
+void* ZipFile::uncompress(const ZipEntry* entry)
+{
+ size_t unlen = entry->getUncompressedLen();
+ size_t clen = entry->getCompressedLen();
+
+ void* buf = malloc(unlen);
+ if (buf == NULL) {
+ return NULL;
+ }
+
+ fseek(mZipFp, 0, SEEK_SET);
+
+ off_t offset = entry->getFileOffset();
+ if (fseek(mZipFp, offset, SEEK_SET) != 0) {
+ goto bail;
+ }
+
+ switch (entry->getCompressionMethod())
+ {
+ case ZipEntry::kCompressStored: {
+ ssize_t amt = fread(buf, 1, unlen, mZipFp);
+ if (amt != (ssize_t)unlen) {
+ goto bail;
+ }
+#if 0
+ printf("data...\n");
+ const unsigned char* p = (unsigned char*)buf;
+ const unsigned char* end = p+unlen;
+ for (int i=0; i<32 && p < end; i++) {
+ printf("0x%08x ", (int)(offset+(i*0x10)));
+ for (int j=0; j<0x10 && p < end; j++) {
+ printf(" %02x", *p);
+ p++;
+ }
+ printf("\n");
+ }
+#endif
+
+ }
+ break;
+ case ZipEntry::kCompressDeflated: {
+ if (!ZipUtils::inflateToBuffer(mZipFp, buf, unlen, clen)) {
+ goto bail;
+ }
+ }
+ break;
+ default:
+ goto bail;
+ }
+ return buf;
+
+bail:
+ free(buf);
+ return NULL;
+}
+
+
+/*
+ * ===========================================================================
+ * ZipFile::EndOfCentralDir
+ * ===========================================================================
+ */
+
+/*
+ * Read the end-of-central-dir fields.
+ *
+ * "buf" should be positioned at the EOCD signature, and should contain
+ * the entire EOCD area including the comment.
+ */
+status_t ZipFile::EndOfCentralDir::readBuf(const unsigned char* buf, int len)
+{
+ /* don't allow re-use */
+ assert(mComment == NULL);
+
+ if (len < kEOCDLen) {
+ /* looks like ZIP file got truncated */
+ LOGD(" Zip EOCD: expected >= %d bytes, found %d\n",
+ kEOCDLen, len);
+ return INVALID_OPERATION;
+ }
+
+ /* this should probably be an assert() */
+ if (ZipEntry::getLongLE(&buf[0x00]) != kSignature)
+ return UNKNOWN_ERROR;
+
+ mDiskNumber = ZipEntry::getShortLE(&buf[0x04]);
+ mDiskWithCentralDir = ZipEntry::getShortLE(&buf[0x06]);
+ mNumEntries = ZipEntry::getShortLE(&buf[0x08]);
+ mTotalNumEntries = ZipEntry::getShortLE(&buf[0x0a]);
+ mCentralDirSize = ZipEntry::getLongLE(&buf[0x0c]);
+ mCentralDirOffset = ZipEntry::getLongLE(&buf[0x10]);
+ mCommentLen = ZipEntry::getShortLE(&buf[0x14]);
+
+ // TODO: validate mCentralDirOffset
+
+ if (mCommentLen > 0) {
+ if (kEOCDLen + mCommentLen > len) {
+ LOGD("EOCD(%d) + comment(%d) exceeds len (%d)\n",
+ kEOCDLen, mCommentLen, len);
+ return UNKNOWN_ERROR;
+ }
+ mComment = new unsigned char[mCommentLen];
+ memcpy(mComment, buf + kEOCDLen, mCommentLen);
+ }
+
+ return NO_ERROR;
+}
+
+/*
+ * Write an end-of-central-directory section.
+ */
+status_t ZipFile::EndOfCentralDir::write(FILE* fp)
+{
+ unsigned char buf[kEOCDLen];
+
+ ZipEntry::putLongLE(&buf[0x00], kSignature);
+ ZipEntry::putShortLE(&buf[0x04], mDiskNumber);
+ ZipEntry::putShortLE(&buf[0x06], mDiskWithCentralDir);
+ ZipEntry::putShortLE(&buf[0x08], mNumEntries);
+ ZipEntry::putShortLE(&buf[0x0a], mTotalNumEntries);
+ ZipEntry::putLongLE(&buf[0x0c], mCentralDirSize);
+ ZipEntry::putLongLE(&buf[0x10], mCentralDirOffset);
+ ZipEntry::putShortLE(&buf[0x14], mCommentLen);
+
+ if (fwrite(buf, 1, kEOCDLen, fp) != kEOCDLen)
+ return UNKNOWN_ERROR;
+ if (mCommentLen > 0) {
+ assert(mComment != NULL);
+ if (fwrite(mComment, mCommentLen, 1, fp) != mCommentLen)
+ return UNKNOWN_ERROR;
+ }
+
+ return NO_ERROR;
+}
+
+/*
+ * Dump the contents of an EndOfCentralDir object.
+ */
+void ZipFile::EndOfCentralDir::dump(void) const
+{
+ LOGD(" EndOfCentralDir contents:\n");
+ LOGD(" diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u\n",
+ mDiskNumber, mDiskWithCentralDir, mNumEntries, mTotalNumEntries);
+ LOGD(" centDirSize=%lu centDirOff=%lu commentLen=%u\n",
+ mCentralDirSize, mCentralDirOffset, mCommentLen);
+}
+
diff --git a/tools/aapt/ZipFile.h b/tools/aapt/ZipFile.h
new file mode 100644
index 000000000000..dbbd072d1692
--- /dev/null
+++ b/tools/aapt/ZipFile.h
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// General-purpose Zip archive access. This class allows both reading and
+// writing to Zip archives, including deletion of existing entries.
+//
+#ifndef __LIBS_ZIPFILE_H
+#define __LIBS_ZIPFILE_H
+
+#include
+#include
+#include
+
+#include "ZipEntry.h"
+
+namespace android {
+
+/*
+ * Manipulate a Zip archive.
+ *
+ * Some changes will not be visible in the until until "flush" is called.
+ *
+ * The correct way to update a file archive is to make all changes to a
+ * copy of the archive in a temporary file, and then unlink/rename over
+ * the original after everything completes. Because we're only interested
+ * in using this for packaging, we don't worry about such things. Crashing
+ * after making changes and before flush() completes could leave us with
+ * an unusable Zip archive.
+ */
+class ZipFile {
+public:
+ ZipFile(void)
+ : mZipFp(NULL), mReadOnly(false), mNeedCDRewrite(false)
+ {}
+ ~ZipFile(void) {
+ if (!mReadOnly)
+ flush();
+ if (mZipFp != NULL)
+ fclose(mZipFp);
+ discardEntries();
+ }
+
+ /*
+ * Open a new or existing archive.
+ */
+ typedef enum {
+ kOpenReadOnly = 0x01,
+ kOpenReadWrite = 0x02,
+ kOpenCreate = 0x04, // create if it doesn't exist
+ kOpenTruncate = 0x08, // if it exists, empty it
+ };
+ status_t open(const char* zipFileName, int flags);
+
+ /*
+ * Add a file to the end of the archive. Specify whether you want the
+ * library to try to store it compressed.
+ *
+ * If "storageName" is specified, the archive will use that instead
+ * of "fileName".
+ *
+ * If there is already an entry with the same name, the call fails.
+ * Existing entries with the same name must be removed first.
+ *
+ * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+ */
+ status_t add(const char* fileName, int compressionMethod,
+ ZipEntry** ppEntry)
+ {
+ return add(fileName, fileName, compressionMethod, ppEntry);
+ }
+ status_t add(const char* fileName, const char* storageName,
+ int compressionMethod, ZipEntry** ppEntry)
+ {
+ return addCommon(fileName, NULL, 0, storageName,
+ ZipEntry::kCompressStored,
+ compressionMethod, ppEntry);
+ }
+
+ /*
+ * Add a file that is already compressed with gzip.
+ *
+ * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+ */
+ status_t addGzip(const char* fileName, const char* storageName,
+ ZipEntry** ppEntry)
+ {
+ return addCommon(fileName, NULL, 0, storageName,
+ ZipEntry::kCompressDeflated,
+ ZipEntry::kCompressDeflated, ppEntry);
+ }
+
+ /*
+ * Add a file from an in-memory data buffer.
+ *
+ * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+ */
+ status_t add(const void* data, size_t size, const char* storageName,
+ int compressionMethod, ZipEntry** ppEntry)
+ {
+ return addCommon(NULL, data, size, storageName,
+ ZipEntry::kCompressStored,
+ compressionMethod, ppEntry);
+ }
+
+ /*
+ * Add an entry by copying it from another zip file. If "padding" is
+ * nonzero, the specified number of bytes will be added to the "extra"
+ * field in the header.
+ *
+ * If "ppEntry" is non-NULL, a pointer to the new entry will be returned.
+ */
+ status_t add(const ZipFile* pSourceZip, const ZipEntry* pSourceEntry,
+ int padding, ZipEntry** ppEntry);
+
+ /*
+ * Mark an entry as having been removed. It is not actually deleted
+ * from the archive or our internal data structures until flush() is
+ * called.
+ */
+ status_t remove(ZipEntry* pEntry);
+
+ /*
+ * Flush changes. If mNeedCDRewrite is set, this writes the central dir.
+ */
+ status_t flush(void);
+
+ /*
+ * Expand the data into the buffer provided. The buffer must hold
+ * at least bytes. Variation expands directly
+ * to a file.
+ *
+ * Returns "false" if an error was encountered in the compressed data.
+ */
+ //bool uncompress(const ZipEntry* pEntry, void* buf) const;
+ //bool uncompress(const ZipEntry* pEntry, FILE* fp) const;
+ void* uncompress(const ZipEntry* pEntry);
+
+ /*
+ * Get an entry, by name. Returns NULL if not found.
+ *
+ * Does not return entries pending deletion.
+ */
+ ZipEntry* getEntryByName(const char* fileName) const;
+
+ /*
+ * Get the Nth entry in the archive.
+ *
+ * This will return an entry that is pending deletion.
+ */
+ int getNumEntries(void) const { return mEntries.size(); }
+ ZipEntry* getEntryByIndex(int idx) const;
+
+private:
+ /* these are private and not defined */
+ ZipFile(const ZipFile& src);
+ ZipFile& operator=(const ZipFile& src);
+
+ class EndOfCentralDir {
+ public:
+ EndOfCentralDir(void) :
+ mDiskNumber(0),
+ mDiskWithCentralDir(0),
+ mNumEntries(0),
+ mTotalNumEntries(0),
+ mCentralDirSize(0),
+ mCentralDirOffset(0),
+ mCommentLen(0),
+ mComment(NULL)
+ {}
+ virtual ~EndOfCentralDir(void) {
+ delete[] mComment;
+ }
+
+ status_t readBuf(const unsigned char* buf, int len);
+ status_t write(FILE* fp);
+
+ //unsigned long mSignature;
+ unsigned short mDiskNumber;
+ unsigned short mDiskWithCentralDir;
+ unsigned short mNumEntries;
+ unsigned short mTotalNumEntries;
+ unsigned long mCentralDirSize;
+ unsigned long mCentralDirOffset; // offset from first disk
+ unsigned short mCommentLen;
+ unsigned char* mComment;
+
+ enum {
+ kSignature = 0x06054b50,
+ kEOCDLen = 22, // EndOfCentralDir len, excl. comment
+
+ kMaxCommentLen = 65535, // longest possible in ushort
+ kMaxEOCDSearch = kMaxCommentLen + EndOfCentralDir::kEOCDLen,
+
+ };
+
+ void dump(void) const;
+ };
+
+
+ /* read all entries in the central dir */
+ status_t readCentralDir(void);
+
+ /* crunch deleted entries out */
+ status_t crunchArchive(void);
+
+ /* clean up mEntries */
+ void discardEntries(void);
+
+ /* common handler for all "add" functions */
+ status_t addCommon(const char* fileName, const void* data, size_t size,
+ const char* storageName, int sourceType, int compressionMethod,
+ ZipEntry** ppEntry);
+
+ /* copy all of "srcFp" into "dstFp" */
+ status_t copyFpToFp(FILE* dstFp, FILE* srcFp, unsigned long* pCRC32);
+ /* copy all of "data" into "dstFp" */
+ status_t copyDataToFp(FILE* dstFp,
+ const void* data, size_t size, unsigned long* pCRC32);
+ /* copy some of "srcFp" into "dstFp" */
+ status_t copyPartialFpToFp(FILE* dstFp, FILE* srcFp, long length,
+ unsigned long* pCRC32);
+ /* like memmove(), but on parts of a single file */
+ status_t filemove(FILE* fp, off_t dest, off_t src, size_t n);
+ /* compress all of "srcFp" into "dstFp", using Deflate */
+ status_t compressFpToFp(FILE* dstFp, FILE* srcFp,
+ const void* data, size_t size, unsigned long* pCRC32);
+
+ /* get modification date from a file descriptor */
+ time_t getModTime(int fd);
+
+ /*
+ * We use stdio FILE*, which gives us buffering but makes dealing
+ * with files >2GB awkward. Until we support Zip64, we're fine.
+ */
+ FILE* mZipFp; // Zip file pointer
+
+ /* one of these per file */
+ EndOfCentralDir mEOCD;
+
+ /* did we open this read-only? */
+ bool mReadOnly;
+
+ /* set this when we trash the central dir */
+ bool mNeedCDRewrite;
+
+ /*
+ * One ZipEntry per entry in the zip file. I'm using pointers instead
+ * of objects because it's easier than making operator= work for the
+ * classes and sub-classes.
+ */
+ Vector mEntries;
+};
+
+}; // namespace android
+
+#endif // __LIBS_ZIPFILE_H
--
cgit v1.2.3-59-g8ed1b
From e583a4ea8c90105eee9b408d39bca3a4af6a2569 Mon Sep 17 00:00:00 2001
From: Mathias Agopian
Date: Fri, 5 Jun 2009 15:11:23 -0700
Subject: get rid of LogSocket which wasn't even implemented (enabled)
---
include/utils/LogSocket.h | 20 -------
libs/utils/Android.mk | 3 +-
libs/utils/LogSocket.cpp | 129 ----------------------------------------------
3 files changed, 1 insertion(+), 151 deletions(-)
delete mode 100644 include/utils/LogSocket.h
delete mode 100644 libs/utils/LogSocket.cpp
diff --git a/include/utils/LogSocket.h b/include/utils/LogSocket.h
deleted file mode 100644
index 01fbfb50e33a..000000000000
--- a/include/utils/LogSocket.h
+++ /dev/null
@@ -1,20 +0,0 @@
-/* utils/LogSocket.h
-**
-** Copyright 2008, The Android Open Source Project
-**
-** This file is dual licensed. It may be redistributed and/or modified
-** under the terms of the Apache 2.0 License OR version 2 of the GNU
-** General Public License.
-*/
-
-#ifndef _UTILS_LOGSOCKET_H
-#define _UTILS_LOGSOCKET_H
-
-#define SOCKET_CLOSE_LOCAL 0
-
-void add_send_stats(int fd, int send);
-void add_recv_stats(int fd, int recv);
-void log_socket_close(int fd, short reason);
-void log_socket_connect(int fd, unsigned int ip, unsigned short port);
-
-#endif /* _UTILS_LOGSOCKET_H */
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 70d440797305..3f5cb85ac841 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -41,8 +41,7 @@ commonSources:= \
ZipFileCRO.cpp \
ZipFileRO.cpp \
ZipUtils.cpp \
- misc.cpp \
- LogSocket.cpp
+ misc.cpp
# For the host
diff --git a/libs/utils/LogSocket.cpp b/libs/utils/LogSocket.cpp
deleted file mode 100644
index 55c1b99aff96..000000000000
--- a/libs/utils/LogSocket.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-#ifndef HAVE_WINSOCK
-//#define SOCKETLOG
-#endif
-
-#ifdef SOCKETLOG
-
-#define LOG_TAG "SOCKETLOG"
-
-#include
-#include
-#include "utils/LogSocket.h"
-#include "utils/logger.h"
-#include "cutils/hashmap.h"
-
-// defined in //device/data/etc/event-log-tags
-#define SOCKET_CLOSE_LOG 51000
-
-static Hashmap* statsMap = NULL;
-
-#define LOG_LIST_NUMBER 5
-
-typedef struct SocketStats {
- int fd;
- unsigned int send;
- unsigned int recv;
- unsigned int ip;
- unsigned short port;
- short reason;
-}SocketStats;
-
-SocketStats *get_socket_stats(int fd) {
- if (statsMap == NULL) {
- statsMap = hashmapCreate(8, &hashmapIntHash, &hashmapIntEquals);
- }
-
- SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd);
- if (s == NULL) {
- // LOGD("create SocketStats for fd %d", fd);
- s = (SocketStats*) malloc(sizeof(SocketStats));
- memset(s, 0, sizeof(SocketStats));
- s->fd = fd;
- hashmapPut(statsMap, &s->fd, s);
- }
- return s;
-}
-
-void log_socket_connect(int fd, unsigned int ip, unsigned short port) {
- // LOGD("log_socket_connect for fd %d ip %d port%d", fd, ip, port);
- SocketStats *s = get_socket_stats(fd);
- s->ip = ip;
- s->port = port;
-}
-
-void add_send_stats(int fd, int send) {
- if (send <=0) {
- LOGE("add_send_stats send %d", send);
- return;
- }
- SocketStats *s = get_socket_stats(fd);
- s->send += send;
- // LOGD("add_send_stats for fd %d ip %d port%d", fd, s->ip, s->port);
-}
-
-void add_recv_stats(int fd, int recv) {
- if (recv <=0) {
- LOGE("add_recv_stats recv %d", recv);
- return;
- }
- SocketStats *s = get_socket_stats(fd);
- s->recv += recv;
- // LOGD("add_recv_stats for fd %d ip %d port%d", fd, s->ip, s->port);
-}
-
-char* put_int(char* buf, int value) {
- *buf = EVENT_TYPE_INT;
- buf++;
- memcpy(buf, &value, sizeof(int));
- return buf + sizeof(int);
-}
-
-void log_socket_close(int fd, short reason) {
- if (statsMap) {
- SocketStats *s = (SocketStats*) hashmapGet(statsMap, &fd);
- if (s != NULL) {
- if (s->send != 0 || s->recv != 0) {
- s->reason = reason;
- // 5 int + list type need 2 bytes
- char buf[LOG_LIST_NUMBER * 5 + 2];
- buf[0] = EVENT_TYPE_LIST;
- buf[1] = LOG_LIST_NUMBER;
- char* writePos = buf + 2;
- writePos = put_int(writePos, s->send);
- writePos = put_int(writePos, s->recv);
- writePos = put_int(writePos, s->ip);
- writePos = put_int(writePos, s->port);
- writePos = put_int(writePos, s->reason);
-
- android_bWriteLog(SOCKET_CLOSE_LOG, buf, sizeof(buf));
- // LOGD("send %d recv %d reason %d", s->send, s->recv, s->reason);
- }
- hashmapRemove(statsMap, &s->fd);
- free(s);
- }
- }
-}
-
-#else
-void add_send_stats(int fd, int send) {}
-void add_recv_stats(int fd, int recv) {}
-void log_socket_close(int fd, short reason) {}
-void log_socket_connect(int fd, unsigned int ip, unsigned short port) {}
-#endif
--
cgit v1.2.3-59-g8ed1b