Hi all,

To prevent the growth of one component being buried in the long history of
bugs and comments, I gave my word to summary them regularly. However, the
easiest thing is usually the toughest thing, so it's a long while since the
last time I did that. Anyway, I think it's time to take a snapshot of
current architecture of screen locker, and try to explain why it looks so
odd. I also hope this is helpful for people unfortunately involved in this
component.

Note: maybe I can find all bugs of the points I mention, but I'm not a
historian and too much details will blur the big picture. So if I can find
the bugs, I will list them here But don't expect it too much. Another
important thing is I can only list things since I devoted to this component
at 1.2. Things before the day are apparently blank to me.

## Controllers

Current LockScreen related functions are in fact with multiple major
controllers (in files):

system/lockscreen/js

* lockscreen.js
* lockscreen_notifications.js
* lockscreen_inputpad.js
* lockscreen_state_manager.js
* lockscreen_media_playback.js
* lockscreen_charging.js

system/js

* secure_window_manager.js
* lock_screen_window_manager.js

shared/js

* lockscreen_connection_info_manager.js
* lockscreen_slide.js

I don't list FindMyDevice since it's major code are in a standalone app.

These are major controllers of each function. Besides that, there are also
lots of helpers of the component, and they all rely on these controllers.
For example, the states named as 'lockscreen_state_*' are all controlled by
'lockscreen_state_manager.js'; and 'lockscreen_notification_builder.js' is
used by 'lockscreen_notifications'. I call these 'controllers' is because
they're the listeners of events and mozSetting changes. Other files should
be simple controllees, although in technical details this is not true.

## What are they for?

The 'lockscreen.js' now still controls most of functions from unlocking to
wallpaper. However, in the recent patch I'm working on (Bug 1189641), I
prepare to turn it as a pure controllee, so the responsibility of deciding
whether the screen should be unlocked will move to the
'lockscreen_state_manager.js'.

The state manager now is mainly for UI changes, and for its own design
defects and the following requirements after the implementation make us
re-designed a new version of state machine. But for now we could only first
migrate to this mid-version state manager. Anyway, after the bug, we can at
least make sure there are only one controller of UI changes and unlocking.
And if there are any new requirements of controlling UI changes on the
screen, they should be implemented in those 'states' of the state manager.

Besides that, since the 'lockscreen.js' has been as a monolithic and
complicated controller so long, we shouldn't stockpile any new features on
it. If new release features ask us to add new "lockscreen" functions,
please create new files under the lockscreen directory and implement them
as standalone components. Component still can call functions or read info
from 'lockscreen.js', but its important to not put states or new functions
on that.

The "keyboard" to input passcode of LockScreen is in fact a DIV controlled
by 'lockscreen_inputpad.js'. And 'lock_screen_window_manager.js' control
this 'window' as we have a real keyboard frame as other apps. This is a
perquisite of enabling real keyboard app for LockScreen, which is also a
feature request since long ago. However, although we once evaluated whether
it's not too difficult to implement that, we still need to overcome the
issue of animation sync and how to implement the special UI of such
keyboard.

Some files as 'lockscreen_media_playback.js' and 'lockscreen_charging.js'
are 'widgets' on the screen. They sometimes require info from
'lockscreen.js', and things like 'lockscreen_notification.js' even need to
control it. All of these communications are done by custom events.

The 'secure_window_manager.js' controls "secure apps" like secure camera
and emergency call. They show up upon LockScreen.

At last, 'lock_screen_window_manager.js' controls the
'lock_screen_window.js', which is an attempt to treat LockScreen as an
ordinary app window. It's also one part of our effort to make LockScreen as
an app. So now, only after 'lock_screen_window_manager.js' call the window
to create itself, the bootstrapping procedure of 'lockscreen_bootstrap.js'
will start itself. These include those lazyloading files.

Also, people may remember it has been chaotic to look up if now device is
locked in System app. At 2.1+ versions, it is provided by 'Service.locked',
which depends on whether the lockscreen window is opened.

## Why?

** important: things happened, so I just list them with no blames, no
matter to me or anybody **

The most incredible thing is all of these were spawned from one single
file: the 'lockscreen.js', which grew as 2K lines between our 1.2 ~ 1.3
releases, and soon became the second largest file in System app (the #1 is
window manger). That's why I pulled the sliding feature from it to
'lockscreen_slide.js' in the shared directory. And if it looks weird today,
it's because the CallScreen (allow you to receive phone call when the
screen is locked) uses the same sliding component to provide almost the
same UI. You can call it's an experimental "widget" or "web component"
before we have real widgets and Gaia components, and it indeed worked well,
especially compare to the notification screen (mentioned later).

So as you can see, LockScreen is in fact a widget manager, which needs to
install those heterogeneous "widgets" on its panels, and also need to
control them to show up or be hidden. It's in fact also a tiny window
manager, if you consider we control the secure apps (ex: emergency call &
secure camera) and the "input pad" for the passcode by our own. All of
these were controlled by the monolithic 'lockscreen.js'. In some cases,
this omnipotent component even needed to handle very detailed events as
which key on the keypad had been pressed, since at that time we didn't pull
it as a standalone 'window' yet.

In the long term, it was impossible to maintain all things in one single
file like this while we were stockpiling new features on that again and
again. So we tried to decouple it and make the architecture more
reasonable. However, we actually faced at least four seriously issues that
were difficult to solve:

1. LockScreen was (and sadly, is) not an app. In fact, it's even not an
iframe
2. Notification screen treats LockScreen as another "screen", not a
standalone component
3. On CI server, Gip, Gij and even unit tests all tried to avoid device
locked, but they used some brutal ways to do that
4. Internal components rely on LockScreen make it must be 'initialized'
before lots of components; some of them were even initialized at the  the
script loaded moment

