Closed Bug 745078 Opened 12 years ago Closed 12 years ago

Get switch device states at any time

Categories

(Firefox OS Graveyard :: General, defect)

ARM
Gonk (Firefox OS)
defect
Not set
normal

Tracking

(blocking-basecamp:+)

RESOLVED FIXED
blocking-basecamp +

People

(Reporter: slee, Assigned: slee)

References

Details

Attachments

(1 file, 5 obsolete files)

When registering the switch observer, there might never be a uevent notifying a state change. We need a mechanism to know the current switch state at any time.
Blocks: 736939
Assignee: nobody → slee
Attached patch Implementation (obsolete) — Splinter Review
Attachment #628653 - Flags: review?(jones.chris.g)
Comment on attachment 628653 [details] [diff] [review]
Implementation

Hi Steven,

I may be missing something, but where do we read the current switch state in this patch?

Imagine a b2g phone boots up with a USB cable already plugged in.  How will our code be able to determine that the USB cable is plugged in?

If I'm missing something, please let me know! :)
Attachment #628653 - Flags: review?(jones.chris.g)
Attached patch Implementation V2 (obsolete) — Splinter Review
Hi Chris, 
I do some changes in this version.
1. It will always check the state of each uevent by opening the file descriptor in the first time.
2. The checking mechanism of headset is changed. I traced the code of android. They do not use switch name. They monitor the fd, |state|, in /devices/virtual/switch/h2w/. It will be more portable and could work on both SGS2 and otoro.
Attachment #628653 - Attachment is obsolete: true
Attachment #643748 - Flags: review?(jones.chris.g)
Attached patch Implementation V3 (obsolete) — Splinter Review
Sorry for that I uploaded the wrong version. This one is the right patch.
Attachment #643748 - Attachment is obsolete: true
Attachment #643748 - Flags: review?(jones.chris.g)
Attachment #643757 - Flags: review?(jones.chris.g)
Comment on attachment 643757 [details] [diff] [review]
Implementation V3

Review of attachment 643757 [details] [diff] [review]:
-----------------------------------------------------------------

I think we need a bit more functionality here. Under GB, the USB Cable was implemented as a virtual switch and you can read sys/devices/virtual/switch/usb_configuration/state to get the state. Under ICS, it was no longer implemented as a virtual switch, and you need to read: /sys/devices/virtual/android_usb/android0/state

So back when I implemented the AutoMounter, we (Chris Jones and I) decided it would be best to emulate the new path as a switch. In order to make the code work on both GB and ICS, this means we can't just read a single location.

I'd be inclined to create a SwitchHandler class, whose default behaviour does whatever is required for the virtual switch, and then provide a derived class to allow the usb stuff to call down to the base class (for GB) or to emulate (for ICS).

The handler class should have a GetState method, a CheckEvent method, and the constructor should take the virtual switch path. You would have an array of pointers to instances of handler classes, some would be instances of the base class, and some might be instances of some derived class. Then whenever SwitchEventObserver::Notify gets a new NetlinkEvent, it would just walk the table and call CheckEvent to determine if this NetlinkEvent corresponds to the switch in question. If CheckEvent returns true, then it would call GetState and dispatch the event. We could also make CheckEvent fill in the state as a parameter so that we don't have to read anything (i.e. it gets the state from the NetlinkEvent).
Comment on attachment 643757 [details] [diff] [review]
Implementation V3

Clearing based on dhylands feedback.
Attachment #643757 - Flags: review?(jones.chris.g)
Attached patch Implementation V4 (obsolete) — Splinter Review
Apply Dave's comments.
Attachment #643757 - Attachment is obsolete: true
Attachment #645240 - Flags: review?(jones.chris.g)
Attachment #645240 - Flags: review?(jones.chris.g) → review?(dhylands)
Comment on attachment 645240 [details] [diff] [review]
Implementation V4

Review of attachment 645240 [details] [diff] [review]:
-----------------------------------------------------------------

This patch needs to be rebased against mozilla-central. As given, it doesn't apply (the comment block at the beginning of the file appears to have changed).

