1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/apps/src/OperatorApps.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,380 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +const Cu = Components.utils; 1.11 +const Cc = Components.classes; 1.12 +const Ci = Components.interfaces; 1.13 + 1.14 +this.EXPORTED_SYMBOLS = ["OperatorAppsRegistry"]; 1.15 + 1.16 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.17 +Cu.import("resource://gre/modules/FileUtils.jsm"); 1.18 +Cu.import("resource://gre/modules/Webapps.jsm"); 1.19 +Cu.import("resource://gre/modules/Services.jsm"); 1.20 +Cu.import("resource://gre/modules/osfile.jsm"); 1.21 +Cu.import("resource://gre/modules/AppsUtils.jsm"); 1.22 +Cu.import("resource://gre/modules/Task.jsm"); 1.23 + 1.24 +let Path = OS.Path; 1.25 + 1.26 +#ifdef MOZ_B2G_RIL 1.27 +XPCOMUtils.defineLazyServiceGetter(this, "iccProvider", 1.28 + "@mozilla.org/ril/content-helper;1", 1.29 + "nsIIccProvider"); 1.30 +#endif 1.31 + 1.32 +function debug(aMsg) { 1.33 + //dump("-*-*- OperatorApps.jsm : " + aMsg + "\n"); 1.34 +} 1.35 + 1.36 +// Single Variant source dir will be set in PREF_SINGLE_VARIANT_DIR 1.37 +// preference. 1.38 +// if PREF_SINGLE_VARIANT_DIR does not exist or has not value, it will use (as 1.39 +// single variant source) the value of 1.40 +// DIRECTORY_NAME + "/" + SINGLE_VARIANT_SOURCE_DIR value instead. 1.41 +// SINGLE_VARIANT_CONF_FILE will be stored on Single Variant Source. 1.42 +// Apps will be stored on an app per directory basis, hanging from 1.43 +// SINGLE_VARIANT_SOURCE_DIR 1.44 +const DIRECTORY_NAME = "webappsDir"; 1.45 +const SINGLE_VARIANT_SOURCE_DIR = "svoperapps"; 1.46 +const SINGLE_VARIANT_CONF_FILE = "singlevariantconf.json"; 1.47 +const PREF_FIRST_RUN_WITH_SIM = "dom.webapps.firstRunWithSIM"; 1.48 +const PREF_SINGLE_VARIANT_DIR = "dom.mozApps.single_variant_sourcedir"; 1.49 +const METADATA = "metadata.json"; 1.50 +const UPDATEMANIFEST = "update.webapp"; 1.51 +const MANIFEST = "manifest.webapp"; 1.52 +const APPLICATION_ZIP = "application.zip"; 1.53 + 1.54 +function isFirstRunWithSIM() { 1.55 + try { 1.56 + if (Services.prefs.prefHasUserValue(PREF_FIRST_RUN_WITH_SIM)) { 1.57 + return Services.prefs.getBoolPref(PREF_FIRST_RUN_WITH_SIM); 1.58 + } 1.59 + } catch(e) { 1.60 + debug ("Error getting pref. " + e); 1.61 + } 1.62 + return true; 1.63 +} 1.64 + 1.65 +#ifdef MOZ_B2G_RIL 1.66 +let iccListener = { 1.67 + notifyStkCommand: function() {}, 1.68 + 1.69 + notifyStkSessionEnd: function() {}, 1.70 + 1.71 + notifyCardStateChanged: function() {}, 1.72 + 1.73 + notifyIccInfoChanged: function() { 1.74 + // TODO: Bug 927709 - OperatorApps for multi-sim 1.75 + // In Multi-sim, there is more than one client in iccProvider. Each 1.76 + // client represents a icc service. To maintain the backward compatibility 1.77 + // with single sim, we always use client 0 for now. Adding support for 1.78 + // multiple sim will be addressed in bug 927709, if needed. 1.79 + let clientId = 0; 1.80 + let iccInfo = iccProvider.getIccInfo(clientId); 1.81 + if (iccInfo && iccInfo.mcc && iccInfo.mnc) { 1.82 + let mcc = iccInfo.mcc; 1.83 + let mnc = iccInfo.mnc; 1.84 + debug("******* iccListener cardIccInfo MCC-MNC: " + mcc + "-" + mnc); 1.85 + iccProvider.unregisterIccMsg(clientId, this); 1.86 + OperatorAppsRegistry._installOperatorApps(mcc, mnc); 1.87 + 1.88 + debug("Broadcast message first-run-with-sim"); 1.89 + let messenger = Cc["@mozilla.org/system-message-internal;1"] 1.90 + .getService(Ci.nsISystemMessagesInternal); 1.91 + messenger.broadcastMessage("first-run-with-sim", { mcc: mcc, 1.92 + mnc: mnc }); 1.93 + } 1.94 + } 1.95 +}; 1.96 +#endif 1.97 + 1.98 +this.OperatorAppsRegistry = { 1.99 + 1.100 + _baseDirectory: null, 1.101 + 1.102 + init: function() { 1.103 + debug("init"); 1.104 +#ifdef MOZ_B2G_RIL 1.105 + if (isFirstRunWithSIM()) { 1.106 + debug("First Run with SIM"); 1.107 + Task.spawn(function() { 1.108 + try { 1.109 + yield this._initializeSourceDir(); 1.110 + // TODO: Bug 927709 - OperatorApps for multi-sim 1.111 + // In Multi-sim, there is more than one client in iccProvider. Each 1.112 + // client represents a icc service. To maintain the backward 1.113 + // compatibility with single sim, we always use client 0 for now. 1.114 + // Adding support for multiple sim will be addressed in bug 927709, if 1.115 + // needed. 1.116 + let clientId = 0; 1.117 + let iccInfo = iccProvider.getIccInfo(clientId); 1.118 + let mcc = 0; 1.119 + let mnc = 0; 1.120 + if (iccInfo && iccInfo.mcc) { 1.121 + mcc = iccInfo.mcc; 1.122 + } 1.123 + if (iccInfo && iccInfo.mnc) { 1.124 + mnc = iccInfo.mnc; 1.125 + } 1.126 + if (mcc && mnc) { 1.127 + this._installOperatorApps(mcc, mnc); 1.128 + } else { 1.129 + iccProvider.registerIccMsg(clientId, iccListener); 1.130 + } 1.131 + } catch (e) { 1.132 + debug("Error Initializing OperatorApps. " + e); 1.133 + } 1.134 + }.bind(this)); 1.135 + } else { 1.136 + debug("No First Run with SIM"); 1.137 + } 1.138 +#endif 1.139 + }, 1.140 + 1.141 + _copyDirectory: function(aOrg, aDst) { 1.142 + debug("copying " + aOrg + " to " + aDst); 1.143 + return aDst && Task.spawn(function() { 1.144 + try { 1.145 + let orgDir = Cc["@mozilla.org/file/local;1"] 1.146 + .createInstance(Ci.nsIFile); 1.147 + orgDir.initWithPath(aOrg); 1.148 + if (!orgDir.exists() || !orgDir.isDirectory()) { 1.149 + debug(aOrg + " does not exist or is not a directory"); 1.150 + return; 1.151 + } 1.152 + 1.153 + let dstDir = Cc["@mozilla.org/file/local;1"] 1.154 + .createInstance(Ci.nsIFile); 1.155 + dstDir.initWithPath(aDst); 1.156 + if (!dstDir.exists()) { 1.157 + dstDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); 1.158 + } 1.159 + 1.160 + let entries = orgDir.directoryEntries; 1.161 + while (entries.hasMoreElements()) { 1.162 + let entry = entries.getNext().QueryInterface(Ci.nsIFile); 1.163 + 1.164 + if (!entry.isDirectory()) { 1.165 + // Remove the file, because copyTo doesn't overwrite files. 1.166 + let dstFile = dstDir.clone(); 1.167 + dstFile.append(entry.leafName); 1.168 + if(dstFile.exists()) { 1.169 + dstFile.remove(false); 1.170 + } 1.171 + 1.172 + entry.copyTo(dstDir, entry.leafName); 1.173 + } else { 1.174 + yield this._copyDirectory(entry.path, 1.175 + Path.join(aDst, entry.leafName)); 1.176 + } 1.177 + } 1.178 + } catch (e) { 1.179 + debug("Error copying " + aOrg + " to " + aDst + ". " + e); 1.180 + } 1.181 + }.bind(this)); 1.182 + }, 1.183 + 1.184 + _initializeSourceDir: function() { 1.185 + return Task.spawn(function() { 1.186 + let svFinalDirName; 1.187 + try { 1.188 + svFinalDirName = Services.prefs.getCharPref(PREF_SINGLE_VARIANT_DIR); 1.189 + } catch(e) { 1.190 + debug ("Error getting pref. " + e); 1.191 + this.appsDir = FileUtils.getFile(DIRECTORY_NAME, 1.192 + [SINGLE_VARIANT_SOURCE_DIR]).path; 1.193 + return; 1.194 + } 1.195 + // If SINGLE_VARIANT_CONF_FILE is in PREF_SINGLE_VARIANT_DIR return 1.196 + // PREF_SINGLE_VARIANT_DIR as sourceDir, else go to 1.197 + // DIRECTORY_NAME + SINGLE_VARIANT_SOURCE_DIR and move all apps (and 1.198 + // configuration file) to PREF_SINGLE_VARIANT_DIR and return 1.199 + // PREF_SINGLE_VARIANT_DIR as sourceDir. 1.200 + let svFinalDir = Cc["@mozilla.org/file/local;1"] 1.201 + .createInstance(Ci.nsIFile); 1.202 + svFinalDir.initWithPath(svFinalDirName); 1.203 + if (!svFinalDir.exists()) { 1.204 + svFinalDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); 1.205 + } 1.206 + 1.207 + let svIndex = svFinalDir.clone(); 1.208 + svIndex.append(SINGLE_VARIANT_CONF_FILE); 1.209 + if (!svIndex.exists()) { 1.210 + let svSourceDir = FileUtils.getFile(DIRECTORY_NAME, 1.211 + [SINGLE_VARIANT_SOURCE_DIR]); 1.212 + 1.213 + yield this._copyDirectory(svSourceDir.path, svFinalDirName); 1.214 + 1.215 + debug("removing directory:" + svSourceDir.path); 1.216 + try { 1.217 + svSourceDir.remove(true); 1.218 + } catch(ex) { } 1.219 + } 1.220 + 1.221 + this.appsDir = svFinalDirName; 1.222 + }.bind(this)); 1.223 + }, 1.224 + 1.225 + set appsDir(aDir) { 1.226 + debug("appsDir SET: " + aDir); 1.227 + if (aDir) { 1.228 + this._baseDirectory = Cc["@mozilla.org/file/local;1"] 1.229 + .createInstance(Ci.nsILocalFile); 1.230 + this._baseDirectory.initWithPath(aDir); 1.231 + } else { 1.232 + this._baseDirectory = null; 1.233 + } 1.234 + }, 1.235 + 1.236 + get appsDir() { 1.237 + return this._baseDirectory; 1.238 + }, 1.239 + 1.240 + eraseVariantAppsNotInList: function(aIdsApp) { 1.241 + if (!aIdsApp || !Array.isArray(aIdsApp)) { 1.242 + aIdsApp = [ ]; 1.243 + } 1.244 + 1.245 + let svDir; 1.246 + try { 1.247 + svDir = this.appsDir.clone(); 1.248 + } catch (e) { 1.249 + debug("eraseVariantAppsNotInList --> Error getting Dir "+ 1.250 + svDir.path + ". " + e); 1.251 + return; 1.252 + } 1.253 + 1.254 + if (!svDir || !svDir.exists()) { 1.255 + return; 1.256 + } 1.257 + 1.258 + let entries = svDir.directoryEntries; 1.259 + while (entries.hasMoreElements()) { 1.260 + let entry = entries.getNext().QueryInterface(Ci.nsIFile); 1.261 + if (entry.isDirectory() && aIdsApp.indexOf(entry.leafName) < 0) { 1.262 + try{ 1.263 + entry.remove(true); 1.264 + } catch(e) { 1.265 + debug("Error removing [" + entry.path + "]." + e); 1.266 + } 1.267 + } 1.268 + } 1.269 + }, 1.270 + 1.271 + _launchInstall: function(isPackage, aId, aMetadata, aManifest) { 1.272 + if (!aManifest) { 1.273 + debug("Error: The application " + aId + " does not have a manifest"); 1.274 + return; 1.275 + } 1.276 + 1.277 + let appData = { 1.278 + app: { 1.279 + installOrigin: aMetadata.installOrigin, 1.280 + origin: aMetadata.origin, 1.281 + manifestURL: aMetadata.manifestURL, 1.282 + manifestHash: AppsUtils.computeHash(JSON.stringify(aManifest)) 1.283 + }, 1.284 + appId: undefined, 1.285 + isBrowser: false, 1.286 + isPackage: isPackage, 1.287 + forceSuccessAck: true 1.288 + }; 1.289 + 1.290 + if (isPackage) { 1.291 + debug("aId:" + aId + ". Installing as packaged app."); 1.292 + let installPack = this.appsDir.clone(); 1.293 + installPack.append(aId); 1.294 + installPack.append(APPLICATION_ZIP); 1.295 + 1.296 + if (!installPack.exists()) { 1.297 + debug("SV " + installPack.path + " file do not exists for app " + aId); 1.298 + return; 1.299 + } 1.300 + 1.301 + appData.app.localInstallPath = installPack.path; 1.302 + appData.app.updateManifest = aManifest; 1.303 + DOMApplicationRegistry.confirmInstall(appData); 1.304 + } else { 1.305 + debug("aId:" + aId + ". Installing as hosted app."); 1.306 + appData.app.manifest = aManifest; 1.307 + DOMApplicationRegistry.confirmInstall(appData); 1.308 + } 1.309 + }, 1.310 + 1.311 + _installOperatorApps: function(aMcc, aMnc) { 1.312 + Task.spawn(function() { 1.313 + debug("Install operator apps ---> mcc:"+ aMcc + ", mnc:" + aMnc); 1.314 + if (!isFirstRunWithSIM()) { 1.315 + debug("Operator apps already installed."); 1.316 + return; 1.317 + } 1.318 + 1.319 + let aIdsApp = yield this._getSingleVariantApps(aMcc, aMnc); 1.320 + debug("installOperatorApps --> aIdsApp:" + JSON.stringify(aIdsApp)); 1.321 + for (let i = 0; i < aIdsApp.length; i++) { 1.322 + let aId = aIdsApp[i]; 1.323 + let aMetadata = yield AppsUtils.loadJSONAsync( 1.324 + Path.join(this.appsDir.path, aId, METADATA)); 1.325 + if (!aMetadata) { 1.326 + debug("Error reading metadata file"); 1.327 + return; 1.328 + } 1.329 + 1.330 + debug("metadata:" + JSON.stringify(aMetadata)); 1.331 + let isPackage = true; 1.332 + let manifest; 1.333 + let manifests = [UPDATEMANIFEST, MANIFEST]; 1.334 + for (let j = 0; j < manifests.length; j++) { 1.335 + manifest = yield AppsUtils.loadJSONAsync( 1.336 + Path.join(this.appsDir.path, aId, manifests[j])); 1.337 + 1.338 + if (!manifest) { 1.339 + isPackage = false; 1.340 + } else { 1.341 + break; 1.342 + } 1.343 + } 1.344 + if (manifest) { 1.345 + this._launchInstall(isPackage, aId, aMetadata, manifest); 1.346 + } else { 1.347 + debug ("Error. Neither " + UPDATEMANIFEST + " file nor " + MANIFEST + 1.348 + " file for " + aId + " app."); 1.349 + } 1.350 + } 1.351 + this.eraseVariantAppsNotInList(aIdsApp); 1.352 + Services.prefs.setBoolPref(PREF_FIRST_RUN_WITH_SIM, false); 1.353 + Services.prefs.savePrefFile(null); 1.354 + }.bind(this)).then(null, function(aError) { 1.355 + debug("Error: " + aError); 1.356 + }); 1.357 + }, 1.358 + 1.359 + _getSingleVariantApps: function(aMcc, aMnc) { 1.360 + 1.361 + function normalizeCode(aCode) { 1.362 + let ncode = "" + aCode; 1.363 + while (ncode.length < 3) { 1.364 + ncode = "0" + ncode; 1.365 + } 1.366 + return ncode; 1.367 + } 1.368 + 1.369 + return Task.spawn(function*() { 1.370 + let key = normalizeCode(aMcc) + "-" + normalizeCode(aMnc); 1.371 + let file = Path.join(this.appsDir.path, SINGLE_VARIANT_CONF_FILE); 1.372 + let aData = yield AppsUtils.loadJSONAsync(file); 1.373 + 1.374 + if (!aData || !(key in aData)) { 1.375 + return []; 1.376 + } 1.377 + 1.378 + return aData[key]; 1.379 + }.bind(this)); 1.380 + } 1.381 +}; 1.382 + 1.383 +OperatorAppsRegistry.init();