[Tizen] Disable ATSPI when release mode
[platform/core/uifw/dali-adaptor.git] / dali / internal / accessibility / bridge / bridge-impl.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19
20 // EXTERNAL INCLUDES
21 #include <dali/devel-api/common/stage.h>
22 #include <dali/integration-api/debug.h>
23 #include <dali/public-api/actors/layer.h>
24 #include <iostream>
25 #include <unordered_map>
26
27 // INTERNAL INCLUDES
28 #include <dali/devel-api/adaptor-framework/environment-variable.h>
29 #include <dali/devel-api/adaptor-framework/window-devel.h>
30 #include <dali/internal/accessibility/bridge/accessibility-common.h>
31 #include <dali/internal/accessibility/bridge/bridge-accessible.h>
32 #include <dali/internal/accessibility/bridge/bridge-action.h>
33 #include <dali/internal/accessibility/bridge/bridge-collection.h>
34 #include <dali/internal/accessibility/bridge/bridge-component.h>
35 #include <dali/internal/accessibility/bridge/bridge-editable-text.h>
36 #include <dali/internal/accessibility/bridge/bridge-hypertext.h>
37 #include <dali/internal/accessibility/bridge/bridge-hyperlink.h>
38 #include <dali/internal/accessibility/bridge/bridge-object.h>
39 #include <dali/internal/accessibility/bridge/bridge-selection.h>
40 #include <dali/internal/accessibility/bridge/bridge-socket.h>
41 #include <dali/internal/accessibility/bridge/bridge-text.h>
42 #include <dali/internal/accessibility/bridge/bridge-value.h>
43 #include <dali/internal/accessibility/bridge/bridge-application.h>
44 #include <dali/internal/accessibility/bridge/dummy/dummy-atspi.h>
45 #include <dali/internal/adaptor/common/adaptor-impl.h>
46 #include <dali/internal/system/common/environment-variables.h>
47 #include <unistd.h>
48
49 using namespace Dali::Accessibility;
50
51 namespace // unnamed namespace
52 {
53
54 const int RETRY_INTERVAL = 1000;
55
56 } // unnamed namespace
57
58 /**
59  * @brief The BridgeImpl class is to implement some Bridge functions.
60  */
61 class BridgeImpl : public virtual BridgeBase,
62                    public BridgeAccessible,
63                    public BridgeObject,
64                    public BridgeComponent,
65                    public BridgeCollection,
66                    public BridgeAction,
67                    public BridgeValue,
68                    public BridgeText,
69                    public BridgeEditableText,
70                    public BridgeSelection,
71                    public BridgeApplication,
72                    public BridgeHypertext,
73                    public BridgeHyperlink,
74                    public BridgeSocket
75 {
76   DBus::DBusClient                                              mAccessibilityStatusClient;
77   DBus::DBusClient                                              mRegistryClient;
78   DBus::DBusClient                                              mDirectReadingClient;
79   bool                                                          mIsScreenReaderEnabled = false;
80   bool                                                          mIsEnabled             = false;
81   std::unordered_map<int32_t, std::function<void(std::string)>> mDirectReadingCallbacks;
82   Dali::Actor                                                   mHighlightedActor;
83   std::function<void(Dali::Actor)>                              mHighlightClearAction;
84   Dali::CallbackBase*                                           mIdleCallback          = NULL;
85   Dali::Timer                                                   mInitializeTimer;
86   Dali::Timer                                                   mReadIsEnabledTimer;
87   Dali::Timer                                                   mReadScreenReaderEnabledTimer;
88   Dali::Timer                                                   mForceUpTimer;
89   std::string                                                   mPreferredBusName;
90
91 public:
92   BridgeImpl()
93   {
94   }
95
96   /**
97    * @copydoc Dali::Accessibility::Bridge::Emit()
98    */
99   Consumed Emit(KeyEventType type, unsigned int keyCode, const std::string& keyName, unsigned int timeStamp, bool isText) override
100   {
101     if(!IsUp())
102     {
103       return Consumed::NO;
104     }
105
106     unsigned int keyType = 0;
107
108     switch(type)
109     {
110       case KeyEventType::KEY_PRESSED:
111       {
112         keyType = 0;
113         break;
114       }
115       case KeyEventType::KEY_RELEASED:
116       {
117         keyType = 1;
118         break;
119       }
120       default:
121       {
122         return Consumed::NO;
123       }
124     }
125
126     return Consumed::NO;
127   }
128
129   /**
130    * @copydoc Dali::Accessibility::Bridge::Pause()
131    */
132   void Pause() override
133   {
134     if(!IsUp())
135     {
136       return;
137     }
138
139     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
140       if(!msg)
141       {
142         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
143       }
144     },
145                                                                                         true);
146   }
147
148   /**
149    * @copydoc Dali::Accessibility::Bridge::Resume()
150    */
151   void Resume() override
152   {
153     if(!IsUp())
154     {
155       return;
156     }
157
158     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("PauseResume").asyncCall([](DBus::ValueOrError<void> msg) {
159       if(!msg)
160       {
161         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
162       }
163     },
164                                                                                         false);
165   }
166
167   /**
168    * @copydoc Dali::Accessibility::Bridge::StopReading()
169    */
170   void StopReading(bool alsoNonDiscardable) override
171   {
172     if(!IsUp())
173     {
174       return;
175     }
176
177     mDirectReadingClient.method<DBus::ValueOrError<void>(bool)>("StopReading").asyncCall([](DBus::ValueOrError<void> msg) {
178       if(!msg)
179       {
180         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
181       }
182     },
183                                                                                         alsoNonDiscardable);
184   }
185
186   /**
187    * @copydoc Dali::Accessibility::Bridge::Say()
188    */
189   void Say(const std::string& text, bool discardable, std::function<void(std::string)> callback) override
190   {
191     if(!IsUp())
192     {
193       return;
194     }
195
196     mDirectReadingClient.method<DBus::ValueOrError<std::string, bool, int32_t>(std::string, bool)>("ReadCommand").asyncCall([=](DBus::ValueOrError<std::string, bool, int32_t> msg) {
197       if(!msg)
198       {
199         LOG() << "Direct reading command failed (" << msg.getError().message << ")\n";
200       }
201       else if(callback)
202       {
203         mDirectReadingCallbacks.emplace(std::get<2>(msg), callback);
204       }
205     },
206                                                                                                                            text,
207                                                                                                                            discardable);
208   }
209
210   /**
211    * @copydoc Dali::Accessibility::Bridge::ForceDown()
212    */
213   void ForceDown() override
214   {
215     if(mData)
216     {
217       if(mData->mCurrentlyHighlightedActor && mData->mHighlightActor)
218       {
219         mData->mCurrentlyHighlightedActor.Remove(mData->mHighlightActor);
220       }
221       mData->mCurrentlyHighlightedActor = {};
222       mData->mHighlightActor            = {};
223
224       mDisabledSignal.Emit();
225       UnembedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"});
226       ReleaseBusName(mPreferredBusName);
227     }
228
229     mHighlightedActor     = {};
230     mHighlightClearAction = {};
231     BridgeAccessible::ForceDown();
232     mRegistryClient       = {};
233     mDirectReadingClient  = {};
234     mDirectReadingCallbacks.clear();
235     mApplication.mChildren.clear();
236     ClearTimer();
237   }
238
239   void ClearTimer()
240   {
241     if(mInitializeTimer)
242     {
243       mInitializeTimer.Stop();
244       mInitializeTimer.Reset();
245     }
246
247     if(mReadIsEnabledTimer)
248     {
249       mReadIsEnabledTimer.Stop();
250       mReadIsEnabledTimer.Reset();
251     }
252
253     if(mReadScreenReaderEnabledTimer)
254     {
255       mReadScreenReaderEnabledTimer.Stop();
256       mReadScreenReaderEnabledTimer.Reset();
257     }
258
259     if(mForceUpTimer)
260     {
261       mForceUpTimer.Stop();
262       mForceUpTimer.Reset();
263     }
264   }
265
266   /**
267    * @copydoc Dali::Accessibility::Bridge::Terminate()
268    */
269   void Terminate() override
270   {
271     if(mData)
272     {
273       // The ~Window() after this point cannot emit DESTROY, because Bridge is not available. So emit DESTROY here.
274       for(auto windowAccessible : mApplication.mChildren)
275       {
276         BridgeObject::Emit(windowAccessible, WindowEvent::DESTROY);
277       }
278       mData->mCurrentlyHighlightedActor = {};
279       mData->mHighlightActor            = {};
280     }
281     ForceDown();
282     if((NULL != mIdleCallback) && Dali::Adaptor::IsAvailable())
283     {
284       Dali::Adaptor::Get().RemoveIdle(mIdleCallback);
285     }
286     mAccessibilityStatusClient        = {};
287     mDbusServer                       = {};
288     mConnectionPtr                    = {};
289   }
290
291   bool ForceUpTimerCallback()
292   {
293     if(ForceUp() != ForceUpResult::FAILED)
294     {
295       return false;
296     }
297     return true;
298   }
299
300   /**
301    * @copydoc Dali::Accessibility::Bridge::ForceUp()
302    */
303   ForceUpResult ForceUp() override
304   {
305     auto forceUpResult = BridgeAccessible::ForceUp();
306     if(forceUpResult == ForceUpResult::ALREADY_UP)
307     {
308       return forceUpResult;
309     }
310     else if(forceUpResult == ForceUpResult::FAILED)
311     {
312       if(!mForceUpTimer)
313       {
314         mForceUpTimer = Dali::Timer::New(RETRY_INTERVAL);
315         mForceUpTimer.TickSignal().Connect(this, &BridgeImpl::ForceUpTimerCallback);
316         mForceUpTimer.Start();
317       }
318       return forceUpResult;
319     }
320
321     BridgeObject::RegisterInterfaces();
322     BridgeAccessible::RegisterInterfaces();
323     BridgeComponent::RegisterInterfaces();
324     BridgeCollection::RegisterInterfaces();
325     BridgeAction::RegisterInterfaces();
326     BridgeValue::RegisterInterfaces();
327     BridgeText::RegisterInterfaces();
328     BridgeEditableText::RegisterInterfaces();
329     BridgeSelection::RegisterInterfaces();
330     BridgeApplication::RegisterInterfaces();
331     BridgeHypertext::RegisterInterfaces();
332     BridgeHyperlink::RegisterInterfaces();
333     BridgeSocket::RegisterInterfaces();
334
335     RegisterOnBridge(&mApplication);
336
337     mRegistryClient      = {AtspiDbusNameRegistry, AtspiDbusPathDec, Accessible::GetInterfaceName(AtspiInterface::DEVICE_EVENT_CONTROLLER), mConnectionPtr};
338     mDirectReadingClient = DBus::DBusClient{DirectReadingDBusName, DirectReadingDBusPath, DirectReadingDBusInterface, mConnectionPtr};
339
340     mDirectReadingClient.addSignal<void(int32_t, std::string)>("ReadingStateChanged", [=](int32_t id, std::string readingState) {
341       auto it = mDirectReadingCallbacks.find(id);
342       if(it != mDirectReadingCallbacks.end())
343       {
344         it->second(readingState);
345         if(readingState != "ReadingPaused" && readingState != "ReadingResumed" && readingState != "ReadingStarted")
346         {
347           mDirectReadingCallbacks.erase(it);
348         }
349       }
350     });
351
352     RequestBusName(mPreferredBusName);
353
354     auto parentAddress = EmbedSocket(mApplication.GetAddress(), {AtspiDbusNameRegistry, "root"});
355     mApplication.mParent.SetAddress(std::move(parentAddress));
356     mEnabledSignal.Emit();
357
358     return ForceUpResult::JUST_STARTED;
359   }
360
361   /**
362    * @brief Sends a signal to dbus that the window is shown.
363    *
364    * @param[in] window The window to be shown
365    * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
366    */
367   void EmitShown(Dali::Window window)
368   {
369     auto windowAccessible = mApplication.GetWindowAccessible(window);
370     if(windowAccessible)
371     {
372       windowAccessible->EmitShowing(true);
373     }
374   }
375
376   /**
377    * @brief Sends a signal to dbus that the window is hidden.
378    *
379    * @param[in] window The window to be hidden
380    * @see Accessible::EmitShowing() and BridgeObject::EmitStateChanged()
381    */
382   void EmitHidden(Dali::Window window)
383   {
384     auto windowAccessible = mApplication.GetWindowAccessible(window);
385     if(windowAccessible)
386     {
387       windowAccessible->EmitShowing(false);
388     }
389   }
390
391   /**
392    * @brief Sends a signal to dbus that the window is activated.
393    *
394    * @param[in] window The window to be activated
395    * @see BridgeObject::Emit()
396    */
397   void EmitActivate(Dali::Window window)
398   {
399     auto windowAccessible = mApplication.GetWindowAccessible(window);
400     if(windowAccessible)
401     {
402       windowAccessible->Emit(WindowEvent::ACTIVATE, 0);
403     }
404   }
405
406   /**
407    * @brief Sends a signal to dbus that the window is deactivated.
408    *
409    * @param[in] window The window to be deactivated
410    * @see BridgeObject::Emit()
411    */
412   void EmitDeactivate(Dali::Window window)
413   {
414     auto windowAccessible = mApplication.GetWindowAccessible(window);
415     if(windowAccessible)
416     {
417       windowAccessible->Emit(WindowEvent::DEACTIVATE, 0);
418     }
419   }
420
421   /**
422    * @copydoc Dali::Accessibility::Bridge::WindowShown()
423    */
424   void WindowShown(Dali::Window window) override
425   {
426     if(IsUp())
427     {
428       EmitShown(window);
429     }
430   }
431
432   /**
433    * @copydoc Dali::Accessibility::Bridge::WindowHidden()
434    */
435   void WindowHidden(Dali::Window window) override
436   {
437     if(IsUp())
438     {
439       EmitHidden(window);
440     }
441   }
442
443   /**
444    * @copydoc Dali::Accessibility::Bridge::WindowFocused()
445    */
446   void WindowFocused(Dali::Window window) override
447   {
448     if(IsUp())
449     {
450       EmitActivate(window);
451     }
452   }
453
454   /**
455    * @copydoc Dali::Accessibility::Bridge::WindowUnfocused()
456    */
457   void WindowUnfocused(Dali::Window window) override
458   {
459     if(IsUp())
460     {
461       EmitDeactivate(window);
462     }
463   }
464
465   /**
466    * @copydoc Dali::Accessibility::Bridge::SuppressScreenReader()
467    */
468   void SuppressScreenReader(bool suppress) override
469   {
470     if(mIsScreenReaderSuppressed == suppress)
471     {
472       return;
473     }
474     mIsScreenReaderSuppressed = suppress;
475     ReadScreenReaderEnabledProperty();
476   }
477
478   void SwitchBridge()
479   {
480     if((!mIsScreenReaderSuppressed && mIsScreenReaderEnabled) || mIsEnabled)
481     {
482       ForceUp();
483     }
484     else
485     {
486       ForceDown();
487     }
488   }
489
490   bool ReadIsEnabledTimerCallback()
491   {
492     ReadIsEnabledProperty();
493     return false;
494   }
495
496   void ReadIsEnabledProperty()
497   {
498     mAccessibilityStatusClient.property<bool>("IsEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
499       if(!msg)
500       {
501         DALI_LOG_ERROR("Get IsEnabled property error: %s\n", msg.getError().message.c_str());
502         if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
503         {
504           if(!mReadIsEnabledTimer)
505           {
506             mReadIsEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
507             mReadIsEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadIsEnabledTimerCallback);
508           }
509           mReadIsEnabledTimer.Start();
510         }
511         return;
512       }
513
514       if(mReadIsEnabledTimer)
515       {
516         mReadIsEnabledTimer.Stop();
517         mReadIsEnabledTimer.Reset();
518       }
519
520       mIsEnabled = std::get<0>(msg);
521       SwitchBridge();
522     });
523   }
524
525   void ListenIsEnabledProperty()
526   {
527     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("IsEnabled", [this](bool res) {
528       mIsEnabled = res;
529       SwitchBridge();
530     });
531   }
532
533   bool ReadScreenReaderEnabledTimerCallback()
534   {
535     ReadScreenReaderEnabledProperty();
536     return false;
537   }
538
539   void ReadScreenReaderEnabledProperty()
540   {
541     // can be true because of SuppressScreenReader before init
542     if (!mAccessibilityStatusClient)
543     {
544       return;
545     }
546
547     mAccessibilityStatusClient.property<bool>("ScreenReaderEnabled").asyncGet([this](DBus::ValueOrError<bool> msg) {
548       if(!msg)
549       {
550         DALI_LOG_ERROR("Get ScreenReaderEnabled property error: %s\n", msg.getError().message.c_str());
551         if(msg.getError().errorType == DBus::ErrorType::INVALID_REPLY)
552         {
553           if(!mReadScreenReaderEnabledTimer)
554           {
555             mReadScreenReaderEnabledTimer = Dali::Timer::New(RETRY_INTERVAL);
556             mReadScreenReaderEnabledTimer.TickSignal().Connect(this, &BridgeImpl::ReadScreenReaderEnabledTimerCallback);
557           }
558           mReadScreenReaderEnabledTimer.Start();
559         }
560         return;
561       }
562
563       if(mReadScreenReaderEnabledTimer)
564       {
565         mReadScreenReaderEnabledTimer.Stop();
566         mReadScreenReaderEnabledTimer.Reset();
567       }
568
569       mIsScreenReaderEnabled = std::get<0>(msg);
570       SwitchBridge();
571     });
572   }
573
574   void EmitScreenReaderEnabledSignal()
575   {
576     if (mIsScreenReaderEnabled)
577     {
578       mScreenReaderEnabledSignal.Emit();
579     }
580     else
581     {
582       mScreenReaderDisabledSignal.Emit();
583     }
584   }
585
586   void ListenScreenReaderEnabledProperty()
587   {
588     mAccessibilityStatusClient.addPropertyChangedEvent<bool>("ScreenReaderEnabled", [this](bool res) {
589       mIsScreenReaderEnabled = res;
590       EmitScreenReaderEnabledSignal();
591       SwitchBridge();
592     });
593   }
594
595   void ReadAndListenProperties()
596   {
597     ReadIsEnabledProperty();
598     ListenIsEnabledProperty();
599
600     ReadScreenReaderEnabledProperty();
601     ListenScreenReaderEnabledProperty();
602   }
603
604   bool InitializeAccessibilityStatusClient()
605   {
606     mAccessibilityStatusClient = DBus::DBusClient{A11yDbusName, A11yDbusPath, A11yDbusStatusInterface, DBus::ConnectionType::SESSION};
607
608     if (!mAccessibilityStatusClient)
609     {
610       DALI_LOG_ERROR("Accessibility Status DbusClient is not ready\n");
611       return false;
612     }
613
614     return true;
615   }
616
617   bool InitializeTimerCallback()
618   {
619     if ( InitializeAccessibilityStatusClient() )
620     {
621       ReadAndListenProperties();
622       return false;
623     }
624     return true;
625   }
626
627   bool OnIdleSignal()
628   {
629     if ( InitializeAccessibilityStatusClient() )
630     {
631       ReadAndListenProperties();
632       mIdleCallback = NULL;
633       return false;
634     }
635
636     if(!mInitializeTimer)
637     {
638       mInitializeTimer = Dali::Timer::New(RETRY_INTERVAL);
639       mInitializeTimer.TickSignal().Connect(this, &BridgeImpl::InitializeTimerCallback);
640     }
641     mInitializeTimer.Start();
642
643     mIdleCallback = NULL;
644     return false;
645   }
646
647   /**
648    * @copydoc Dali::Accessibility::Bridge::Initialize()
649    */
650   void Initialize() override
651   {
652     if ( InitializeAccessibilityStatusClient() )
653     {
654       ReadAndListenProperties();
655       return;
656     }
657
658     // Initialize failed. Try it again on Idle
659     if( Dali::Adaptor::IsAvailable() )
660     {
661       Dali::Adaptor& adaptor = Dali::Adaptor::Get();
662       if( NULL == mIdleCallback )
663       {
664         mIdleCallback = MakeCallback( this, &BridgeImpl::OnIdleSignal );
665         adaptor.AddIdle( mIdleCallback, true );
666       }
667     }
668   }
669
670   /**
671    * @copydoc Dali::Accessibility::Bridge::GetScreenReaderEnabled()
672    */
673   bool GetScreenReaderEnabled() override
674   {
675     return mIsScreenReaderEnabled;
676   }
677
678   /**
679    * @copydoc Dali::Accessibility::Bridge::IsEnabled()
680    */
681   bool IsEnabled() override
682   {
683     return mIsEnabled;
684   }
685
686   Address EmbedSocket(const Address& plug, const Address& socket) override
687   {
688     auto client = CreateSocketClient(socket);
689     auto reply  = client.method<Address(Address)>("Embed").call(plug);
690
691     if(!reply)
692     {
693       DALI_LOG_ERROR("Failed to embed socket %s: %s", socket.ToString().c_str(), reply.getError().message.c_str());
694       return {};
695     }
696
697     return std::get<0>(reply.getValues());
698   }
699
700   void EmbedAtkSocket(const Address& plug, const Address& socket) override
701   {
702     auto client = CreateSocketClient(socket);
703
704     client.method<void(std::string)>("Embedded").asyncCall([](DBus::ValueOrError<void>) {}, ATSPI_PREFIX_PATH + plug.GetPath());
705   }
706
707   void UnembedSocket(const Address& plug, const Address& socket) override
708   {
709     auto client = CreateSocketClient(socket);
710
711     client.method<void(Address)>("Unembed").asyncCall([](DBus::ValueOrError<void>) {}, plug);
712   }
713
714   void SetSocketOffset(ProxyAccessible* socket, std::int32_t x, std::int32_t y) override
715   {
716     AddCoalescableMessage(CoalescableMessages::SET_OFFSET, socket, 1.0f, [=]() {
717       auto client = CreateSocketClient(socket->GetAddress());
718
719       client.method<void(std::int32_t, std::int32_t)>("SetOffset").asyncCall([](DBus::ValueOrError<void>) {}, x, y);
720     });
721   }
722
723   void SetExtentsOffset(std::int32_t x, std::int32_t y) override
724   {
725     if(mData)
726     {
727       mData->mExtentsOffset = {x, y};
728     }
729   }
730
731   void SetPreferredBusName(std::string_view preferredBusName) override
732   {
733     if(preferredBusName == mPreferredBusName)
734     {
735       return;
736     }
737
738     std::string oldPreferredBusName = std::move(mPreferredBusName);
739     mPreferredBusName               = std::string{preferredBusName};
740
741     if(IsUp())
742     {
743       ReleaseBusName(oldPreferredBusName);
744       RequestBusName(mPreferredBusName);
745     }
746     // else: request/release will be handled by ForceUp/ForceDown, respectively
747   }
748
749 private:
750   DBus::DBusClient CreateSocketClient(const Address& socket)
751   {
752     return {socket.GetBus(), ATSPI_PREFIX_PATH + socket.GetPath(), Accessible::GetInterfaceName(AtspiInterface::SOCKET), mConnectionPtr};
753   }
754
755   void RequestBusName(const std::string& busName)
756   {
757     if(busName.empty())
758     {
759       return;
760     }
761
762     DBus::requestBusName(mConnectionPtr, busName);
763   }
764
765   void ReleaseBusName(const std::string& busName)
766   {
767     if(busName.empty())
768     {
769       return;
770     }
771
772     DBus::releaseBusName(mConnectionPtr, busName);
773   }
774 }; // BridgeImpl
775
776 namespace // unnamed namespace
777 {
778
779 bool INITIALIZED_BRIDGE = false;
780
781 /**
782  * @brief Creates BridgeImpl instance.
783  *
784  * @return The BridgeImpl instance
785  * @note This method is to check environment variable first. If ATSPI is disable using env, it returns dummy bridge instance.
786  */
787 std::shared_ptr<Bridge> CreateBridge()
788 {
789   INITIALIZED_BRIDGE = true;
790
791   try
792   {
793     /* check environment variable first */
794     const char* envAtspiDisabled = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_DISABLE_ATSPI);
795     if(envAtspiDisabled && std::atoi(envAtspiDisabled) != 0)
796     {
797       return Dali::Accessibility::DummyBridge::GetInstance();
798     }
799
800     // check if debug mode
801     if(access("/etc/debug", F_OK) != 0)
802     {
803       return Dali::Accessibility::DummyBridge::GetInstance();
804     }
805
806     return std::make_shared<BridgeImpl>();
807   }
808   catch(const std::exception&)
809   {
810     DALI_LOG_ERROR("Failed to initialize AT-SPI bridge");
811     return Dali::Accessibility::DummyBridge::GetInstance();
812   }
813 }
814
815 } // unnamed namespace
816
817 // Dali::Accessibility::Bridge class implementation
818
819 std::shared_ptr<Bridge> Bridge::GetCurrentBridge()
820 {
821   static std::shared_ptr<Bridge> bridge;
822
823   if(bridge)
824   {
825     return bridge;
826   }
827   else if(mAutoInitState == AutoInitState::ENABLED)
828   {
829     bridge = CreateBridge();
830
831     /* check environment variable for suppressing screen-reader */
832     const char* envSuppressScreenReader = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_ENV_SUPPRESS_SCREEN_READER);
833     if(envSuppressScreenReader && std::atoi(envSuppressScreenReader) != 0)
834     {
835       bridge->SuppressScreenReader(true);
836     }
837
838     return bridge;
839   }
840
841   return Dali::Accessibility::DummyBridge::GetInstance();
842 }
843
844 void Bridge::DisableAutoInit()
845 {
846   if(INITIALIZED_BRIDGE)
847   {
848     DALI_LOG_ERROR("Bridge::DisableAutoInit() called after bridge auto-initialization");
849   }
850
851   mAutoInitState = AutoInitState::DISABLED;
852 }
853
854 void Bridge::EnableAutoInit()
855 {
856   mAutoInitState = AutoInitState::ENABLED;
857
858   if(INITIALIZED_BRIDGE)
859   {
860     return;
861   }
862
863   auto rootLayer       = Dali::Stage::GetCurrent().GetRootLayer(); // A root layer of the default window.
864   auto window          = Dali::DevelWindow::Get(rootLayer);
865   auto applicationName = Dali::Internal::Adaptor::Adaptor::GetApplicationPackageName();
866
867   auto accessible = Accessibility::Accessible::Get(rootLayer);
868
869   auto bridge = Bridge::GetCurrentBridge();
870   bridge->AddTopLevelWindow(accessible);
871   bridge->SetApplicationName(applicationName);
872   bridge->Initialize();
873
874   if(window && window.IsVisible())
875   {
876     bridge->WindowShown(window);
877   }
878 }
879
880 std::string Bridge::MakeBusNameForWidget(std::string_view widgetInstanceId)
881 {
882   // The bus name should consist of dot-separated alphanumeric elements, e.g. "com.example.BusName123".
883   // Allowed characters in each element: "[A-Z][a-z][0-9]_", but no element may start with a digit.
884
885   static const char prefix[]   = "com.samsung.dali.widget_";
886   static const char underscore = '_';
887
888   std::stringstream tmp;
889
890   tmp << prefix;
891
892   for(char ch : widgetInstanceId)
893   {
894     tmp << (std::isalnum(ch) ? ch : underscore);
895   }
896
897   return tmp.str();
898 }