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/. */
4
5 #include "AutoMounter.h"
6 #include "AutoMounterSetting.h"
7
8 #include "base/message_loop.h"
9 #include "jsapi.h"
10 #include "mozilla/Services.h"
11 #include "nsCOMPtr.h"
12 #include "nsContentUtils.h"
13 #include "nsDebug.h"
14 #include "nsIObserverService.h"
15 #include "nsISettingsService.h"
16 #include "nsJSUtils.h"
17 #include "nsPrintfCString.h"
18 #include "nsServiceManagerUtils.h"
19 #include "nsString.h"
20 #include "nsThreadUtils.h"
21 #include "xpcpublic.h"
22 #include "mozilla/dom/ScriptSettings.h"
23 #include "mozilla/Attributes.h"
24 #include "mozilla/dom/BindingUtils.h"
25 #include "mozilla/dom/SettingChangeNotificationBinding.h"
26
27 #undef LOG
28 #undef ERR
29 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AutoMounterSetting" , ## args)
30 #define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "AutoMounterSetting" , ## args)
31
32 #define UMS_MODE "ums.mode"
33 #define UMS_STATUS "ums.status"
34 #define UMS_VOLUME_ENABLED_PREFIX "ums.volume."
35 #define UMS_VOLUME_ENABLED_SUFFIX ".enabled"
36 #define MOZSETTINGS_CHANGED "mozsettings-changed"
37
38 using namespace mozilla::dom;
39
40 namespace mozilla {
41 namespace system {
42
43 class SettingsServiceCallback final : public nsISettingsServiceCallback
44 {
45 public:
46 NS_DECL_THREADSAFE_ISUPPORTS
47
SettingsServiceCallback()48 SettingsServiceCallback() {}
49
Handle(const nsAString & aName,JS::Handle<JS::Value> aResult)50 NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
51 {
52 if (aResult.isInt32()) {
53 int32_t mode = aResult.toInt32();
54 SetAutoMounterMode(mode);
55 }
56 return NS_OK;
57 }
58
HandleError(const nsAString & aName)59 NS_IMETHOD HandleError(const nsAString& aName)
60 {
61 ERR("SettingsCallback::HandleError: %s\n", NS_LossyConvertUTF16toASCII(aName).get());
62 return NS_OK;
63 }
64
65 protected:
~SettingsServiceCallback()66 ~SettingsServiceCallback() {}
67 };
68
69 NS_IMPL_ISUPPORTS(SettingsServiceCallback, nsISettingsServiceCallback)
70
71 class CheckVolumeSettingsCallback final : public nsISettingsServiceCallback
72 {
73 public:
74 NS_DECL_THREADSAFE_ISUPPORTS
75
CheckVolumeSettingsCallback(const nsACString & aVolumeName)76 CheckVolumeSettingsCallback(const nsACString& aVolumeName)
77 : mVolumeName(aVolumeName) {}
78
Handle(const nsAString & aName,JS::Handle<JS::Value> aResult)79 NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
80 {
81 if (aResult.isBoolean()) {
82 bool isSharingEnabled = aResult.toBoolean();
83 SetAutoMounterSharingMode(mVolumeName, isSharingEnabled);
84 }
85 return NS_OK;
86 }
87
HandleError(const nsAString & aName)88 NS_IMETHOD HandleError(const nsAString& aName)
89 {
90 ERR("CheckVolumeSettingsCallback::HandleError: %s\n", NS_LossyConvertUTF16toASCII(aName).get());
91 return NS_OK;
92 }
93
94 protected:
~CheckVolumeSettingsCallback()95 ~CheckVolumeSettingsCallback() {}
96
97 private:
98 nsCString mVolumeName;
99 };
100
NS_IMPL_ISUPPORTS(CheckVolumeSettingsCallback,nsISettingsServiceCallback)101 NS_IMPL_ISUPPORTS(CheckVolumeSettingsCallback, nsISettingsServiceCallback)
102
103 AutoMounterSetting::AutoMounterSetting()
104 : mStatus(AUTOMOUNTER_STATUS_DISABLED)
105 {
106 MOZ_ASSERT(NS_IsMainThread());
107
108 // Setup an observer to watch changes to the setting
109 nsCOMPtr<nsIObserverService> observerService =
110 mozilla::services::GetObserverService();
111 if (!observerService) {
112 ERR("GetObserverService failed");
113 return;
114 }
115 nsresult rv;
116 rv = observerService->AddObserver(this, MOZSETTINGS_CHANGED, false);
117 if (NS_FAILED(rv)) {
118 ERR("AddObserver failed");
119 return;
120 }
121
122 // Force ums.mode to be 0 initially. We do this because settings are persisted.
123 // We don't want UMS to be enabled until such time as the phone is unlocked,
124 // and gaia/apps/system/js/storage.js takes care of detecting when the phone
125 // becomes unlocked and changes ums.mode appropriately.
126 nsCOMPtr<nsISettingsService> settingsService =
127 do_GetService("@mozilla.org/settingsService;1");
128 if (!settingsService) {
129 ERR("Failed to get settingsLock service!");
130 return;
131 }
132 nsCOMPtr<nsISettingsServiceLock> lock;
133 settingsService->CreateLock(nullptr, getter_AddRefs(lock));
134 nsCOMPtr<nsISettingsServiceCallback> callback = new SettingsServiceCallback();
135 JS::Rooted<JS::Value> value(RootingCx());
136 value.setInt32(AUTOMOUNTER_DISABLE);
137 lock->Set(UMS_MODE, value, callback, nullptr);
138 value.setInt32(mStatus);
139 lock->Set(UMS_STATUS, value, nullptr, nullptr);
140 }
141
~AutoMounterSetting()142 AutoMounterSetting::~AutoMounterSetting()
143 {
144 nsCOMPtr<nsIObserverService> observerService =
145 mozilla::services::GetObserverService();
146 if (observerService) {
147 observerService->RemoveObserver(this, MOZSETTINGS_CHANGED);
148 }
149 }
150
NS_IMPL_ISUPPORTS(AutoMounterSetting,nsIObserver) const151 NS_IMPL_ISUPPORTS(AutoMounterSetting, nsIObserver)
152
153 const char *
154 AutoMounterSetting::StatusStr(int32_t aStatus)
155 {
156 switch (aStatus) {
157 case AUTOMOUNTER_STATUS_DISABLED: return "Disabled";
158 case AUTOMOUNTER_STATUS_ENABLED: return "Enabled";
159 case AUTOMOUNTER_STATUS_FILES_OPEN: return "FilesOpen";
160 }
161 return "??? Unknown ???";
162 }
163
164 class CheckVolumeSettingsRunnable : public Runnable
165 {
166 public:
CheckVolumeSettingsRunnable(const nsACString & aVolumeName)167 CheckVolumeSettingsRunnable(const nsACString& aVolumeName)
168 : mVolumeName(aVolumeName) {}
169
Run()170 NS_IMETHOD Run() override
171 {
172 MOZ_ASSERT(NS_IsMainThread());
173 nsCOMPtr<nsISettingsService> settingsService =
174 do_GetService("@mozilla.org/settingsService;1");
175 NS_ENSURE_TRUE(settingsService, NS_ERROR_FAILURE);
176 nsCOMPtr<nsISettingsServiceLock> lock;
177 settingsService->CreateLock(nullptr, getter_AddRefs(lock));
178 nsCOMPtr<nsISettingsServiceCallback> callback =
179 new CheckVolumeSettingsCallback(mVolumeName);
180 nsPrintfCString setting(UMS_VOLUME_ENABLED_PREFIX "%s" UMS_VOLUME_ENABLED_SUFFIX,
181 mVolumeName.get());
182 lock->Get(setting.get(), callback);
183 return NS_OK;
184 }
185
186 private:
187 nsCString mVolumeName;
188 };
189
190 //static
191 void
CheckVolumeSettings(const nsACString & aVolumeName)192 AutoMounterSetting::CheckVolumeSettings(const nsACString& aVolumeName)
193 {
194 NS_DispatchToMainThread(new CheckVolumeSettingsRunnable(aVolumeName));
195 }
196
197 class SetStatusRunnable : public Runnable
198 {
199 public:
SetStatusRunnable(int32_t aStatus)200 SetStatusRunnable(int32_t aStatus) : mStatus(aStatus) {}
201
Run()202 NS_IMETHOD Run() override
203 {
204 MOZ_ASSERT(NS_IsMainThread());
205 nsCOMPtr<nsISettingsService> settingsService =
206 do_GetService("@mozilla.org/settingsService;1");
207 NS_ENSURE_TRUE(settingsService, NS_ERROR_FAILURE);
208 nsCOMPtr<nsISettingsServiceLock> lock;
209 settingsService->CreateLock(nullptr, getter_AddRefs(lock));
210 // lock may be null if this gets called during shutdown.
211 if (lock) {
212 JS::Rooted<JS::Value> value(RootingCx(),
213 JS::Int32Value(mStatus));
214 lock->Set(UMS_STATUS, value, nullptr, nullptr);
215 }
216 return NS_OK;
217 }
218
219 private:
220 int32_t mStatus;
221 };
222
223 //static
224 void
SetStatus(int32_t aStatus)225 AutoMounterSetting::SetStatus(int32_t aStatus)
226 {
227 if (aStatus != mStatus) {
228 LOG("Changing status from '%s' to '%s'",
229 StatusStr(mStatus), StatusStr(aStatus));
230 mStatus = aStatus;
231 NS_DispatchToMainThread(new SetStatusRunnable(aStatus));
232 }
233 }
234
235 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)236 AutoMounterSetting::Observe(nsISupports* aSubject,
237 const char* aTopic,
238 const char16_t* aData)
239 {
240 if (strcmp(aTopic, MOZSETTINGS_CHANGED) != 0) {
241 return NS_OK;
242 }
243
244 // Note that this function gets called for any and all settings changes,
245 // so we need to carefully check if we have the one we're interested in.
246 //
247 // The string that we're interested in will be a JSON string that looks like:
248 // {"key":"ums.autoMount","value":true}
249
250 RootedDictionary<SettingChangeNotification> setting(RootingCx());
251 if (!WrappedJSToDictionary(aSubject, setting)) {
252 return NS_OK;
253 }
254
255 // Check for ums.mode changes
256 if (setting.mKey.EqualsASCII(UMS_MODE)) {
257 if (!setting.mValue.isInt32()) {
258 return NS_OK;
259 }
260 int32_t mode = setting.mValue.toInt32();
261 SetAutoMounterMode(mode);
262 return NS_OK;
263 }
264
265 // Check for ums.volume.NAME.enabled
266 if (StringBeginsWith(setting.mKey, NS_LITERAL_STRING(UMS_VOLUME_ENABLED_PREFIX)) &&
267 StringEndsWith(setting.mKey, NS_LITERAL_STRING(UMS_VOLUME_ENABLED_SUFFIX))) {
268 if (!setting.mValue.isBoolean()) {
269 return NS_OK;
270 }
271 const size_t prefixLen = sizeof(UMS_VOLUME_ENABLED_PREFIX) - 1;
272 const size_t suffixLen = sizeof(UMS_VOLUME_ENABLED_SUFFIX) - 1;
273 nsDependentSubstring volumeName =
274 Substring(setting.mKey, prefixLen, setting.mKey.Length() - prefixLen - suffixLen);
275 bool isSharingEnabled = setting.mValue.toBoolean();
276 SetAutoMounterSharingMode(NS_LossyConvertUTF16toASCII(volumeName), isSharingEnabled);
277 return NS_OK;
278 }
279
280 return NS_OK;
281 }
282
283 } // namespace system
284 } // namespace mozilla
285