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