Mobile Development: manage the event db, what wakes up your device

The event db (it is my naming) holds all events and actions that can be invoked to launch an app or fire some events.

NotificationList

The tool shows all known notifications on a WM device. You can browse the event db and examine the defined events. Additionally the tool shows power change notifications.

noti_01   noti_02   noti_03

In the mid window above you can see there is a timed event that will occur at 0:00 and start \windows\calupd.exe. This will wake your device all night and update the calendar entries for re-occurring schedules etc.

The right window shows the power notifications on a suspend/resume cycle.

Using the options menu you can save a list of the defined notification events.


Example file:

App Args Event Start End Type
sqmevent.exe ‘AppRunAtTime’ None 14052013 07:33 01010001 00:00 Time
\windows\enrollnot.exe ‘AppRunAtTime’ None 14052013 12:00 01010001 00:00 Time
\Windows\calupd.exe ‘AppRunAtTime’ None 15052013 00:00 01010001 00:00 Time
CALNOT.EXE ‘AppRunAtTime’ None 20052013 00:00 01010001 00:00 Time
ceipui.exe ‘AppRunAtTime’ None 27052013 23:48 01010001 00:00 Time
clocknot.exe ‘AppRunAtTime’ None 03112013 02:00 01010001 00:00 Time
\\.\Notifications\NamedEvents\MSPPDUGateway_TimeChangeEvent TimeChange 01010001 00:00 01010001 00:00 Event
\\.\Notifications\NamedEvents\MSPPDUGateway_TimeChangeEvent TimeChange 01010001 00:00 01010001 00:00 Event
\\.\Notifications\NamedEvents\MSPPDUGateway_TimeChangeEvent TimeChange 01010001 00:00 01010001 00:00 Event
\\.\Notifications\NamedEvents\MSPPDUGateway_TimeChangeEvent TimeChange 01010001 00:00 01010001 00:00 Event
\\.\Notifications\NamedEvents\MSPPDUGateway_TimeChangeEvent TimeChange 01010001 00:00 01010001 00:00 Event
\\.\Notifications\NamedEvents\MSPPDUGateway_TimeChangeEvent TimeChange 01010001 00:00 01010001 00:00 Event
\\.\Notifications\NamedEvents\MSPPDUGateway_TimeChangeEvent TimeChange 01010001 00:00 01010001 00:00 Event
\\.\Notifications\NamedEvents\MSPPDUGateway_TimeChangeEvent TimeChange 01010001 00:00 01010001 00:00 Event
\\.\Notifications\NamedEvents\MSPPDUGateway_TimeChangeEvent TimeChange 01010001 00:00 01010001 00:00 Event
\\.\Notifications\NamedEvents\MSPPDUGateway_TimeChangeEvent TimeChange 01010001 00:00 01010001 00:00 Event
\\.\Notifications\NamedEvents\MSPPDUGateway_TimeChangeEvent TimeChange 01010001 00:00 01010001 00:00 Event
\\.\Notifications\NamedEvents\MSPPDUGateway_TimeChangeEvent TimeChange 01010001 00:00 01010001 00:00 Event
\\.\Notifications\NamedEvents\WLMScheduler_TimeChangeEvent TimeZoneChange 01010001 00:00 01010001 00:00 Event
\\.\Notifications\NamedEvents\WLMScheduler_TimeChangeEvent TimeChange 01010001 00:00 01010001 00:00 Event
calnot.exe ‘AppRunAfterRestore’ RestoreEnd 01010001 00:00 01010001 00:00 Event
calnot.exe ‘AppRunAfterTimeChange’ TimeChange 01010001 00:00 01010001 00:00 Event
ceipui.exe ‘AppRunAfterTzChange’ TimeZoneChange 01010001 00:00 01010001 00:00 Event
ceipui.exe ‘AppRunAfterTimeChange’ TimeChange 01010001 00:00 01010001 00:00 Event
clocknot.exe ‘AppRunAfterTimeChange’ TimeChange 01010001 00:00 01010001 00:00 Event
clocknot.exe ‘AppRunAfterRestore’ RestoreEnd 01010001 00:00 01010001 00:00 Event
repllog.exe ‘AppRunAfterRndisFnDetected’ RndisFNDetected 01010001 00:00 01010001 00:00 Event
repllog.exe ‘AppRunAfterTimeChange’ TimeChange 01010001 00:00 01010001 00:00 Event
repllog.exe ‘AppRunAtRs232Detect’ RS232Detected 01010001 00:00 01010001 00:00 Event
Tskschedule.exe ‘-Time’ TimeZoneChange 01010001 00:00 01010001 00:00 Event
Tskschedule.exe ‘-Time’ TimeChange 01010001 00:00 01010001 00:00 Event