::: hal/gonk/GonkSwitch.cpp
@@ +28,5 @@
>  
> +class SwitchHandlerBase : public RefCounted<SwitchHandlerBase>
> +{
> +public:
> +  SwitchHandlerBase(const char* path, SwitchDevice device) : 

nit: The colon should be on the next line.
nit: The arguments should be aPath and aDevice (a = argument and this comment applies to all of the arguments throughout the rest of the patch as well)

@@ +45,5 @@
> +  void GetInitialState() { 
> +    int fd = open(mStatePath, O_RDONLY);
> +    if (fd <= 0) {
> +      return;
> +    }

Use:   ScopedClose autoClose(fd);
here, then you don't have to worry about closing the file descriptor.

@@ +48,5 @@
> +      return;
> +    }
> +    
> +    char state[16];
> +    read(fd, state, sizeof(state));

Take a look at https://mxr.mozilla.org/mozilla-central/source/dom/system/gonk/AutoMounter.cpp#98 for a proper implementation.

You need to make sure that state is null terminated, and for convenience you should strip off any trailing \n, otherwise strcmp won't work.

@@ +60,3 @@
>  };
>  
> +class HeadsetSwitchHandler : public SwitchHandlerBase

So I think that this is more complicated than it needs to be.

I was envisoning that the SwitchHandler class would be able to deal with all of the virtual switch devices.

The uevent for a headset insertion looks like:

change@/devices/virtual/switch/h2w
  ACTION=change
  DEVPATH=/devices/virtual/switch/h2w
  SUBSYSTEM=switch
  SWITCH_NAME=h2w
  SWITCH_STATE=2
  SEQNUM=2581

The uevent delivered for the USB configuration under GB looks like:

change@/devices/virtual/switch/usb_configuration
  ACTION=change
  DEVPATH=/devices/virtual/switch/usb_configuration
  SUBSYSTEM=switch
  SWITCH_NAME=usb_configuration
  SWITCH_STATE=0
  SEQNUM=5038

and the uevent delivered for the USB configuration under ICS looks like:

change@/devices/virtual/android_usb/android0
  ACTION=change
  DEVPATH=/devices/virtual/android_usb/android0
  SUBSYSTEM=android_usb
  USB_STATE=CONFIGURED
  SEQNUM=1802

The only difference between the headset and the USB-GB is that the SWITCH_NAME and DEVPATH changes.

So you should be able to create a SwitchHandler class which deals with both of these cases by doing something like the following in your InitHandlers function:

sHandler.AppendElement(new SwitchHandler("h2w", SWITCH_HEADPHONES)); (use constant rather than hardcoded string)
sHandler.AppendElement(new UsbSwitchHandler());

The UsbSwitchHandler would be derived from the SwitchHandler class and would check for the android_usb subsystem, and if it doesn't match, call the base class (whose constructor would have been passed "usb_configuration")