And other minor issues hindered us to decouple it.

The first thing was almost the root cause of all evils. Due to it's not an
app or iframe, when other components in System app need to read or write
info of LockScreen, they chose the shortest path: grab the instance and
directly manipulated it. Although this anti-pattern is greatly abandoned
now (thanks everybody), at past it caused lots of problems from reasoning
how to detect if screen is locked to 'shared' the same notification screen
on both UtilityTray and LockScreen (the issue#2). The most regrettable
thing is we once had a great chance to change that (Bug 898348) before all
the following features landed, but when we faced the dilemma of which path
we should chose, we chose to refactor the code first. After that, even I've
tried to make it as an app again, I just encountered serious CI issues so
to continue it will only delay the release features (I remember it's v2.0),
therefore I gave it up.

One serious consequence caused by this failure is we're hardly to implement
replacable screen locker, since all the things are belong to System all and
in the same iframe. So it's almost impossible to allow 3rd-party app
developers change things from its UI to behaviors.

Another minor issue is we're difficult to stop growing up System by all
*.js files and HTML content of LockScreen. Although I finally pulled the
HTML part from 'index.html' to another file, and wrote a build script to
merge them at build time, this still caused some mysterious failures only
on b2g-inbound, which blocked Alive's new bootstrapping and only be
workarounded at all.

Back to the case: although we were failed to put things in an iframe, we at
least should clarify all the internal dependencies of LockScreen and others
in System app. And apparently, the most complicated one is Notification
Screen. In the past version, namely with the much simpler versions of
LockScreen, it seemed reasonable to append one notification to two places
at the same time: one for the utility tray and another for screen locker.
That's why you now still can see there are some function depends on the
LockScreen instance. The result of this refactoring are
'lockscreen_notification.js' and 'lockscreen_notification_builder.js',
which were also for the new UX feature to ask LockScreen own different
looks & feel from Utility Tray, and the clickable notifications on the
screen. These changes were landed from v2.0 to v2.1.

And another earlier but more successful change is we implemented the
'lock_screen_window_manager' at v1.4, to make it follow the ordinary path
of launching and closing an app window, although there was no real iframe
for that at all. This also benefited us to make a new way to detect whether
LockScreen is on: if the window is opened, Service.locked should be true.
Otherwise, it's false. The similar and even earlier change is at v1.3, we
landed the 'secure_window_manager.js' to control all secure app window.
Both of them are important to keep reducing the size and items from the
original 'lockscreen.js'. Before these two managers, all of such "windows"
were controlled only by the monolithic controller.

Now let's talk about the state manger.

State manager was landed to solve Bug 1043892 at v2.0. It tried to solve
the problem that we relied on lots of variables and even CSS classes to
manage panel switching, and these were all managed in the 'lockscreen.js'.
The major issue of this was the relation of inputs and the transitioning
were unclear, since they were scattered about different state changes.
Therefore, the original design of state manager was to collect all of these
inputs (ex: events and mozSettings) in one place, and we could decide which
state (and panel) we should transfer to with clear rules and reasons.
Basically, this worked under such premises and assumptions:

1. LockScreen and the inner states had been initialized when the manager
were launched
2. At beginning It should only control the panel UI, but not how and when
unlocking the screen
3. There was only one state at one time: this should prevent data racing of
writing and reading
4. Each state could output some new data as the next input. This was for
continuing state transferring, like to draw how three states transfer from
the first to the last one

However we've found these premises and assumptions were failed one by one.
The #4 failed since the sliced the logic of describing how a continuing
process to become some vague input changes, rather than directly indicate
which is the next state it need to transfer to. And #3 failed because state
transferring usually takes time to perform animation, although this was
considered in the original design and enhanced by adding a 'transferOut'
method. The #2 failed was because more and more bugs proved that before we
revoke all the listeners of legacy LockScreen, racings couldn't be
eliminated. Finally, #1 failed because some pending readings were covered
by some "default" values, plus we now ask components to be available for
user as soon as possible, so to block UI before we get all the info is
impossible.

As a result, we spent time to design a new state machine according these
experiences and other existing designs in Gaia, which was completed at
v2.1. However, to migrate it from the existing two legacy designs was not
easy, so we experimentally migrated the clock widget on LockScreen to the
new design. And the result was not bad, so I tried to re-write the
LockScreenConnInfo widget as the second attempt in Feb 2015 (since 2.2 is
so long I mark the month).

The end of the story is simple: people told me LockScreen would be
cancelled in the fancy v3, so it seemed worthless to continue these efforts
to migrating to the new design, although I have continued the state machine
and released it as an open framework (see? I still did some substantial
things). That's the major reason why now you feel the whole code are so
strange and mixed up, and it wasn't changed so long. Anyway, after the time
of troubles as we all know, it looks like in v2.5 we don't cancel it after
all. Therefore I need to work on it again. At least, I need to fix those
covered security bugs now revealed by the new bootstrapping of System app,
and intermittent tests. My current task queues are:

1. Fix recent security bugs almost by the same cause; for the old and
current branches
2. Remove all listeners of important values from 'lockscreen.js' to the
state manager
3. Fix intermittent error of media widget Gij tests
4. Fix test failures for greening the Mulet

That's all. If there are anything incorrect, please patch it in this
thread. Happy Monday!

-- 
<http://about.me/snowmantw>Greg Weng

http://about.me/snowmantw

*Understand y f = f [ y f ] ; lose last remaining non-major friend*

*    -- Anonymous*
_______________________________________________
dev-b2g mailing list
dev-b2g@lists.mozilla.org
https://lists.mozilla.org/listinfo/dev-b2g

Reply via email to