Merge "Additional cleanup after stack deletion." into klp-modular-dev
diff --git a/docs/html/google/gcm/ccs.jd b/docs/html/google/gcm/ccs.jd
index 4389e3d..90d8d4c 100644
--- a/docs/html/google/gcm/ccs.jd
+++ b/docs/html/google/gcm/ccs.jd
@@ -535,6 +535,8 @@
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.PacketInterceptor;
import org.jivesoftware.smack.PacketListener;
+import org.jivesoftware.smack.SmackException;
+import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.PacketTypeFilter;
@@ -544,352 +546,378 @@
import org.jivesoftware.smack.packet.PacketExtension;
import org.jivesoftware.smack.provider.PacketExtensionProvider;
import org.jivesoftware.smack.provider.ProviderManager;
+import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.util.StringUtils;
import org.json.simple.JSONValue;
import org.json.simple.parser.ParseException;
import org.xmlpull.v1.XmlPullParser;
+import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
-import java.util.Random;
+import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.SSLSocketFactory;
+
/**
- * Sample Smack implementation of a client for GCM Cloud Connection Server.
+ * Sample Smack implementation of a client for GCM Cloud Connection Server. This
+ * code can be run as a standalone CCS client.
*
* <p>For illustration purposes only.
*/
public class SmackCcsClient {
- Logger logger = Logger.getLogger("SmackCcsClient");
+ private static final Logger logger = Logger.getLogger("SmackCcsClient");
- public static final String GCM_SERVER = "gcm.googleapis.com";
- public static final int GCM_PORT = 5235;
+ private static final String GCM_SERVER = "gcm.googleapis.com";
+ private static final int GCM_PORT = 5235;
- public static final String GCM_ELEMENT_NAME = "gcm";
- public static final String GCM_NAMESPACE = "google:mobile:data";
+ private static final String GCM_ELEMENT_NAME = "gcm";
+ private static final String GCM_NAMESPACE = "google:mobile:data";
- static Random random = new Random();
- XMPPConnection connection;
- ConnectionConfiguration config;
+ static {
- /**
- * XMPP Packet Extension for GCM Cloud Connection Server.
- */
- class GcmPacketExtension extends DefaultPacketExtension {
- String json;
-
- public GcmPacketExtension(String json) {
- super(GCM_ELEMENT_NAME, GCM_NAMESPACE);
- this.json = json;
+ ProviderManager.addExtensionProvider(GCM_ELEMENT_NAME, GCM_NAMESPACE,
+ new PacketExtensionProvider() {
+ @Override
+ public PacketExtension parseExtension(XmlPullParser parser) throws
+ Exception {
+ String json = parser.nextText();
+ return new GcmPacketExtension(json);
+ }
+ });
}
- public String getJson() {
- return json;
+ private XMPPConnection connection;
+
+ /**
+ * Indicates whether the connection is in draining state, which means that it
+ * will not accept any new downstream messages.
+ */
+ protected volatile boolean connectionDraining = false;
+
+ /**
+ * Sends a downstream message to GCM.
+ *
+ * @return true if the message has been successfully sent.
+ */
+ public boolean sendDownstreamMessage(String jsonRequest) throws
+ NotConnectedException {
+ if (!connectionDraining) {
+ send(jsonRequest);
+ return true;
+ }
+ logger.info("Dropping downstream message since the connection is draining");
+ return false;
}
- @Override
- public String toXML() {
- return String.format("<%s xmlns=\"%s\">%s</%s>", GCM_ELEMENT_NAME,
- GCM_NAMESPACE, json, GCM_ELEMENT_NAME);
+ /**
+ * Returns a random message id to uniquely identify a message.
+ *
+ * <p>Note: This is generated by a pseudo random number generator for
+ * illustration purpose, and is not guaranteed to be unique.
+ */
+ public String nextMessageId() {
+ return "m-" + UUID.randomUUID().toString();
}
- @SuppressWarnings("unused")
- public Packet toPacket() {
- return new Message() {
- // Must override toXML() because it includes a <body>
+ /**
+ * Sends a packet with contents provided.
+ */
+ protected void send(String jsonRequest) throws NotConnectedException {
+ Packet request = new GcmPacketExtension(jsonRequest).toPacket();
+ connection.sendPacket(request);
+ }
+
+ /**
+ * Handles an upstream data message from a device application.
+ *
+ * <p>This sample echo server sends an echo message back to the device.
+ * Subclasses should override this method to properly process upstream messages.
+ */
+ protected void handleUpstreamMessage(Map<String, Object> jsonObject) {
+ // PackageName of the application that sent this message.
+ String category = (String) jsonObject.get("category");
+ String from = (String) jsonObject.get("from");
+ @SuppressWarnings("unchecked")
+ Map<String, String> payload = (Map<String, String>) jsonObject.get("data");
+ payload.put("ECHO", "Application: " + category);
+
+ // Send an ECHO response back
+ String echo = createJsonMessage(from, nextMessageId(), payload,
+ "echo:CollapseKey", null, false);
+
+ try {
+ sendDownstreamMessage(echo);
+ } catch (NotConnectedException e) {
+ logger.log(Level.WARNING, "Not connected anymore, echo message is
+ not sent", e);
+ }
+ }
+
+ /**
+ * Handles an ACK.
+ *
+ * <p>Logs a {@code INFO} message, but subclasses could override it to
+ * properly handle ACKs.
+ */
+ protected void handleAckReceipt(Map<String, Object> jsonObject) {
+ String messageId = (String) jsonObject.get("message_id");
+ String from = (String) jsonObject.get("from");
+ logger.log(Level.INFO, "handleAckReceipt() from: " + from + ",
+ messageId: " + messageId);
+ }
+
+ /**
+ * Handles a NACK.
+ *
+ * <p>Logs a {@code INFO} message, but subclasses could override it to
+ * properly handle NACKs.
+ */
+ protected void handleNackReceipt(Map<String, Object> jsonObject) {
+ String messageId = (String) jsonObject.get("message_id");
+ String from = (String) jsonObject.get("from");
+ logger.log(Level.INFO, "handleNackReceipt() from: " + from + ",
+ messageId: " + messageId);
+ }
+
+ protected void handleControlMessage(Map<String, Object> jsonObject) {
+ logger.log(Level.INFO, "handleControlMessage(): " + jsonObject);
+ String controlType = (String) jsonObject.get("control_type");
+ if ("CONNECTION_DRAINING".equals(controlType)) {
+ connectionDraining = true;
+ } else {
+ logger.log(Level.INFO, "Unrecognized control type: %s. This could
+ happen if new features are " + "added to the CCS protocol.",
+ controlType);
+ }
+ }
+
+ /**
+ * Creates a JSON encoded GCM message.
+ *
+ * @param to RegistrationId of the target device (Required).
+ * @param messageId Unique messageId for which CCS will send an
+ * "ack/nack" (Required).
+ * @param payload Message content intended for the application. (Optional).
+ * @param collapseKey GCM collapse_key parameter (Optional).
+ * @param timeToLive GCM time_to_live parameter (Optional).
+ * @param delayWhileIdle GCM delay_while_idle parameter (Optional).
+ * @return JSON encoded GCM message.
+ */
+ public static String createJsonMessage(String to, String messageId,
+ Map<String, String> payload, String collapseKey, Long timeToLive,
+ Boolean delayWhileIdle) {
+ Map<String, Object> message = new HashMap<String, Object>();
+ message.put("to", to);
+ if (collapseKey != null) {
+ message.put("collapse_key", collapseKey);
+ }
+ if (timeToLive != null) {
+ message.put("time_to_live", timeToLive);
+ }
+ if (delayWhileIdle != null && delayWhileIdle) {
+ message.put("delay_while_idle", true);
+ }
+ message.put("message_id", messageId);
+ message.put("data", payload);
+ return JSONValue.toJSONString(message);
+ }
+
+ /**
+ * Creates a JSON encoded ACK message for an upstream message received
+ * from an application.
+ *
+ * @param to RegistrationId of the device who sent the upstream message.
+ * @param messageId messageId of the upstream message to be acknowledged to CCS.
+ * @return JSON encoded ack.
+ */
+ protected static String createJsonAck(String to, String messageId) {
+ Map<String, Object> message = new HashMap<String, Object>();
+ message.put("message_type", "ack");
+ message.put("to", to);
+ message.put("message_id", messageId);
+ return JSONValue.toJSONString(message);
+ }
+
+ /**
+ * Connects to GCM Cloud Connection Server using the supplied credentials.
+ *
+ * @param senderId Your GCM project number
+ * @param apiKey API Key of your project
+ */
+ public void connect(long senderId, String apiKey)
+ throws XMPPException, IOException, SmackException {
+ ConnectionConfiguration config =
+ new ConnectionConfiguration(GCM_SERVER, GCM_PORT);
+ config.setSecurityMode(SecurityMode.enabled);
+ config.setReconnectionAllowed(true);
+ config.setRosterLoadedAtLogin(false);
+ config.setSendPresence(false);
+ config.setSocketFactory(SSLSocketFactory.getDefault());
+
+ connection = new XMPPTCPConnection(config);
+ connection.connect();
+
+ connection.addConnectionListener(new LoggingConnectionListener());
+
+ // Handle incoming packets
+ connection.addPacketListener(new PacketListener() {
+
+ @Override
+ public void processPacket(Packet packet) {
+ logger.log(Level.INFO, "Received: " + packet.toXML());
+ Message incomingMessage = (Message) packet;
+ GcmPacketExtension gcmPacket =
+ (GcmPacketExtension) incomingMessage.
+ getExtension(GCM_NAMESPACE);
+ String json = gcmPacket.getJson();
+ try {
+ @SuppressWarnings("unchecked")
+ Map<String, Object> jsonObject =
+ (Map<String, Object>) JSONValue.
+ parseWithException(json);
+
+ // present for "ack"/"nack", null otherwise
+ Object messageType = jsonObject.get("message_type");
+
+ if (messageType == null) {
+ // Normal upstream data message
+ handleUpstreamMessage(jsonObject);
+
+ // Send ACK to CCS
+ String messageId = (String) jsonObject.get("message_id");
+ String from = (String) jsonObject.get("from");
+ String ack = createJsonAck(from, messageId);
+ send(ack);
+ } else if ("ack".equals(messageType.toString())) {
+ // Process Ack
+ handleAckReceipt(jsonObject);
+ } else if ("nack".equals(messageType.toString())) {
+ // Process Nack
+ handleNackReceipt(jsonObject);
+ } else if ("control".equals(messageType.toString())) {
+ // Process control message
+ handleControlMessage(jsonObject);
+ } else {
+ logger.log(Level.WARNING,
+ "Unrecognized message type (%s)",
+ messageType.toString());
+ }
+ } catch (ParseException e) {
+ logger.log(Level.SEVERE, "Error parsing JSON " + json, e);
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "Failed to process packet", e);
+ }
+ }
+ }, new PacketTypeFilter(Message.class));
+
+ // Log all outgoing packets
+ connection.addPacketInterceptor(new PacketInterceptor() {
+ @Override
+ public void interceptPacket(Packet packet) {
+ logger.log(Level.INFO, "Sent: {0}", packet.toXML());
+ }
+ }, new PacketTypeFilter(Message.class));
+
+ connection.login(senderId + "@gcm.googleapis.com", apiKey);
+ }
+
+ public static void main(String[] args) throws Exception {
+ final long senderId = 1234567890L; // your GCM sender id
+ final String password = "Your API key";
+
+ SmackCcsClient ccsClient = new SmackCcsClient();
+
+ ccsClient.connect(senderId, password);
+
+ // Send a sample hello downstream message to a device.
+ String toRegId = "RegistrationIdOfTheTargetDevice";
+ String messageId = ccsClient.nextMessageId();
+ Map<String, String> payload = new HashMap<String, String>();
+ payload.put("Hello", "World");
+ payload.put("CCS", "Dummy Message");
+ payload.put("EmbeddedMessageId", messageId);
+ String collapseKey = "sample";
+ Long timeToLive = 10000L;
+ String message = createJsonMessage(toRegId, messageId, payload,
+ collapseKey, timeToLive, true);
+
+ ccsClient.sendDownstreamMessage(message);
+ }
+
+ /**
+ * XMPP Packet Extension for GCM Cloud Connection Server.
+ */
+ private static final class GcmPacketExtension extends DefaultPacketExtension {
+
+ private final String json;
+
+ public GcmPacketExtension(String json) {
+ super(GCM_ELEMENT_NAME, GCM_NAMESPACE);
+ this.json = json;
+ }
+
+ public String getJson() {
+ return json;
+ }
+
@Override
public String toXML() {
-
- StringBuilder buf = new StringBuilder();
- buf.append("<message");
- if (getXmlns() != null) {
- buf.append(" xmlns=\"").append(getXmlns()).append("\"");
- }
- if (getLanguage() != null) {
- buf.append(" xml:lang=\"").append(getLanguage()).append("\"");
- }
- if (getPacketID() != null) {
- buf.append(" id=\"").append(getPacketID()).append("\"");
- }
- if (getTo() != null) {
- buf.append(" to=\"").append(StringUtils.escapeForXML(getTo())).append("\"");
- }
- if (getFrom() != null) {
- buf.append(" from=\"").append(StringUtils.escapeForXML(getFrom())).append("\"");
- }
- buf.append(">");
- buf.append(GcmPacketExtension.this.toXML());
- buf.append("</message>");
- return buf.toString();
+ return String.format("<%s xmlns=\"%s\">%s</%s>",
+ GCM_ELEMENT_NAME, GCM_NAMESPACE,
+ StringUtils.escapeForXML(json), GCM_ELEMENT_NAME);
}
- };
- }
- }
- public SmackCcsClient() {
- // Add GcmPacketExtension
- ProviderManager.getInstance().addExtensionProvider(GCM_ELEMENT_NAME,
- GCM_NAMESPACE, new PacketExtensionProvider() {
-
- @Override
- public PacketExtension parseExtension(XmlPullParser parser)
- throws Exception {
- String json = parser.nextText();
- GcmPacketExtension packet = new GcmPacketExtension(json);
- return packet;
- }
- });
- }
-
- /**
- * Returns a random message id to uniquely identify a message.
- *
- * <p>Note:
- * This is generated by a pseudo random number generator for illustration purpose,
- * and is not guaranteed to be unique.
- *
- */
- public String getRandomMessageId() {
- return "m-" + Long.toString(random.nextLong());
- }
-
- /**
- * Sends a downstream GCM message.
- */
- public void send(String jsonRequest) {
- Packet request = new GcmPacketExtension(jsonRequest).toPacket();
- connection.sendPacket(request);
- }
-
- /**
- * Handles an upstream data message from a device application.
- *
- * <p>This sample echo server sends an echo message back to the device.
- * Subclasses should override this method to process an upstream message.
- */
- public void handleIncomingDataMessage(Map<String, Object> jsonObject) {
- String from = jsonObject.get("from").toString();
-
- // PackageName of the application that sent this message.
- String category = jsonObject.get("category").toString();
-
- // Use the packageName as the collapseKey in the echo packet
- String collapseKey = "echo:CollapseKey";
- @SuppressWarnings("unchecked")
- Map<String, String> payload = (Map<String, String>) jsonObject.get("data");
- payload.put("ECHO", "Application: " + category);
-
- // Send an ECHO response back
- String echo = createJsonMessage(from, getRandomMessageId(), payload, collapseKey, null, false);
- send(echo);
- }
-
- /**
- * Handles an ACK.
- *
- * <p>By default, it only logs a {@code INFO} message, but subclasses could override it to
- * properly handle ACKS.
- */
- public void handleAckReceipt(Map<String, Object> jsonObject) {
- String messageId = jsonObject.get("message_id").toString();
- String from = jsonObject.get("from").toString();
- logger.log(Level.INFO, "handleAckReceipt() from: " + from + ", messageId: " + messageId);
- }
-
- /**
- * Handles a NACK.
- *
- * <p>By default, it only logs a {@code INFO} message, but subclasses could override it to
- * properly handle NACKS.
- */
- public void handleNackReceipt(Map<String, Object> jsonObject) {
- String messageId = jsonObject.get("message_id").toString();
- String from = jsonObject.get("from").toString();
- logger.log(Level.INFO, "handleNackReceipt() from: " + from + ", messageId: " + messageId);
- }
-
- /**
- * Creates a JSON encoded GCM message.
- *
- * @param to RegistrationId of the target device (Required).
- * @param messageId Unique messageId for which CCS will send an "ack/nack" (Required).
- * @param payload Message content intended for the application. (Optional).
- * @param collapseKey GCM collapse_key parameter (Optional).
- * @param timeToLive GCM time_to_live parameter (Optional).
- * @param delayWhileIdle GCM delay_while_idle parameter (Optional).
- * @return JSON encoded GCM message.
- */
- public static String createJsonMessage(String to, String messageId, Map<String, String> payload,
- String collapseKey, Long timeToLive, Boolean delayWhileIdle) {
- Map<String, Object> message = new HashMap<String, Object>();
- message.put("to", to);
- if (collapseKey != null) {
- message.put("collapse_key", collapseKey);
- }
- if (timeToLive != null) {
- message.put("time_to_live", timeToLive);
- }
- if (delayWhileIdle != null && delayWhileIdle) {
- message.put("delay_while_idle", true);
- }
- message.put("message_id", messageId);
- message.put("data", payload);
- return JSONValue.toJSONString(message);
- }
-
- /**
- * Creates a JSON encoded ACK message for an upstream message received from an application.
- *
- * @param to RegistrationId of the device who sent the upstream message.
- * @param messageId messageId of the upstream message to be acknowledged to CCS.
- * @return JSON encoded ack.
- */
- public static String createJsonAck(String to, String messageId) {
- Map<String, Object> message = new HashMap<String, Object>();
- message.put("message_type", "ack");
- message.put("to", to);
- message.put("message_id", messageId);
- return JSONValue.toJSONString(message);
- }
-
- /**
- * Connects to GCM Cloud Connection Server using the supplied credentials.
- *
- * @param username GCM_SENDER_ID@gcm.googleapis.com
- * @param password API Key
- * @throws XMPPException
- */
- public void connect(String username, String password) throws XMPPException {
- config = new ConnectionConfiguration(GCM_SERVER, GCM_PORT);
- config.setSecurityMode(SecurityMode.enabled);
- config.setReconnectionAllowed(true);
- config.setRosterLoadedAtLogin(false);
- config.setSendPresence(false);
- config.setSocketFactory(SSLSocketFactory.getDefault());
-
- // NOTE: Set to true to launch a window with information about packets sent and received
- config.setDebuggerEnabled(true);
-
- // -Dsmack.debugEnabled=true
- XMPPConnection.DEBUG_ENABLED = true;
-
- connection = new XMPPConnection(config);
- connection.connect();
-
- connection.addConnectionListener(new ConnectionListener() {
-
- @Override
- public void reconnectionSuccessful() {
- logger.info("Reconnecting..");
- }
-
- @Override
- public void reconnectionFailed(Exception e) {
- logger.log(Level.INFO, "Reconnection failed.. ", e);
- }
-
- @Override
- public void reconnectingIn(int seconds) {
- logger.log(Level.INFO, "Reconnecting in %d secs", seconds);
- }
-
- @Override
- public void connectionClosedOnError(Exception e) {
- logger.log(Level.INFO, "Connection closed on error.");
- }
-
- @Override
- public void connectionClosed() {
- logger.info("Connection closed.");
- }
- });
-
- // Handle incoming packets
- connection.addPacketListener(new PacketListener() {
-
- @Override
- public void processPacket(Packet packet) {
- logger.log(Level.INFO, "Received: " + packet.toXML());
- Message incomingMessage = (Message) packet;
- GcmPacketExtension gcmPacket =
- (GcmPacketExtension) incomingMessage.getExtension(GCM_NAMESPACE);
- String json = gcmPacket.getJson();
- try {
- @SuppressWarnings("unchecked")
- Map<String, Object> jsonObject =
- (Map<String, Object>) JSONValue.parseWithException(json);
-
- // present for "ack"/"nack", null otherwise
- Object messageType = jsonObject.get("message_type");
-
- if (messageType == null) {
- // Normal upstream data message
- handleIncomingDataMessage(jsonObject);
-
- // Send ACK to CCS
- String messageId = jsonObject.get("message_id").toString();
- String from = jsonObject.get("from").toString();
- String ack = createJsonAck(from, messageId);
- send(ack);
- } else if ("ack".equals(messageType.toString())) {
- // Process Ack
- handleAckReceipt(jsonObject);
- } else if ("nack".equals(messageType.toString())) {
- // Process Nack
- handleNackReceipt(jsonObject);
- } else {
- logger.log(Level.WARNING, "Unrecognized message type (%s)",
- messageType.toString());
- }
- } catch (ParseException e) {
- logger.log(Level.SEVERE, "Error parsing JSON " + json, e);
- } catch (Exception e) {
- logger.log(Level.SEVERE, "Couldn't send echo.", e);
+ public Packet toPacket() {
+ Message message = new Message();
+ message.addExtension(this);
+ return message;
}
- }
- }, new PacketTypeFilter(Message.class));
-
-
- // Log all outgoing packets
- connection.addPacketInterceptor(new PacketInterceptor() {
- @Override
- public void interceptPacket(Packet packet) {
- logger.log(Level.INFO, "Sent: {0}", packet.toXML());
- }
- }, new PacketTypeFilter(Message.class));
-
- connection.login(username, password);
- }
-
- public static void main(String [] args) {
- final String userName = "Your GCM Sender Id" + "@gcm.googleapis.com";
- final String password = "API Key";
-
- SmackCcsClient ccsClient = new SmackCcsClient();
-
- try {
- ccsClient.connect(userName, password);
- } catch (XMPPException e) {
- e.printStackTrace();
}
- // Send a sample hello downstream message to a device.
- String toRegId = "RegistrationIdOfTheTargetDevice";
- String messageId = ccsClient.getRandomMessageId();
- Map<String, String> payload = new HashMap<String, String>();
- payload.put("Hello", "World");
- payload.put("CCS", "Dummy Message");
- payload.put("EmbeddedMessageId", messageId);
- String collapseKey = "sample";
- Long timeToLive = 10000L;
- Boolean delayWhileIdle = true;
- ccsClient.send(createJsonMessage(toRegId, messageId, payload, collapseKey,
- timeToLive, delayWhileIdle));
- }
+ private static final class LoggingConnectionListener
+ implements ConnectionListener {
+
+ @Override
+ public void connected(XMPPConnection xmppConnection) {
+ logger.info("Connected.");
+ }
+
+ @Override
+ public void authenticated(XMPPConnection xmppConnection) {
+ logger.info("Authenticated.");
+ }
+
+ @Override
+ public void reconnectionSuccessful() {
+ logger.info("Reconnecting..");
+ }
+
+ @Override
+ public void reconnectionFailed(Exception e) {
+ logger.log(Level.INFO, "Reconnection failed.. ", e);
+ }
+
+ @Override
+ public void reconnectingIn(int seconds) {
+ logger.log(Level.INFO, "Reconnecting in %d secs", seconds);
+ }
+
+ @Override
+ public void connectionClosedOnError(Exception e) {
+ logger.info("Connection closed on error.");
+ }
+
+ @Override
+ public void connectionClosed() {
+ logger.info("Connection closed.");
+ }
+ }
}</pre>
+
<h3 id="python">Python sample</h3>
<p>Here is an example of a CCS app server written in Python. This sample echo
diff --git a/docs/html/google/play-services/setup.jd b/docs/html/google/play-services/setup.jd
index cc2047e..ebd3694 100644
--- a/docs/html/google/play-services/setup.jd
+++ b/docs/html/google/play-services/setup.jd
@@ -129,7 +129,7 @@
<ol>
<li>Copy the library project at <code><android-sdk>/extras/google/google_play_services/libproject/google-play-services_lib/</code> to the location where you maintain your Android app projects.</li>
- <li>In your app project, reference Google Play services library project. See
+ <li>In your app project, reference the Google Play services library project. See
<a href="{@docRoot}tools/projects/projects-cmdline.html#ReferencingLibraryProject">Referencing
a Library Project on the Command Line</a> for more information on how to do this.
<p class="note"><strong>Note:</strong>
diff --git a/docs/html/google/play/billing/billing_overview.jd b/docs/html/google/play/billing/billing_overview.jd
index 301d911..12f8c9a 100644
--- a/docs/html/google/play/billing/billing_overview.jd
+++ b/docs/html/google/play/billing/billing_overview.jd
@@ -38,6 +38,16 @@
features that you need to understand in order to add In-app
Billing features into your application.</p>
+<p class="note"><b>Note</b>: Ensure that you comply with applicable laws in the countries where you
+distribute apps. For example, in EU countries, laws based on the
+<a href="http://ec.europa.eu/justice/consumer-marketing/unfair-trade/unfair-practices/">Unfair
+Commercial Practices Directive</a> prohibit direct exhortations to children to buy advertised
+products or to persuade their parents or other adults to buy advertised products for them.
+See the
+<a href="http://ec.europa.eu/consumers/enforcement/docs/common_position_on_online_games_en.pdf">position
+of the EU consumer protection authorities</a> for more information on this and other topics.
+</p>
+
<h2 id="api">In-app Billing API</h2>
<p>Your application accesses the In-app Billing service using an API that is
exposed by the Google Play app that is installed on the device. The Google Play
diff --git a/docs/html/tools/sdk/ndk/index.jd b/docs/html/tools/sdk/ndk/index.jd
index b77a72a..71b15d5 100644
--- a/docs/html/tools/sdk/ndk/index.jd
+++ b/docs/html/tools/sdk/ndk/index.jd
@@ -349,8 +349,7 @@
<input id="agree" type="checkbox" name="agree" value="1" onclick="onAgreeChecked()" />
<label id="agreeLabel" for="agree">I have read and agree with the above terms and conditions</label>
</p>
-<p><a href="" class="button disabled ndk" id="downloadForRealz" onclick="return
-onDownloadNdkForRealz(this);"></a></p>
+<p><a href="" class="button disabled ndk" id="downloadForRealz" onclick="return onDownloadNdkForRealz(this);"></a></p>
</div>
diff --git a/docs/html/training/wearables/data-layer/events.jd b/docs/html/training/wearables/data-layer/events.jd
index 0146c4e..a37afe0 100644
--- a/docs/html/training/wearables/data-layer/events.jd
+++ b/docs/html/training/wearables/data-layer/events.jd
@@ -43,7 +43,8 @@
@Override
public void onResult(final DataItemResult result) {
if(result.getStatus().isSuccess()) {
- Log.d(TAG, "Data item set: " + result.getDataItem().getUri());
+ Log.d(TAG, "Data item set: " + result.getDataItem().getUri());
+ }
}
});
</pre>
@@ -293,7 +294,7 @@
@Override
protected void onStop() {
if (null != mGoogleApiClient && mGoogleApiClient.isConnected()) {
- Wearable.NodeApi.removeListener(mGoogleApiClient, this);
+ Wearable.DataApi.removeListener(mGoogleApiClient, this);
mGoogleApiClient.disconnect();
}
super.onStop();
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 610f6cf..b5f88f0 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -88,6 +88,7 @@
import android.view.ViewRootImpl;
import android.view.ViewStub;
import android.view.Window;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -2094,6 +2095,22 @@
}
@Override
+ public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
+ if (mOutsetBottom != null) {
+ final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
+ int bottom = (int) mOutsetBottom.getDimension(metrics);
+ WindowInsets newInsets = insets.replaceSystemWindowInsets(
+ insets.getSystemWindowInsetLeft(), insets.getSystemWindowInsetTop(),
+ insets.getSystemWindowInsetRight(),
+ insets.getSystemWindowInsetBottom() + bottom);
+ return super.dispatchApplyWindowInsets(newInsets);
+ } else {
+ return super.dispatchApplyWindowInsets(insets);
+ }
+ }
+
+
+ @Override
public boolean onTouchEvent(MotionEvent event) {
return onInterceptTouchEvent(event);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5626c7a..e3e79f0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -10930,14 +10930,19 @@
public void waitForAllWindowsDrawn(IRemoteCallback callback, long timeout) {
synchronized (mWindowMap) {
mWaitingForDrawnCallback = callback;
- final WindowList windows = getDefaultWindowListLocked();
- for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
- final WindowState win = windows.get(winNdx);
- if (win.mHasSurface) {
- win.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
- // Force add to mResizingWindows.
- win.mLastContentInsets.set(-1, -1, -1, -1);
- mWaitingForDrawn.add(win);
+ for (int displayNdx = mDisplayContents.size() - 1; displayNdx >= 0; --displayNdx) {
+ final WindowList windows =
+ mDisplayContents.valueAt(displayNdx).getWindowList();
+ for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
+ final WindowState win = windows.get(winNdx);
+ if (win.mHasSurface) {
+ win.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
+ // Force add to mResizingWindows.
+ win.mLastContentInsets.set(-1, -1, -1, -1);
+ if (DEBUG_SCREEN_ON) Slog.d(TAG, "waitForAllWindowsDrawn: adding " +
+ win);
+ mWaitingForDrawn.add(win);
+ }
}
}
requestTraversalLocked();