summaryrefslogtreecommitdiffstats
path: root/b2g/components/RecoveryService.js
blob: 493763e6d209a1f50801c5ed8d9294e23d1a59da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* 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/. */
"use strict";

const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;

Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/ctypes.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");

const RECOVERYSERVICE_CID = Components.ID("{b3caca5d-0bb0-48c6-912b-6be6cbf08832}");
const RECOVERYSERVICE_CONTRACTID = "@mozilla.org/recovery-service;1";

function log(msg) {
  dump("-*- RecoveryService: " + msg + "\n");
}

const isGonk = AppConstants.platform === 'gonk';

if (isGonk) {
  var librecovery = (function() {
    let library;
    try {
      library = ctypes.open("librecovery.so");
    } catch (e) {
      log("Unable to open librecovery.so");
      throw Cr.NS_ERROR_FAILURE;
    }
    // Bug 1163956, modify updatePath from ctyps.char.ptr to ctype.char.array(4096)
    // align with librecovery.h. 4096 comes from PATH_MAX
    let FotaUpdateStatus = new ctypes.StructType("FotaUpdateStatus", [
                                                  { result: ctypes.int },
                                                  { updatePath: ctypes.char.array(4096) }
                                                ]);

    return {
      factoryReset:        library.declare("factoryReset",
                                           ctypes.default_abi,
                                           ctypes.int),
      installFotaUpdate:   library.declare("installFotaUpdate",
                                           ctypes.default_abi,
                                           ctypes.int,
                                           ctypes.char.ptr,
                                           ctypes.int),

      FotaUpdateStatus:    FotaUpdateStatus,
      getFotaUpdateStatus: library.declare("getFotaUpdateStatus",
                                           ctypes.default_abi,
                                           ctypes.int,
                                           FotaUpdateStatus.ptr)
    };
  })();

}

const gFactoryResetFile = "__post_reset_cmd__";

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(reason) {
    if (!isGonk) {
      Cr.NS_ERROR_FAILURE;
    }

    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");
      }
    }

    log("factoryReset " + reason);
    let commands = [];
    if (reason == "wipe") {
      let volumeService = Cc["@mozilla.org/telephony/volume-service;1"]
                          .getService(Ci.nsIVolumeService);
      let volNames = volumeService.getVolumeNames();
      log("Found " + volNames.length + " volumes");

      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);
        commands.push("wipe " + volume.mountPoint);
      }
    } else if (reason == "root") {
      commands.push("root");
    }

    if (commands.length > 0) {
      Cu.import("resource://gre/modules/osfile.jsm");
      let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
      dir.initWithPath("/persist");
      var postResetFile = dir.exists() ?
                          OS.Path.join("/persist", gFactoryResetFile):
                          OS.Path.join("/cache", gFactoryResetFile);
      let encoder = new TextEncoder();
      let text = commands.join("\n");
      let array = encoder.encode(text);
      let promise = OS.File.writeAtomic(postResetFile, array,
                                        { tmpPath: postResetFile + ".tmp" });

      promise.then(doReset, function onError(error) {
        log("Error: " + error);
      });
    } else {
      doReset();
    }
  },

  installFotaUpdate: function RS_installFotaUpdate(updatePath) {
    if (!isGonk) {
      throw Cr.NS_ERROR_FAILURE;
    }

    // If this succeeds, then the device reboots and this never returns
    if (librecovery.installFotaUpdate(updatePath, updatePath.length) != 0) {
      log("Error: FOTA install failed. Trying again after clearing cache.");
    }
    var cache = Cc["@mozilla.org/netwerk/cache-storage-service;1"].getService(Ci.nsICacheStorageService);
    cache.clear();
    if (librecovery.installFotaUpdate(updatePath, updatePath.length) != 0) {
      log("Error: FOTA install failed again");
    }
  },

  getFotaUpdateStatus: function RS_getFotaUpdateStatus() {
    let status =  Ci.nsIRecoveryService.FOTA_UPDATE_UNKNOWN;

    if (isGonk) {
      let cStatus = librecovery.FotaUpdateStatus();

      if (librecovery.getFotaUpdateStatus(cStatus.address()) == 0) {
        status = cStatus.result;
      }
    }
    return status;
  }
};

this.NSGetFactory = XPCOMUtils.generateNSGetFactory([RecoveryService]);