Bumping manifests a=b2g-bump
[gecko.git] / b2g / components / DirectoryProvider.js
blob3e9f81eca680371f5d2d1259c5844f28a1aea726
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 file,
3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 const Cc = Components.classes;
6 const Ci = Components.interfaces;
7 const Cu = Components.utils;
8 const Cr = Components.results;
10 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
11 Cu.import("resource://gre/modules/Services.jsm");
13 const XRE_OS_UPDATE_APPLY_TO_DIR = "OSUpdApplyToD"
14 const UPDATE_ARCHIVE_DIR = "UpdArchD"
15 const LOCAL_DIR = "/data/local";
16 const UPDATES_DIR = "updates/0";
17 const FOTA_DIR = "updates/fota";
19 XPCOMUtils.defineLazyServiceGetter(Services, "env",
20                                    "@mozilla.org/process/environment;1",
21                                    "nsIEnvironment");
23 XPCOMUtils.defineLazyServiceGetter(Services, "um",
24                                    "@mozilla.org/updates/update-manager;1",
25                                    "nsIUpdateManager");
27 XPCOMUtils.defineLazyServiceGetter(Services, "volumeService",
28                                    "@mozilla.org/telephony/volume-service;1",
29                                    "nsIVolumeService");
31 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
32                                    "@mozilla.org/childprocessmessagemanager;1",
33                                    "nsISyncMessageSender");
35 XPCOMUtils.defineLazyGetter(this, "gExtStorage", function dp_gExtStorage() {
36     return Services.env.get("EXTERNAL_STORAGE");
37 });
39 // This exists to mark the affected code for bug 828858.
40 const gUseSDCard = true;
42 const VERBOSE = 1;
43 let log =
44   VERBOSE ?
45   function log_dump(msg) { dump("DirectoryProvider: " + msg + "\n"); } :
46   function log_noop(msg) { };
48 function DirectoryProvider() {
51 DirectoryProvider.prototype = {
52   classID: Components.ID("{9181eb7c-6f87-11e1-90b1-4f59d80dd2e5}"),
54   QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider]),
55   _xpcom_factory: XPCOMUtils.generateSingletonFactory(DirectoryProvider),
57   _profD: null,
59   getFile: function dp_getFile(prop, persistent) {
60 #ifdef MOZ_WIDGET_GONK
61     let localProps = ["cachePDir", "webappsDir", "PrefD", "indexedDBPDir",
62                       "permissionDBPDir", "UpdRootD"];
63     if (localProps.indexOf(prop) != -1) {
64       let file = Cc["@mozilla.org/file/local;1"]
65                    .createInstance(Ci.nsILocalFile)
66       file.initWithPath(LOCAL_DIR);
67       persistent.value = true;
68       return file;
69     }
70     if (prop == "ProfD") {
71       let dir = Cc["@mozilla.org/file/local;1"]
72                   .createInstance(Ci.nsILocalFile);
73       dir.initWithPath(LOCAL_DIR+"/tests/profile");
74       if (dir.exists()) {
75         persistent.value = true;
76         return dir;
77       }
78     }
79     if (prop == "coreAppsDir") {
80       let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile)
81       file.initWithPath("/system/b2g");
82       persistent.value = true;
83       return file;
84     }
85     if (prop == UPDATE_ARCHIVE_DIR) {
86       // getUpdateDir will set persistent to false since it may toggle between
87       // /data/local/ and /mnt/sdcard based on free space and/or availability
88       // of the sdcard.
89       // before download, check if free space is 2.1 times of update.mar
90       return this.getUpdateDir(persistent, UPDATES_DIR, 2.1);
91     }
92     if (prop == XRE_OS_UPDATE_APPLY_TO_DIR) {
93       // getUpdateDir will set persistent to false since it may toggle between
94       // /data/local/ and /mnt/sdcard based on free space and/or availability
95       // of the sdcard.
96       // before apply, check if free space is 1.1 times of update.mar
97       return this.getUpdateDir(persistent, FOTA_DIR, 1.1);
98     }
99 #else
100     // In desktop builds, coreAppsDir is the same as the profile directory.
101     // We just need to get the path from the parent, and it is then used to
102     // build jar:remoteopenfile:// uris.
103     if (prop == "coreAppsDir") {
104       let appsDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
105       appsDir.append("webapps");
106       persistent.value = true;
107       return appsDir;
108     } else if (prop == "ProfD") {
109       let inParent = Cc["@mozilla.org/xre/app-info;1"]
110                        .getService(Ci.nsIXULRuntime)
111                        .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
112       if (inParent) {
113         // Just bail out to use the default from toolkit.
114         return null;
115       }
116       if (!this._profD) {
117         this._profD = cpmm.sendSyncMessage("getProfD", {})[0];
118       }
119       let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
120       file.initWithPath(this._profD);
121       persistent.value = true;
122       return file;
123     }
124 #endif
125     return null;
126   },
128   // The VolumeService only exists on the device, and not on desktop
129   volumeHasFreeSpace: function dp_volumeHasFreeSpace(volumePath, requiredSpace) {
130     if (!volumePath) {
131       return false;
132     }
133     if (!Services.volumeService) {
134       return false;
135     }
136     let volume = Services.volumeService.createOrGetVolumeByPath(volumePath);
137     if (!volume || volume.state !== Ci.nsIVolume.STATE_MOUNTED) {
138       return false;
139     }
140     let stat = volume.getStats();
141     if (!stat) {
142       return false;
143     }
144     return requiredSpace <= stat.freeBytes;
145   },
147   findUpdateDirWithFreeSpace: function dp_findUpdateDirWithFreeSpace(requiredSpace, subdir) {
148     if (!Services.volumeService) {
149       return this.createUpdatesDir(LOCAL_DIR, subdir);
150     }
152     let activeUpdate = Services.um.activeUpdate;
153     if (gUseSDCard) {
154       if (this.volumeHasFreeSpace(gExtStorage, requiredSpace)) {
155         let extUpdateDir = this.createUpdatesDir(gExtStorage, subdir);
156         if (extUpdateDir !== null) {
157           return extUpdateDir;
158         }
159         log("Warning: " + gExtStorage + " has enough free space for update " +
160             activeUpdate.name + ", but is not writable");
161       }
162     }
164     if (this.volumeHasFreeSpace(LOCAL_DIR, requiredSpace)) {
165       let localUpdateDir = this.createUpdatesDir(LOCAL_DIR, subdir);
166       if (localUpdateDir !== null) {
167         return localUpdateDir;
168       }
169       log("Warning: " + LOCAL_DIR + " has enough free space for update " +
170           activeUpdate.name + ", but is not writable");
171     }
173     return null;
174   },
176   getUpdateDir: function dp_getUpdateDir(persistent, subdir, multiple) {
177     let defaultUpdateDir = this.getDefaultUpdateDir();
178     persistent.value = false;
180     let activeUpdate = Services.um.activeUpdate;
181     if (!activeUpdate) {
182       log("Warning: No active update found, using default update dir: " +
183           defaultUpdateDir);
184       return defaultUpdateDir;
185     }
187     let selectedPatch = activeUpdate.selectedPatch;
188     if (!selectedPatch) {
189       log("Warning: No selected patch, using default update dir: " +
190           defaultUpdateDir);
191       return defaultUpdateDir;
192     }
194     let requiredSpace = selectedPatch.size * multiple;
195     let updateDir = this.findUpdateDirWithFreeSpace(requiredSpace, subdir);
196     if (updateDir) {
197       return updateDir;
198     }
200     // If we've gotten this far, there isn't enough free space to download the patch
201     // on either external storage or /data/local. All we can do is report the
202     // error and let upstream code handle it more gracefully.
203     log("Error: No volume found with " + requiredSpace + " bytes for downloading"+
204         " update " + activeUpdate.name);
205     activeUpdate.errorCode = Cr.NS_ERROR_FILE_TOO_BIG;
206     return null;
207   },
209   createUpdatesDir: function dp_createUpdatesDir(root, subdir) {
210       let dir = Cc["@mozilla.org/file/local;1"]
211                    .createInstance(Ci.nsILocalFile);
212       dir.initWithPath(root);
213       if (!dir.isWritable()) {
214         log("Error: " + dir.path + " isn't writable");
215         return null;
216       }
217       dir.appendRelativePath(subdir);
218       if (dir.exists()) {
219         if (dir.isDirectory() && dir.isWritable()) {
220           return dir;
221         }
222         // subdir is either a file or isn't writable. In either case we
223         // can't use it.
224         log("Error: " + dir.path + " is a file or isn't writable");
225         return null;
226       }
227       // subdir doesn't exist, and the parent is writable, so try to
228       // create it. This can fail if a file named updates exists.
229       try {
230         dir.create(Ci.nsIFile.DIRECTORY_TYPE, 0770);
231       } catch (e) {
232         // The create failed for some reason. We can't use it.
233         log("Error: " + dir.path + " unable to create directory");
234         return null;
235       }
236       return dir;
237   },
239   getDefaultUpdateDir: function dp_getDefaultUpdateDir() {
240     let path = gExtStorage;
241     if (!path) {
242       path = LOCAL_DIR;
243     }
245     if (Services.volumeService) {
246       let extVolume = Services.volumeService.createOrGetVolumeByPath(path);
247       if (!extVolume) {
248         path = LOCAL_DIR;
249       }
250     }
252     let dir = Cc["@mozilla.org/file/local;1"]
253                  .createInstance(Ci.nsILocalFile)
254     dir.initWithPath(path);
256     if (!dir.exists() && path != LOCAL_DIR) {
257       // Fallback to LOCAL_DIR if we didn't fallback earlier
258       dir.initWithPath(LOCAL_DIR);
260       if (!dir.exists()) {
261         throw Cr.NS_ERROR_FILE_NOT_FOUND;
262       }
263     }
265     dir.appendRelativePath("updates");
266     return dir;
267   }
270 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DirectoryProvider]);