You can see the applications or named events that are issued. Then there are arguments that are provided to the application. You can also see the event type.

There are different notification types:

        public enum CeNotificationType
        {
            CNT_EVENT = 1,          //@flag CNT_EVENT  | System event notification
            CNT_TIME,               //@flag CNT_TIME   | Time-based notification
            CNT_PERIOD,             //@flag CNT_PERIOD | Time-based notification is active for
                                    // time period between stStart and stEnd
            CNT_CLASSICTIME         //@flag CNT_CLASSICTIME | equivalent to using (obsolete)
            // CeSetUserNotification function - standard command line is
            // supplied. lpszArguments must be NULL
        }

CeSetUserNotification is spared here. This structure is used to display user notification bubbles on the screen.

The notification API enables you to setup notification for different events:

        public enum CeNotificationEvent
        {
            NOTIFICATION_EVENT_NONE,
            NOTIFICATION_EVENT_TIME_CHANGE,
            NOTIFICATION_EVENT_SYNC_END,
            NOTIFICATION_EVENT_ON_AC_POWER,
            NOTIFICATION_EVENT_OFF_AC_POWER,
            NOTIFICATION_EVENT_NET_CONNECT,
            NOTIFICATION_EVENT_NET_DISCONNECT,
            NOTIFICATION_EVENT_DEVICE_CHANGE,
            NOTIFICATION_EVENT_IR_DISCOVERED,
            NOTIFICATION_EVENT_RS232_DETECTED,
            NOTIFICATION_EVENT_RESTORE_END,
            NOTIFICATION_EVENT_WAKEUP,
            NOTIFICATION_EVENT_TZ_CHANGE,
            NOTIFICATION_EVENT_MACHINE_NAME_CHANGE,
            NOTIFICATION_EVENT_RNDIS_FN_DETECTED,
            NOTIFICATION_EVENT_INTERNET_PROXY_CHANGE, 
            NOTIFICATION_EVENT_LAST = NOTIFICATION_EVENT_INTERNET_PROXY_CHANGE
        };

Please note that not all devices support all these types. As the names are more less speaking names I do not explain these event types any further.

If you want to add your own periodic, timed, application, you should look at my Tasker application.

PowerNotifications

Although not directly related to the above events db, it may also good to know how you can subscribe to power notifications. The sample uses the MS PowerNotifications Message queue. When registering for the power messages, you can define a filter to get only power notification messages you are interested in.

        const uint POWER_NOTIFY_ALL = 0xFFFFFFFF;
        const uint PBT_TRANSITION          =  0x00000001;  // broadcast specifying system power state transition        
        const uint PBT_RESUME = 0x00000002;  // broadcast notifying a resume, specifies previous state
        const uint PBT_POWERSTATUSCHANGE = 0x00000004;  // power supply switched to/from AC/DC
        const uint PBT_POWERINFOCHANGE = 0x00000008;

Every message comes with a data structure:

        [StructLayout(LayoutKind.Sequential)]
        struct POWER_BROADCAST
        {
            UInt32 dwMsg;
            UInt32 dwFlags;
            UInt32 dwLength;
            string sSystemPowerState; //WCHAR SystemPowerState[1];
        }

