Bug 1037212 - [Flame] Phone is not completely erased when a ringtone is set from the Music App r=dougt,dhylands,khuey
authorFabrice Desré <fabrice@mozilla.com>
Thu, 17 Jul 2014 18:30:47 -0700
changeset 215552 326396f9cc4e197baad1179bcb41dc4269629ae6
parent 215551 ed44d33e42b29b02a7fbd1cc808b18c2c58a2753
child 215553 1da3553507a718fe1b7fc0d4b43125e7c4e309ff
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdougt, dhylands, khuey
bugs1037212
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1037212 - [Flame] Phone is not completely erased when a ringtone is set from the Music App r=dougt,dhylands,khuey
b2g/chrome/content/shell.js
b2g/components/ProcessGlobal.js
b2g/components/RecoveryService.js
dom/devicestorage/nsDeviceStorage.cpp
dom/power/PowerManager.cpp
dom/power/PowerManager.h
dom/system/gonk/nsIVolumeService.idl
dom/system/gonk/nsVolumeService.cpp
dom/webidl/MozPowerManager.webidl
hal/Hal.cpp
hal/Hal.h
hal/fallback/FallbackFactoryReset.cpp
hal/gonk/GonkHal.cpp
hal/gonk/nsIRecoveryService.idl
hal/sandbox/PHal.ipdl
hal/sandbox/SandboxHal.cpp
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -1268,17 +1268,17 @@ window.addEventListener('ContentStart', 
   let volumeService = Cc["@mozilla.org/telephony/volume-service;1"]
                         .getService(Ci.nsIVolumeService);
 
   let stats = volumeService.createOrGetVolumeByPath(path).getStats();
 
   // We must set the size in KB, and keep a bit of free space.
   let size = Math.floor(stats.totalBytes / 1024) - 1024;
   Services.prefs.setIntPref("browser.cache.disk.capacity", size);
-}) ()
+})();
 #endif
 
 // Calling this observer will cause a shutdown an a profile reset.
 // Use eg. : Services.obs.notifyObservers(null, 'b2g-reset-profile', null);
 Services.obs.addObserver(function resetProfile(subject, topic, data) {
   Services.obs.removeObserver(resetProfile, topic);
 
   // Listening for 'profile-before-change2' which is late in the shutdown
--- a/b2g/components/ProcessGlobal.js
+++ b/b2g/components/ProcessGlobal.js
@@ -28,35 +28,91 @@ Cu.import("resource://gre/modules/CSPUti
 function debug(msg) {
   log(msg);
 }
 function log(msg) {
   // This file implements console.log(), so use dump().
   //dump('ProcessGlobal: ' + msg + '\n');
 }
 
+const gFactoryResetFile = "/persist/__post_reset_cmd__";
+
 function ProcessGlobal() {}
 ProcessGlobal.prototype = {
   classID: Components.ID('{1a94c87a-5ece-4d11-91e1-d29c29f21b28}'),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
 
+  wipeDir: function(path) {
+    log("wipeDir " + path);
+    let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+    dir.initWithPath(path);
+    if (!dir.exists() || !dir.isDirectory()) {
+      return;
+    }
+    let entries = dir.directoryEntries;
+    while (entries.hasMoreElements()) {
+      let file = entries.getNext().QueryInterface(Ci.nsIFile);
+      log("Deleting " + file.path);
+      try {
+        file.remove(true);
+      } catch(e) {}
+    }
+  },
+
+  processWipeFile: function(text) {
+    log("processWipeFile " + text);
+    let lines = text.split("\n");
+    lines.forEach((line) => {
+      log(line);
+      let params = line.split(" ");
+      if (params[0] == "wipe") {
+        this.wipeDir(params[1]);
+      }
+    });
+  },
+
+  cleanupAfterFactoryReset: function() {
+    log("cleanupAfterWipe start");
+
+    let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+    file.initWithPath(gFactoryResetFile);
+    if (!file.exists()) {
+      debug("Nothing to wipe.")
+      return;
+    }
+
+    Cu.import("resource://gre/modules/osfile.jsm");
+    let promise = OS.File.read(gFactoryResetFile);
+    promise.then(
+      (array) => {
+        file.remove(false);
+        let decoder = new TextDecoder();
+        this.processWipeFile(decoder.decode(array));
+      }
+    );
+
+    log("cleanupAfterWipe end.");
+  },
+
   observe: function pg_observe(subject, topic, data) {
     switch (topic) {
     case 'app-startup': {
       Services.obs.addObserver(this, 'console-api-log-event', false);
       let inParent = Cc["@mozilla.org/xre/app-info;1"]
                        .getService(Ci.nsIXULRuntime)
                        .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
       if (inParent) {
         let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
                      .getService(Ci.nsIMessageListenerManager);
         ppmm.addMessageListener("getProfD", function(message) {
           return Services.dirsvc.get("ProfD", Ci.nsIFile).path;
         });
+
+        this.cleanupAfterFactoryReset();
       }
       break;
     }
     case 'console-api-log-event': {
       // Pipe `console` log messages to the nsIConsoleService which
       // writes them to logcat on Gonk.
       let message = subject.wrappedJSObject;
       let prefix = ('Content JS ' + message.level.toUpperCase() +
--- a/b2g/components/RecoveryService.js
+++ b/b2g/components/RecoveryService.js
@@ -43,40 +43,71 @@ let librecovery = (function() {
 
     FotaUpdateStatus:    FotaUpdateStatus,
     getFotaUpdateStatus: library.declare("getFotaUpdateStatus",
                                          ctypes.default_abi,
                                          ctypes.int,
                                          FotaUpdateStatus.ptr)
   };
 })();
+
+const gFactoryResetFile = "/persist/__post_reset_cmd__";
+
 #endif
 
 function RecoveryService() {}
 
 RecoveryService.prototype = {
   classID: RECOVERYSERVICE_CID,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIRecoveryService]),
   classInfo: XPCOMUtils.generateCI({
     classID: RECOVERYSERVICE_CID,
     contractID: RECOVERYSERVICE_CONTRACTID,
     interfaces: [Ci.nsIRecoveryService],
     classDescription: "B2G Recovery Service"
   }),
 
-  factoryReset: function RS_factoryReset() {
+  factoryReset: function RS_factoryReset(reason) {
 #ifdef MOZ_WIDGET_GONK
-    // If this succeeds, then the device reboots and this never returns
-    if (librecovery.factoryReset() != 0) {
-      log("Error: Factory reset failed. Trying again after clearing cache.");
+    function doReset() {
+      // If this succeeds, then the device reboots and this never returns
+      if (librecovery.factoryReset() != 0) {
+        log("Error: Factory reset failed. Trying again after clearing cache.");
+      }
+      let cache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
+                    .getService(Ci.nsICacheStorageService);
+      cache.clear();
+      if (librecovery.factoryReset() != 0) {
+        log("Error: Factory reset failed again");
+      }
     }
-    var cache = Cc["@mozilla.org/netwerk/cache-storage-service;1"].getService(Ci.nsICacheStorageService);
-    cache.clear();
-    if (librecovery.factoryReset() != 0) {
-      log("Error: Factory reset failed again");
+
+    log("factoryReset " + reason);
+    if (reason == "wipe") {
+      let volumeService = Cc["@mozilla.org/telephony/volume-service;1"]
+                          .getService(Ci.nsIVolumeService);
+      let volNames = volumeService.getVolumeNames();
+      log("Found " + volNames.length + " volumes");
+      let text = "";
+      for (let i = 0; i < volNames.length; i++) {
+        let name = volNames.queryElementAt(i, Ci.nsISupportsString);
+        let volume = volumeService.getVolumeByName(name.data);
+        log("Got volume: " + name.data + " at " + volume.mountPoint);
+        text += "wipe " + volume.mountPoint + "\n";
+      }
+
+      Cu.import("resource://gre/modules/osfile.jsm");
+      let encoder = new TextEncoder();
+      let array = encoder.encode(text);
+      let promise = OS.File.writeAtomic(gFactoryResetFile, array,
+                                        { tmpPath: gFactoryResetFile + ".tmp" });
+
+      promise.then(doReset);
+    } else {
+      doReset();
     }
 #endif
     throw Cr.NS_ERROR_FAILURE;
   },
 
   installFotaUpdate: function RS_installFotaUpdate(updatePath) {
 #ifdef MOZ_WIDGET_GONK
     // If this succeeds, then the device reboots and this never returns
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -24,16 +24,17 @@
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/LazyIdleThread.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Scoped.h"
 #include "mozilla/Services.h"
 
+#include "nsArrayUtils.h"
 #include "nsAutoPtr.h"
 #include "nsGlobalWindow.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIFile.h"
 #include "nsIDirectoryEnumerator.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsIDOMFile.h"
@@ -3341,17 +3342,29 @@ nsDOMDeviceStorage::GetOrderedVolumeName
 {
   if (sVolumeNameCache && sVolumeNameCache->Length() > 0) {
     aVolumeNames.AppendElements(*sVolumeNameCache);
     return;
   }
 #ifdef MOZ_WIDGET_GONK
   nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
   if (vs) {
-    vs->GetVolumeNames(aVolumeNames);
+    nsCOMPtr<nsIArray> volNames;
+    vs->GetVolumeNames(getter_AddRefs(volNames));
+    uint32_t length = -1;
+    volNames->GetLength(&length);
+    for (uint32_t i = 0; i < length; i++) {
+      nsCOMPtr<nsISupportsString> str = do_QueryElementAt(volNames, i);
+      if (str) {
+        nsAutoString s;
+        if (NS_SUCCEEDED(str->GetData(s)) && !s.IsEmpty()) {
+          aVolumeNames.AppendElement(s);
+        }
+      }
+    }
 
     // If the volume sdcard exists, then we want it to be first.
 
     VolumeNameArray::index_type sdcardIndex;
     sdcardIndex = aVolumeNames.IndexOf(NS_LITERAL_STRING("sdcard"));
     if (sdcardIndex != VolumeNameArray::NoIndex && sdcardIndex > 0) {
       aVolumeNames.RemoveElementAt(sdcardIndex);
       aVolumeNames.InsertElementAt(0, NS_LITERAL_STRING("sdcard"));
--- a/dom/power/PowerManager.cpp
+++ b/dom/power/PowerManager.cpp
@@ -73,19 +73,19 @@ PowerManager::Reboot(ErrorResult& aRv)
   if (pmService) {
     pmService->Reboot();
   } else {
     aRv.Throw(NS_ERROR_UNEXPECTED);
   }
 }
 
 void
-PowerManager::FactoryReset()
+PowerManager::FactoryReset(mozilla::dom::FactoryResetReason& aReason)
 {
-  hal::FactoryReset();
+  hal::FactoryReset(aReason);
 }
 
 void
 PowerManager::PowerOff(ErrorResult& aRv)
 {
   nsCOMPtr<nsIPowerManagerService> pmService =
     do_GetService(POWERMANAGERSERVICE_CONTRACTID);
   if (pmService) {
--- a/dom/power/PowerManager.h
+++ b/dom/power/PowerManager.h
@@ -7,16 +7,17 @@
 
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 #include "nsIDOMWakeLockListener.h"
 #include "nsIDOMWindow.h"
 #include "nsWeakReference.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
+#include "mozilla/dom/MozPowerManagerBinding.h"
 
 class nsPIDOMWindow;
 
 namespace mozilla {
 class ErrorResult;
 
 namespace dom {
 
@@ -40,17 +41,17 @@ public:
 
   // WebIDL
   nsIDOMWindow* GetParentObject() const
   {
     return mWindow;
   }
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
   void Reboot(ErrorResult& aRv);
-  void FactoryReset();
+  void FactoryReset(mozilla::dom::FactoryResetReason& aReason);
   void PowerOff(ErrorResult& aRv);
   void AddWakeLockListener(nsIDOMMozWakeLockListener* aListener);
   void RemoveWakeLockListener(nsIDOMMozWakeLockListener* aListener);
   void GetWakeLockState(const nsAString& aTopic, nsAString& aState,
                         ErrorResult& aRv);
   bool ScreenEnabled();
   void SetScreenEnabled(bool aEnabled);
   bool KeyLightEnabled();
--- a/dom/system/gonk/nsIVolumeService.idl
+++ b/dom/system/gonk/nsIVolumeService.idl
@@ -1,34 +1,30 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 #include "nsIVolume.idl"
 #include "nsIVolumeMountLock.idl"
 
-%{C++
-#include "nsTArray.h"
-#include "nsString.h"
-%}
-[ref] native nsStringTArrayRef(nsTArray<nsString>);
+interface nsIArray;
 
-[scriptable, uuid(a3b110cd-74f2-43cb-84c6-2a87713f2774)]
+[scriptable, uuid(cab99ab4-542e-4387-bd40-db6ef30e4f5f)]
 interface nsIVolumeService : nsISupports
 {
     nsIVolume getVolumeByName(in DOMString volName);
     nsIVolume getVolumeByPath(in DOMString path);
     nsIVolume createOrGetVolumeByPath(in DOMString path);
 
     void BroadcastVolume(in DOMString volName);
 
     nsIVolumeMountLock createMountLock(in DOMString volName);
 
-    [noscript] void getVolumeNames(in nsStringTArrayRef aVolNames);
+    nsIArray getVolumeNames();
 
     /* for test case only to simulate sdcard insertion/removal */
     void createFakeVolume(in DOMString name, in DOMString path);
     void SetFakeVolumeState(in DOMString name, in long state);
 };
 
 %{C++
 #define NS_VOLUMESERVICE_CID \
--- a/dom/system/gonk/nsVolumeService.cpp
+++ b/dom/system/gonk/nsVolumeService.cpp
@@ -7,19 +7,21 @@
 #include "Volume.h"
 #include "VolumeManager.h"
 #include "VolumeServiceIOThread.h"
 
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsDependentSubstring.h"
 #include "nsIDOMWakeLockListener.h"
+#include "nsIMutableArray.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIPowerManagerService.h"
+#include "nsISupportsPrimitives.h"
 #include "nsISupportsUtils.h"
 #include "nsIVolume.h"
 #include "nsIVolumeService.h"
 #include "nsLocalFile.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
@@ -251,27 +253,44 @@ nsVolumeService::CreateOrGetVolumeByPath
                                          false /* isSharing */,
                                          false /* isFormatting */,
                                          true  /* isFake */);
   vol.forget(aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsVolumeService::GetVolumeNames(nsTArray<nsString>& aVolNames)
+nsVolumeService::GetVolumeNames(nsIArray** aVolNames)
 {
+  NS_ENSURE_ARG_POINTER(aVolNames);
   MonitorAutoLock autoLock(mArrayMonitor);
 
+  *aVolNames = nullptr;
+
+  nsresult rv;
+  nsCOMPtr<nsIMutableArray> volNames =
+    do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
   nsVolume::Array::index_type volIndex;
   for (volIndex = 0; volIndex < numVolumes; volIndex++) {
     nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
-    aVolNames.AppendElement(vol->Name());
+    nsCOMPtr<nsISupportsString> isupportsString =
+      do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = isupportsString->SetData(vol->Name());
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = volNames->AppendElement(isupportsString, false);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  NS_ADDREF(*aVolNames = volNames);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsVolumeService::CreateMountLock(const nsAString& aVolumeName, nsIVolumeMountLock **aResult)
 {
   nsCOMPtr<nsIVolumeMountLock> mountLock = nsVolumeMountLock::Create(aVolumeName);
   if (!mountLock) {
--- a/dom/webidl/MozPowerManager.webidl
+++ b/dom/webidl/MozPowerManager.webidl
@@ -1,25 +1,35 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 interface MozWakeLockListener;
 
 /**
+  * The reason for the factory reset.
+  * "normal" : simple factory reset.
+  * "wipe"   : will also attempt to wipe all user storage areas.
+  */
+enum FactoryResetReason {
+    "normal",
+    "wipe"
+};
+
+/**
  * This interface implements navigator.mozPower
  */
 interface MozPowerManager
 {
     [Throws]
     void    powerOff();
     [Throws]
     void    reboot();
-    void    factoryReset();
+    void    factoryReset(optional FactoryResetReason reason = "normal");
 
     /**
      * The listeners are notified when a resource changes its lock state to:
      *  - unlocked
      *  - locked but not visible
      *  - locked and visible
      */
     void    addWakeLockListener(MozWakeLockListener aListener);
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -1200,20 +1200,20 @@ GetFMBandSettings(FMRadioCountry aCountr
       break;
     default:
       MOZ_ASSERT(0);
       break;
     };
     return settings;
 }
 
-void FactoryReset()
+void FactoryReset(mozilla::dom::FactoryResetReason& aReason)
 {
   AssertMainThread();
-  PROXY_IF_SANDBOXED(FactoryReset());
+  PROXY_IF_SANDBOXED(FactoryReset(aReason));
 }
 
 void
 StartDiskSpaceWatcher()
 {
   AssertMainProcess();
   AssertMainThread();
   PROXY_IF_SANDBOXED(StartDiskSpaceWatcher());
--- a/hal/Hal.h
+++ b/hal/Hal.h
@@ -9,16 +9,17 @@
 
 #include "mozilla/hal_sandbox/PHal.h"
 #include "mozilla/HalTypes.h"
 #include "base/basictypes.h"
 #include "mozilla/Observer.h"
 #include "mozilla/Types.h"
 #include "nsTArray.h"
 #include "prlog.h"
+#include "mozilla/dom/MozPowerManagerBinding.h"
 #include "mozilla/dom/battery/Types.h"
 #include "mozilla/dom/network/Types.h"
 #include "mozilla/dom/power/Types.h"
 #include "mozilla/hal_sandbox/PHal.h"
 #include "mozilla/dom/ScreenOrientation.h"
 #include "mozilla/HalScreenConfiguration.h"
 
 /*
@@ -570,17 +571,17 @@ hal::FMRadioSettings GetFMBandSettings(h
  *
  * This API is currently only allowed to be used from the main process.
  */
 void StartForceQuitWatchdog(hal::ShutdownMode aMode, int32_t aTimeoutSecs);
 
 /**
  * Perform Factory Reset to wipe out all user data.
  */
-void FactoryReset();
+void FactoryReset(mozilla::dom::FactoryResetReason& aReason);
 
 /**
  * Start monitoring the status of gamepads attached to the system.
  */
 void StartMonitoringGamepadStatus();
 
 /**
  * Stop monitoring the status of gamepads attached to the system.
--- a/hal/fallback/FallbackFactoryReset.cpp
+++ b/hal/fallback/FallbackFactoryReset.cpp
@@ -6,13 +6,13 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Hal.h"
 
 namespace mozilla {
 namespace hal_impl {
 
 void
-FactoryReset()
+FactoryReset(mozilla::dom::FactoryResetReason&)
 {}
 
 } // namespace hal_impl
 } // namespace mozilla
--- a/hal/gonk/GonkHal.cpp
+++ b/hal/gonk/GonkHal.cpp
@@ -100,16 +100,17 @@
 #define BATTERY_CHARGING_ARGB 0x00FF0000
 #endif
 #ifndef BATTERY_FULL_ARGB
 #define BATTERY_FULL_ARGB 0x0000FF00
 #endif
 
 using namespace mozilla;
 using namespace mozilla::hal;
+using namespace mozilla::dom;
 
 namespace mozilla {
 namespace hal_impl {
 
 struct LightConfiguration {
   hal::LightType light;
   hal::LightMode mode;
   hal::FlashMode flash;
@@ -1686,22 +1687,26 @@ SetCurrentThreadPriority(ThreadPriority 
     }
     default:
       LOG("Unrecognized thread priority %d; Doing nothing", aThreadPriority);
       return;
   }
 }
 
 void
-FactoryReset()
+FactoryReset(FactoryResetReason& aReason)
 {
   nsCOMPtr<nsIRecoveryService> recoveryService =
     do_GetService("@mozilla.org/recovery-service;1");
   if (!recoveryService) {
     NS_WARNING("Could not get recovery service!");
     return;
   }
 
-  recoveryService->FactoryReset();
+  if (aReason == FactoryResetReason::Wipe) {
+    recoveryService->FactoryReset("wipe");
+  } else {
+    recoveryService->FactoryReset("normal");
+  }
 }
 
 } // hal_impl
 } // mozilla
--- a/hal/gonk/nsIRecoveryService.idl
+++ b/hal/gonk/nsIRecoveryService.idl
@@ -1,32 +1,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-[scriptable, uuid(acb93ff8-aa6d-4bc8-bedd-2a6a3b802a74)]
+[scriptable, uuid(bc24fb33-a0c1-49ca-aa43-05f167e02fb6)]
 interface nsIRecoveryService : nsISupports
 {
   /**
    * Possible values of fotaStatus.result. These should stay in sync with
    * librecovery/librecovery.h
    */
   const long FOTA_UPDATE_UNKNOWN = 0;
   const long FOTA_UPDATE_FAIL    = 1;
   const long FOTA_UPDATE_SUCCESS = 2;
 
   /**
    * Uses recovery to wipe the data and cache partitions. If this call is
    * successful, the device should reboot before the function call ever returns.
    *
    * @throws NS_ERROR_FAILURE when rebooting into recovery fails for some reason.
    */
-  void factoryReset();
+  void factoryReset(in string reason);
 
   /**
    * Use recovery to install an OTA update.zip. If this call is
    * successful, the device should reboot before the function call ever returns.
    *
    * @throws NS_ERROR_FAILURE when rebooting into recovery fails for some reason.
    */
   void installFotaUpdate(in string updatePath);
--- a/hal/sandbox/PHal.ipdl
+++ b/hal/sandbox/PHal.ipdl
@@ -154,17 +154,17 @@ parent:
       returns (bool allowed);
     UnlockScreenOrientation();
  
     EnableSwitchNotifications(SwitchDevice aDevice);
     DisableSwitchNotifications(SwitchDevice aDevice);
     sync GetCurrentSwitchState(SwitchDevice aDevice)
       returns (SwitchState aState);
 
-    FactoryReset();
+    FactoryReset(nsString aReason);
 
 child:
     NotifySensorChange(SensorData aSensorData);
 
 parent:
     EnableSensorNotifications(SensorType aSensor);
     DisableSensorNotifications(SensorType aSensor);
 
--- a/hal/sandbox/SandboxHal.cpp
+++ b/hal/sandbox/SandboxHal.cpp
@@ -421,19 +421,23 @@ GetFMRadioSignalStrength()
 
 void
 CancelFMRadioSeek()
 {
   NS_RUNTIMEABORT("FM radio cannot be called from sandboxed contexts.");
 }
 
 void
-FactoryReset()
+FactoryReset(FactoryResetReason& aReason)
 {
-  Hal()->SendFactoryReset();
+  if (aReason == FactoryResetReason::Normal) {
+    Hal()->SendFactoryReset(NS_LITERAL_STRING("normal"));
+  } else if (aReason == FactoryResetReason::Wipe) {
+    Hal()->SendFactoryReset(NS_LITERAL_STRING("wipe"));
+  }
 }
 
 void
 StartDiskSpaceWatcher()
 {
   NS_RUNTIMEABORT("StartDiskSpaceWatcher() can't be called from sandboxed contexts.");
 }
 
@@ -836,22 +840,33 @@ public:
   }
 
   void Notify(const SystemTimezoneChangeInformation& aSystemTimezoneChangeInfo)
   {
     unused << SendNotifySystemTimezoneChange(aSystemTimezoneChangeInfo);
   }
 
   virtual bool
-  RecvFactoryReset()
+  RecvFactoryReset(const nsString& aReason) MOZ_OVERRIDE
   {
     if (!AssertAppProcessPermission(this, "power")) {
       return false;
     }
-    hal::FactoryReset();
+
+    FactoryResetReason reason = FactoryResetReason::Normal;
+    if (aReason.EqualsLiteral("normal")) {
+      reason = FactoryResetReason::Normal;
+    } else if (aReason.EqualsLiteral("wipe")) {
+      reason = FactoryResetReason::Wipe;
+    } else {
+      // Invalid factory reset reason. That should never happen.
+      return false;
+    }
+
+    hal::FactoryReset(reason);
     return true;
   }
 
   virtual mozilla::ipc::IProtocol*
   CloneProtocol(Channel* aChannel,
                 mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE
   {
     ContentParent* contentParent = aCtx->GetContentParent();