You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by fi...@apache.org on 2013/05/24 01:41:53 UTC
[32/38] updated android, ios,
bb libraries to 2.8.x branch. fixed a few assertions with project
changes. removed blackberry support until create script can be finalized.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-android/framework/src/com/squareup/okhttp/internal/spdy/SpdyConnection.java
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/src/com/squareup/okhttp/internal/spdy/SpdyConnection.java b/lib/cordova-android/framework/src/com/squareup/okhttp/internal/spdy/SpdyConnection.java
new file mode 100644
index 0000000..fccd14f
--- /dev/null
+++ b/lib/cordova-android/framework/src/com/squareup/okhttp/internal/spdy/SpdyConnection.java
@@ -0,0 +1,579 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.squareup.okhttp.internal.spdy;
+
+import com.squareup.okhttp.internal.NamedRunnable;
+import com.squareup.okhttp.internal.Util;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import static java.util.concurrent.Executors.defaultThreadFactory;
+
+/**
+ * A socket connection to a remote peer. A connection hosts streams which can
+ * send and receive data.
+ *
+ * <p>Many methods in this API are <strong>synchronous:</strong> the call is
+ * completed before the method returns. This is typical for Java but atypical
+ * for SPDY. This is motivated by exception transparency: an IOException that
+ * was triggered by a certain caller can be caught and handled by that caller.
+ */
+public final class SpdyConnection implements Closeable {
+
+ // Internal state of this connection is guarded by 'this'. No blocking
+ // operations may be performed while holding this lock!
+ //
+ // Socket writes are guarded by spdyWriter.
+ //
+ // Socket reads are unguarded but are only made by the reader thread.
+ //
+ // Certain operations (like SYN_STREAM) need to synchronize on both the
+ // spdyWriter (to do blocking I/O) and this (to create streams). Such
+ // operations must synchronize on 'this' last. This ensures that we never
+ // wait for a blocking operation while holding 'this'.
+
+ static final int FLAG_FIN = 0x1;
+ static final int FLAG_UNIDIRECTIONAL = 0x2;
+
+ static final int TYPE_DATA = 0x0;
+ static final int TYPE_SYN_STREAM = 0x1;
+ static final int TYPE_SYN_REPLY = 0x2;
+ static final int TYPE_RST_STREAM = 0x3;
+ static final int TYPE_SETTINGS = 0x4;
+ static final int TYPE_NOOP = 0x5;
+ static final int TYPE_PING = 0x6;
+ static final int TYPE_GOAWAY = 0x7;
+ static final int TYPE_HEADERS = 0x8;
+ static final int TYPE_WINDOW_UPDATE = 0x9;
+ static final int TYPE_CREDENTIAL = 0x10;
+ static final int VERSION = 3;
+
+ static final int GOAWAY_OK = 0;
+ static final int GOAWAY_PROTOCOL_ERROR = 1;
+ static final int GOAWAY_INTERNAL_ERROR = 2;
+
+ private static final ExecutorService executor =
+ new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
+ new SynchronousQueue<Runnable>(), defaultThreadFactory());
+
+ /** True if this peer initiated the connection. */
+ final boolean client;
+
+ /**
+ * User code to run in response to an incoming stream. Callbacks must not be
+ * run on the callback executor.
+ */
+ private final IncomingStreamHandler handler;
+ private final SpdyReader spdyReader;
+ private final SpdyWriter spdyWriter;
+
+ private final Map<Integer, SpdyStream> streams = new HashMap<Integer, SpdyStream>();
+ private final String hostName;
+ private int lastGoodStreamId;
+ private int nextStreamId;
+ private boolean shutdown;
+ private long idleStartTimeNs = System.nanoTime();
+
+ /** Lazily-created map of in-flight pings awaiting a response. Guarded by this. */
+ private Map<Integer, Ping> pings;
+ private int nextPingId;
+
+ /** Lazily-created settings for this connection. */
+ Settings settings;
+
+ private SpdyConnection(Builder builder) {
+ client = builder.client;
+ handler = builder.handler;
+ spdyReader = new SpdyReader(builder.in);
+ spdyWriter = new SpdyWriter(builder.out);
+ nextStreamId = builder.client ? 1 : 2;
+ nextPingId = builder.client ? 1 : 2;
+
+ hostName = builder.hostName;
+
+ new Thread(new Reader(), "Spdy Reader " + hostName).start();
+ }
+
+ /**
+ * Returns the number of {@link SpdyStream#isOpen() open streams} on this
+ * connection.
+ */
+ public synchronized int openStreamCount() {
+ return streams.size();
+ }
+
+ private synchronized SpdyStream getStream(int id) {
+ return streams.get(id);
+ }
+
+ synchronized SpdyStream removeStream(int streamId) {
+ SpdyStream stream = streams.remove(streamId);
+ if (stream != null && streams.isEmpty()) {
+ setIdle(true);
+ }
+ return stream;
+ }
+
+ private synchronized void setIdle(boolean value) {
+ idleStartTimeNs = value ? System.nanoTime() : 0L;
+ }
+
+ /** Returns true if this connection is idle. */
+ public synchronized boolean isIdle() {
+ return idleStartTimeNs != 0L;
+ }
+
+ /** Returns the time in ns when this connection became idle or 0L if connection is not idle. */
+ public synchronized long getIdleStartTimeNs() {
+ return idleStartTimeNs;
+ }
+
+ /**
+ * Returns a new locally-initiated stream.
+ *
+ * @param out true to create an output stream that we can use to send data
+ * to the remote peer. Corresponds to {@code FLAG_FIN}.
+ * @param in true to create an input stream that the remote peer can use to
+ * send data to us. Corresponds to {@code FLAG_UNIDIRECTIONAL}.
+ */
+ public SpdyStream newStream(List<String> requestHeaders, boolean out, boolean in)
+ throws IOException {
+ int flags = (out ? 0 : FLAG_FIN) | (in ? 0 : FLAG_UNIDIRECTIONAL);
+ int associatedStreamId = 0; // TODO: permit the caller to specify an associated stream?
+ int priority = 0; // TODO: permit the caller to specify a priority?
+ int slot = 0; // TODO: permit the caller to specify a slot?
+ SpdyStream stream;
+ int streamId;
+
+ synchronized (spdyWriter) {
+ synchronized (this) {
+ if (shutdown) {
+ throw new IOException("shutdown");
+ }
+ streamId = nextStreamId;
+ nextStreamId += 2;
+ stream = new SpdyStream(streamId, this, flags, priority, slot, requestHeaders, settings);
+ if (stream.isOpen()) {
+ streams.put(streamId, stream);
+ setIdle(false);
+ }
+ }
+
+ spdyWriter.synStream(flags, streamId, associatedStreamId, priority, slot, requestHeaders);
+ }
+
+ return stream;
+ }
+
+ void writeSynReply(int streamId, int flags, List<String> alternating) throws IOException {
+ spdyWriter.synReply(flags, streamId, alternating);
+ }
+
+ /** Writes a complete data frame. */
+ void writeFrame(byte[] bytes, int offset, int length) throws IOException {
+ synchronized (spdyWriter) {
+ spdyWriter.out.write(bytes, offset, length);
+ }
+ }
+
+ void writeSynResetLater(final int streamId, final int statusCode) {
+ executor.submit(
+ new NamedRunnable(String.format("Spdy Writer %s stream %d", hostName, streamId)) {
+ @Override public void execute() {
+ try {
+ writeSynReset(streamId, statusCode);
+ } catch (IOException ignored) {
+ }
+ }
+ });
+ }
+
+ void writeSynReset(int streamId, int statusCode) throws IOException {
+ spdyWriter.rstStream(streamId, statusCode);
+ }
+
+ void writeWindowUpdateLater(final int streamId, final int deltaWindowSize) {
+ executor.submit(
+ new NamedRunnable(String.format("Spdy Writer %s stream %d", hostName, streamId)) {
+ @Override public void execute() {
+ try {
+ writeWindowUpdate(streamId, deltaWindowSize);
+ } catch (IOException ignored) {
+ }
+ }
+ });
+ }
+
+ void writeWindowUpdate(int streamId, int deltaWindowSize) throws IOException {
+ spdyWriter.windowUpdate(streamId, deltaWindowSize);
+ }
+
+ /**
+ * Sends a ping frame to the peer. Use the returned object to await the
+ * ping's response and observe its round trip time.
+ */
+ public Ping ping() throws IOException {
+ Ping ping = new Ping();
+ int pingId;
+ synchronized (this) {
+ if (shutdown) {
+ throw new IOException("shutdown");
+ }
+ pingId = nextPingId;
+ nextPingId += 2;
+ if (pings == null) pings = new HashMap<Integer, Ping>();
+ pings.put(pingId, ping);
+ }
+ writePing(pingId, ping);
+ return ping;
+ }
+
+ private void writePingLater(final int streamId, final Ping ping) {
+ executor.submit(new NamedRunnable(String.format("Spdy Writer %s ping %d", hostName, streamId)) {
+ @Override public void execute() {
+ try {
+ writePing(streamId, ping);
+ } catch (IOException ignored) {
+ }
+ }
+ });
+ }
+
+ private void writePing(int id, Ping ping) throws IOException {
+ synchronized (spdyWriter) {
+ // Observe the sent time immediately before performing I/O.
+ if (ping != null) ping.send();
+ spdyWriter.ping(0, id);
+ }
+ }
+
+ private synchronized Ping removePing(int id) {
+ return pings != null ? pings.remove(id) : null;
+ }
+
+ /** Sends a noop frame to the peer. */
+ public void noop() throws IOException {
+ spdyWriter.noop();
+ }
+
+ public void flush() throws IOException {
+ synchronized (spdyWriter) {
+ spdyWriter.out.flush();
+ }
+ }
+
+ /**
+ * Degrades this connection such that new streams can neither be created
+ * locally, nor accepted from the remote peer. Existing streams are not
+ * impacted. This is intended to permit an endpoint to gracefully stop
+ * accepting new requests without harming previously established streams.
+ *
+ * @param statusCode one of {@link #GOAWAY_OK}, {@link
+ * #GOAWAY_INTERNAL_ERROR} or {@link #GOAWAY_PROTOCOL_ERROR}.
+ */
+ public void shutdown(int statusCode) throws IOException {
+ synchronized (spdyWriter) {
+ int lastGoodStreamId;
+ synchronized (this) {
+ if (shutdown) {
+ return;
+ }
+ shutdown = true;
+ lastGoodStreamId = this.lastGoodStreamId;
+ }
+ spdyWriter.goAway(0, lastGoodStreamId, statusCode);
+ }
+ }
+
+ /**
+ * Closes this connection. This cancels all open streams and unanswered
+ * pings. It closes the underlying input and output streams and shuts down
+ * internal executor services.
+ */
+ @Override public void close() throws IOException {
+ close(GOAWAY_OK, SpdyStream.RST_CANCEL);
+ }
+
+ private void close(int shutdownStatusCode, int rstStatusCode) throws IOException {
+ assert (!Thread.holdsLock(this));
+ IOException thrown = null;
+ try {
+ shutdown(shutdownStatusCode);
+ } catch (IOException e) {
+ thrown = e;
+ }
+
+ SpdyStream[] streamsToClose = null;
+ Ping[] pingsToCancel = null;
+ synchronized (this) {
+ if (!streams.isEmpty()) {
+ streamsToClose = streams.values().toArray(new SpdyStream[streams.size()]);
+ streams.clear();
+ setIdle(false);
+ }
+ if (pings != null) {
+ pingsToCancel = pings.values().toArray(new Ping[pings.size()]);
+ pings = null;
+ }
+ }
+
+ if (streamsToClose != null) {
+ for (SpdyStream stream : streamsToClose) {
+ try {
+ stream.close(rstStatusCode);
+ } catch (IOException e) {
+ if (thrown != null) thrown = e;
+ }
+ }
+ }
+
+ if (pingsToCancel != null) {
+ for (Ping ping : pingsToCancel) {
+ ping.cancel();
+ }
+ }
+
+ try {
+ spdyReader.close();
+ } catch (IOException e) {
+ thrown = e;
+ }
+ try {
+ spdyWriter.close();
+ } catch (IOException e) {
+ if (thrown == null) thrown = e;
+ }
+
+ if (thrown != null) throw thrown;
+ }
+
+ public static class Builder {
+ private String hostName;
+ private InputStream in;
+ private OutputStream out;
+ private IncomingStreamHandler handler = IncomingStreamHandler.REFUSE_INCOMING_STREAMS;
+ public boolean client;
+
+ public Builder(boolean client, Socket socket) throws IOException {
+ this("", client, socket.getInputStream(), socket.getOutputStream());
+ }
+
+ public Builder(boolean client, InputStream in, OutputStream out) {
+ this("", client, in, out);
+ }
+
+ /**
+ * @param client true if this peer initiated the connection; false if
+ * this peer accepted the connection.
+ */
+ public Builder(String hostName, boolean client, Socket socket) throws IOException {
+ this(hostName, client, socket.getInputStream(), socket.getOutputStream());
+ }
+
+ /**
+ * @param client true if this peer initiated the connection; false if this
+ * peer accepted the connection.
+ */
+ public Builder(String hostName, boolean client, InputStream in, OutputStream out) {
+ this.hostName = hostName;
+ this.client = client;
+ this.in = in;
+ this.out = out;
+ }
+
+ public Builder handler(IncomingStreamHandler handler) {
+ this.handler = handler;
+ return this;
+ }
+
+ public SpdyConnection build() {
+ return new SpdyConnection(this);
+ }
+ }
+
+ private class Reader implements Runnable, SpdyReader.Handler {
+ @Override public void run() {
+ int shutdownStatusCode = GOAWAY_INTERNAL_ERROR;
+ int rstStatusCode = SpdyStream.RST_INTERNAL_ERROR;
+ try {
+ while (spdyReader.nextFrame(this)) {
+ }
+ shutdownStatusCode = GOAWAY_OK;
+ rstStatusCode = SpdyStream.RST_CANCEL;
+ } catch (IOException e) {
+ shutdownStatusCode = GOAWAY_PROTOCOL_ERROR;
+ rstStatusCode = SpdyStream.RST_PROTOCOL_ERROR;
+ } finally {
+ try {
+ close(shutdownStatusCode, rstStatusCode);
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
+ @Override public void data(int flags, int streamId, InputStream in, int length)
+ throws IOException {
+ SpdyStream dataStream = getStream(streamId);
+ if (dataStream == null) {
+ writeSynResetLater(streamId, SpdyStream.RST_INVALID_STREAM);
+ Util.skipByReading(in, length);
+ return;
+ }
+ dataStream.receiveData(in, length);
+ if ((flags & SpdyConnection.FLAG_FIN) != 0) {
+ dataStream.receiveFin();
+ }
+ }
+
+ @Override
+ public void synStream(int flags, int streamId, int associatedStreamId, int priority, int slot,
+ List<String> nameValueBlock) {
+ final SpdyStream synStream;
+ final SpdyStream previous;
+ synchronized (SpdyConnection.this) {
+ synStream =
+ new SpdyStream(streamId, SpdyConnection.this, flags, priority, slot, nameValueBlock,
+ settings);
+ if (shutdown) {
+ return;
+ }
+ lastGoodStreamId = streamId;
+ previous = streams.put(streamId, synStream);
+ }
+ if (previous != null) {
+ previous.closeLater(SpdyStream.RST_PROTOCOL_ERROR);
+ removeStream(streamId);
+ return;
+ }
+
+ executor.submit(
+ new NamedRunnable(String.format("Callback %s stream %d", hostName, streamId)) {
+ @Override public void execute() {
+ try {
+ handler.receive(synStream);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ }
+
+ @Override public void synReply(int flags, int streamId, List<String> nameValueBlock)
+ throws IOException {
+ SpdyStream replyStream = getStream(streamId);
+ if (replyStream == null) {
+ writeSynResetLater(streamId, SpdyStream.RST_INVALID_STREAM);
+ return;
+ }
+ replyStream.receiveReply(nameValueBlock);
+ if ((flags & SpdyConnection.FLAG_FIN) != 0) {
+ replyStream.receiveFin();
+ }
+ }
+
+ @Override public void headers(int flags, int streamId, List<String> nameValueBlock)
+ throws IOException {
+ SpdyStream replyStream = getStream(streamId);
+ if (replyStream != null) {
+ replyStream.receiveHeaders(nameValueBlock);
+ }
+ }
+
+ @Override public void rstStream(int flags, int streamId, int statusCode) {
+ SpdyStream rstStream = removeStream(streamId);
+ if (rstStream != null) {
+ rstStream.receiveRstStream(statusCode);
+ }
+ }
+
+ @Override public void settings(int flags, Settings newSettings) {
+ SpdyStream[] streamsToNotify = null;
+ synchronized (SpdyConnection.this) {
+ if (settings == null || (flags & Settings.FLAG_CLEAR_PREVIOUSLY_PERSISTED_SETTINGS) != 0) {
+ settings = newSettings;
+ } else {
+ settings.merge(newSettings);
+ }
+ if (!streams.isEmpty()) {
+ streamsToNotify = streams.values().toArray(new SpdyStream[streams.size()]);
+ }
+ }
+ if (streamsToNotify != null) {
+ for (SpdyStream stream : streamsToNotify) {
+ // The synchronization here is ugly. We need to synchronize on 'this' to guard
+ // reads to 'settings'. We synchronize on 'stream' to guard the state change.
+ // And we need to acquire the 'stream' lock first, since that may block.
+ synchronized (stream) {
+ synchronized (this) {
+ stream.receiveSettings(settings);
+ }
+ }
+ }
+ }
+ }
+
+ @Override public void noop() {
+ }
+
+ @Override public void ping(int flags, int streamId) {
+ if (client != (streamId % 2 == 1)) {
+ // Respond to a client ping if this is a server and vice versa.
+ writePingLater(streamId, null);
+ } else {
+ Ping ping = removePing(streamId);
+ if (ping != null) {
+ ping.receive();
+ }
+ }
+ }
+
+ @Override public void goAway(int flags, int lastGoodStreamId, int statusCode) {
+ synchronized (SpdyConnection.this) {
+ shutdown = true;
+
+ // Fail all streams created after the last good stream ID.
+ for (Iterator<Map.Entry<Integer, SpdyStream>> i = streams.entrySet().iterator();
+ i.hasNext(); ) {
+ Map.Entry<Integer, SpdyStream> entry = i.next();
+ int streamId = entry.getKey();
+ if (streamId > lastGoodStreamId && entry.getValue().isLocallyInitiated()) {
+ entry.getValue().receiveRstStream(SpdyStream.RST_REFUSED_STREAM);
+ i.remove();
+ }
+ }
+ }
+ }
+
+ @Override public void windowUpdate(int flags, int streamId, int deltaWindowSize) {
+ SpdyStream stream = getStream(streamId);
+ if (stream != null) {
+ stream.receiveWindowUpdate(deltaWindowSize);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-android/framework/src/com/squareup/okhttp/internal/spdy/SpdyReader.java
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/src/com/squareup/okhttp/internal/spdy/SpdyReader.java b/lib/cordova-android/framework/src/com/squareup/okhttp/internal/spdy/SpdyReader.java
new file mode 100644
index 0000000..7d3f2bd
--- /dev/null
+++ b/lib/cordova-android/framework/src/com/squareup/okhttp/internal/spdy/SpdyReader.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.squareup.okhttp.internal.spdy;
+
+import com.squareup.okhttp.internal.Util;
+import java.io.Closeable;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.ProtocolException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
+
+/** Read spdy/3 frames. */
+final class SpdyReader implements Closeable {
+ static final byte[] DICTIONARY;
+ static {
+ try {
+ DICTIONARY = ("\u0000\u0000\u0000\u0007options\u0000\u0000\u0000\u0004hea"
+ + "d\u0000\u0000\u0000\u0004post\u0000\u0000\u0000\u0003put\u0000\u0000\u0000\u0006dele"
+ + "te\u0000\u0000\u0000\u0005trace\u0000\u0000\u0000\u0006accept\u0000\u0000\u0000"
+ + "\u000Eaccept-charset\u0000\u0000\u0000\u000Faccept-encoding\u0000\u0000\u0000\u000Fa"
+ + "ccept-language\u0000\u0000\u0000\raccept-ranges\u0000\u0000\u0000\u0003age\u0000"
+ + "\u0000\u0000\u0005allow\u0000\u0000\u0000\rauthorization\u0000\u0000\u0000\rcache-co"
+ + "ntrol\u0000\u0000\u0000\nconnection\u0000\u0000\u0000\fcontent-base\u0000\u0000"
+ + "\u0000\u0010content-encoding\u0000\u0000\u0000\u0010content-language\u0000\u0000"
+ + "\u0000\u000Econtent-length\u0000\u0000\u0000\u0010content-location\u0000\u0000\u0000"
+ + "\u000Bcontent-md5\u0000\u0000\u0000\rcontent-range\u0000\u0000\u0000\fcontent-type"
+ + "\u0000\u0000\u0000\u0004date\u0000\u0000\u0000\u0004etag\u0000\u0000\u0000\u0006expe"
+ + "ct\u0000\u0000\u0000\u0007expires\u0000\u0000\u0000\u0004from\u0000\u0000\u0000"
+ + "\u0004host\u0000\u0000\u0000\bif-match\u0000\u0000\u0000\u0011if-modified-since"
+ + "\u0000\u0000\u0000\rif-none-match\u0000\u0000\u0000\bif-range\u0000\u0000\u0000"
+ + "\u0013if-unmodified-since\u0000\u0000\u0000\rlast-modified\u0000\u0000\u0000\blocati"
+ + "on\u0000\u0000\u0000\fmax-forwards\u0000\u0000\u0000\u0006pragma\u0000\u0000\u0000"
+ + "\u0012proxy-authenticate\u0000\u0000\u0000\u0013proxy-authorization\u0000\u0000"
+ + "\u0000\u0005range\u0000\u0000\u0000\u0007referer\u0000\u0000\u0000\u000Bretry-after"
+ + "\u0000\u0000\u0000\u0006server\u0000\u0000\u0000\u0002te\u0000\u0000\u0000\u0007trai"
+ + "ler\u0000\u0000\u0000\u0011transfer-encoding\u0000\u0000\u0000\u0007upgrade\u0000"
+ + "\u0000\u0000\nuser-agent\u0000\u0000\u0000\u0004vary\u0000\u0000\u0000\u0003via"
+ + "\u0000\u0000\u0000\u0007warning\u0000\u0000\u0000\u0010www-authenticate\u0000\u0000"
+ + "\u0000\u0006method\u0000\u0000\u0000\u0003get\u0000\u0000\u0000\u0006status\u0000"
+ + "\u0000\u0000\u0006200 OK\u0000\u0000\u0000\u0007version\u0000\u0000\u0000\bHTTP/1.1"
+ + "\u0000\u0000\u0000\u0003url\u0000\u0000\u0000\u0006public\u0000\u0000\u0000\nset-coo"
+ + "kie\u0000\u0000\u0000\nkeep-alive\u0000\u0000\u0000\u0006origin100101201202205206300"
+ + "302303304305306307402405406407408409410411412413414415416417502504505203 Non-Authori"
+ + "tative Information204 No Content301 Moved Permanently400 Bad Request401 Unauthorized"
+ + "403 Forbidden404 Not Found500 Internal Server Error501 Not Implemented503 Service Un"
+ + "availableJan Feb Mar Apr May Jun Jul Aug Sept Oct Nov Dec 00:00:00 Mon, Tue, Wed, Th"
+ + "u, Fri, Sat, Sun, GMTchunked,text/html,image/png,image/jpg,image/gif,application/xml"
+ + ",application/xhtml+xml,text/plain,text/javascript,publicprivatemax-age=gzip,deflate,"
+ + "sdchcharset=utf-8charset=iso-8859-1,utf-,*,enq=0.").getBytes(Util.UTF_8.name());
+ } catch (UnsupportedEncodingException e) {
+ throw new AssertionError();
+ }
+ }
+
+ private final DataInputStream in;
+ private final DataInputStream nameValueBlockIn;
+ private int compressedLimit;
+
+ SpdyReader(InputStream in) {
+ this.in = new DataInputStream(in);
+ this.nameValueBlockIn = newNameValueBlockStream();
+ }
+
+ /**
+ * Send the next frame to {@code handler}. Returns true unless there are no
+ * more frames on the stream.
+ */
+ public boolean nextFrame(Handler handler) throws IOException {
+ int w1;
+ try {
+ w1 = in.readInt();
+ } catch (IOException e) {
+ return false; // This might be a normal socket close.
+ }
+ int w2 = in.readInt();
+
+ boolean control = (w1 & 0x80000000) != 0;
+ int flags = (w2 & 0xff000000) >>> 24;
+ int length = (w2 & 0xffffff);
+
+ if (control) {
+ int version = (w1 & 0x7fff0000) >>> 16;
+ int type = (w1 & 0xffff);
+
+ if (version != 3) {
+ throw new ProtocolException("version != 3: " + version);
+ }
+
+ switch (type) {
+ case SpdyConnection.TYPE_SYN_STREAM:
+ readSynStream(handler, flags, length);
+ return true;
+
+ case SpdyConnection.TYPE_SYN_REPLY:
+ readSynReply(handler, flags, length);
+ return true;
+
+ case SpdyConnection.TYPE_RST_STREAM:
+ readRstStream(handler, flags, length);
+ return true;
+
+ case SpdyConnection.TYPE_SETTINGS:
+ readSettings(handler, flags, length);
+ return true;
+
+ case SpdyConnection.TYPE_NOOP:
+ if (length != 0) throw ioException("TYPE_NOOP length: %d != 0", length);
+ handler.noop();
+ return true;
+
+ case SpdyConnection.TYPE_PING:
+ readPing(handler, flags, length);
+ return true;
+
+ case SpdyConnection.TYPE_GOAWAY:
+ readGoAway(handler, flags, length);
+ return true;
+
+ case SpdyConnection.TYPE_HEADERS:
+ readHeaders(handler, flags, length);
+ return true;
+
+ case SpdyConnection.TYPE_WINDOW_UPDATE:
+ readWindowUpdate(handler, flags, length);
+ return true;
+
+ case SpdyConnection.TYPE_CREDENTIAL:
+ Util.skipByReading(in, length);
+ throw new UnsupportedOperationException("TODO"); // TODO: implement
+
+ default:
+ throw new IOException("Unexpected frame");
+ }
+ } else {
+ int streamId = w1 & 0x7fffffff;
+ handler.data(flags, streamId, in, length);
+ return true;
+ }
+ }
+
+ private void readSynStream(Handler handler, int flags, int length) throws IOException {
+ int w1 = in.readInt();
+ int w2 = in.readInt();
+ int s3 = in.readShort();
+ int streamId = w1 & 0x7fffffff;
+ int associatedStreamId = w2 & 0x7fffffff;
+ int priority = (s3 & 0xe000) >>> 13;
+ int slot = s3 & 0xff;
+ List<String> nameValueBlock = readNameValueBlock(length - 10);
+ handler.synStream(flags, streamId, associatedStreamId, priority, slot, nameValueBlock);
+ }
+
+ private void readSynReply(Handler handler, int flags, int length) throws IOException {
+ int w1 = in.readInt();
+ int streamId = w1 & 0x7fffffff;
+ List<String> nameValueBlock = readNameValueBlock(length - 4);
+ handler.synReply(flags, streamId, nameValueBlock);
+ }
+
+ private void readRstStream(Handler handler, int flags, int length) throws IOException {
+ if (length != 8) throw ioException("TYPE_RST_STREAM length: %d != 8", length);
+ int streamId = in.readInt() & 0x7fffffff;
+ int statusCode = in.readInt();
+ handler.rstStream(flags, streamId, statusCode);
+ }
+
+ private void readHeaders(Handler handler, int flags, int length) throws IOException {
+ int w1 = in.readInt();
+ int streamId = w1 & 0x7fffffff;
+ List<String> nameValueBlock = readNameValueBlock(length - 4);
+ handler.headers(flags, streamId, nameValueBlock);
+ }
+
+ private void readWindowUpdate(Handler handler, int flags, int length) throws IOException {
+ if (length != 8) throw ioException("TYPE_WINDOW_UPDATE length: %d != 8", length);
+ int w1 = in.readInt();
+ int w2 = in.readInt();
+ int streamId = w1 & 0x7fffffff;
+ int deltaWindowSize = w2 & 0x7fffffff;
+ handler.windowUpdate(flags, streamId, deltaWindowSize);
+ }
+
+ private DataInputStream newNameValueBlockStream() {
+ // Limit the inflater input stream to only those bytes in the Name/Value block.
+ final InputStream throttleStream = new InputStream() {
+ @Override public int read() throws IOException {
+ return Util.readSingleByte(this);
+ }
+
+ @Override public int read(byte[] buffer, int offset, int byteCount) throws IOException {
+ byteCount = Math.min(byteCount, compressedLimit);
+ int consumed = in.read(buffer, offset, byteCount);
+ compressedLimit -= consumed;
+ return consumed;
+ }
+
+ @Override public void close() throws IOException {
+ in.close();
+ }
+ };
+
+ // Subclass inflater to install a dictionary when it's needed.
+ Inflater inflater = new Inflater() {
+ @Override
+ public int inflate(byte[] buffer, int offset, int count) throws DataFormatException {
+ int result = super.inflate(buffer, offset, count);
+ if (result == 0 && needsDictionary()) {
+ setDictionary(DICTIONARY);
+ result = super.inflate(buffer, offset, count);
+ }
+ return result;
+ }
+ };
+
+ return new DataInputStream(new InflaterInputStream(throttleStream, inflater));
+ }
+
+ private List<String> readNameValueBlock(int length) throws IOException {
+ this.compressedLimit += length;
+ try {
+ int numberOfPairs = nameValueBlockIn.readInt();
+ if (numberOfPairs < 0) {
+ Logger.getLogger(getClass().getName()).warning("numberOfPairs < 0: " + numberOfPairs);
+ throw ioException("numberOfPairs < 0");
+ }
+ List<String> entries = new ArrayList<String>(numberOfPairs * 2);
+ for (int i = 0; i < numberOfPairs; i++) {
+ String name = readString();
+ String values = readString();
+ if (name.length() == 0) throw ioException("name.length == 0");
+ if (values.length() == 0) throw ioException("values.length == 0");
+ entries.add(name);
+ entries.add(values);
+ }
+
+ if (compressedLimit != 0) {
+ Logger.getLogger(getClass().getName()).warning("compressedLimit > 0: " + compressedLimit);
+ }
+
+ return entries;
+ } catch (DataFormatException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ private String readString() throws DataFormatException, IOException {
+ int length = nameValueBlockIn.readInt();
+ byte[] bytes = new byte[length];
+ Util.readFully(nameValueBlockIn, bytes);
+ return new String(bytes, 0, length, "UTF-8");
+ }
+
+ private void readPing(Handler handler, int flags, int length) throws IOException {
+ if (length != 4) throw ioException("TYPE_PING length: %d != 4", length);
+ int id = in.readInt();
+ handler.ping(flags, id);
+ }
+
+ private void readGoAway(Handler handler, int flags, int length) throws IOException {
+ if (length != 8) throw ioException("TYPE_GOAWAY length: %d != 8", length);
+ int lastGoodStreamId = in.readInt() & 0x7fffffff;
+ int statusCode = in.readInt();
+ handler.goAway(flags, lastGoodStreamId, statusCode);
+ }
+
+ private void readSettings(Handler handler, int flags, int length) throws IOException {
+ int numberOfEntries = in.readInt();
+ if (length != 4 + 8 * numberOfEntries) {
+ throw ioException("TYPE_SETTINGS length: %d != 4 + 8 * %d", length, numberOfEntries);
+ }
+ Settings settings = new Settings();
+ for (int i = 0; i < numberOfEntries; i++) {
+ int w1 = in.readInt();
+ int value = in.readInt();
+ int idFlags = (w1 & 0xff000000) >>> 24;
+ int id = w1 & 0xffffff;
+ settings.set(id, idFlags, value);
+ }
+ handler.settings(flags, settings);
+ }
+
+ private static IOException ioException(String message, Object... args) throws IOException {
+ throw new IOException(String.format(message, args));
+ }
+
+ @Override public void close() throws IOException {
+ Util.closeAll(in, nameValueBlockIn);
+ }
+
+ public interface Handler {
+ void data(int flags, int streamId, InputStream in, int length) throws IOException;
+
+ void synStream(int flags, int streamId, int associatedStreamId, int priority, int slot,
+ List<String> nameValueBlock);
+
+ void synReply(int flags, int streamId, List<String> nameValueBlock) throws IOException;
+ void headers(int flags, int streamId, List<String> nameValueBlock) throws IOException;
+ void rstStream(int flags, int streamId, int statusCode);
+ void settings(int flags, Settings settings);
+ void noop();
+ void ping(int flags, int streamId);
+ void goAway(int flags, int lastGoodStreamId, int statusCode);
+ void windowUpdate(int flags, int streamId, int deltaWindowSize);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-android/framework/src/com/squareup/okhttp/internal/spdy/SpdyStream.java
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/src/com/squareup/okhttp/internal/spdy/SpdyStream.java b/lib/cordova-android/framework/src/com/squareup/okhttp/internal/spdy/SpdyStream.java
new file mode 100644
index 0000000..744a04e
--- /dev/null
+++ b/lib/cordova-android/framework/src/com/squareup/okhttp/internal/spdy/SpdyStream.java
@@ -0,0 +1,733 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.squareup.okhttp.internal.spdy;
+
+import com.squareup.okhttp.internal.Util;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.net.SocketTimeoutException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.squareup.okhttp.internal.Util.checkOffsetAndCount;
+import static com.squareup.okhttp.internal.Util.pokeInt;
+import static java.nio.ByteOrder.BIG_ENDIAN;
+
+/** A logical bidirectional stream. */
+public final class SpdyStream {
+
+ // Internal state is guarded by this. No long-running or potentially
+ // blocking operations are performed while the lock is held.
+
+ private static final int DATA_FRAME_HEADER_LENGTH = 8;
+
+ private static final String[] STATUS_CODE_NAMES = {
+ null,
+ "PROTOCOL_ERROR",
+ "INVALID_STREAM",
+ "REFUSED_STREAM",
+ "UNSUPPORTED_VERSION",
+ "CANCEL",
+ "INTERNAL_ERROR",
+ "FLOW_CONTROL_ERROR",
+ "STREAM_IN_USE",
+ "STREAM_ALREADY_CLOSED",
+ "INVALID_CREDENTIALS",
+ "FRAME_TOO_LARGE"
+ };
+
+ public static final int RST_PROTOCOL_ERROR = 1;
+ public static final int RST_INVALID_STREAM = 2;
+ public static final int RST_REFUSED_STREAM = 3;
+ public static final int RST_UNSUPPORTED_VERSION = 4;
+ public static final int RST_CANCEL = 5;
+ public static final int RST_INTERNAL_ERROR = 6;
+ public static final int RST_FLOW_CONTROL_ERROR = 7;
+ public static final int RST_STREAM_IN_USE = 8;
+ public static final int RST_STREAM_ALREADY_CLOSED = 9;
+ public static final int RST_INVALID_CREDENTIALS = 10;
+ public static final int RST_FRAME_TOO_LARGE = 11;
+
+ /**
+ * The number of unacknowledged bytes at which the input stream will send
+ * the peer a {@code WINDOW_UPDATE} frame. Must be less than this client's
+ * window size, otherwise the remote peer will stop sending data on this
+ * stream. (Chrome 25 uses 5 MiB.)
+ */
+ public static final int WINDOW_UPDATE_THRESHOLD = Settings.DEFAULT_INITIAL_WINDOW_SIZE / 2;
+
+ private final int id;
+ private final SpdyConnection connection;
+ private final int priority;
+ private final int slot;
+ private long readTimeoutMillis = 0;
+ private int writeWindowSize;
+
+ /** Headers sent by the stream initiator. Immutable and non null. */
+ private final List<String> requestHeaders;
+
+ /** Headers sent in the stream reply. Null if reply is either not sent or not sent yet. */
+ private List<String> responseHeaders;
+
+ private final SpdyDataInputStream in = new SpdyDataInputStream();
+ private final SpdyDataOutputStream out = new SpdyDataOutputStream();
+
+ /**
+ * The reason why this stream was abnormally closed. If there are multiple
+ * reasons to abnormally close this stream (such as both peers closing it
+ * near-simultaneously) then this is the first reason known to this peer.
+ */
+ private int rstStatusCode = -1;
+
+ SpdyStream(int id, SpdyConnection connection, int flags, int priority, int slot,
+ List<String> requestHeaders, Settings settings) {
+ if (connection == null) throw new NullPointerException("connection == null");
+ if (requestHeaders == null) throw new NullPointerException("requestHeaders == null");
+ this.id = id;
+ this.connection = connection;
+ this.priority = priority;
+ this.slot = slot;
+ this.requestHeaders = requestHeaders;
+
+ if (isLocallyInitiated()) {
+ // I am the sender
+ in.finished = (flags & SpdyConnection.FLAG_UNIDIRECTIONAL) != 0;
+ out.finished = (flags & SpdyConnection.FLAG_FIN) != 0;
+ } else {
+ // I am the receiver
+ in.finished = (flags & SpdyConnection.FLAG_FIN) != 0;
+ out.finished = (flags & SpdyConnection.FLAG_UNIDIRECTIONAL) != 0;
+ }
+
+ setSettings(settings);
+ }
+
+ /**
+ * Returns true if this stream is open. A stream is open until either:
+ * <ul>
+ * <li>A {@code SYN_RESET} frame abnormally terminates the stream.
+ * <li>Both input and output streams have transmitted all data and
+ * headers.
+ * </ul>
+ * Note that the input stream may continue to yield data even after a stream
+ * reports itself as not open. This is because input data is buffered.
+ */
+ public synchronized boolean isOpen() {
+ if (rstStatusCode != -1) {
+ return false;
+ }
+ if ((in.finished || in.closed) && (out.finished || out.closed) && responseHeaders != null) {
+ return false;
+ }
+ return true;
+ }
+
+ /** Returns true if this stream was created by this peer. */
+ public boolean isLocallyInitiated() {
+ boolean streamIsClient = (id % 2 == 1);
+ return connection.client == streamIsClient;
+ }
+
+ public SpdyConnection getConnection() {
+ return connection;
+ }
+
+ public List<String> getRequestHeaders() {
+ return requestHeaders;
+ }
+
+ /**
+ * Returns the stream's response headers, blocking if necessary if they
+ * have not been received yet.
+ */
+ public synchronized List<String> getResponseHeaders() throws IOException {
+ try {
+ while (responseHeaders == null && rstStatusCode == -1) {
+ wait();
+ }
+ if (responseHeaders != null) {
+ return responseHeaders;
+ }
+ throw new IOException("stream was reset: " + rstStatusString());
+ } catch (InterruptedException e) {
+ InterruptedIOException rethrow = new InterruptedIOException();
+ rethrow.initCause(e);
+ throw rethrow;
+ }
+ }
+
+ /**
+ * Returns the reason why this stream was closed, or -1 if it closed
+ * normally or has not yet been closed. Valid reasons are {@link
+ * #RST_PROTOCOL_ERROR}, {@link #RST_INVALID_STREAM}, {@link
+ * #RST_REFUSED_STREAM}, {@link #RST_UNSUPPORTED_VERSION}, {@link
+ * #RST_CANCEL}, {@link #RST_INTERNAL_ERROR} and {@link
+ * #RST_FLOW_CONTROL_ERROR}.
+ */
+ public synchronized int getRstStatusCode() {
+ return rstStatusCode;
+ }
+
+ /**
+ * Sends a reply to an incoming stream.
+ *
+ * @param out true to create an output stream that we can use to send data
+ * to the remote peer. Corresponds to {@code FLAG_FIN}.
+ */
+ public void reply(List<String> responseHeaders, boolean out) throws IOException {
+ assert (!Thread.holdsLock(SpdyStream.this));
+ int flags = 0;
+ synchronized (this) {
+ if (responseHeaders == null) {
+ throw new NullPointerException("responseHeaders == null");
+ }
+ if (isLocallyInitiated()) {
+ throw new IllegalStateException("cannot reply to a locally initiated stream");
+ }
+ if (this.responseHeaders != null) {
+ throw new IllegalStateException("reply already sent");
+ }
+ this.responseHeaders = responseHeaders;
+ if (!out) {
+ this.out.finished = true;
+ flags |= SpdyConnection.FLAG_FIN;
+ }
+ }
+ connection.writeSynReply(id, flags, responseHeaders);
+ }
+
+ /**
+ * Sets the maximum time to wait on input stream reads before failing with a
+ * {@code SocketTimeoutException}, or {@code 0} to wait indefinitely.
+ */
+ public void setReadTimeout(long readTimeoutMillis) {
+ this.readTimeoutMillis = readTimeoutMillis;
+ }
+
+ public long getReadTimeoutMillis() {
+ return readTimeoutMillis;
+ }
+
+ /** Returns an input stream that can be used to read data from the peer. */
+ public InputStream getInputStream() {
+ return in;
+ }
+
+ /**
+ * Returns an output stream that can be used to write data to the peer.
+ *
+ * @throws IllegalStateException if this stream was initiated by the peer
+ * and a {@link #reply} has not yet been sent.
+ */
+ public OutputStream getOutputStream() {
+ synchronized (this) {
+ if (responseHeaders == null && !isLocallyInitiated()) {
+ throw new IllegalStateException("reply before requesting the output stream");
+ }
+ }
+ return out;
+ }
+
+ /**
+ * Abnormally terminate this stream. This blocks until the {@code RST_STREAM}
+ * frame has been transmitted.
+ */
+ public void close(int rstStatusCode) throws IOException {
+ if (!closeInternal(rstStatusCode)) {
+ return; // Already closed.
+ }
+ connection.writeSynReset(id, rstStatusCode);
+ }
+
+ /**
+ * Abnormally terminate this stream. This enqueues a {@code RST_STREAM}
+ * frame and returns immediately.
+ */
+ public void closeLater(int rstStatusCode) {
+ if (!closeInternal(rstStatusCode)) {
+ return; // Already closed.
+ }
+ connection.writeSynResetLater(id, rstStatusCode);
+ }
+
+ /** Returns true if this stream was closed. */
+ private boolean closeInternal(int rstStatusCode) {
+ assert (!Thread.holdsLock(this));
+ synchronized (this) {
+ if (this.rstStatusCode != -1) {
+ return false;
+ }
+ if (in.finished && out.finished) {
+ return false;
+ }
+ this.rstStatusCode = rstStatusCode;
+ notifyAll();
+ }
+ connection.removeStream(id);
+ return true;
+ }
+
+ void receiveReply(List<String> strings) throws IOException {
+ assert (!Thread.holdsLock(SpdyStream.this));
+ boolean streamInUseError = false;
+ boolean open = true;
+ synchronized (this) {
+ if (isLocallyInitiated() && responseHeaders == null) {
+ responseHeaders = strings;
+ open = isOpen();
+ notifyAll();
+ } else {
+ streamInUseError = true;
+ }
+ }
+ if (streamInUseError) {
+ closeLater(SpdyStream.RST_STREAM_IN_USE);
+ } else if (!open) {
+ connection.removeStream(id);
+ }
+ }
+
+ void receiveHeaders(List<String> headers) throws IOException {
+ assert (!Thread.holdsLock(SpdyStream.this));
+ boolean protocolError = false;
+ synchronized (this) {
+ if (responseHeaders != null) {
+ List<String> newHeaders = new ArrayList<String>();
+ newHeaders.addAll(responseHeaders);
+ newHeaders.addAll(headers);
+ this.responseHeaders = newHeaders;
+ } else {
+ protocolError = true;
+ }
+ }
+ if (protocolError) {
+ closeLater(SpdyStream.RST_PROTOCOL_ERROR);
+ }
+ }
+
+ void receiveData(InputStream in, int length) throws IOException {
+ assert (!Thread.holdsLock(SpdyStream.this));
+ this.in.receive(in, length);
+ }
+
+ void receiveFin() {
+ assert (!Thread.holdsLock(SpdyStream.this));
+ boolean open;
+ synchronized (this) {
+ this.in.finished = true;
+ open = isOpen();
+ notifyAll();
+ }
+ if (!open) {
+ connection.removeStream(id);
+ }
+ }
+
+ synchronized void receiveRstStream(int statusCode) {
+ if (rstStatusCode == -1) {
+ rstStatusCode = statusCode;
+ notifyAll();
+ }
+ }
+
+ private void setSettings(Settings settings) {
+ assert (Thread.holdsLock(connection)); // Because 'settings' is guarded by 'connection'.
+ this.writeWindowSize =
+ settings != null ? settings.getInitialWindowSize(Settings.DEFAULT_INITIAL_WINDOW_SIZE)
+ : Settings.DEFAULT_INITIAL_WINDOW_SIZE;
+ }
+
+ void receiveSettings(Settings settings) {
+ assert (Thread.holdsLock(this));
+ setSettings(settings);
+ notifyAll();
+ }
+
+ synchronized void receiveWindowUpdate(int deltaWindowSize) {
+ out.unacknowledgedBytes -= deltaWindowSize;
+ notifyAll();
+ }
+
+ private String rstStatusString() {
+ return rstStatusCode > 0 && rstStatusCode < STATUS_CODE_NAMES.length
+ ? STATUS_CODE_NAMES[rstStatusCode] : Integer.toString(rstStatusCode);
+ }
+
+ int getPriority() {
+ return priority;
+ }
+
+ int getSlot() {
+ return slot;
+ }
+
+ /**
+ * An input stream that reads the incoming data frames of a stream. Although
+ * this class uses synchronization to safely receive incoming data frames,
+ * it is not intended for use by multiple readers.
+ */
+ private final class SpdyDataInputStream extends InputStream {
+ // Store incoming data bytes in a circular buffer. When the buffer is
+ // empty, pos == -1. Otherwise pos is the first byte to read and limit
+ // is the first byte to write.
+ //
+ // { - - - X X X X - - - }
+ // ^ ^
+ // pos limit
+ //
+ // { X X X - - - - X X X }
+ // ^ ^
+ // limit pos
+
+ private final byte[] buffer = new byte[Settings.DEFAULT_INITIAL_WINDOW_SIZE];
+
+ /** the next byte to be read, or -1 if the buffer is empty. Never buffer.length */
+ private int pos = -1;
+
+ /** the last byte to be read. Never buffer.length */
+ private int limit;
+
+ /** True if the caller has closed this stream. */
+ private boolean closed;
+
+ /**
+ * True if either side has cleanly shut down this stream. We will
+ * receive no more bytes beyond those already in the buffer.
+ */
+ private boolean finished;
+
+ /**
+ * The total number of bytes consumed by the application (with {@link
+ * #read}), but not yet acknowledged by sending a {@code WINDOW_UPDATE}
+ * frame.
+ */
+ private int unacknowledgedBytes = 0;
+
+ @Override public int available() throws IOException {
+ synchronized (SpdyStream.this) {
+ checkNotClosed();
+ if (pos == -1) {
+ return 0;
+ } else if (limit > pos) {
+ return limit - pos;
+ } else {
+ return limit + (buffer.length - pos);
+ }
+ }
+ }
+
+ @Override public int read() throws IOException {
+ return Util.readSingleByte(this);
+ }
+
+ @Override public int read(byte[] b, int offset, int count) throws IOException {
+ synchronized (SpdyStream.this) {
+ checkOffsetAndCount(b.length, offset, count);
+ waitUntilReadable();
+ checkNotClosed();
+
+ if (pos == -1) {
+ return -1;
+ }
+
+ int copied = 0;
+
+ // drain from [pos..buffer.length)
+ if (limit <= pos) {
+ int bytesToCopy = Math.min(count, buffer.length - pos);
+ System.arraycopy(buffer, pos, b, offset, bytesToCopy);
+ pos += bytesToCopy;
+ copied += bytesToCopy;
+ if (pos == buffer.length) {
+ pos = 0;
+ }
+ }
+
+ // drain from [pos..limit)
+ if (copied < count) {
+ int bytesToCopy = Math.min(limit - pos, count - copied);
+ System.arraycopy(buffer, pos, b, offset + copied, bytesToCopy);
+ pos += bytesToCopy;
+ copied += bytesToCopy;
+ }
+
+ // Flow control: notify the peer that we're ready for more data!
+ unacknowledgedBytes += copied;
+ if (unacknowledgedBytes >= WINDOW_UPDATE_THRESHOLD) {
+ connection.writeWindowUpdateLater(id, unacknowledgedBytes);
+ unacknowledgedBytes = 0;
+ }
+
+ if (pos == limit) {
+ pos = -1;
+ limit = 0;
+ }
+
+ return copied;
+ }
+ }
+
+ /**
+ * Returns once the input stream is either readable or finished. Throws
+ * a {@link SocketTimeoutException} if the read timeout elapses before
+ * that happens.
+ */
+ private void waitUntilReadable() throws IOException {
+ long start = 0;
+ long remaining = 0;
+ if (readTimeoutMillis != 0) {
+ start = (System.nanoTime() / 1000000);
+ remaining = readTimeoutMillis;
+ }
+ try {
+ while (pos == -1 && !finished && !closed && rstStatusCode == -1) {
+ if (readTimeoutMillis == 0) {
+ SpdyStream.this.wait();
+ } else if (remaining > 0) {
+ SpdyStream.this.wait(remaining);
+ remaining = start + readTimeoutMillis - (System.nanoTime() / 1000000);
+ } else {
+ throw new SocketTimeoutException();
+ }
+ }
+ } catch (InterruptedException e) {
+ throw new InterruptedIOException();
+ }
+ }
+
+ void receive(InputStream in, int byteCount) throws IOException {
+ assert (!Thread.holdsLock(SpdyStream.this));
+
+ if (byteCount == 0) {
+ return;
+ }
+
+ int pos;
+ int limit;
+ int firstNewByte;
+ boolean finished;
+ boolean flowControlError;
+ synchronized (SpdyStream.this) {
+ finished = this.finished;
+ pos = this.pos;
+ firstNewByte = this.limit;
+ limit = this.limit;
+ flowControlError = byteCount > buffer.length - available();
+ }
+
+ // If the peer sends more data than we can handle, discard it and close the connection.
+ if (flowControlError) {
+ Util.skipByReading(in, byteCount);
+ closeLater(SpdyStream.RST_FLOW_CONTROL_ERROR);
+ return;
+ }
+
+ // Discard data received after the stream is finished. It's probably a benign race.
+ if (finished) {
+ Util.skipByReading(in, byteCount);
+ return;
+ }
+
+ // Fill the buffer without holding any locks. First fill [limit..buffer.length) if that
+ // won't overwrite unread data. Then fill [limit..pos). We can't hold a lock, otherwise
+ // writes will be blocked until reads complete.
+ if (pos < limit) {
+ int firstCopyCount = Math.min(byteCount, buffer.length - limit);
+ Util.readFully(in, buffer, limit, firstCopyCount);
+ limit += firstCopyCount;
+ byteCount -= firstCopyCount;
+ if (limit == buffer.length) {
+ limit = 0;
+ }
+ }
+ if (byteCount > 0) {
+ Util.readFully(in, buffer, limit, byteCount);
+ limit += byteCount;
+ }
+
+ synchronized (SpdyStream.this) {
+ // Update the new limit, and mark the position as readable if necessary.
+ this.limit = limit;
+ if (this.pos == -1) {
+ this.pos = firstNewByte;
+ SpdyStream.this.notifyAll();
+ }
+ }
+ }
+
+ @Override public void close() throws IOException {
+ synchronized (SpdyStream.this) {
+ closed = true;
+ SpdyStream.this.notifyAll();
+ }
+ cancelStreamIfNecessary();
+ }
+
+ private void checkNotClosed() throws IOException {
+ if (closed) {
+ throw new IOException("stream closed");
+ }
+ if (rstStatusCode != -1) {
+ throw new IOException("stream was reset: " + rstStatusString());
+ }
+ }
+ }
+
+ private void cancelStreamIfNecessary() throws IOException {
+ assert (!Thread.holdsLock(SpdyStream.this));
+ boolean open;
+ boolean cancel;
+ synchronized (this) {
+ cancel = !in.finished && in.closed && (out.finished || out.closed);
+ open = isOpen();
+ }
+ if (cancel) {
+ // RST this stream to prevent additional data from being sent. This
+ // is safe because the input stream is closed (we won't use any
+ // further bytes) and the output stream is either finished or closed
+ // (so RSTing both streams doesn't cause harm).
+ SpdyStream.this.close(RST_CANCEL);
+ } else if (!open) {
+ connection.removeStream(id);
+ }
+ }
+
+ /**
+ * An output stream that writes outgoing data frames of a stream. This class
+ * is not thread safe.
+ */
+ private final class SpdyDataOutputStream extends OutputStream {
+ private final byte[] buffer = new byte[8192];
+ private int pos = DATA_FRAME_HEADER_LENGTH;
+
+ /** True if the caller has closed this stream. */
+ private boolean closed;
+
+ /**
+ * True if either side has cleanly shut down this stream. We shall send
+ * no more bytes.
+ */
+ private boolean finished;
+
+ /**
+ * The total number of bytes written out to the peer, but not yet
+ * acknowledged with an incoming {@code WINDOW_UPDATE} frame. Writes
+ * block if they cause this to exceed the {@code WINDOW_SIZE}.
+ */
+ private int unacknowledgedBytes = 0;
+
+ @Override public void write(int b) throws IOException {
+ Util.writeSingleByte(this, b);
+ }
+
+ @Override public void write(byte[] bytes, int offset, int count) throws IOException {
+ assert (!Thread.holdsLock(SpdyStream.this));
+ checkOffsetAndCount(bytes.length, offset, count);
+ checkNotClosed();
+
+ while (count > 0) {
+ if (pos == buffer.length) {
+ writeFrame(false);
+ }
+ int bytesToCopy = Math.min(count, buffer.length - pos);
+ System.arraycopy(bytes, offset, buffer, pos, bytesToCopy);
+ pos += bytesToCopy;
+ offset += bytesToCopy;
+ count -= bytesToCopy;
+ }
+ }
+
+ @Override public void flush() throws IOException {
+ assert (!Thread.holdsLock(SpdyStream.this));
+ checkNotClosed();
+ if (pos > DATA_FRAME_HEADER_LENGTH) {
+ writeFrame(false);
+ connection.flush();
+ }
+ }
+
+ @Override public void close() throws IOException {
+ assert (!Thread.holdsLock(SpdyStream.this));
+ synchronized (SpdyStream.this) {
+ if (closed) {
+ return;
+ }
+ closed = true;
+ }
+ writeFrame(true);
+ connection.flush();
+ cancelStreamIfNecessary();
+ }
+
+ private void writeFrame(boolean last) throws IOException {
+ assert (!Thread.holdsLock(SpdyStream.this));
+
+ int length = pos - DATA_FRAME_HEADER_LENGTH;
+ synchronized (SpdyStream.this) {
+ waitUntilWritable(length, last);
+ unacknowledgedBytes += length;
+ }
+ int flags = 0;
+ if (last) {
+ flags |= SpdyConnection.FLAG_FIN;
+ }
+ pokeInt(buffer, 0, id & 0x7fffffff, BIG_ENDIAN);
+ pokeInt(buffer, 4, (flags & 0xff) << 24 | length & 0xffffff, BIG_ENDIAN);
+ connection.writeFrame(buffer, 0, pos);
+ pos = DATA_FRAME_HEADER_LENGTH;
+ }
+
+ /**
+ * Returns once the peer is ready to receive {@code count} bytes.
+ *
+ * @throws IOException if the stream was finished or closed, or the
+ * thread was interrupted.
+ */
+ private void waitUntilWritable(int count, boolean last) throws IOException {
+ try {
+ while (unacknowledgedBytes + count >= writeWindowSize) {
+ SpdyStream.this.wait(); // Wait until we receive a WINDOW_UPDATE.
+
+ // The stream may have been closed or reset while we were waiting!
+ if (!last && closed) {
+ throw new IOException("stream closed");
+ } else if (finished) {
+ throw new IOException("stream finished");
+ } else if (rstStatusCode != -1) {
+ throw new IOException("stream was reset: " + rstStatusString());
+ }
+ }
+ } catch (InterruptedException e) {
+ throw new InterruptedIOException();
+ }
+ }
+
+ private void checkNotClosed() throws IOException {
+ synchronized (SpdyStream.this) {
+ if (closed) {
+ throw new IOException("stream closed");
+ } else if (finished) {
+ throw new IOException("stream finished");
+ } else if (rstStatusCode != -1) {
+ throw new IOException("stream was reset: " + rstStatusString());
+ }
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-android/framework/src/com/squareup/okhttp/internal/spdy/SpdyWriter.java
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/src/com/squareup/okhttp/internal/spdy/SpdyWriter.java b/lib/cordova-android/framework/src/com/squareup/okhttp/internal/spdy/SpdyWriter.java
new file mode 100644
index 0000000..b3d1d1f
--- /dev/null
+++ b/lib/cordova-android/framework/src/com/squareup/okhttp/internal/spdy/SpdyWriter.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.squareup.okhttp.internal.spdy;
+
+import com.squareup.okhttp.internal.Platform;
+import com.squareup.okhttp.internal.Util;
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.zip.Deflater;
+
+/** Write spdy/3 frames. */
+final class SpdyWriter implements Closeable {
+ final DataOutputStream out;
+ private final ByteArrayOutputStream nameValueBlockBuffer;
+ private final DataOutputStream nameValueBlockOut;
+
+ SpdyWriter(OutputStream out) {
+ this.out = new DataOutputStream(out);
+
+ Deflater deflater = new Deflater();
+ deflater.setDictionary(SpdyReader.DICTIONARY);
+ nameValueBlockBuffer = new ByteArrayOutputStream();
+ nameValueBlockOut = new DataOutputStream(
+ Platform.get().newDeflaterOutputStream(nameValueBlockBuffer, deflater, true));
+ }
+
+ public synchronized void synStream(int flags, int streamId, int associatedStreamId, int priority,
+ int slot, List<String> nameValueBlock) throws IOException {
+ writeNameValueBlockToBuffer(nameValueBlock);
+ int length = 10 + nameValueBlockBuffer.size();
+ int type = SpdyConnection.TYPE_SYN_STREAM;
+
+ int unused = 0;
+ out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
+ out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
+ out.writeInt(streamId & 0x7fffffff);
+ out.writeInt(associatedStreamId & 0x7fffffff);
+ out.writeShort((priority & 0x7) << 13 | (unused & 0x1f) << 8 | (slot & 0xff));
+ nameValueBlockBuffer.writeTo(out);
+ out.flush();
+ }
+
+ public synchronized void synReply(int flags, int streamId, List<String> nameValueBlock)
+ throws IOException {
+ writeNameValueBlockToBuffer(nameValueBlock);
+ int type = SpdyConnection.TYPE_SYN_REPLY;
+ int length = nameValueBlockBuffer.size() + 4;
+
+ out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
+ out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
+ out.writeInt(streamId & 0x7fffffff);
+ nameValueBlockBuffer.writeTo(out);
+ out.flush();
+ }
+
+ public synchronized void headers(int flags, int streamId, List<String> nameValueBlock)
+ throws IOException {
+ writeNameValueBlockToBuffer(nameValueBlock);
+ int type = SpdyConnection.TYPE_HEADERS;
+ int length = nameValueBlockBuffer.size() + 4;
+
+ out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
+ out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
+ out.writeInt(streamId & 0x7fffffff);
+ nameValueBlockBuffer.writeTo(out);
+ out.flush();
+ }
+
+ public synchronized void rstStream(int streamId, int statusCode) throws IOException {
+ int flags = 0;
+ int type = SpdyConnection.TYPE_RST_STREAM;
+ int length = 8;
+ out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
+ out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
+ out.writeInt(streamId & 0x7fffffff);
+ out.writeInt(statusCode);
+ out.flush();
+ }
+
+ public synchronized void data(int flags, int streamId, byte[] data) throws IOException {
+ int length = data.length;
+ out.writeInt(streamId & 0x7fffffff);
+ out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
+ out.write(data);
+ out.flush();
+ }
+
+ private void writeNameValueBlockToBuffer(List<String> nameValueBlock) throws IOException {
+ nameValueBlockBuffer.reset();
+ int numberOfPairs = nameValueBlock.size() / 2;
+ nameValueBlockOut.writeInt(numberOfPairs);
+ for (String s : nameValueBlock) {
+ nameValueBlockOut.writeInt(s.length());
+ nameValueBlockOut.write(s.getBytes("UTF-8"));
+ }
+ nameValueBlockOut.flush();
+ }
+
+ public synchronized void settings(int flags, Settings settings) throws IOException {
+ int type = SpdyConnection.TYPE_SETTINGS;
+ int size = settings.size();
+ int length = 4 + size * 8;
+ out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
+ out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
+ out.writeInt(size);
+ for (int i = 0; i <= Settings.COUNT; i++) {
+ if (!settings.isSet(i)) continue;
+ int settingsFlags = settings.flags(i);
+ out.writeInt((settingsFlags & 0xff) << 24 | (i & 0xffffff));
+ out.writeInt(settings.get(i));
+ }
+ out.flush();
+ }
+
+ public synchronized void noop() throws IOException {
+ int type = SpdyConnection.TYPE_NOOP;
+ int length = 0;
+ int flags = 0;
+ out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
+ out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
+ out.flush();
+ }
+
+ public synchronized void ping(int flags, int id) throws IOException {
+ int type = SpdyConnection.TYPE_PING;
+ int length = 4;
+ out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
+ out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
+ out.writeInt(id);
+ out.flush();
+ }
+
+ public synchronized void goAway(int flags, int lastGoodStreamId, int statusCode)
+ throws IOException {
+ int type = SpdyConnection.TYPE_GOAWAY;
+ int length = 8;
+ out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
+ out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
+ out.writeInt(lastGoodStreamId);
+ out.writeInt(statusCode);
+ out.flush();
+ }
+
+ public synchronized void windowUpdate(int streamId, int deltaWindowSize) throws IOException {
+ int type = SpdyConnection.TYPE_WINDOW_UPDATE;
+ int flags = 0;
+ int length = 8;
+ out.writeInt(0x80000000 | (SpdyConnection.VERSION & 0x7fff) << 16 | type & 0xffff);
+ out.writeInt((flags & 0xff) << 24 | length & 0xffffff);
+ out.writeInt(streamId);
+ out.writeInt(deltaWindowSize);
+ out.flush();
+ }
+
+ @Override public void close() throws IOException {
+ Util.closeAll(out, nameValueBlockOut);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-android/framework/src/org/apache/cordova/AudioHandler.java
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/AudioHandler.java b/lib/cordova-android/framework/src/org/apache/cordova/AudioHandler.java
index fd8c9df..20c3c4e 100644
--- a/lib/cordova-android/framework/src/org/apache/cordova/AudioHandler.java
+++ b/lib/cordova-android/framework/src/org/apache/cordova/AudioHandler.java
@@ -20,7 +20,7 @@ package org.apache.cordova;
import org.apache.cordova.api.CallbackContext;
import org.apache.cordova.api.CordovaPlugin;
-
+import org.apache.cordova.api.DataResource;
import android.content.Context;
import android.media.AudioManager;
@@ -56,6 +56,11 @@ public class AudioHandler extends CordovaPlugin {
this.pausedForPhone = new ArrayList<AudioPlayer>();
}
+ public String getFilePath(String url, String source){
+ DataResource dataResource = DataResource.initiateNewDataRequestForUri(url, this.webView.pluginManager, cordova, source);
+ return dataResource.getRealFile().getPath();
+ }
+
/**
* Executes the request and returns PluginResult.
* @param action The action to execute.
@@ -68,13 +73,13 @@ public class AudioHandler extends CordovaPlugin {
String result = "";
if (action.equals("startRecordingAudio")) {
- this.startRecordingAudio(args.getString(0), FileHelper.stripFileProtocol(args.getString(1)));
+ this.startRecordingAudio(args.getString(0), getFilePath(args.getString(1), "AudioHandler.startRecordingAudio"));
}
else if (action.equals("stopRecordingAudio")) {
this.stopRecordingAudio(args.getString(0));
}
else if (action.equals("startPlayingAudio")) {
- this.startPlayingAudio(args.getString(0), FileHelper.stripFileProtocol(args.getString(1)));
+ this.startPlayingAudio(args.getString(0), getFilePath(args.getString(1), "AudioHandler.startPlayingAudio"));
}
else if (action.equals("seekToAudio")) {
this.seekToAudio(args.getString(0), args.getInt(1));
@@ -102,7 +107,7 @@ public class AudioHandler extends CordovaPlugin {
}
else if (action.equals("create")) {
String id = args.getString(0);
- String src = FileHelper.stripFileProtocol(args.getString(1));
+ String src = getFilePath(args.getString(1), "AudioHandler.create");
AudioPlayer audio = new AudioPlayer(this, id, src);
this.players.put(id, audio);
}
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-android/framework/src/org/apache/cordova/CameraLauncher.java
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/CameraLauncher.java b/lib/cordova-android/framework/src/org/apache/cordova/CameraLauncher.java
index 426d250..1974dd7 100755
--- a/lib/cordova-android/framework/src/org/apache/cordova/CameraLauncher.java
+++ b/lib/cordova-android/framework/src/org/apache/cordova/CameraLauncher.java
@@ -20,15 +20,16 @@ package org.apache.cordova;
import java.io.ByteArrayOutputStream;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.codec.binary.Base64;
import org.apache.cordova.api.CallbackContext;
import org.apache.cordova.api.CordovaPlugin;
+import org.apache.cordova.api.DataResource;
import org.apache.cordova.api.LOG;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
@@ -42,7 +43,6 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Bitmap.CompressFormat;
-import android.graphics.Rect;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
@@ -290,7 +290,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
// If sending base64 image back
if (destType == DATA_URL) {
- bitmap = getScaledBitmap(FileHelper.stripFileProtocol(imageUri.toString()));
+ bitmap = getScaledBitmap(imageUri.toString());
if (bitmap == null) {
// Try to get the bitmap from intent.
bitmap = (Bitmap)intent.getExtras().get("data");
@@ -316,7 +316,9 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
if (this.saveToPhotoAlbum) {
Uri inputUri = getUriFromMediaStore();
//Just because we have a media URI doesn't mean we have a real file, we need to make it
- uri = Uri.fromFile(new File(FileHelper.getRealPath(inputUri, this.cordova)));
+ DataResource dataResource = DataResource.initiateNewDataRequestForUri(inputUri, webView.pluginManager, cordova, "CameraLauncher.CameraExitIntent");
+ File file = dataResource.getRealFile();
+ uri = Uri.fromFile(file);
} else {
uri = Uri.fromFile(new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), System.currentTimeMillis() + ".jpg"));
}
@@ -332,14 +334,15 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
this.callbackContext.success(uri.toString());
} else {
- bitmap = getScaledBitmap(FileHelper.stripFileProtocol(imageUri.toString()));
+ bitmap = getScaledBitmap(imageUri.toString());
if (rotate != 0 && this.correctOrientation) {
bitmap = getRotatedBitmap(rotate, bitmap, exif);
}
// Add compressed version of captured image to returned media store Uri
- OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
+ DataResource dataResource = DataResource.initiateNewDataRequestForUri(uri, webView.pluginManager, cordova, "CameraLauncher.CameraExitIntent");
+ OutputStream os = dataResource.getOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
os.close();
@@ -347,7 +350,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
if (this.encodingType == JPEG) {
String exifPath;
if (this.saveToPhotoAlbum) {
- exifPath = FileHelper.getRealPath(uri, this.cordova);
+ exifPath = dataResource.getRealFile().getPath();
} else {
exifPath = uri.getPath();
}
@@ -398,8 +401,9 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
this.callbackContext.success(uri.toString());
} else {
String uriString = uri.toString();
+ DataResource dataResource = DataResource.initiateNewDataRequestForUri(uri, webView.pluginManager, cordova, "CameraLauncher.CameraExitIntent");
// Get the path to the image. Makes loading so much easier.
- String mimeType = FileHelper.getMimeType(uriString, this.cordova);
+ String mimeType = dataResource.getMimeType();
// If we don't have a valid image so quit.
if (!("image/jpeg".equalsIgnoreCase(mimeType) || "image/png".equalsIgnoreCase(mimeType))) {
Log.d(LOG_TAG, "I either have a null image path or bitmap");
@@ -440,7 +444,8 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
// Create an ExifHelper to save the exif data that is lost during compression
String resizePath = DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/resize.jpg";
// Some content: URIs do not map to file paths (e.g. picasa).
- String realPath = FileHelper.getRealPath(uri, this.cordova);
+ File realFile = DataResource.initiateNewDataRequestForUri(uri, webView.pluginManager, cordova, "CameraLauncher.CameraExitIntent").getRealFile();
+ String realPath = realFile != null? realFile.getPath() : null;
ExifHelper exif = new ExifHelper();
if (realPath != null && this.encodingType == JPEG) {
try {
@@ -534,8 +539,15 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
*/
private void writeUncompressedImage(Uri uri) throws FileNotFoundException,
IOException {
- FileInputStream fis = new FileInputStream(FileHelper.stripFileProtocol(imageUri.toString()));
- OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
+ DataResource inputDataResource = DataResource.initiateNewDataRequestForUri(imageUri, webView.pluginManager, cordova, "CameraLauncher.writeUncompressedImage");
+ InputStream fis = inputDataResource.getInputStream();
+ DataResource outDataResource = DataResource.initiateNewDataRequestForUri(uri, webView.pluginManager, cordova, "CameraLauncher.writeUncompressedImage");
+ OutputStream os = outDataResource.getOutputStream();
+ if(fis == null) {
+ throw new FileNotFoundException("Could not get the input file");
+ } else if(os == null) {
+ throw new FileNotFoundException("Could not get the output file");
+ }
byte[] buffer = new byte[4096];
int len;
while ((len = fis.read(buffer)) != -1) {
@@ -578,14 +590,15 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
*/
private Bitmap getScaledBitmap(String imageUrl) throws IOException {
// If no new width or height were specified return the original bitmap
+ DataResource dataResource = DataResource.initiateNewDataRequestForUri(imageUrl, webView.pluginManager, cordova, "CameraLauncher.getScaledBitmap");
if (this.targetWidth <= 0 && this.targetHeight <= 0) {
- return BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova));
+ return BitmapFactory.decodeStream(dataResource.getInputStream());
}
// figure out the original width and height of the image
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
- BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova), null, options);
+ BitmapFactory.decodeStream(dataResource.getInputStream(), null, options);
//CB-2292: WTF? Why is the width null?
if(options.outWidth == 0 || options.outHeight == 0)
@@ -599,7 +612,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
// Load in the smallest bitmap possible that is closest to the size we want
options.inJustDecodeBounds = false;
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, this.targetWidth, this.targetHeight);
- Bitmap unscaledBitmap = BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova), null, options);
+ Bitmap unscaledBitmap = BitmapFactory.decodeStream(dataResource.getInputStream(), null, options);
if (unscaledBitmap == null) {
return null;
}
@@ -698,16 +711,20 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
bitmap.recycle();
}
- // Clean up initial camera-written image file.
- (new File(FileHelper.stripFileProtocol(oldImage.toString()))).delete();
+ DataResource dataResource = DataResource.initiateNewDataRequestForUri(oldImage, webView.pluginManager, cordova, "CameraLauncher.cleanup");
+ File file = dataResource.getRealFile();
+ if(file != null) {
+ // Clean up initial camera-written image file.
+ file.delete();
- checkForDuplicateImage(imageType);
- // Scan for the gallery to update pic refs in gallery
- if (this.saveToPhotoAlbum && newImage != null) {
- this.scanForGallery(newImage);
- }
+ checkForDuplicateImage(imageType);
+ // Scan for the gallery to update pic refs in gallery
+ if (this.saveToPhotoAlbum && newImage != null) {
+ this.scanForGallery(newImage);
+ }
- System.gc();
+ System.gc();
+ }
}
/**
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-android/framework/src/org/apache/cordova/Capture.java
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/Capture.java b/lib/cordova-android/framework/src/org/apache/cordova/Capture.java
index 5f737ca..d7ad419 100644
--- a/lib/cordova-android/framework/src/org/apache/cordova/Capture.java
+++ b/lib/cordova-android/framework/src/org/apache/cordova/Capture.java
@@ -26,6 +26,7 @@ import java.io.OutputStream;
import android.os.Build;
import org.apache.cordova.api.CallbackContext;
import org.apache.cordova.api.CordovaPlugin;
+import org.apache.cordova.api.DataResource;
import org.apache.cordova.api.LOG;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
@@ -129,7 +130,8 @@ public class Capture extends CordovaPlugin {
// If the mimeType isn't set the rest will fail
// so let's see if we can determine it.
if (mimeType == null || mimeType.equals("") || "null".equals(mimeType)) {
- mimeType = FileHelper.getMimeType(filePath, cordova);
+ DataResource dataResource = DataResource.initiateNewDataRequestForUri(filePath, webView.pluginManager, cordova, "Capture.dataResource");
+ mimeType = dataResource.getMimeType();
}
Log.d(LOG_TAG, "Mime type = " + mimeType);
@@ -156,7 +158,8 @@ public class Capture extends CordovaPlugin {
private JSONObject getImageData(String filePath, JSONObject obj) throws JSONException {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
- BitmapFactory.decodeFile(FileHelper.stripFileProtocol(filePath), options);
+ DataResource dataResource = DataResource.initiateNewDataRequestForUri(filePath, webView.pluginManager, cordova, "Capture.getImageData");
+ BitmapFactory.decodeFile(dataResource.getRealFile().getPath(), options);
obj.put("height", options.outHeight);
obj.put("width", options.outWidth);
return obj;
@@ -348,7 +351,8 @@ public class Capture extends CordovaPlugin {
* @throws IOException
*/
private JSONObject createMediaFile(Uri data) {
- File fp = new File(FileHelper.getRealPath(data, this.cordova));
+ DataResource dataResource = DataResource.initiateNewDataRequestForUri(data, webView.pluginManager, cordova, "Capture.createMediaFile");
+ File fp = dataResource.getRealFile();
JSONObject obj = new JSONObject();
try {
@@ -358,6 +362,7 @@ public class Capture extends CordovaPlugin {
// Because of an issue with MimeTypeMap.getMimeTypeFromExtension() all .3gpp files
// are reported as video/3gpp. I'm doing this hacky check of the URI to see if it
// is stored in the audio or video content store.
+
if (fp.getAbsoluteFile().toString().endsWith(".3gp") || fp.getAbsoluteFile().toString().endsWith(".3gpp")) {
if (data.toString().contains("/audio/")) {
obj.put("type", AUDIO_3GPP);
@@ -365,7 +370,7 @@ public class Capture extends CordovaPlugin {
obj.put("type", VIDEO_3GPP);
}
} else {
- obj.put("type", FileHelper.getMimeType(fp.getAbsolutePath(), cordova));
+ obj.put("type", dataResource.getMimeType());
}
obj.put("lastModifiedDate", fp.lastModified());
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/88ad654c/lib/cordova-android/framework/src/org/apache/cordova/Config.java
----------------------------------------------------------------------
diff --git a/lib/cordova-android/framework/src/org/apache/cordova/Config.java b/lib/cordova-android/framework/src/org/apache/cordova/Config.java
index 594c2b2..c2ced34 100644
--- a/lib/cordova-android/framework/src/org/apache/cordova/Config.java
+++ b/lib/cordova-android/framework/src/org/apache/cordova/Config.java
@@ -50,9 +50,8 @@ public class Config {
private static Config self = null;
public static void init(Activity action) {
- if (self == null) {
- self = new Config(action);
- }
+ //Just re-initialize this! Seriously, we lose this all the time
+ self = new Config(action);
}
// Intended to be used for testing only; creates an empty configuration.
@@ -97,7 +96,7 @@ public class Config {
}
else if (strNode.equals("log")) {
String level = xml.getAttributeValue(null, "level");
- LOG.i("CordovaLog", "Found log level %s", level);
+ Log.d(TAG, "The <log> tags is deprecated. Use <preference name=\"loglevel\" value=\"" + level + "\"/> instead.");
if (level != null) {
LOG.setLogLevel(level);
}
@@ -113,30 +112,27 @@ public class Config {
Note: We should probably pass in the classname for the variable splash on splashscreen!
*/
- if(name.equals("splashscreen")) {
+ if (name.equals("loglevel")) {
+ String level = xml.getAttributeValue(null, "value");
+ LOG.setLogLevel(level);
+ } else if (name.equals("splashscreen")) {
String value = xml.getAttributeValue(null, "value");
int resource = 0;
- if (value != null)
+ if (value == null)
{
value = "splash";
}
resource = action.getResources().getIdentifier(value, "drawable", action.getPackageName());
action.getIntent().putExtra(name, resource);
- LOG.i("CordovaLog", "Found preference for %s=%s", name, value);
- Log.d("CordovaLog", "Found preference for " + name + "=" + value);
}
else if(name.equals("backgroundColor")) {
int value = xml.getAttributeIntValue(null, "value", Color.BLACK);
action.getIntent().putExtra(name, value);
- LOG.i("CordovaLog", "Found preference for %s=%d", name, value);
- Log.d("CordovaLog", "Found preference for " + name + "=" + Integer.toString(value));
}
else if(name.equals("loadUrlTimeoutValue")) {
int value = xml.getAttributeIntValue(null, "value", 20000);
action.getIntent().putExtra(name, value);
- LOG.i("CordovaLog", "Found preference for %s=%d", name, value);
- Log.d("CordovaLog", "Found preference for " + name + "=" + Integer.toString(value));
}
else if(name.equals("keepRunning"))
{
@@ -157,12 +153,9 @@ public class Config {
{
String value = xml.getAttributeValue(null, "value");
action.getIntent().putExtra(name, value);
- LOG.i("CordovaLog", "Found preference for %s=%s", name, value);
- Log.d("CordovaLog", "Found preference for " + name + "=" + value);
}
/*
LOG.i("CordovaLog", "Found preference for %s=%s", name, value);
- Log.d("CordovaLog", "Found preference for " + name + "=" + value);
*/
}
else if (strNode.equals("content")) {