@@ +71,5 @@
> +  ~HeadsetSwitchHandler() { }
> +  virtual bool CheckEvent(NetlinkEvent* event) { 
> +    const char* subSystem = event->getSubsystem();
> +    
> +    if (strcmp(subSystem, "switch") == 0) {

Use early return

@@ +76,5 @@
> +      const char *devPath;
> +      const char *switchState;
> +      devPath = event->findParam("DEVPATH");
> +      switchState = event->findParam("SWITCH_STATE");
> +      if (strcmp(devPath, HEADSET_SWITCH_DEV_PATH)) {

I'd be inclined to test SWITCH_NAME rather than DEV_PATH, but either should work.

@@ +81,5 @@
> +        return false;
> +      }
> +
> +      mState = ConvertState(switchState);
> +      return true;

The CheckEvent method should be genericized and folded into the base class.

@@ +104,5 @@
> +  virtual ~UsbSwitchHandlerGB() { }
> +  virtual bool CheckEvent(NetlinkEvent* event) { 
> +    const char* subSystem = event->getSubsystem();
> +    
> +    if (strcmp(subSystem, "usb_configuration") == 0) {

Use early return

@@ +116,5 @@
> +  SwitchState ConvertState(const char* state) {
> +    if (!state) {
> +      return SWITCH_STATE_UNKNOWN;
> +    }
> +    return strcmp(state, "CONFIGURED") == 0 ? SWITCH_STATE_ON : SWITCH_STATE_OFF;

This would be incorrect (as seen from the uevents delivered).

@@ +131,5 @@
> +
> +  virtual bool CheckEvent(NetlinkEvent* event) { 
> +    const char* subSystem = event->getSubsystem();
> +    
> +    if (strcmp(subSystem, "android_usb") == 0) {

Use early return

@@ +139,5 @@
> +    return false;
> +  }
> +};
> +
> +static nsTArray<RefPtr<SwitchHandlerBase>> sHandler;

nit: You need a space between the >> so that it doesn't get confused with the >> operator.

Declaring nsTArray as a static will cause the following warning on a debug build:

WARNING: XPCOM objects created/destroyed from static ctor/dtor

See bug 773792 and/or bug 773790 for further details.

I'd be inclined to make sHandler be a member variable of the SwitchEventObserver class, and move InitHandlers into the beginning of SwitchEventObservers::Init.

If the nsTArray is a member then you won't a static constructor to worry about.

@@ +183,5 @@
>      mEventInfo[aDevice].mEnable = false;
>      mEnableNum--;
>    }
>  
>    void Notify(const NetlinkEvent& event) {

nit: The opening curly brace for a function should always be on a new line (unlike the opening curly for if/while, etc).

The same applies to class declarations

class ClassName
{

It looks like this wasn't caught in the original review, so I'll live with it either way.

@@ +255,4 @@
>    }
>  };
>  
>  SwitchEventObserver* sSwitchObserver;

nit: Not part of your patch, but this should be a RefPtr rather than a bare pointer. Then you don't need the delete. Just assign sSwitchObserver to be NULL.
Attachment #645240 - Flags: review?(dhylands) → review-
(In reply to Dave Hylands [:dhylands] from comment #8)
> @@ +60,3 @@
> >  };
> >  
> > +class HeadsetSwitchHandler : public SwitchHandlerBase
> 
> So I think that this is more complicated than it needs to be.
> 
> I was envisoning that the SwitchHandler class would be able to deal with all
> of the virtual switch devices.
> 
> The uevent for a headset insertion looks like:
> 
> change@/devices/virtual/switch/h2w
>   ACTION=change
>   DEVPATH=/devices/virtual/switch/h2w
>   SUBSYSTEM=switch
>   SWITCH_NAME=h2w
>   SWITCH_STATE=2
>   SEQNUM=2581
> 
> The uevent delivered for the USB configuration under GB looks like:
> 
> change@/devices/virtual/switch/usb_configuration
>   ACTION=change
>   DEVPATH=/devices/virtual/switch/usb_configuration
>   SUBSYSTEM=switch
>   SWITCH_NAME=usb_configuration
>   SWITCH_STATE=0
>   SEQNUM=5038
> 
> and the uevent delivered for the USB configuration under ICS looks like:
> 
> change@/devices/virtual/android_usb/android0
>   ACTION=change
>   DEVPATH=/devices/virtual/android_usb/android0
>   SUBSYSTEM=android_usb
>   USB_STATE=CONFIGURED
>   SEQNUM=1802
> 
> The only difference between the headset and the USB-GB is that the
> SWITCH_NAME and DEVPATH changes.
> 
> So you should be able to create a SwitchHandler class which deals with both
> of these cases by doing something like the following in your InitHandlers
> function:
> 
> sHandler.AppendElement(new SwitchHandler("h2w", SWITCH_HEADPHONES)); (use
> constant rather than hardcoded string)
> sHandler.AppendElement(new UsbSwitchHandler());
> 
> The UsbSwitchHandler would be derived from the SwitchHandler class and would
> check for the android_usb subsystem, and if it doesn't match, call the base
> class (whose constructor would have been passed "usb_configuration")
> 
But the uevent on otoro is,
  change@/devices/virtual/switch/h2w
    ACTION=change
    DEVPATH=/devices/virtual/switch/h2w
    SUBSYSTEM=switch
    SWITCH_NAME=Headset
    SWITCH_STATE=1
    SEQNUM=1602
If we unplug headset, the SWITCH_NAME will be "No Device". I also trace the code
in Android and found that they use DEVPATH to check whether the uevent is headset.

For usb under GB and headset, we could put them in the same class, but the function
itself may be complicated. 

> @@ +81,5 @@
> > +        return false;
> > +      }
> > +
> > +      mState = ConvertState(switchState);
> > +      return true;
> 
> The CheckEvent method should be genericized and folded into the base class.
I will put this function in the base class.

> @@ +139,5 @@
> > +    return false;
> > +  }
> > +};
> > +
> > +static nsTArray<RefPtr<SwitchHandlerBase>> sHandler;
> 
> nit: You need a space between the >> so that it doesn't get confused with
> the >> operator.
> 
> Declaring nsTArray as a static will cause the following warning on a debug
> build:
> 
> WARNING: XPCOM objects created/destroyed from static ctor/dtor
> 
> See bug 773792 and/or bug 773790 for further details.
> 
> I'd be inclined to make sHandler be a member variable of the
> SwitchEventObserver class, and move InitHandlers into the beginning of
> SwitchEventObservers::Init.
> 
> If the nsTArray is a member then you won't a static constructor to worry
> about.
OK~ Thanks for your suggestions.
(In reply to StevenLee from comment #9)
> (In reply to Dave Hylands [:dhylands] from comment #8)
> > @@ +60,3 @@
> > >  };
> > >  
> > > +class HeadsetSwitchHandler : public SwitchHandlerBase
> > 
> > So I think that this is more complicated than it needs to be.
> > 
> > I was envisoning that the SwitchHandler class would be able to deal with all
> > of the virtual switch devices.
> > 
> > The uevent for a headset insertion looks like:
> > 
> > change@/devices/virtual/switch/h2w
> >   ACTION=change
> >   DEVPATH=/devices/virtual/switch/h2w
> >   SUBSYSTEM=switch
> >   SWITCH_NAME=h2w
> >   SWITCH_STATE=2
> >   SEQNUM=2581
...snip...
> > The UsbSwitchHandler would be derived from the SwitchHandler class and would
> > check for the android_usb subsystem, and if it doesn't match, call the base
> > class (whose constructor would have been passed "usb_configuration")
> > 
> But the uevent on otoro is,
>   change@/devices/virtual/switch/h2w
>     ACTION=change
>     DEVPATH=/devices/virtual/switch/h2w
>     SUBSYSTEM=switch
>     SWITCH_NAME=Headset
>     SWITCH_STATE=1
>     SEQNUM=1602
> If we unplug headset, the SWITCH_NAME will be "No Device". I also trace the
> code
> in Android and found that they use DEVPATH to check whether the uevent is
> headset.

That's a pretty clear argument for using DEVPATH then :) (and probably worth mentioning in the code as a comment)

> For usb under GB and headset, we could put them in the same class, but the
> function
> itself may be complicated. 

Given the information that you've presented, what I'd like to see is that the DEVPATH and device be passed as arguments to the constructor. Then the code for headset and USB GB should be identical. They'll both check that subssystem == switch, that DEVPATH matches what was passed in the constructor. and will use SWITCH_STATE to determine the value.

The class that you wrote called HeadsetHandler is almost exactly what I was looking for as the base class. The only thing its missing is that it uses a hardcoded HEADSET_SWITCH_DEV_PATH, and I'd like it to use a member variable which was initialized from the constructor.

The state file, for reading the initial state can be constructed by using:

/sys + DEVPATH + /state

So you'd construct SwitchHandler(HEADSET_SWITCH_DEV_PATH, SWITCH_HEADPHONES) to deal with the headset and if we were just using GB, then you'd construct SwitchHandler(GB_USB_SWITCH_DEV_PATH, SWITCH_USB) (where GB_USB_SWITCH_DEV_PATH is "/devices/virtual/switch/usb_configuration").

And for consistency, since we're using SWITCH_USB and SWITCH_HEADSET, can we use:
SWITCH_USB_DEVPATH_GB and SWITCH_HEADSET_DEVPATH?

For the USB-ICS case, we'd use SWITCH_USB_DEVPATH_ICS
Hi Dave, 
I found that "USB_STATE" on ICS has 3 possible values, 
DISCONNECTED, CONNECTED, and CONFIGURED. If the device is connected by cannot be configured, user cannot use it. So 
could I ignore CONNECTED status?
Yeah - the code in the VolumeManager treats CONFIGURED as 1 (on), and anything else as 0 (off).
Attached patch Implementation V5 (obsolete) — Splinter Review
Attachment #645240 - Attachment is obsolete: true
Attachment #647111 - Flags: review?(dhylands)
Comment on attachment 647111 [details] [diff] [review]
Implementation V5

Review of attachment 647111 [details] [diff] [review]:
-----------------------------------------------------------------

Excellent. 

I think that the architecture is right now. Most of my remaining comments are nits, and some of them aren't even really part of your patch, but I think that they should probably be cleaned up.

So I think you just need a bit of cleanup and you'll be good to go.

::: hal/gonk/GonkSwitch.cpp
@@ +102,5 @@
> +
> +  void GetInitialState()
> +  {
> +    char statePath[128];
> +    if (snprintf(statePath, sizeof(statePath), "/sys%s/state", mDevPath) <= 0) {

You should use an nsPrintfCString here and then you don't need to worry about the buffer size.
https://mxr.mozilla.org/mozilla-central/source/xpcom/string/public/nsPrintfCString.h

@@ +169,5 @@
> +  SwitchState ConvertState(const char* aState)
> +  {
> +    if (!aState) {
> +      MOZ_ASSERT(aState);
> +      return SWITCH_STATE_UNKNOWN;

Why the difference between SwitchHandlerUsbIcs::ConvertState and SwitchHandler::ConvertState?

Shouldn't you just MOZ_ASSERT(aState) and not bother with returning SWITCH_STATE_UNKNOWN? It's a path that should never happen. If there is a specific reason for the difference, then it should be commented.

@@ +174,2 @@
>      }
> +    return strcmp(aState, "CONFIGURED") == 0 ? SWITCH_STATE_OFF : SWITCH_STATE_ON;

This is backwards. aState equal to "CONFIGURED" should correspond to SWITCH_STATE_ON

@@ +178,5 @@
>  
>  class SwitchEventRunnable : public nsRunnable
>  {
>  public:
>    SwitchEventRunnable(SwitchEvent& event) : mEvent(event) {}

nit: Not part of your patch, but this should be called aEvent rather than event.

@@ +204,3 @@
>  
>    int GetEnableCount() {
>      return mEnableNum;

nit: Not part of your patch. This should probably be called mEnableCount or mNumEnabledSwitches.

@@ +215,5 @@
>      mEventInfo[aDevice].mEnable = false;
>      mEnableNum--;
>    }
>  
>    void Notify(const NetlinkEvent& event) {

nit: This should be called aEvent as well.

@@ +249,2 @@
>  private:
>    class EventInfo {

nit: Also not part of your path, but the open curly for a class or function should go on the next line. I noticed this on alot of the inline functions.

@@ +256,2 @@
>      SwitchEvent mEvent;
>      bool mEnable;

nit: Also not part of your patch, but I'd call this mEnabled. Then the if statements read properly:

    if (info.mEnabled)

@@ +265,5 @@
> +    mHandler.AppendElement(new SwitchHandler(SWITCH_HEADSET_DEVPATH, SWITCH_HEADPHONES));
> +    mHandler.AppendElement(new SwitchHandler(SWITCH_USB_DEVPATH_GB, SWITCH_USB));
> +    mHandler.AppendElement(new SwitchHandlerUsbIcs(SWITCH_USB_DEVPATH_ICS));
> +    
> +    for (size_t i = 0; i < mHandler.Length(); i++) {

nit: The nsTArray class has some subtypes called index_type and size_type that should be used for declaring indicies and sizes.

So if you created a typedef

typedef nsTArray<RefPtr<SwitchHandler> > SwitchHandlerArray;

Then you can do:

for (SwitchHandlerArray::index_type i = 0; ...

I also prefer to create my own variable for the number of elements rather than calling Length() each time (in this case, calling Length() doesn't incur any real penalty, but in general it's a bad idea to be calling a function to get the length for each iteration through the array).

So I tend to do something like:

SwitchHandlerArray::size_type numHandlers = mHandler.Length();
SwitchHandlerArray::index_type handlerIndex;
for (handlerIndex = 0; handlerIndex < numHandlers; handlerIndex++)

@@ +278,4 @@
>      }
>    }
>  
> +  SwitchDevice GetEventInfo(const NetlinkEvent& event, SwitchState& state) {

nit: aEvent and aState
Attachment #647111 - Flags: review?(dhylands) → review-
Attachment #647111 - Attachment is obsolete: true
Attachment #647468 - Flags: review?(dhylands)
Comment on attachment 647468 [details] [diff] [review]
Implementation V6

Review of attachment 647468 [details] [diff] [review]:
-----------------------------------------------------------------

Looks good. I was able to hook it up to the AutoMounter and verified that USB ICS stuff works as expected.

::: hal/gonk/GonkSwitch.cpp
@@ +93,5 @@
> +
> +  SwitchState GetState()
> +  { 
> +    return mState;
> +  }

So just for future reference, it's fine for these to look like:

SwitchState GetState() { return mState; }

or the way that you've shown it.

SwitchState GetState() { 
  return mState;
}

is the form I was looking to get fixed.

You don't need to change this back, I'm just pointing it out.
Attachment #647468 - Flags: review?(dhylands) → review+
Hi Dave, 
Got it. 
Thanks~
Keywords: checkin-needed
I've already submitted a try run:
https://tbpl.mozilla.org/?tree=Try&rev=bb701bbf46ae

I'll land it tomorrow (once the try run has completed).
Keywords: checkin-needed
https://hg.mozilla.org/mozilla-central/rev/c3606b8bbe01
Status: NEW → RESOLVED
Closed: 12 years ago
Resolution: --- → FIXED
blocking-basecamp: --- → +
Keywords: qawanted
Keywords: qawanted
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: