зеркало из https://github.com/mozilla/gecko-dev.git
Bug 785124 - Pt 2 - JS changes to updater to allow storing update.mar to sdcard. r=marshall
From 93958fee051e9355930edba538eabeb91f4b442d Mon Sep 17 00:00:00 2001 sdcard and lock sdcard while in use --- b2g/components/DirectoryProvider.js | 154 ++++++++++++++++++++++++++--- b2g/components/UpdatePrompt.js | 16 ++- toolkit/mozapps/update/nsUpdateService.js | 124 ++++++++++++++++++++++- 3 files changed, 276 insertions(+), 18 deletions(-) * * * Fix log stmt
This commit is contained in:
Родитель
e458463bf7
Коммит
a4b95968d1
|
@ -11,12 +11,27 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const XRE_OS_UPDATE_APPLY_TO_DIR = "OSUpdApplyToD"
|
||||
const UPDATE_ARCHIVE_DIR = "UpdArchD"
|
||||
const LOCAL_DIR = "/data/local";
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(Services, "env",
|
||||
"@mozilla.org/process/environment;1",
|
||||
"nsIEnvironment");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(Services, "volumeService",
|
||||
"@mozilla.org/telephony/volume-service;1",
|
||||
"nsIVolumeService");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gExtStorage", function dp_gExtStorage() {
|
||||
return Services.env.get("EXTERNAL_STORAGE");
|
||||
});
|
||||
|
||||
const VERBOSE = 1;
|
||||
let log =
|
||||
VERBOSE ?
|
||||
function log_dump(msg) { dump("DirectoryProvider: " + msg + "\n"); } :
|
||||
function log_noop(msg) { };
|
||||
|
||||
function DirectoryProvider() {
|
||||
}
|
||||
|
||||
|
@ -35,36 +50,148 @@ DirectoryProvider.prototype = {
|
|||
file.initWithPath(LOCAL_DIR);
|
||||
persistent.value = true;
|
||||
return file;
|
||||
} else if (prop == "coreAppsDir") {
|
||||
}
|
||||
if (prop == "coreAppsDir") {
|
||||
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile)
|
||||
file.initWithPath("/system/b2g");
|
||||
persistent.value = true;
|
||||
return file;
|
||||
} else if (prop == XRE_OS_UPDATE_APPLY_TO_DIR) {
|
||||
return this.getOSUpdateApplyToDir(persistent);
|
||||
}
|
||||
if (prop == XRE_OS_UPDATE_APPLY_TO_DIR ||
|
||||
prop == UPDATE_ARCHIVE_DIR) {
|
||||
let file = this.getUpdateDir(persistent);
|
||||
return file;
|
||||
}
|
||||
#endif
|
||||
return null;
|
||||
},
|
||||
|
||||
// The VolumeService only exists on the device, and not on desktop
|
||||
volumeHasFreeSpace: function dp_volumeHasFreeSpace(volumePath, requiredSpace) {
|
||||
if (!volumePath) {
|
||||
return false;
|
||||
}
|
||||
if (!Services.volumeService) {
|
||||
return false;
|
||||
}
|
||||
let volume = Services.volumeService.getVolumeByPath(volumePath);
|
||||
if (!volume || volume.state !== Ci.nsIVolume.STATE_MOUNTED) {
|
||||
return false;
|
||||
}
|
||||
let stat = volume.getStats();
|
||||
if (!stat) {
|
||||
return false;
|
||||
}
|
||||
return requiredSpace <= stat.freeBytes;
|
||||
},
|
||||
|
||||
findUpdateDirWithFreeSpace: function dp_findUpdateDirWithFreeSpace(requiredSpace) {
|
||||
if (!Services.volumeService) {
|
||||
return this.createUpdatesDir(LOCAL_DIR);
|
||||
}
|
||||
let activeUpdate = Services.um.activeUpdate;
|
||||
if (this.volumeHasFreeSpace(gExtStorage, requiredSpace)) {
|
||||
let extUpdateDir = this.createUpdatesDir(gExtStorage);
|
||||
if (extUpdateDir !== null) {
|
||||
return extUpdateDir;
|
||||
}
|
||||
log("Warning: " + gExtStorage + " has enough free space for update " +
|
||||
activeUpdate.name + ", but is not writable");
|
||||
}
|
||||
|
||||
if (this.volumeHasFreeSpace(LOCAL_DIR, requiredSpace)) {
|
||||
let localUpdateDir = this.createUpdatesDir(LOCAL_DIR);
|
||||
if (localUpdateDir !== null) {
|
||||
return localUpdateDir;
|
||||
}
|
||||
log("Warning: " + LOCAL_DIR + " has enough free space for update " +
|
||||
activeUpdate.name + ", but is not writable");
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
getOSUpdateApplyToDir: function dp_getOSUpdateApplyToDir(persistent) {
|
||||
// TODO add logic to check available storage space,
|
||||
// and iterate through pref(s) to find alternative dirs if
|
||||
// necessary.
|
||||
getUpdateDir: function dp_getUpdateDir(persistent) {
|
||||
let defaultUpdateDir = this.getDefaultUpdateDir();
|
||||
persistent.value = false;
|
||||
|
||||
let path = Services.env.get("EXTERNAL_STORAGE");
|
||||
let activeUpdate = Services.um.activeUpdate;
|
||||
if (!activeUpdate) {
|
||||
log("Warning: No active update found, using default update dir: " +
|
||||
defaultUpdateDir);
|
||||
return defaultUpdateDir;
|
||||
}
|
||||
|
||||
let selectedPatch = activeUpdate.selectedPatch;
|
||||
if (!selectedPatch) {
|
||||
log("Warning: No selected patch, using default update dir: " +
|
||||
defaultUpdateDir);
|
||||
return defaultUpdateDir;
|
||||
}
|
||||
|
||||
let requiredSpace = selectedPatch.size * 2;
|
||||
let updateDir = this.findUpdateDirWithFreeSpace(requiredSpace, persistent);
|
||||
if (updateDir) {
|
||||
return updateDir;
|
||||
}
|
||||
|
||||
// If we've gotten this far, there isn't enough free space to download the patch
|
||||
// on either external storage or /data/local. All we can do is report the
|
||||
// error and let upstream code handle it more gracefully.
|
||||
log("Error: No volume found with " + requiredSpace + " bytes for downloading"+
|
||||
" update " + activeUpdate.name);
|
||||
throw Cr.NS_ERROR_FILE_TOO_BIG;
|
||||
},
|
||||
|
||||
createUpdatesDir: function dp_createUpdatesDir(root) {
|
||||
let dir = Cc["@mozilla.org/file/local;1"]
|
||||
.createInstance(Ci.nsILocalFile);
|
||||
dir.initWithPath(root);
|
||||
if (!dir.isWritable()) {
|
||||
return null;
|
||||
}
|
||||
dir.appendRelativePath("updates/0");
|
||||
if (dir.exists()) {
|
||||
if (dir.isDirectory() && dir.isWritable()) {
|
||||
return dir;
|
||||
}
|
||||
// updates/0 is either a file or isn't writable. In either case we
|
||||
// can't use it.
|
||||
log("Error: " + dir.path + " is a file or isn't writable");
|
||||
return null;
|
||||
}
|
||||
// updates/0 doesn't exist, and the parent is writable, so try to
|
||||
// create it. This can fail if a file named updates exists.
|
||||
try {
|
||||
dir.create(Ci.nsIFile.DIRECTORY_TYPE, 0770);
|
||||
} catch (e) {
|
||||
// The create failed for some reason. We can't use it.
|
||||
log("Error: " + dir.path + " unable to create directory");
|
||||
return null;
|
||||
}
|
||||
return dir;
|
||||
},
|
||||
|
||||
getDefaultUpdateDir: function dp_getDefaultUpdateDir() {
|
||||
let path = gExtStorage;
|
||||
if (!path) {
|
||||
path = LOCAL_PATH;
|
||||
path = LOCAL_DIR;
|
||||
}
|
||||
|
||||
if (Services.volumeService) {
|
||||
let extVolume = Services.volumeService.getVolumeByPath(path);
|
||||
if (!extVolume) {
|
||||
path = LOCAL_DIR;
|
||||
}
|
||||
}
|
||||
|
||||
let dir = Cc["@mozilla.org/file/local;1"]
|
||||
.createInstance(Ci.nsILocalFile)
|
||||
dir.initWithPath(path);
|
||||
|
||||
if (!dir.exists() && path != LOCAL_PATH) {
|
||||
// Fallback to LOCAL_PATH if we didn't fallback earlier
|
||||
dir.initWithPath(LOCAL_PATH);
|
||||
if (!dir.exists() && path != LOCAL_DIR) {
|
||||
// Fallback to LOCAL_DIR if we didn't fallback earlier
|
||||
dir.initWithPath(LOCAL_DIR);
|
||||
|
||||
if (!dir.exists()) {
|
||||
throw Cr.NS_ERROR_FILE_NOT_FOUND;
|
||||
|
@ -72,7 +199,6 @@ DirectoryProvider.prototype = {
|
|||
}
|
||||
|
||||
dir.appendRelativePath("updates");
|
||||
persistent.value = false;
|
||||
return dir;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -22,6 +22,9 @@ const PREF_APPLY_PROMPT_TIMEOUT = "b2g.update.apply-prompt-timeout";
|
|||
const PREF_APPLY_IDLE_TIMEOUT = "b2g.update.apply-idle-timeout";
|
||||
|
||||
const NETWORK_ERROR_OFFLINE = 111;
|
||||
const FILE_ERROR_TOO_BIG = 112;
|
||||
|
||||
const STATE_DOWNLOADING = 'downloading';
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(Services, "aus",
|
||||
"@mozilla.org/updates/update-service;1",
|
||||
|
@ -272,8 +275,17 @@ UpdatePrompt.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
Services.aus.downloadUpdate(aUpdate, true);
|
||||
Services.aus.addDownloadListener(this);
|
||||
let status = Services.aus.downloadUpdate(aUpdate, true);
|
||||
if (status == STATE_DOWNLOADING) {
|
||||
Services.aus.addDownloadListener(this);
|
||||
return;
|
||||
}
|
||||
|
||||
log("Error downloading update " + aUpdate.name + ": " + aUpdate.errorCode);
|
||||
if (aUpdate.errorCode == FILE_ERROR_TOO_BIG) {
|
||||
aUpdate.statusText = "file-too-big";
|
||||
}
|
||||
this.showUpdateError(aUpdate);
|
||||
},
|
||||
|
||||
handleDownloadCancel: function UP_handleDownloadCancel() {
|
||||
|
|
|
@ -74,10 +74,18 @@ const KEY_GRED = "GreD";
|
|||
#define USE_UPDROOT
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#define USE_UPDATE_ARCHIVE_DIR
|
||||
#endif
|
||||
|
||||
#ifdef USE_UPDROOT
|
||||
const KEY_UPDROOT = "UpdRootD";
|
||||
#endif
|
||||
|
||||
#ifdef USE_UPDATE_ARCHIVE_DIR
|
||||
const KEY_UPDATE_ARCHIVE_DIR = "UpdArchD"
|
||||
#endif
|
||||
|
||||
#ifdef XP_WIN
|
||||
#define SKIP_STAGE_UPDATES_TEST
|
||||
#elifdef MOZ_WIDGET_GONK
|
||||
|
@ -99,7 +107,8 @@ const FILE_UPDATE_ARCHIVE = "update.apk";
|
|||
#else
|
||||
const FILE_UPDATE_ARCHIVE = "update.mar";
|
||||
#endif
|
||||
const FILE_UPDATE_LOG = "update.log"
|
||||
const FILE_UPDATE_LINK = "update.link";
|
||||
const FILE_UPDATE_LOG = "update.log";
|
||||
const FILE_UPDATES_DB = "updates.xml";
|
||||
const FILE_UPDATE_ACTIVE = "active-update.xml";
|
||||
const FILE_PERMS_TEST = "update.test";
|
||||
|
@ -155,6 +164,7 @@ const CERT_ATTR_CHECK_FAILED_NO_UPDATE = 100;
|
|||
const CERT_ATTR_CHECK_FAILED_HAS_UPDATE = 101;
|
||||
const BACKGROUNDCHECK_MULTIPLE_FAILURES = 110;
|
||||
const NETWORK_ERROR_OFFLINE = 111;
|
||||
const FILE_ERROR_TOO_BIG = 112;
|
||||
|
||||
const DOWNLOAD_CHUNK_SIZE = 300000; // bytes
|
||||
const DOWNLOAD_BACKGROUND_INTERVAL = 600; // seconds
|
||||
|
@ -174,6 +184,12 @@ const DEFAULT_SOCKET_MAX_ERRORS = 10;
|
|||
const DEFAULT_UPDATE_RETRY_TIMEOUT = 2000;
|
||||
|
||||
var gLocale = null;
|
||||
#ifdef MOZ_B2G
|
||||
var gVolumeMountLock = null;
|
||||
XPCOMUtils.defineLazyGetter(this, "gExtStorage", function aus_gExtStorage() {
|
||||
return Services.env.get("EXTERNAL_STORAGE");
|
||||
});
|
||||
#endif
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
|
@ -731,6 +747,74 @@ function writeStatusFile(dir, state) {
|
|||
writeStringToFile(statusFile, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the link file from the update.link file in the
|
||||
* specified directory.
|
||||
* @param dir
|
||||
* The dir to look for an update.link file in
|
||||
* @return The contents of the update.link file.
|
||||
*/
|
||||
function readLinkFile(dir) {
|
||||
var linkFile = dir.clone();
|
||||
linkFile.append(FILE_UPDATE_LINK);
|
||||
var link = readStringFromFile(linkFile) || STATE_NONE;
|
||||
LOG("readLinkFile - link: " + link + ", path: " + linkFile.path);
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a link file, which allows the actual patch to live in
|
||||
* a directory different from the update directory.
|
||||
* @param dir
|
||||
* The patch directory where the update.link file
|
||||
* should be written.
|
||||
* @param patchFile
|
||||
* The fully qualified filename of the patchfile.
|
||||
*/
|
||||
function writeLinkFile(dir, patchFile) {
|
||||
var linkFile = dir.clone();
|
||||
linkFile.append(FILE_UPDATE_LINK);
|
||||
writeStringToFile(linkFile, patchFile.path);
|
||||
#ifdef MOZ_B2G
|
||||
if (patchFile.path.indexOf(gExtStorage) == 0) {
|
||||
// The patchfile is being stored on external storage. Try to lock it
|
||||
// so that it doesn't get shared with the PC while we're downloading
|
||||
// to it.
|
||||
acquireSDCardMountLock();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires a VolumeMountLock for the sdcard volume.
|
||||
*
|
||||
* This prevents the SDCard from being shared with the PC while
|
||||
* we're downloading the update.
|
||||
*/
|
||||
function acquireSDCardMountLock() {
|
||||
#ifdef MOZ_B2G
|
||||
let volsvc = Cc["@mozilla.org/telephony/volume-service;1"].
|
||||
getService(Ci.nsIVolumeService);
|
||||
if (volsvc) {
|
||||
gSDCardMountLock = volsvc.createMountLock("sdcard");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases any SDCard mount lock that we might have.
|
||||
*
|
||||
* This once again allows the SDCard to be shared with the PC.
|
||||
*/
|
||||
function releaseSDCardMountLock() {
|
||||
#ifdef MOZ_B2G
|
||||
if (gSDCardMountLock) {
|
||||
gSDCardMountLock.unlock();
|
||||
gSDCardMountLock = null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the service should be used to attempt an update
|
||||
* or not. For now this is only when PREF_APP_UPDATE_SERVICE_ENABLED
|
||||
|
@ -899,6 +983,7 @@ function cleanUpUpdatesDir(aBackgroundUpdate) {
|
|||
LOG("cleanUpUpdatesDir - failed to remove file " + f.path);
|
||||
}
|
||||
}
|
||||
releaseSDCardMountLock();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2798,6 +2883,8 @@ UpdateManager.prototype = {
|
|||
var prompter = Cc["@mozilla.org/updates/update-prompt;1"].
|
||||
createInstance(Ci.nsIUpdatePrompt);
|
||||
prompter.showUpdateDownloaded(update, true);
|
||||
} else {
|
||||
releaseSDCardMountLock();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -3146,6 +3233,7 @@ Downloader.prototype = {
|
|||
if (this._request && this._request instanceof Ci.nsIRequest) {
|
||||
this._request.cancel(cancelError);
|
||||
}
|
||||
releaseSDCardMountLock();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -3316,6 +3404,28 @@ Downloader.prototype = {
|
|||
return this._request != null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the nsIFile to use for downloading the active update's selected patch
|
||||
*/
|
||||
_getUpdateArchiveFile: function Downloader__getUpdateArchiveFile() {
|
||||
var updateArchive;
|
||||
#ifdef USE_UPDATE_ARCHIVE_DIR
|
||||
try {
|
||||
updateArchive = FileUtils.getDir(KEY_UPDATE_ARCHIVE_DIR, [], true);
|
||||
} catch (e) {
|
||||
if (e == Cr.NS_ERROR_FILE_TOO_BIG) {
|
||||
this._update.errorCode = FILE_ERROR_TOO_BIG;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
#else
|
||||
updateArchive = getUpdatesDir().clone();
|
||||
#endif
|
||||
|
||||
updateArchive.append(FILE_UPDATE_ARCHIVE);
|
||||
return updateArchive;
|
||||
},
|
||||
|
||||
/**
|
||||
* Download and stage the given update.
|
||||
* @param update
|
||||
|
@ -3339,8 +3449,16 @@ Downloader.prototype = {
|
|||
}
|
||||
this.isCompleteUpdate = this._patch.type == "complete";
|
||||
|
||||
var patchFile = updateDir.clone();
|
||||
patchFile.append(FILE_UPDATE_ARCHIVE);
|
||||
var patchFile = this._getUpdateArchiveFile();
|
||||
if (!patchFile) {
|
||||
return STATE_NONE;
|
||||
}
|
||||
|
||||
if (patchFile.path.indexOf(updateDir.path) != 0) {
|
||||
// The patchFile is in a directory which is different from the
|
||||
// updateDir, create a link file.
|
||||
writeLinkFile(updateDir, patchFile);
|
||||
}
|
||||
|
||||
var uri = Services.io.newURI(this._patch.URL, null, null);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче