| 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");
|
| + }
|
| +}
|
|
|