dom/apps/src/OperatorApps.jsm

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 "use strict";
michael@0 6
michael@0 7 const Cu = Components.utils;
michael@0 8 const Cc = Components.classes;
michael@0 9 const Ci = Components.interfaces;
michael@0 10
michael@0 11 this.EXPORTED_SYMBOLS = ["OperatorAppsRegistry"];
michael@0 12
michael@0 13 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 14 Cu.import("resource://gre/modules/FileUtils.jsm");
michael@0 15 Cu.import("resource://gre/modules/Webapps.jsm");
michael@0 16 Cu.import("resource://gre/modules/Services.jsm");
michael@0 17 Cu.import("resource://gre/modules/osfile.jsm");
michael@0 18 Cu.import("resource://gre/modules/AppsUtils.jsm");
michael@0 19 Cu.import("resource://gre/modules/Task.jsm");
michael@0 20
michael@0 21 let Path = OS.Path;
michael@0 22
michael@0 23 #ifdef MOZ_B2G_RIL
michael@0 24 XPCOMUtils.defineLazyServiceGetter(this, "iccProvider",
michael@0 25 "@mozilla.org/ril/content-helper;1",
michael@0 26 "nsIIccProvider");
michael@0 27 #endif
michael@0 28
michael@0 29 function debug(aMsg) {
michael@0 30 //dump("-*-*- OperatorApps.jsm : " + aMsg + "\n");
michael@0 31 }
michael@0 32
michael@0 33 // Single Variant source dir will be set in PREF_SINGLE_VARIANT_DIR
michael@0 34 // preference.
michael@0 35 // if PREF_SINGLE_VARIANT_DIR does not exist or has not value, it will use (as
michael@0 36 // single variant source) the value of
michael@0 37 // DIRECTORY_NAME + "/" + SINGLE_VARIANT_SOURCE_DIR value instead.
michael@0 38 // SINGLE_VARIANT_CONF_FILE will be stored on Single Variant Source.
michael@0 39 // Apps will be stored on an app per directory basis, hanging from
michael@0 40 // SINGLE_VARIANT_SOURCE_DIR
michael@0 41 const DIRECTORY_NAME = "webappsDir";
michael@0 42 const SINGLE_VARIANT_SOURCE_DIR = "svoperapps";
michael@0 43 const SINGLE_VARIANT_CONF_FILE = "singlevariantconf.json";
michael@0 44 const PREF_FIRST_RUN_WITH_SIM = "dom.webapps.firstRunWithSIM";
michael@0 45 const PREF_SINGLE_VARIANT_DIR = "dom.mozApps.single_variant_sourcedir";
michael@0 46 const METADATA = "metadata.json";
michael@0 47 const UPDATEMANIFEST = "update.webapp";
michael@0 48 const MANIFEST = "manifest.webapp";
michael@0 49 const APPLICATION_ZIP = "application.zip";
michael@0 50
michael@0 51 function isFirstRunWithSIM() {
michael@0 52 try {
michael@0 53 if (Services.prefs.prefHasUserValue(PREF_FIRST_RUN_WITH_SIM)) {
michael@0 54 return Services.prefs.getBoolPref(PREF_FIRST_RUN_WITH_SIM);
michael@0 55 }
michael@0 56 } catch(e) {
michael@0 57 debug ("Error getting pref. " + e);
michael@0 58 }
michael@0 59 return true;
michael@0 60 }
michael@0 61
michael@0 62 #ifdef MOZ_B2G_RIL
michael@0 63 let iccListener = {
michael@0 64 notifyStkCommand: function() {},
michael@0 65
michael@0 66 notifyStkSessionEnd: function() {},
michael@0 67
michael@0 68 notifyCardStateChanged: function() {},
michael@0 69
michael@0 70 notifyIccInfoChanged: function() {
michael@0 71 // TODO: Bug 927709 - OperatorApps for multi-sim
michael@0 72 // In Multi-sim, there is more than one client in iccProvider. Each
michael@0 73 // client represents a icc service. To maintain the backward compatibility
michael@0 74 // with single sim, we always use client 0 for now. Adding support for
michael@0 75 // multiple sim will be addressed in bug 927709, if needed.
michael@0 76 let clientId = 0;
michael@0 77 let iccInfo = iccProvider.getIccInfo(clientId);
michael@0 78 if (iccInfo && iccInfo.mcc && iccInfo.mnc) {
michael@0 79 let mcc = iccInfo.mcc;
michael@0 80 let mnc = iccInfo.mnc;
michael@0 81 debug("******* iccListener cardIccInfo MCC-MNC: " + mcc + "-" + mnc);
michael@0 82 iccProvider.unregisterIccMsg(clientId, this);
michael@0 83 OperatorAppsRegistry._installOperatorApps(mcc, mnc);
michael@0 84
michael@0 85 debug("Broadcast message first-run-with-sim");
michael@0 86 let messenger = Cc["@mozilla.org/system-message-internal;1"]
michael@0 87 .getService(Ci.nsISystemMessagesInternal);
michael@0 88 messenger.broadcastMessage("first-run-with-sim", { mcc: mcc,
michael@0 89 mnc: mnc });
michael@0 90 }
michael@0 91 }
michael@0 92 };
michael@0 93 #endif
michael@0 94
michael@0 95 this.OperatorAppsRegistry = {
michael@0 96
michael@0 97 _baseDirectory: null,
michael@0 98
michael@0 99 init: function() {
michael@0 100 debug("init");
michael@0 101 #ifdef MOZ_B2G_RIL
michael@0 102 if (isFirstRunWithSIM()) {
michael@0 103 debug("First Run with SIM");
michael@0 104 Task.spawn(function() {
michael@0 105 try {
michael@0 106 yield this._initializeSourceDir();
michael@0 107 // TODO: Bug 927709 - OperatorApps for multi-sim
michael@0 108 // In Multi-sim, there is more than one client in iccProvider. Each
michael@0 109 // client represents a icc service. To maintain the backward
michael@0 110 // compatibility with single sim, we always use client 0 for now.
michael@0 111 // Adding support for multiple sim will be addressed in bug 927709, if
michael@0 112 // needed.
michael@0 113 let clientId = 0;
michael@0 114 let iccInfo = iccProvider.getIccInfo(clientId);
michael@0 115 let mcc = 0;
michael@0 116 let mnc = 0;
michael@0 117 if (iccInfo && iccInfo.mcc) {
michael@0 118 mcc = iccInfo.mcc;
michael@0 119 }
michael@0 120 if (iccInfo && iccInfo.mnc) {
michael@0 121 mnc = iccInfo.mnc;
michael@0 122 }
michael@0 123 if (mcc && mnc) {
michael@0 124 this._installOperatorApps(mcc, mnc);
michael@0 125 } else {
michael@0 126 iccProvider.registerIccMsg(clientId, iccListener);
michael@0 127 }
michael@0 128 } catch (e) {
michael@0 129 debug("Error Initializing OperatorApps. " + e);
michael@0 130 }
michael@0 131 }.bind(this));
michael@0 132 } else {
michael@0 133 debug("No First Run with SIM");
michael@0 134 }
michael@0 135 #endif
michael@0 136 },
michael@0 137
michael@0 138 _copyDirectory: function(aOrg, aDst) {
michael@0 139 debug("copying " + aOrg + " to " + aDst);
michael@0 140 return aDst && Task.spawn(function() {
michael@0 141 try {
michael@0 142 let orgDir = Cc["@mozilla.org/file/local;1"]
michael@0 143 .createInstance(Ci.nsIFile);
michael@0 144 orgDir.initWithPath(aOrg);
michael@0 145 if (!orgDir.exists() || !orgDir.isDirectory()) {
michael@0 146 debug(aOrg + " does not exist or is not a directory");
michael@0 147 return;
michael@0 148 }
michael@0 149
michael@0 150 let dstDir = Cc["@mozilla.org/file/local;1"]
michael@0 151 .createInstance(Ci.nsIFile);
michael@0 152 dstDir.initWithPath(aDst);
michael@0 153 if (!dstDir.exists()) {
michael@0 154 dstDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
michael@0 155 }
michael@0 156
michael@0 157 let entries = orgDir.directoryEntries;
michael@0 158 while (entries.hasMoreElements()) {
michael@0 159 let entry = entries.getNext().QueryInterface(Ci.nsIFile);
michael@0 160
michael@0 161 if (!entry.isDirectory()) {
michael@0 162 // Remove the file, because copyTo doesn't overwrite files.
michael@0 163 let dstFile = dstDir.clone();
michael@0 164 dstFile.append(entry.leafName);
michael@0 165 if(dstFile.exists()) {
michael@0 166 dstFile.remove(false);
michael@0 167 }
michael@0 168
michael@0 169 entry.copyTo(dstDir, entry.leafName);
michael@0 170 } else {
michael@0 171 yield this._copyDirectory(entry.path,
michael@0 172 Path.join(aDst, entry.leafName));
michael@0 173 }
michael@0 174 }
michael@0 175 } catch (e) {
michael@0 176 debug("Error copying " + aOrg + " to " + aDst + ". " + e);
michael@0 177 }
michael@0 178 }.bind(this));
michael@0 179 },
michael@0 180
michael@0 181 _initializeSourceDir: function() {
michael@0 182 return Task.spawn(function() {
michael@0 183 let svFinalDirName;
michael@0 184 try {
michael@0 185 svFinalDirName = Services.prefs.getCharPref(PREF_SINGLE_VARIANT_DIR);
michael@0 186 } catch(e) {
michael@0 187 debug ("Error getting pref. " + e);
michael@0 188 this.appsDir = FileUtils.getFile(DIRECTORY_NAME,
michael@0 189 [SINGLE_VARIANT_SOURCE_DIR]).path;
michael@0 190 return;
michael@0 191 }
michael@0 192 // If SINGLE_VARIANT_CONF_FILE is in PREF_SINGLE_VARIANT_DIR return
michael@0 193 // PREF_SINGLE_VARIANT_DIR as sourceDir, else go to
michael@0 194 // DIRECTORY_NAME + SINGLE_VARIANT_SOURCE_DIR and move all apps (and
michael@0 195 // configuration file) to PREF_SINGLE_VARIANT_DIR and return
michael@0 196 // PREF_SINGLE_VARIANT_DIR as sourceDir.
michael@0 197 let svFinalDir = Cc["@mozilla.org/file/local;1"]
michael@0 198 .createInstance(Ci.nsIFile);
michael@0 199 svFinalDir.initWithPath(svFinalDirName);
michael@0 200 if (!svFinalDir.exists()) {
michael@0 201 svFinalDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
michael@0 202 }
michael@0 203
michael@0 204 let svIndex = svFinalDir.clone();
michael@0 205 svIndex.append(SINGLE_VARIANT_CONF_FILE);
michael@0 206 if (!svIndex.exists()) {
michael@0 207 let svSourceDir = FileUtils.getFile(DIRECTORY_NAME,
michael@0 208 [SINGLE_VARIANT_SOURCE_DIR]);
michael@0 209
michael@0 210 yield this._copyDirectory(svSourceDir.path, svFinalDirName);
michael@0 211
michael@0 212 debug("removing directory:" + svSourceDir.path);
michael@0 213 try {
michael@0 214 svSourceDir.remove(true);
michael@0 215 } catch(ex) { }
michael@0 216 }
michael@0 217
michael@0 218 this.appsDir = svFinalDirName;
michael@0 219 }.bind(this));
michael@0 220 },
michael@0 221
michael@0 222 set appsDir(aDir) {
michael@0 223 debug("appsDir SET: " + aDir);
michael@0 224 if (aDir) {
michael@0 225 this._baseDirectory = Cc["@mozilla.org/file/local;1"]
michael@0 226 .createInstance(Ci.nsILocalFile);
michael@0 227 this._baseDirectory.initWithPath(aDir);
michael@0 228 } else {
michael@0 229 this._baseDirectory = null;
michael@0 230 }
michael@0 231 },
michael@0 232
michael@0 233 get appsDir() {
michael@0 234 return this._baseDirectory;
michael@0 235 },
michael@0 236
michael@0 237 eraseVariantAppsNotInList: function(aIdsApp) {
michael@0 238 if (!aIdsApp || !Array.isArray(aIdsApp)) {
michael@0 239 aIdsApp = [ ];
michael@0 240 }
michael@0 241
michael@0 242 let svDir;
michael@0 243 try {
michael@0 244 svDir = this.appsDir.clone();
michael@0 245 } catch (e) {
michael@0 246 debug("eraseVariantAppsNotInList --> Error getting Dir "+
michael@0 247 svDir.path + ". " + e);
michael@0 248 return;
michael@0 249 }
michael@0 250
michael@0 251 if (!svDir || !svDir.exists()) {
michael@0 252 return;
michael@0 253 }
michael@0 254
michael@0 255 let entries = svDir.directoryEntries;
michael@0 256 while (entries.hasMoreElements()) {
michael@0 257 let entry = entries.getNext().QueryInterface(Ci.nsIFile);
michael@0 258 if (entry.isDirectory() && aIdsApp.indexOf(entry.leafName) < 0) {
michael@0 259 try{
michael@0 260 entry.remove(true);
michael@0 261 } catch(e) {
michael@0 262 debug("Error removing [" + entry.path + "]." + e);
michael@0 263 }
michael@0 264 }
michael@0 265 }
michael@0 266 },
michael@0 267
michael@0 268 _launchInstall: function(isPackage, aId, aMetadata, aManifest) {
michael@0 269 if (!aManifest) {
michael@0 270 debug("Error: The application " + aId + " does not have a manifest");
michael@0 271 return;
michael@0 272 }
michael@0 273
michael@0 274 let appData = {
michael@0 275 app: {
michael@0 276 installOrigin: aMetadata.installOrigin,
michael@0 277 origin: aMetadata.origin,
michael@0 278 manifestURL: aMetadata.manifestURL,
michael@0 279 manifestHash: AppsUtils.computeHash(JSON.stringify(aManifest))
michael@0 280 },
michael@0 281 appId: undefined,
michael@0 282 isBrowser: false,
michael@0 283 isPackage: isPackage,
michael@0 284 forceSuccessAck: true
michael@0 285 };
michael@0 286
michael@0 287 if (isPackage) {
michael@0 288 debug("aId:" + aId + ". Installing as packaged app.");
michael@0 289 let installPack = this.appsDir.clone();
michael@0 290 installPack.append(aId);
michael@0 291 installPack.append(APPLICATION_ZIP);
michael@0 292
michael@0 293 if (!installPack.exists()) {
michael@0 294 debug("SV " + installPack.path + " file do not exists for app " + aId);
michael@0 295 return;
michael@0 296 }
michael@0 297
michael@0 298 appData.app.localInstallPath = installPack.path;
michael@0 299 appData.app.updateManifest = aManifest;
michael@0 300 DOMApplicationRegistry.confirmInstall(appData);
michael@0 301 } else {
michael@0 302 debug("aId:" + aId + ". Installing as hosted app.");
michael@0 303 appData.app.manifest = aManifest;
michael@0 304 DOMApplicationRegistry.confirmInstall(appData);
michael@0 305 }
michael@0 306 },
michael@0 307
michael@0 308 _installOperatorApps: function(aMcc, aMnc) {
michael@0 309 Task.spawn(function() {
michael@0 310 debug("Install operator apps ---> mcc:"+ aMcc + ", mnc:" + aMnc);
michael@0 311 if (!isFirstRunWithSIM()) {
michael@0 312 debug("Operator apps already installed.");
michael@0 313 return;
michael@0 314 }
michael@0 315
michael@0 316 let aIdsApp = yield this._getSingleVariantApps(aMcc, aMnc);
michael@0 317 debug("installOperatorApps --> aIdsApp:" + JSON.stringify(aIdsApp));
michael@0 318 for (let i = 0; i < aIdsApp.length; i++) {
michael@0 319 let aId = aIdsApp[i];
michael@0 320 let aMetadata = yield AppsUtils.loadJSONAsync(
michael@0 321 Path.join(this.appsDir.path, aId, METADATA));
michael@0 322 if (!aMetadata) {
michael@0 323 debug("Error reading metadata file");
michael@0 324 return;
michael@0 325 }
michael@0 326
michael@0 327 debug("metadata:" + JSON.stringify(aMetadata));
michael@0 328 let isPackage = true;
michael@0 329 let manifest;
michael@0 330 let manifests = [UPDATEMANIFEST, MANIFEST];
michael@0 331 for (let j = 0; j < manifests.length; j++) {
michael@0 332 manifest = yield AppsUtils.loadJSONAsync(
michael@0 333 Path.join(this.appsDir.path, aId, manifests[j]));
michael@0 334
michael@0 335 if (!manifest) {
michael@0 336 isPackage = false;
michael@0 337 } else {
michael@0 338 break;
michael@0 339 }
michael@0 340 }
michael@0 341 if (manifest) {
michael@0 342 this._launchInstall(isPackage, aId, aMetadata, manifest);
michael@0 343 } else {
michael@0 344 debug ("Error. Neither " + UPDATEMANIFEST + " file nor " + MANIFEST +
michael@0 345 " file for " + aId + " app.");
michael@0 346 }
michael@0 347 }
michael@0 348 this.eraseVariantAppsNotInList(aIdsApp);
michael@0 349 Services.prefs.setBoolPref(PREF_FIRST_RUN_WITH_SIM, false);
michael@0 350 Services.prefs.savePrefFile(null);
michael@0 351 }.bind(this)).then(null, function(aError) {
michael@0 352 debug("Error: " + aError);
michael@0 353 });
michael@0 354 },
michael@0 355
michael@0 356 _getSingleVariantApps: function(aMcc, aMnc) {
michael@0 357
michael@0 358 function normalizeCode(aCode) {
michael@0 359 let ncode = "" + aCode;
michael@0 360 while (ncode.length < 3) {
michael@0 361 ncode = "0" + ncode;
michael@0 362 }
michael@0 363 return ncode;
michael@0 364 }
michael@0 365
michael@0 366 return Task.spawn(function*() {
michael@0 367 let key = normalizeCode(aMcc) + "-" + normalizeCode(aMnc);
michael@0 368 let file = Path.join(this.appsDir.path, SINGLE_VARIANT_CONF_FILE);
michael@0 369 let aData = yield AppsUtils.loadJSONAsync(file);
michael@0 370
michael@0 371 if (!aData || !(key in aData)) {
michael@0 372 return [];
michael@0 373 }
michael@0 374
michael@0 375 return aData[key];
michael@0 376 }.bind(this));
michael@0 377 }
michael@0 378 };
michael@0 379
michael@0 380 OperatorAppsRegistry.init();

mercurial