• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "accessibility_utils.h"
17 #include "accessibility_config_observer.h"
18 
19 #include <uv.h>
20 
21 #include "hilog_wrapper.h"
22 #include "napi/native_api.h"
23 #include "napi/native_node_api.h"
24 
25 using namespace OHOS;
26 using namespace OHOS::Accessibility;
27 using namespace OHOS::AccessibilityNapi;
28 using namespace OHOS::AccessibilityConfig;
29 
OnConfigChanged(const ConfigValue & value)30 void NAccessibilityConfigObserver::OnConfigChanged(const ConfigValue &value)
31 {
32     HILOG_INFO("id = [%{public}d]", static_cast<int32_t>(configId_));
33     if (configId_ == CONFIG_CAPTION_STATE) {
34         NotifyStateChanged2JS(value.captionState);
35     } else if (configId_ == CONFIG_CAPTION_STYLE) {
36         NotifyPropertyChanged2JS(value.captionStyle);
37     } else if (configId_ == CONFIG_SCREEN_MAGNIFICATION) {
38         NotifyStateChanged2JS(value.screenMagnifier);
39     } else if (configId_ == CONFIG_MOUSE_KEY) {
40         NotifyStateChanged2JS(value.mouseKey);
41     } else if (configId_ == CONFIG_SHORT_KEY) {
42         NotifyStateChanged2JS(value.shortkey);
43     } else if (configId_ == CONFIG_SHORT_KEY_TARGET) {
44         NotifyStringChanged2JS(value.shortkey_target);
45     } else if (configId_ == CONFIG_MOUSE_AUTOCLICK) {
46         NotifyIntChanged2JS(value.mouseAutoClick);
47     } else if (configId_ == CONFIG_DALTONIZATION_COLOR_FILTER) {
48         NotifyStringChanged2JS(ConvertDaltonizationTypeToString(value.daltonizationColorFilter));
49     } else if (configId_ == CONFIG_CONTENT_TIMEOUT) {
50         NotifyIntChanged2JS(static_cast<int32_t>(value.contentTimeout));
51     } else if (configId_ == CONFIG_BRIGHTNESS_DISCOUNT) {
52         NotifyFloatChanged2JS(value.brightnessDiscount);
53     } else if (configId_ == CONFIG_AUDIO_BALANCE) {
54         NotifyFloatChanged2JS(value.audioBalance);
55     } else if (configId_ ==  CONFIG_HIGH_CONTRAST_TEXT) {
56         NotifyStateChanged2JS(value.highContrastText);
57     } else if (configId_ == CONFIG_INVERT_COLOR) {
58         NotifyStateChanged2JS(value.invertColor);
59     } else if (configId_ == CONFIG_ANIMATION_OFF) {
60         NotifyStateChanged2JS(value.animationOff);
61     } else if (configId_ == CONFIG_AUDIO_MONO) {
62         NotifyStateChanged2JS(value.audioMono);
63     } else {
64         HILOG_DEBUG("on config changed invalid parameter id = [%{public}d]", static_cast<int32_t>(configId_));
65     }
66 }
67 
NotifyStateChanged(uv_work_t * work)68 int NAccessibilityConfigObserver::NotifyStateChanged(uv_work_t *work)
69 {
70     uv_loop_s *loop = nullptr;
71     napi_get_uv_event_loop(env_, &loop);
72     int ret = uv_queue_work_with_qos(loop, work, [](uv_work_t *work) {},
73         [](uv_work_t *work, int status) {
74             StateCallbackInfo *callbackInfo = static_cast<StateCallbackInfo*>(work->data);
75             napi_handle_scope scope = nullptr;
76             napi_open_handle_scope(callbackInfo->env_, &scope);
77             if (scope == nullptr) {
78                 HILOG_DEBUG("NotifyStateChanged scope is nullptr");
79                 return;
80             }
81             napi_value jsEvent;
82             napi_create_object(callbackInfo->env_, &jsEvent);
83             napi_get_boolean(callbackInfo->env_, callbackInfo->state_, &jsEvent);
84 
85             napi_value handler = nullptr;
86             napi_value callResult = nullptr;
87             napi_get_reference_value(callbackInfo->env_, callbackInfo->ref_, &handler);
88             napi_value undefined = nullptr;
89             napi_get_undefined(callbackInfo->env_, &undefined);
90             napi_call_function(callbackInfo->env_, undefined, handler, 1, &jsEvent, &callResult);
91             int32_t result;
92             napi_get_value_int32(callbackInfo->env_, callResult, &result);
93             HILOG_INFO("NotifyStateChangedJS napi_call_function result[%{public}d]", result);
94             napi_close_handle_scope(callbackInfo->env_, scope);
95             delete callbackInfo;
96             callbackInfo = nullptr;
97             delete work;
98             work = nullptr;
99         },
100         uv_qos_default);
101     return ret;
102 }
103 
NotifyPropertyChanged(uv_work_t * work)104 int NAccessibilityConfigObserver::NotifyPropertyChanged(uv_work_t *work)
105 {
106     uv_loop_s *loop = nullptr;
107     napi_get_uv_event_loop(env_, &loop);
108     int ret = uv_queue_work_with_qos(loop, work, [](uv_work_t *work) {},
109         [](uv_work_t *work, int status) {
110             CaptionCallbackInfo *callbackInfo = static_cast<CaptionCallbackInfo*>(work->data);
111             napi_handle_scope scope = nullptr;
112             napi_open_handle_scope(callbackInfo->env_, &scope);
113             if (scope == nullptr) {
114                 return;
115             }
116             napi_value jsEvent;
117             napi_create_object(callbackInfo->env_, &jsEvent);
118             ConvertCaptionPropertyToJS(callbackInfo->env_, jsEvent, callbackInfo->caption_);
119 
120             napi_value handler = nullptr;
121             napi_value callResult = nullptr;
122             napi_get_reference_value(callbackInfo->env_, callbackInfo->ref_, &handler);
123             napi_value undefined = nullptr;
124             napi_get_undefined(callbackInfo->env_, &undefined);
125             napi_call_function(callbackInfo->env_, undefined, handler, 1, &jsEvent, &callResult);
126             int32_t result;
127             napi_get_value_int32(callbackInfo->env_, callResult, &result);
128             HILOG_INFO("NotifyPropertyChangedJS napi_call_function result[%{public}d]", result);
129             napi_close_handle_scope(callbackInfo->env_, scope);
130             delete callbackInfo;
131             callbackInfo = nullptr;
132             delete work;
133             work = nullptr;
134         },
135         uv_qos_default);
136     return ret;
137 }
138 
NotifyStringChanged(uv_work_t * work)139 int NAccessibilityConfigObserver::NotifyStringChanged(uv_work_t *work)
140 {
141     uv_loop_s *loop = nullptr;
142     napi_get_uv_event_loop(env_, &loop);
143     int ret = uv_queue_work_with_qos(loop, work, [](uv_work_t *work) {},
144         [](uv_work_t *work, int status) {
145             StateCallbackInfo *callbackInfo = static_cast<StateCallbackInfo*>(work->data);
146             napi_handle_scope scope = nullptr;
147             napi_open_handle_scope(callbackInfo->env_, &scope);
148             if (scope == nullptr) {
149                 HILOG_DEBUG("NotifyStringChanged scope is nullptr");
150                 return;
151             }
152             napi_value jsEvent;
153             napi_create_string_utf8(callbackInfo->env_, callbackInfo->stringValue_.c_str(),
154                 callbackInfo->stringValue_.length(), &jsEvent);
155             napi_value handler = nullptr;
156             napi_value callResult = nullptr;
157             napi_get_reference_value(callbackInfo->env_, callbackInfo->ref_, &handler);
158             napi_value undefined = nullptr;
159             napi_get_undefined(callbackInfo->env_, &undefined);
160             napi_call_function(callbackInfo->env_, undefined, handler, 1, &jsEvent, &callResult);
161             size_t result;
162             const uint32_t BUF_SIZE = 1024;
163             char buf[BUF_SIZE] = {0};
164             napi_get_value_string_utf8(callbackInfo->env_, callResult, buf, BUF_SIZE, &result);
165             HILOG_INFO("NotifyStringChanged2JSInner napi_call_function result[%{public}zu]", result);
166             napi_close_handle_scope(callbackInfo->env_, scope);
167             delete callbackInfo;
168             callbackInfo = nullptr;
169             delete work;
170             work = nullptr;
171         },
172         uv_qos_default);
173     return ret;
174 }
175 
NotifyIntChanged(uv_work_t * work)176 int NAccessibilityConfigObserver::NotifyIntChanged(uv_work_t *work)
177 {
178     uv_loop_s *loop = nullptr;
179     napi_get_uv_event_loop(env_, &loop);
180     int ret = uv_queue_work_with_qos(
181         loop,
182         work,
183         [](uv_work_t *work) {},
184         [](uv_work_t *work, int status) {
185             StateCallbackInfo *callbackInfo = static_cast<StateCallbackInfo*>(work->data);
186             napi_handle_scope scope = nullptr;
187             napi_open_handle_scope(callbackInfo->env_, &scope);
188             if (scope == nullptr) {
189                 HILOG_DEBUG("NotifyStateChanged scope is nullptr");
190                 return;
191             }
192             napi_value jsEvent;
193             napi_create_int32(callbackInfo->env_, callbackInfo->int32Value_, &jsEvent);
194 
195             napi_value handler = nullptr;
196             napi_value callResult = nullptr;
197             napi_get_reference_value(callbackInfo->env_, callbackInfo->ref_, &handler);
198             napi_value undefined = nullptr;
199             napi_get_undefined(callbackInfo->env_, &undefined);
200             napi_call_function(callbackInfo->env_, undefined, handler, 1, &jsEvent, &callResult);
201             int32_t result;
202             napi_get_value_int32(callbackInfo->env_, callResult, &result);
203             HILOG_INFO("NotifyIntChanged2JSInner napi_call_function result[%{public}d]", result);
204             napi_close_handle_scope(callbackInfo->env_, scope);
205             delete callbackInfo;
206             callbackInfo = nullptr;
207             delete work;
208             work = nullptr;
209         },
210         uv_qos_default);
211     return ret;
212 }
213 
NotifyUintChanged(uv_work_t * work)214 int NAccessibilityConfigObserver::NotifyUintChanged(uv_work_t *work)
215 {
216     uv_loop_s *loop = nullptr;
217     napi_get_uv_event_loop(env_, &loop);
218     int ret = uv_queue_work_with_qos(
219         loop,
220         work,
221         [](uv_work_t *work) {},
222         [](uv_work_t *work, int status) {
223             StateCallbackInfo *callbackInfo = static_cast<StateCallbackInfo*>(work->data);
224             napi_handle_scope scope = nullptr;
225             napi_open_handle_scope(callbackInfo->env_, &scope);
226             if (scope == nullptr) {
227                 HILOG_DEBUG("NotifyUintChanged scope is nullptr");
228                 return;
229             }
230             napi_value jsEvent;
231             napi_create_uint32(callbackInfo->env_, callbackInfo->uint32Value_, &jsEvent);
232 
233             napi_value handler = nullptr;
234             napi_value callResult = nullptr;
235             napi_get_reference_value(callbackInfo->env_, callbackInfo->ref_, &handler);
236             napi_value undefined = nullptr;
237             napi_get_undefined(callbackInfo->env_, &undefined);
238             napi_call_function(callbackInfo->env_, undefined, handler, 1, &jsEvent, &callResult);
239             uint32_t result;
240             napi_get_value_uint32(callbackInfo->env_, callResult, &result);
241             HILOG_INFO("NotifyUintChanged2JSInner napi_call_function result[%{public}d]", result);
242             napi_close_handle_scope(callbackInfo->env_, scope);
243             delete callbackInfo;
244             callbackInfo = nullptr;
245             delete work;
246             work = nullptr;
247         },
248         uv_qos_default);
249     return ret;
250 }
251 
NotifyFloatChanged(uv_work_t * work)252 int NAccessibilityConfigObserver::NotifyFloatChanged(uv_work_t *work)
253 {
254     uv_loop_s *loop = nullptr;
255     napi_get_uv_event_loop(env_, &loop);
256     int ret = uv_queue_work_with_qos(
257         loop,
258         work,
259         [](uv_work_t *work) {},
260         [](uv_work_t *work, int status) {
261             StateCallbackInfo *callbackInfo = static_cast<StateCallbackInfo*>(work->data);
262             napi_handle_scope scope = nullptr;
263             napi_open_handle_scope(callbackInfo->env_, &scope);
264             if (scope == nullptr) {
265                 HILOG_DEBUG("NotifyFloatChanged scope is nullptr");
266                 return;
267             }
268             napi_value jsEvent;
269             napi_create_double(callbackInfo->env_, double(callbackInfo->floatValue_), &jsEvent);
270 
271             napi_value handler = nullptr;
272             napi_value callResult = nullptr;
273             napi_get_reference_value(callbackInfo->env_, callbackInfo->ref_, &handler);
274             napi_value undefined = nullptr;
275             napi_get_undefined(callbackInfo->env_, &undefined);
276             napi_call_function(callbackInfo->env_, undefined, handler, 1, &jsEvent, &callResult);
277             int32_t result;
278             napi_get_value_int32(callbackInfo->env_, callResult, &result);
279             HILOG_INFO("NotifyFloatChanged2JSInner napi_call_function result[%{public}d]", result);
280             napi_close_handle_scope(callbackInfo->env_, scope);
281             delete callbackInfo;
282             callbackInfo = nullptr;
283             delete work;
284             work = nullptr;
285         },
286         uv_qos_default);
287     return ret;
288 }
289 
NotifyStateChanged2JS(bool enabled)290 void NAccessibilityConfigObserver::NotifyStateChanged2JS(bool enabled)
291 {
292     HILOG_INFO("id = [%{public}d] enabled = [%{public}s]", static_cast<int32_t>(configId_), enabled ? "true" : "false");
293 
294     StateCallbackInfo *callbackInfo = new(std::nothrow) StateCallbackInfo();
295     if (!callbackInfo) {
296         HILOG_ERROR("Failed to create callbackInfo.");
297         return;
298     }
299     callbackInfo->state_ = enabled;
300     callbackInfo->env_ = env_;
301     callbackInfo->ref_ = handlerRef_;
302     uv_work_t *work = new(std::nothrow) uv_work_t;
303     if (!work) {
304         HILOG_ERROR("NotifyStateChanged2JS Failed to create work.");
305         delete callbackInfo;
306         callbackInfo = nullptr;
307         return;
308     }
309     work->data = static_cast<void*>(callbackInfo);
310     int ret = NotifyStateChanged(work);
311     if (ret != 0) {
312         HILOG_ERROR("Failed to execute NotifyStateChanged2JS work queue");
313         delete callbackInfo;
314         callbackInfo = nullptr;
315         delete work;
316         work = nullptr;
317     }
318 }
319 
NotifyPropertyChanged2JS(const OHOS::AccessibilityConfig::CaptionProperty & caption)320 void NAccessibilityConfigObserver::NotifyPropertyChanged2JS(const OHOS::AccessibilityConfig::CaptionProperty &caption)
321 {
322     HILOG_INFO("id = [%{public}d]", static_cast<int32_t>(configId_));
323 
324     CaptionCallbackInfo *callbackInfo = new(std::nothrow) CaptionCallbackInfo();
325     if (!callbackInfo) {
326         HILOG_ERROR("Failed to create callbackInfo.");
327         return;
328     }
329     callbackInfo->caption_ = caption;
330     callbackInfo->env_ = env_;
331     callbackInfo->ref_ = handlerRef_;
332     uv_work_t *work = new(std::nothrow) uv_work_t;
333     if (!work) {
334         HILOG_ERROR("NotifyPropertyChanged2JS Failed to create work.");
335         delete callbackInfo;
336         callbackInfo = nullptr;
337         return;
338     }
339     work->data = static_cast<void*>(callbackInfo);
340     int ret = NotifyPropertyChanged(work);
341     if (ret != 0) {
342         HILOG_ERROR("Failed to execute NotifyPropertyChanged2JS work queue");
343         delete callbackInfo;
344         callbackInfo = nullptr;
345         delete work;
346         work = nullptr;
347     }
348 }
349 
NotifyStringChanged2JS(const std::string & value)350 void NAccessibilityConfigObserver::NotifyStringChanged2JS(const std::string& value)
351 {
352     HILOG_INFO("value = [%{public}s]", value.c_str());
353 
354     StateCallbackInfo *callbackInfo = new(std::nothrow) StateCallbackInfo();
355     if (!callbackInfo) {
356         HILOG_ERROR("Failed to create callbackInfo.");
357         return;
358     }
359     callbackInfo->stringValue_ = value;
360     callbackInfo->env_ = env_;
361     callbackInfo->ref_ = handlerRef_;
362     uv_work_t *work = new(std::nothrow) uv_work_t;
363     if (!work) {
364         HILOG_ERROR("NotifyStringChanged2JS Failed to create work.");
365         delete callbackInfo;
366         callbackInfo = nullptr;
367         return;
368     }
369     work->data = static_cast<void*>(callbackInfo);
370     int ret = NotifyStringChanged(work);
371     if (ret != 0) {
372         HILOG_ERROR("Failed to execute NotifyStringChanged2JS work queue");
373         delete callbackInfo;
374         callbackInfo = nullptr;
375         delete work;
376         work = nullptr;
377     }
378 }
379 
NotifyIntChanged2JS(int32_t value)380 void NAccessibilityConfigObserver::NotifyIntChanged2JS(int32_t value)
381 {
382     HILOG_INFO("id = [%{public}d] value = [%{public}d]", static_cast<int32_t>(configId_), value);
383 
384     StateCallbackInfo *callbackInfo = new(std::nothrow) StateCallbackInfo();
385     if (!callbackInfo) {
386         HILOG_ERROR("Failed to create callbackInfo.");
387         return;
388     }
389     callbackInfo->int32Value_ = value;
390     callbackInfo->env_ = env_;
391     callbackInfo->ref_ = handlerRef_;
392     uv_work_t *work = new(std::nothrow) uv_work_t;
393     if (!work) {
394         HILOG_ERROR("NotifyIntChanged2JS Failed to create work.");
395         delete callbackInfo;
396         callbackInfo = nullptr;
397         return;
398     }
399     work->data = static_cast<void*>(callbackInfo);
400     int ret = NotifyIntChanged(work);
401     if (ret != 0) {
402         HILOG_ERROR("Failed to execute NotifyIntChanged2JS work queue");
403         delete callbackInfo;
404         callbackInfo = nullptr;
405         delete work;
406         work = nullptr;
407     }
408 }
409 
NotifyUintChanged2JS(uint32_t value)410 void NAccessibilityConfigObserver::NotifyUintChanged2JS(uint32_t value)
411 {
412     HILOG_INFO("id = [%{public}d] value = [%{public}u]", static_cast<int32_t>(configId_), value);
413 
414     StateCallbackInfo *callbackInfo = new(std::nothrow) StateCallbackInfo();
415     if (!callbackInfo) {
416         HILOG_ERROR("Failed to create callbackInfo.");
417         return;
418     }
419     callbackInfo->uint32Value_ = value;
420     callbackInfo->env_ = env_;
421     callbackInfo->ref_ = handlerRef_;
422     uv_work_t *work = new(std::nothrow) uv_work_t;
423     if (!work) {
424         HILOG_ERROR("NotifyUintChanged2JS Failed to create work.");
425         delete callbackInfo;
426         callbackInfo = nullptr;
427         return;
428     }
429     work->data = static_cast<void*>(callbackInfo);
430     int ret = NotifyUintChanged(work);
431     if (ret != 0) {
432         HILOG_ERROR("Failed to execute NotifyUintChanged2JS work queue");
433         delete callbackInfo;
434         callbackInfo = nullptr;
435         delete work;
436         work = nullptr;
437     }
438 }
439 
NotifyFloatChanged2JS(float value)440 void NAccessibilityConfigObserver::NotifyFloatChanged2JS(float value)
441 {
442     HILOG_INFO("id = [%{public}d] value = [%{public}f]", static_cast<int32_t>(configId_), value);
443 
444     StateCallbackInfo *callbackInfo = new(std::nothrow) StateCallbackInfo();
445     if (!callbackInfo) {
446         HILOG_ERROR("Failed to create callbackInfo.");
447         return;
448     }
449     callbackInfo->floatValue_ = value;
450     callbackInfo->env_ = env_;
451     callbackInfo->ref_ = handlerRef_;
452     uv_work_t *work = new(std::nothrow) uv_work_t;
453     if (!work) {
454         HILOG_ERROR("NotifyFloatChanged2JS Failed to create work.");
455         delete callbackInfo;
456         callbackInfo = nullptr;
457         return;
458     }
459     work->data = static_cast<void*>(callbackInfo);
460     int ret = NotifyFloatChanged(work);
461     if (ret != 0) {
462         HILOG_ERROR("Failed to execute NotifyFloatChanged2JS work queue");
463         delete callbackInfo;
464         callbackInfo = nullptr;
465         delete work;
466         work = nullptr;
467     }
468 }
469 
SubscribeToFramework()470 void NAccessibilityConfigObserverImpl::SubscribeToFramework()
471 {
472     auto &instance = OHOS::AccessibilityConfig::AccessibilityConfig::GetInstance();
473     for (int32_t index = 0; index < static_cast<int32_t>(CONFIG_ID_MAX); index ++) {
474         instance.SubscribeConfigObserver(static_cast<CONFIG_ID>(index), shared_from_this(), false);
475     }
476 }
477 
OnConfigChanged(const OHOS::AccessibilityConfig::CONFIG_ID id,const OHOS::AccessibilityConfig::ConfigValue & value)478 void NAccessibilityConfigObserverImpl::OnConfigChanged(
479     const OHOS::AccessibilityConfig::CONFIG_ID id, const OHOS::AccessibilityConfig::ConfigValue& value)
480 {
481     HILOG_INFO();
482     std::lock_guard<std::mutex> lock(mutex_);
483     for (auto &observer : observers_) {
484         if (observer && observer->configId_ == id) {
485             observer->OnConfigChanged(value);
486         }
487     }
488 }
489 
SubscribeObserver(napi_env env,OHOS::AccessibilityConfig::CONFIG_ID id,napi_value observer)490 void NAccessibilityConfigObserverImpl::SubscribeObserver(napi_env env,
491     OHOS::AccessibilityConfig::CONFIG_ID id, napi_value observer)
492 {
493     HILOG_INFO();
494     std::lock_guard<std::mutex> lock(mutex_);
495     for (auto iter = observers_.begin(); iter != observers_.end();) {
496         if (CheckObserverEqual(env, observer, (*iter)->env_, (*iter)->handlerRef_)) {
497             HILOG_DEBUG("SubscribeObserver Observer exist");
498             return;
499         } else {
500             iter++;
501         }
502     }
503 
504     napi_ref handler = nullptr;
505     napi_create_reference(env, observer, 1, &handler);
506     std::shared_ptr<NAccessibilityConfigObserver> observerPtr =
507         std::make_shared<NAccessibilityConfigObserver>(env, handler, id);
508 
509     observers_.emplace_back(observerPtr);
510 }
511 
UnsubscribeObserver(napi_env env,OHOS::AccessibilityConfig::CONFIG_ID id,napi_value observer)512 void NAccessibilityConfigObserverImpl::UnsubscribeObserver(napi_env env,
513     OHOS::AccessibilityConfig::CONFIG_ID id, napi_value observer)
514 {
515     HILOG_INFO();
516     std::lock_guard<std::mutex> lock(mutex_);
517     for (auto iter = observers_.begin(); iter != observers_.end();) {
518         if ((*iter)->configId_ == id) {
519             if (CheckObserverEqual(env, observer, (*iter)->env_, (*iter)->handlerRef_)) {
520                 observers_.erase(iter);
521                 return;
522             } else {
523                 iter++;
524             }
525         } else {
526             iter++;
527         }
528     }
529 }
530 
UnsubscribeObservers(OHOS::AccessibilityConfig::CONFIG_ID id)531 void NAccessibilityConfigObserverImpl::UnsubscribeObservers(OHOS::AccessibilityConfig::CONFIG_ID id)
532 {
533     HILOG_INFO();
534     std::lock_guard<std::mutex> lock(mutex_);
535     for (auto iter = observers_.begin(); iter != observers_.end();) {
536         if ((*iter)->configId_ == id) {
537             iter = observers_.erase(iter);
538         } else {
539             iter++;
540         }
541     }
542 }