(FFOS Gecko & Gaia) OTA - Do real check

  虽然代码分析了很多,但是还没有真正做check的工作,下面就来找到真正的checker。

  代码位置:gecko/toolkit/mozapps/update/nsUpdateService.js。参考之前的(FFOS Gecko & Gaia) OTA - 代码模块总览,nsUpdateService.js中的Checker对象,实现了nsIUpdateChecker这个interface。下面就来分析Checker对象的实现。

1.  checkForUpdates函数:

  实现略长,在代码中添加注释分析。

checkForUpdates: function UC_checkForUpdates(listener, force) {
  LOG("Checker: checkForUpdates, force: " + force);
  if (!listener)
    throw Cr.NS_ERROR_NULL_POINTER;
  // 通知‘update-check-start‘事件,监听者是谁呢?还记得上一篇分析的UpdatePrompt实现了nsIObserver吗?
  Services.obs.notifyObservers(null, "update-check-start", null);
 // 这个函数很重要,它通过获取很多settings和preferences,还会通过libutils库,来获取很多系统参数,拼接成一个url,  // 这个url就是OTA server URL
  var url = this.getUpdateURL(force);
  if (!url || (!this.enabled && !force))
    return;
  // 通过XHR发送GET请求,获取update信息
  this._request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
                  createInstance(Ci.nsISupports);
  // This is here to let unit test code override XHR
  if (this._request.wrappedJSObject) {
    this._request = this._request.wrappedJSObject;
  }
  this._request.open("GET", url, true);
  var allowNonBuiltIn = !getPref("getBoolPref",
                                 PREF_APP_UPDATE_CERT_REQUIREBUILTIN, true);
  this._request.channel.notificationCallbacks = new gCertUtils.BadCertHandler(allowNonBuiltIn);
  // Prevent the request from reading from the cache.
  this._request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
  // Prevent the request from writing to the cache.
  this._request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;

  this._request.overrideMimeType("text/xml");
  // The Cache-Control header is only interpreted by proxies and the
  // final destination. It does not help if a resource is already
  // cached locally.
  this._request.setRequestHeader("Cache-Control", "no-cache");
  // HTTP/1.0 servers might not implement Cache-Control and
  // might only implement Pragma: no-cache
  this._request.setRequestHeader("Pragma", "no-cache");

  var self = this;  // 设置XHR event listener
  this._request.addEventListener("error", function(event) { self.onError(event); } ,false);
  this._request.addEventListener("load", function(event) { self.onLoad(event); }, false);

  LOG("Checker:checkForUpdates - sending request to: " + url);
  this._request.send(null);
  // 将传进来的listener保存下来,在XHR的event handler中回调
  this._callback = listener;
},

2. onLoad函数

  当request请求返回时,会回调onLoad函数

onLoad: function UC_onLoad(event) {
  LOG("Checker:onLoad - request completed downloading document");

  var prefs = Services.prefs;
  var certs = null;
  if (!getPref("getCharPref", PREF_APP_UPDATE_URL_OVERRIDE, null) &&
      getPref("getBoolPref", PREF_APP_UPDATE_CERT_CHECKATTRS, true)) {
    certs = gCertUtils.readCertPrefs(PREF_APP_UPDATE_CERTS_BRANCH);
  }

  try {
    // Analyze the resulting DOM and determine the set of updates.  // 之前没有找到对于request response解析的部分,其实秘密就在于这里,Checker中并没有显示的定义_updates属性,而是定义了一个get _updates()函数。   // 这是js的特性,如果定义了get/set property()函数,就表示可以get/set这个以property命名的属性。
    var updates = this._updates;
    LOG("Checker:onLoad - number of updates available: " + updates.length);
    var allowNonBuiltIn = !getPref("getBoolPref",
                                   PREF_APP_UPDATE_CERT_REQUIREBUILTIN, true);
    gCertUtils.checkCert(this._request.channel, allowNonBuiltIn, certs);

    if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CERT_ERRORS))
      Services.prefs.clearUserPref(PREF_APP_UPDATE_CERT_ERRORS);

    if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_BACKGROUNDERRORS))
      Services.prefs.clearUserPref(PREF_APP_UPDATE_BACKGROUNDERRORS);

    // Tell the callback about the updates

    this._callback.onCheckComplete(event.target, updates, updates.length);
  }
  catch (e) {
    LOG("Checker:onLoad - there was a problem checking for updates. " +
        "Exception: " + e);
    var request = event.target;
    var status = this._getChannelStatus(request);
    LOG("Checker:onLoad - request.status: " + status);
    var update = new Update(null);
    update.errorCode = status;
    update.statusText = getStatusTextFromCode(status, 404);

    if (this._isHttpStatusCode(status)) {
      update.errorCode = HTTP_ERROR_OFFSET + status;
    }
    if (e.result && e.result == Cr.NS_ERROR_ILLEGAL_VALUE) {
      update.errorCode = updates[0] ? CERT_ATTR_CHECK_FAILED_HAS_UPDATE
                                    : CERT_ATTR_CHECK_FAILED_NO_UPDATE;
    }

    this._callback.onError(request, update);
  }

  this._callback = null;
  this._request = null;
},

3. get _updates()函数

  解析request response,根据mozilla的文档,这个response是一个update.xml文件,具体的格式请参考https://wiki.mozilla.org/Software_Update:updates.xml_Format

  通过解析update.xml文件,返回一个nsIUpdate数组,也就是Update对象的数组。Update的构造函数又会解析update.xml中的所有patch标签,每个patch标签会对应的生成一个nsIUpdatePatch,也就是UpdatePatch,也就是说Update对象里会包含1-多个UpdatePatch对象,并且Update对象里会保存一些其他的信息,比如是否为OS Update等,具体的解析过程请参考代码。


