1/* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5this.EXPORTED_SYMBOLS = ["UpdateUtils"]; 6 7const { classes: Cc, interfaces: Ci, utils: Cu } = Components; 8 9Cu.import("resource://gre/modules/AppConstants.jsm"); 10Cu.import("resource://gre/modules/Services.jsm"); 11Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 12Cu.import("resource://gre/modules/NetUtil.jsm"); 13Cu.import("resource://gre/modules/Preferences.jsm"); 14Cu.import("resource://gre/modules/ctypes.jsm"); 15 16const FILE_UPDATE_LOCALE = "update.locale"; 17const PREF_APP_DISTRIBUTION = "distribution.id"; 18const PREF_APP_DISTRIBUTION_VERSION = "distribution.version"; 19const PREF_APP_B2G_VERSION = "b2g.version"; 20const PREF_APP_UPDATE_CUSTOM = "app.update.custom"; 21const PREF_APP_UPDATE_IMEI_HASH = "app.update.imei_hash"; 22 23 24this.UpdateUtils = { 25 /** 26 * Read the update channel from defaults only. We do this to ensure that 27 * the channel is tightly coupled with the application and does not apply 28 * to other instances of the application that may use the same profile. 29 * 30 * @param [optional] aIncludePartners 31 * Whether or not to include the partner bits. Default: true. 32 */ 33 getUpdateChannel(aIncludePartners = true) { 34 let channel = AppConstants.MOZ_UPDATE_CHANNEL; 35 let defaults = Services.prefs.getDefaultBranch(null); 36 try { 37 channel = defaults.getCharPref("app.update.channel"); 38 } catch (e) { 39 // use default value when pref not found 40 } 41 42 if (aIncludePartners) { 43 try { 44 let partners = Services.prefs.getChildList("app.partner.").sort(); 45 if (partners.length) { 46 channel += "-cck"; 47 partners.forEach(function (prefName) { 48 channel += "-" + Services.prefs.getCharPref(prefName); 49 }); 50 } 51 } catch (e) { 52 Cu.reportError(e); 53 } 54 } 55 56 return channel; 57 }, 58 59 get UpdateChannel() { 60 return this.getUpdateChannel(); 61 }, 62 63 /** 64 * Formats a URL by replacing %...% values with OS, build and locale specific 65 * values. 66 * 67 * @param url 68 * The URL to format. 69 * @return The formatted URL. 70 */ 71 formatUpdateURL(url) { 72 url = url.replace(/%PRODUCT%/g, Services.appinfo.name); 73 url = url.replace(/%VERSION%/g, Services.appinfo.version); 74 url = url.replace(/%BUILD_ID%/g, Services.appinfo.appBuildID); 75 url = url.replace(/%BUILD_TARGET%/g, Services.appinfo.OS + "_" + this.ABI); 76 url = url.replace(/%OS_VERSION%/g, this.OSVersion); 77 url = url.replace(/%SYSTEM_CAPABILITIES%/g, gSystemCapabilities); 78 if (/%LOCALE%/.test(url)) { 79 url = url.replace(/%LOCALE%/g, this.Locale); 80 } 81 url = url.replace(/%CHANNEL%/g, this.UpdateChannel); 82 url = url.replace(/%PLATFORM_VERSION%/g, Services.appinfo.platformVersion); 83 url = url.replace(/%DISTRIBUTION%/g, 84 getDistributionPrefValue(PREF_APP_DISTRIBUTION)); 85 url = url.replace(/%DISTRIBUTION_VERSION%/g, 86 getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION)); 87 url = url.replace(/%CUSTOM%/g, Preferences.get(PREF_APP_UPDATE_CUSTOM, "")); 88 url = url.replace(/\+/g, "%2B"); 89 90 if (AppConstants.platform == "gonk") { 91 let sysLibs = {}; 92 Cu.import("resource://gre/modules/systemlibs.js", sysLibs); 93 let productDevice = sysLibs.libcutils.property_get("ro.product.device"); 94 let buildType = sysLibs.libcutils.property_get("ro.build.type"); 95 url = url.replace(/%PRODUCT_MODEL%/g, 96 sysLibs.libcutils.property_get("ro.product.model")); 97 if (buildType == "user" || buildType == "userdebug") { 98 url = url.replace(/%PRODUCT_DEVICE%/g, productDevice); 99 } else { 100 url = url.replace(/%PRODUCT_DEVICE%/g, productDevice + "-" + buildType); 101 } 102 url = url.replace(/%B2G_VERSION%/g, 103 Preferences.get(PREF_APP_B2G_VERSION, null)); 104 url = url.replace(/%IMEI%/g, 105 Preferences.get(PREF_APP_UPDATE_IMEI_HASH, "default")); 106 } 107 108 return url; 109 } 110}; 111 112/* Get the distribution pref values, from defaults only */ 113function getDistributionPrefValue(aPrefName) { 114 var prefValue = "default"; 115 116 try { 117 prefValue = Services.prefs.getDefaultBranch(null).getCharPref(aPrefName); 118 } catch (e) { 119 // use default when pref not found 120 } 121 122 return prefValue; 123} 124 125/** 126 * Gets the locale from the update.locale file for replacing %LOCALE% in the 127 * update url. The update.locale file can be located in the application 128 * directory or the GRE directory with preference given to it being located in 129 * the application directory. 130 */ 131XPCOMUtils.defineLazyGetter(UpdateUtils, "Locale", function() { 132 let channel; 133 let locale; 134 for (let res of ['app', 'gre']) { 135 channel = NetUtil.newChannel({ 136 uri: "resource://" + res + "/" + FILE_UPDATE_LOCALE, 137 contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_XMLHTTPREQUEST, 138 loadUsingSystemPrincipal: true 139 }); 140 try { 141 let inputStream = channel.open2(); 142 locale = NetUtil.readInputStreamToString(inputStream, inputStream.available()); 143 } catch (e) {} 144 if (locale) 145 return locale.trim(); 146 } 147 148 Cu.reportError(FILE_UPDATE_LOCALE + " file doesn't exist in either the " + 149 "application or GRE directories"); 150 151 return null; 152}); 153 154/** 155 * Provides adhoc system capability information for application update. 156 */ 157XPCOMUtils.defineLazyGetter(this, "gSystemCapabilities", function aus_gSC() { 158 if (AppConstants.platform == "win") { 159 const PF_MMX_INSTRUCTIONS_AVAILABLE = 3; // MMX 160 const PF_XMMI_INSTRUCTIONS_AVAILABLE = 6; // SSE 161 const PF_XMMI64_INSTRUCTIONS_AVAILABLE = 10; // SSE2 162 const PF_SSE3_INSTRUCTIONS_AVAILABLE = 13; // SSE3 163 164 let lib = ctypes.open("kernel32.dll"); 165 let IsProcessorFeaturePresent = lib.declare("IsProcessorFeaturePresent", 166 ctypes.winapi_abi, 167 ctypes.int32_t, /* success */ 168 ctypes.uint32_t); /* DWORD */ 169 let instructionSet = "unknown"; 170 try { 171 if (IsProcessorFeaturePresent(PF_SSE3_INSTRUCTIONS_AVAILABLE)) { 172 instructionSet = "SSE3"; 173 } else if (IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE)) { 174 instructionSet = "SSE2"; 175 } else if (IsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE)) { 176 instructionSet = "SSE"; 177 } else if (IsProcessorFeaturePresent(PF_MMX_INSTRUCTIONS_AVAILABLE)) { 178 instructionSet = "MMX"; 179 } 180 } catch (e) { 181 instructionSet = "error"; 182 Cu.reportError("Error getting processor instruction set. " + 183 "Exception: " + e); 184 } 185 186 lib.close(); 187 return instructionSet; 188 } 189 190 if (AppConstants == "linux") { 191 let instructionSet = "unknown"; 192 if (navigator.cpuHasSSE2) { 193 instructionSet = "SSE2"; 194 } 195 return instructionSet; 196 } 197 198 return "NA" 199}); 200 201/* Windows only getter that returns the processor architecture. */ 202XPCOMUtils.defineLazyGetter(this, "gWinCPUArch", function aus_gWinCPUArch() { 203 // Get processor architecture 204 let arch = "unknown"; 205 206 const WORD = ctypes.uint16_t; 207 const DWORD = ctypes.uint32_t; 208 209 // This structure is described at: 210 // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx 211 const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO', 212 [ 213 {wProcessorArchitecture: WORD}, 214 {wReserved: WORD}, 215 {dwPageSize: DWORD}, 216 {lpMinimumApplicationAddress: ctypes.voidptr_t}, 217 {lpMaximumApplicationAddress: ctypes.voidptr_t}, 218 {dwActiveProcessorMask: DWORD.ptr}, 219 {dwNumberOfProcessors: DWORD}, 220 {dwProcessorType: DWORD}, 221 {dwAllocationGranularity: DWORD}, 222 {wProcessorLevel: WORD}, 223 {wProcessorRevision: WORD} 224 ]); 225 226 let kernel32 = false; 227 try { 228 kernel32 = ctypes.open("Kernel32"); 229 } catch (e) { 230 Cu.reportError("Unable to open kernel32! Exception: " + e); 231 } 232 233 if (kernel32) { 234 try { 235 let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo", 236 ctypes.default_abi, 237 ctypes.void_t, 238 SYSTEM_INFO.ptr); 239 let winSystemInfo = SYSTEM_INFO(); 240 // Default to unknown 241 winSystemInfo.wProcessorArchitecture = 0xffff; 242 243 GetNativeSystemInfo(winSystemInfo.address()); 244 switch (winSystemInfo.wProcessorArchitecture) { 245 case 9: 246 arch = "x64"; 247 break; 248 case 6: 249 arch = "IA64"; 250 break; 251 case 0: 252 arch = "x86"; 253 break; 254 } 255 } catch (e) { 256 Cu.reportError("Error getting processor architecture. " + 257 "Exception: " + e); 258 } finally { 259 kernel32.close(); 260 } 261 } 262 263 return arch; 264}); 265 266XPCOMUtils.defineLazyGetter(UpdateUtils, "ABI", function() { 267 let abi = null; 268 try { 269 abi = Services.appinfo.XPCOMABI; 270 } 271 catch (e) { 272 Cu.reportError("XPCOM ABI unknown"); 273 } 274 275 if (AppConstants.platform == "macosx") { 276 // Mac universal build should report a different ABI than either macppc 277 // or mactel. 278 let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"]. 279 getService(Ci.nsIMacUtils); 280 281 if (macutils.isUniversalBinary) { 282 abi += "-u-" + macutils.architecturesInBinary; 283 } 284 } else if (AppConstants.platform == "win") { 285 // Windows build should report the CPU architecture that it's running on. 286 abi += "-" + gWinCPUArch; 287 } 288 return abi; 289}); 290 291XPCOMUtils.defineLazyGetter(UpdateUtils, "OSVersion", function() { 292 let osVersion; 293 try { 294 osVersion = Services.sysinfo.getProperty("name") + " " + 295 Services.sysinfo.getProperty("version"); 296 } 297 catch (e) { 298 Cu.reportError("OS Version unknown."); 299 } 300 301 if (osVersion) { 302 if (AppConstants.platform == "win") { 303 const BYTE = ctypes.uint8_t; 304 const WORD = ctypes.uint16_t; 305 const DWORD = ctypes.uint32_t; 306 const WCHAR = ctypes.char16_t; 307 const BOOL = ctypes.int; 308 309 // This structure is described at: 310 // http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx 311 const SZCSDVERSIONLENGTH = 128; 312 const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW', 313 [ 314 {dwOSVersionInfoSize: DWORD}, 315 {dwMajorVersion: DWORD}, 316 {dwMinorVersion: DWORD}, 317 {dwBuildNumber: DWORD}, 318 {dwPlatformId: DWORD}, 319 {szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)}, 320 {wServicePackMajor: WORD}, 321 {wServicePackMinor: WORD}, 322 {wSuiteMask: WORD}, 323 {wProductType: BYTE}, 324 {wReserved: BYTE} 325 ]); 326 327 // This structure is described at: 328 // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx 329 const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO', 330 [ 331 {wProcessorArchitecture: WORD}, 332 {wReserved: WORD}, 333 {dwPageSize: DWORD}, 334 {lpMinimumApplicationAddress: ctypes.voidptr_t}, 335 {lpMaximumApplicationAddress: ctypes.voidptr_t}, 336 {dwActiveProcessorMask: DWORD.ptr}, 337 {dwNumberOfProcessors: DWORD}, 338 {dwProcessorType: DWORD}, 339 {dwAllocationGranularity: DWORD}, 340 {wProcessorLevel: WORD}, 341 {wProcessorRevision: WORD} 342 ]); 343 344 let kernel32 = false; 345 try { 346 kernel32 = ctypes.open("Kernel32"); 347 } catch (e) { 348 Cu.reportError("Unable to open kernel32! " + e); 349 osVersion += ".unknown (unknown)"; 350 } 351 352 if (kernel32) { 353 try { 354 // Get Service pack info 355 try { 356 let GetVersionEx = kernel32.declare("GetVersionExW", 357 ctypes.default_abi, 358 BOOL, 359 OSVERSIONINFOEXW.ptr); 360 let winVer = OSVERSIONINFOEXW(); 361 winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size; 362 363 if (0 !== GetVersionEx(winVer.address())) { 364 osVersion += "." + winVer.wServicePackMajor + 365 "." + winVer.wServicePackMinor; 366 } else { 367 Cu.reportError("Unknown failure in GetVersionEX (returned 0)"); 368 osVersion += ".unknown"; 369 } 370 } catch (e) { 371 Cu.reportError("Error getting service pack information. Exception: " + e); 372 osVersion += ".unknown"; 373 } 374 } finally { 375 kernel32.close(); 376 } 377 378 // Add processor architecture 379 osVersion += " (" + gWinCPUArch + ")"; 380 } 381 } 382 383 try { 384 osVersion += " (" + Services.sysinfo.getProperty("secondaryLibrary") + ")"; 385 } 386 catch (e) { 387 // Not all platforms have a secondary widget library, so an error is nothing to worry about. 388 } 389 osVersion = encodeURIComponent(osVersion); 390 } 391 return osVersion; 392}); 393