Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(945)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/omaha/ResponseParser.java

Issue 67313003: Upstream the updated Java Omaha XML parser (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressing nits Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/omaha/ResponseParser.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/ResponseParser.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/ResponseParser.java
new file mode 100644
index 0000000000000000000000000000000000000000..63064515028ba9fa7f1ee6d430ba58def6bbb0ef
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/ResponseParser.java
@@ -0,0 +1,273 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.omaha;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import org.chromium.chrome.browser.omaha.XMLParser.Node;
+
+/**
+ * Parses XML responses from the Omaha Update Server.
+ *
+ * Expects XML formatted like:
+ * <?xml version="1.0" encoding="UTF-8"?>
+ * <daystart elapsed_seconds="65524"/>
+ * <app appid="{appid}" status="ok">
+ * <updatecheck status="ok">
+ * <urls>
+ * <url codebase="https://market.android.com/details?id=com.google.android.apps.chrome/"/>
+ * </urls>
+ * <manifest version="0.16.4130.199">
+ * <packages>
+ * <package hash="0" name="dummy.apk" required="true" size="0"/>
+ * </packages>
+ * <actions>
+ * <action event="install" run="dummy.apk"/>
+ * <action event="postinstall"/>
+ * </actions>
+ * </manifest>
+ * </updatecheck>
+ * <ping status="ok"/>
+ * </app>
+ * </response>
+ *
+ * The appid is dependent on the variant of Chrome that is running.
+ */
+public class ResponseParser {
+ private static final String TAG = "ResponseParser";
+
+ // Tags that we care to parse from the response.
+ private static final String TAG_APP = "app";
+ private static final String TAG_DAYSTART = "daystart";
+ private static final String TAG_EVENT = "event";
+ private static final String TAG_MANIFEST = "manifest";
+ private static final String TAG_PING = "ping";
+ private static final String TAG_RESPONSE = "response";
+ private static final String TAG_UPDATECHECK = "updatecheck";
+ private static final String TAG_URL = "url";
+ private static final String TAG_URLS = "urls";
+
+ private final String mAppId;
+ private final boolean mExpectInstallEvent;
+ private final boolean mExpectPing;
+ private final boolean mExpectUpdatecheck;
+ private final boolean mStrictParsingMode;
+
+ private Integer mDaystartSeconds;
+ private String mAppStatus;
+
+ private String mUpdateStatus;
+ private String mNewVersion;
+ private String mUrl;
+
+ private boolean mParsedInstallEvent;
+ private boolean mParsedPing;
+ private boolean mParsedUpdatecheck;
+
+ public ResponseParser(String appId, boolean expectInstallEvent, boolean expectPing,
+ boolean expectUpdatecheck) {
+ this(false, appId, expectInstallEvent, expectPing, expectUpdatecheck);
+ }
+
+ public ResponseParser(boolean strictParsing, String appId, boolean expectInstallEvent,
+ boolean expectPing, boolean expectUpdatecheck) {
+ mStrictParsingMode = strictParsing;
+ mAppId = appId;
+ mExpectInstallEvent = expectInstallEvent;
+ mExpectPing = expectPing;
+ mExpectUpdatecheck = expectUpdatecheck;
+ }
+
+ public void parseResponse(String xml) throws RequestFailureException {
+ XMLParser parser = new XMLParser(xml);
+ Node rootNode = parser.getRootNode();
+ parseRootNode(rootNode);
+ }
+
+ public int getDaystartSeconds() {
+ if (mDaystartSeconds == null) return 0;
+ return mDaystartSeconds;
+ }
+
+ public String getNewVersion() {
+ return mNewVersion;
+ }
+
+ public String getURL() {
+ return mUrl;
+ }
+
+ public String getAppStatus() {
+ return mAppStatus;
+ }
+
+ public String getUpdateStatus() {
+ return mUpdateStatus;
+ }
+
+ private void resetParsedData() {
+ mDaystartSeconds = null;
+ mNewVersion = null;
+ mUrl = null;
+ mUpdateStatus = null;
+ mAppStatus = null;
+
+ mParsedInstallEvent = false;
+ mParsedPing = false;
+ mParsedUpdatecheck = false;
+ }
+
+ private boolean logError(Node node, int errorCode) throws RequestFailureException {
+ String errorMessage = "Failed to parse: " + node.tag;
+ if (mStrictParsingMode) throw new RequestFailureException(errorMessage, errorCode);
+
+ Log.e(TAG, errorMessage);
+ return false;
+ }
+
+ private void parseRootNode(Node rootNode) throws RequestFailureException {
+ for (int i = 0; i < rootNode.children.size(); ++i) {
+ if (TextUtils.equals(TAG_RESPONSE, rootNode.children.get(i).tag)) {
+ if (parseResponseNode(rootNode.children.get(i))) return;
+ break;
+ }
+ }
+
+ // The tag was bad; reset all of our state and bail.
+ resetParsedData();
+ logError(rootNode, RequestFailureException.ERROR_PARSE_ROOT);
+ }
+
+ private boolean parseResponseNode(Node node) throws RequestFailureException {
+ boolean success = true;
+ String serverType = node.attributes.get("server");
+ success &= TextUtils.equals("3.0", node.attributes.get("protocol"));
+
+ if (!TextUtils.equals("prod", serverType)) Log.w(TAG, "Server type: " + serverType);
+
+ for (int i = 0; i < node.children.size(); ++i) {
+ Node current = node.children.get(i);
+ if (TextUtils.equals(TAG_DAYSTART, current.tag)) {
+ success &= parseDaystartNode(current);
+ } else if (TextUtils.equals(TAG_APP, current.tag)) {
+ success &= parseAppNode(current);
+ } else {
+ Log.w(TAG, "Ignoring unknown child of <" + node.tag + "> : " + current.tag);
+ }
+ }
+
+ if (!success) {
+ return logError(node, RequestFailureException.ERROR_PARSE_RESPONSE);
+ } else if (mDaystartSeconds == null) {
+ return logError(node, RequestFailureException.ERROR_PARSE_DAYSTART);
+ } else if (mAppStatus == null) {
+ return logError(node, RequestFailureException.ERROR_PARSE_APP);
+ } else if (mExpectInstallEvent != mParsedInstallEvent) {
+ return logError(node, RequestFailureException.ERROR_PARSE_EVENT);
+ } else if (mExpectPing != mParsedPing) {
+ return logError(node, RequestFailureException.ERROR_PARSE_PING);
+ } else if (mExpectUpdatecheck != mParsedUpdatecheck) {
+ return logError(node, RequestFailureException.ERROR_PARSE_UPDATECHECK);
+ }
+
+ return true;
+ }
+
+ private boolean parseDaystartNode(Node node) throws RequestFailureException {
+ try {
+ mDaystartSeconds = Integer.parseInt(node.attributes.get("elapsed_seconds"));
+ } catch (NumberFormatException e) {
+ return logError(node, RequestFailureException.ERROR_PARSE_DAYSTART);
+ }
+ return true;
+ }
+
+ private boolean parseAppNode(Node node) throws RequestFailureException {
+ boolean success = true;
+ success &= TextUtils.equals(mAppId, node.attributes.get("appid"));
+
+ mAppStatus = node.attributes.get("status");
+ if (TextUtils.equals("ok", mAppStatus)) {
+ for (int i = 0; i < node.children.size(); ++i) {
+ Node current = node.children.get(i);
+ if (TextUtils.equals(TAG_UPDATECHECK, current.tag)) {
+ success &= parseUpdatecheck(current);
+ } else if (TextUtils.equals(TAG_EVENT, current.tag)) {
+ parseEvent(current);
+ } else if (TextUtils.equals(TAG_PING, current.tag)) {
+ parsePing(current);
+ }
+ }
+ } else if (TextUtils.equals("restricted", mAppStatus)) {
+ // Omaha isn't allowed to get data in this country. Pretend the request was fine.
+ } else {
+ success = false;
+ }
+
+ if (success) return true;
+ return logError(node, RequestFailureException.ERROR_PARSE_APP);
+ }
+
+ private boolean parseUpdatecheck(Node node) throws RequestFailureException {
+ boolean success = true;
+
+ mUpdateStatus = node.attributes.get("status");
+ if (TextUtils.equals("ok", mUpdateStatus)) {
+ for (int i = 0; i < node.children.size(); ++i) {
+ Node current = node.children.get(i);
+ if (TextUtils.equals(TAG_URLS, current.tag)) {
+ parseUrls(current);
+ } else if (TextUtils.equals(TAG_MANIFEST, current.tag)) {
+ parseManifest(current);
+ }
+ }
+
+ // Confirm all the tags we expected to see were parsed properly.
+ if (mUrl == null) {
+ return logError(node, RequestFailureException.ERROR_PARSE_URLS);
+ } else if (mNewVersion == null) {
+ return logError(node, RequestFailureException.ERROR_PARSE_MANIFEST);
+ }
+ } else if (TextUtils.equals("noupdate", mUpdateStatus)) {
+ // No update is available. Don't bother searching for other attributes.
+ } else if (mUpdateStatus != null && mUpdateStatus.startsWith("error")) {
+ Log.w(TAG, "Ignoring error status for " + node.tag + ": " + mUpdateStatus);
+ } else {
+ Log.w(TAG, "Ignoring unknown status for " + node.tag + ": " + mUpdateStatus);
+ }
+
+ mParsedUpdatecheck = true;
+ return true;
+ }
+
+ private void parsePing(Node node) {
+ if (TextUtils.equals("ok", node.attributes.get("status"))) mParsedPing = true;
+ }
+
+ private void parseEvent(Node node) {
+ if (TextUtils.equals("ok", node.attributes.get("status"))) mParsedInstallEvent = true;
+ }
+
+ private void parseUrls(Node node) {
+ for (int i = 0; i < node.children.size(); ++i) {
+ Node current = node.children.get(i);
+ if (TextUtils.equals(TAG_URL, current.tag)) parseUrl(current);
+ }
+ }
+
+ private void parseUrl(Node node) {
+ String url = node.attributes.get("codebase");
+ if (url == null) return;
+
+ // The URL gets a "/" tacked onto it by the server. Remove it.
+ if (url.endsWith("/")) url = url.substring(0, url.length() - 1);
+ mUrl = url;
+ }
+
+ private void parseManifest(Node node) {
+ mNewVersion = node.attributes.get("version");
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698