/**
  * Returns an array of nsIUpdate objects discovered by the update check.
  * @throws if the XML document element node name is not updates.
  */

get _updates() {
  var updatesElement = this._request.responseXML.documentElement;
  if (!updatesElement) {
    LOG("Checker:_updates get - empty updates document?!");
    return [];
  }

  if (updatesElement.nodeName != "updates") {
    LOG("Checker:_updates get - unexpected node name!");
    throw new Error("Unexpected node name, expected: updates, got: " +
                    updatesElement.nodeName);
  }

  const ELEMENT_NODE = Ci.nsIDOMNode.ELEMENT_NODE;
  var updates = [];
  for (var i = 0; i < updatesElement.childNodes.length; ++i) {
    var updateElement = updatesElement.childNodes.item(i);
    if (updateElement.nodeType != ELEMENT_NODE ||
        updateElement.localName != "update")
      continue;

    updateElement.QueryInterface(Ci.nsIDOMElement);
    let update;
    try {
      update = new Update(updateElement);
    } catch (e) {
      LOG("Checker:_updates get - invalid <update/>, ignoring...");
      continue;
    }
    update.serviceURL = this.getUpdateURL(this._forced);
    update.channel = UpdateChannel.get();
    updates.push(update);
  }
  return updates;
},
时间: 2024-10-14 07:37:28

(FFOS Gecko & Gaia) OTA - Do real check的相关文章

(FFOS Gecko &amp; Gaia) OTA - 又回到UpdatePrompt

当download完成时,又回到了UpdatePrompt,因为此时需要提示user下载完成,确认apply. 1. UpdatePrompt.showUpdateDownloaded showUpdateDownloaded: function UP_showUpdateDownloaded(aUpdate, aBackground) { if (this._systemUpdateListener) { this._systemUpdateListener.onUpdateReady();

(FFOS Gecko &amp; Gaia) OTA - 处理check结果

当通过Checker检测到update以后,会通知UpdatePrompt中的updateCheckListener. 1. UpdateCheckListener.onCheckComplete onCheckComplete: function UCL_onCheckComplete(request, updates, updateCount) { if (Services.um.activeUpdate) { // We're actively downloading an update,

(FFOS Gecko &amp; Gaia) OTA - 进入Gecko层

代码位置:gecko/b2g/components/UpdatePrompt.js SystemApp发出'force-update-check'事件,也就从gaia进入到了gecko层. 1. 首先大概介绍一下UpdatePrompt.js. (a) UpdatePrompt.js里包含了2个对象,分别是UpdatePrompt和UpdateCheckListener. (b) UpdatePrompt实现了“@mozilla.org/updates/update-prompt;1”这个XPC

(FFOS Gecko &amp; Gaia) OTA - 代码模块总览

OTA整体框架里会涉及以下代码,从gaia到gecko都有,而且由于历史原因,复用了desktop browser的一些模块,还有一些冗余代码,分析时走了不少弯路. 1. Gaia部分 (a) gaia/apps/settings/js/panels/about/update_check.js 这个Settings中OTA功能的入口,通过AMD(Asynchronous Module Definition)规范,定义了一个模块UpdateCheck. (b) gaia/apps/system/j

(FFOS Gecko &amp; Gaia) OTA - 重回Gaia层

SystemApp中的UpdateManager作为gaia和gecko的通信桥梁,会接收gecko中UpdatePrompt发送的'update-available'事件. 1. UpdateManager.handleEvent var detail = evt.detail; if (detail.type && detail.type === 'update-available') { // systemUpdatable是在UpdateManager初始化时new出来的,Upda

(FFOS Gecko &amp; Gaia) OTA - 转移至System App

代码位置:gaia/apps/system/js/update_manager.js 1. update_manager.js向全局的window对象导出了一个对象UpdateManager,其他的js module可以直接访问UpdateManager. exports.UpdateManager = UpdateManager; 2. UpdateManager监听了settings app中对于‘gaia.system.checkForUpdates’的设置,也就是当‘gaia.syste

(FFOS Gecko &amp; Gaia) OTA - 关键的apply

这篇分析已经是尾声了,在UpdatePrompt中,调用了UpdateService的applyOsUpdate函数. 1. UpdateService.applyOsUpdate 这个函数很简单,就是获取到update.zip以后,调用recovery service去完成更新. applyOsUpdate: function AUS_applyOsUpdate(aUpdate) { if (!aUpdate.isOSUpdate || aUpdate.state != STATE_APPLI

(FFOS Gecko &amp; Gaia) OTA - 真正的download

前面分析了这么多,还没有真正的走到download流程.这篇就去了解真正的downloader. 1. UpdateService.downloadUpdate 看来这正的worker就是最后new出来的Downloader. downloadUpdate: function AUS_downloadUpdate(update, background) { if (!update) throw Cr.NS_ERROR_NULL_POINTER; // Don't download the upd

(FFOS Gecko) - several ways of registering a XPCOM Component

1. JavaScript Component (1) add a CustomComponent.manifest # The {classID} here must match the classID in CustomComponent.js component {e6b866e3-41b2-4f05-a4d2-3d4bde0f7ef8} components/CustomComponent.js contract @foobar/customcomponent;1 {e6b866e3-4