And we have a lot of flags to lookup:
        //
        // System Power (Source/State/Option) Flags
        //
        [Flags]
        enum PowerState:uint
        {
            // upper bytes: common power state bits
            //#define POWER_STATE(f)           ((f) &  0xFFFF0000);        // power state mask
            POWER_STATE_NA              = 0x00,
            POWER_STATE_ON              = 0x00010000,        // on state
            POWER_STATE_OFF             = 0x00020000,        // no power, full off
            POWER_STATE_CRITICAL        = 0x00040000,        // critical off
            POWER_STATE_BOOT            = 0x00080000,        // boot state
            POWER_STATE_IDLE            = 0x00100000,        // idle state
            POWER_STATE_SUSPEND         = 0x00200000,        // suspend state
            POWER_STATE_UNATTENDED      = 0x00400000,        // Unattended state.
            POWER_STATE_RESET           = 0x00800000,        // reset state
            POWER_STATE_USERIDLE        = 0x01000000,        // user idle state
            POWER_STATE_BACKLIGHTON     = 0x02000000,        // device scree backlight on
            POWER_STATE_PASSWORD        = 0x10000000,        // This state is password protected.
        }
        [Flags]
        enum PowerEventType
        {
            PBT_TRANSITION = 0x00000001,
            PBT_RESUME = 0x00000002,
            PBT_POWERSTATUSCHANGE = 0x00000004,
            PBT_POWERINFOCHANGE = 0x00000008,
        }

        [Flags]
        enum PowerState1
        {
            POWER_STATE_ON = (0x00010000),
            POWER_STATE_OFF = (0x00020000),

            POWER_STATE_CRITICAL = (0x00040000),
            POWER_STATE_BOOT = (0x00080000),
            POWER_STATE_IDLE = (0x00100000),
            POWER_STATE_SUSPEND = (0x00200000),
            POWER_STATE_RESET = (0x00800000),
        }

And, of course, we need some P/Invoke:

        #region DllImports
        [DllImport("coredll.dll")]
        private static extern IntPtr RequestPowerNotifications(IntPtr hMsgQ, uint Flags);
        [DllImport("coredll.dll")]
        private static extern uint WaitForSingleObject(IntPtr hHandle, int wait);
        [DllImport("coredll.dll")]
        private static extern IntPtr CreateMsgQueue(string name, ref MsgQOptions options);
        [DllImport("coredll.dll")]
        private static extern bool ReadMsgQueue(IntPtr hMsgQ, byte[] lpBuffer, uint cbBufSize, ref uint lpNumRead, int dwTimeout, ref uint pdwFlags);
        #endregion

The main work is done inside a thread function:

        private void DoWork()
        {
            byte[] buf = new byte[10000];
            uint nRead = 0, flags = 0, res = 0;

            System.Diagnostics.Debug.WriteLine("starting loop");
            try
            {
                while (!done)
                {
                    res = WaitForSingleObject(ptr, 2500);
                    if (res == 0)
                    {
                        ReadMsgQueue(ptr, buf, (uint)buf.Length, ref nRead, -1, ref flags);
                        //System.Diagnostics.Debug.WriteLine("message: " + ConvertByteArray(buf, 0) + " flag: " + ConvertByteArray(buf, 4));
                        uint flag = ConvertByteArray(buf, 4);
                        string msg = "";
                        msg += ((PowerState)flag).ToString();
                        if (msg=="")
                            msg = "Unknown Flag: " + flag.ToString();
                        if (msg=="0")
                            msg = "POWER_STATE_NA";

                        if (msg != "")
                        {
                            if(OnMsg!=null)
                                OnMsg(this, new PwrEventArgs(msg, flag));
                            System.Diagnostics.Debug.WriteLine(msg);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                if (!done)
                {
                    System.Diagnostics.Debug.WriteLine("Got exception: " + ex);
                }
            }
            System.Diagnostics.Debug.WriteLine("loop ended");
        }

The sample code implements some event and delegates that you can subscribe to to get power notification messages.

Source code available via NotificationsList

Leave a Reply