1 /* |
|
2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 #include <QObject> |
|
19 |
|
20 #ifdef Q_OS_SYMBIAN |
|
21 #include <e32cmn.h> |
|
22 #include <w32std.h> |
|
23 #include <apgwgnam.h> |
|
24 #include <apgtask.h> |
|
25 #include <eikenv.h> |
|
26 #include <avkondomainpskeys.h> // keyguard state |
|
27 #include <hwrmpowerstatesdkpskeys.h> // battery status |
|
28 #include <UsbWatcherInternalPSKeys.h> // usb status |
|
29 #include <usbman.h> |
|
30 #include <usbpersonalityids.h> |
|
31 |
|
32 #include <QMetaEnum> |
|
33 #include <QString> |
|
34 #include <QVariant> |
|
35 #include <QTimer> |
|
36 #include <qsymbianevent.h> |
|
37 |
|
38 #endif // Q_OS_SYMBIAN |
|
39 |
|
40 #include "cxutils.h" |
|
41 #include "cxuieventlog.h" |
|
42 #include "cxuiapplication.h" |
|
43 #include "cxesettings.h" |
|
44 #include "cxuiapplicationframeworkmonitorprivate.h" |
|
45 |
|
46 namespace{ |
|
47 const int CXUI_USB_MODE_CHECK_TIMER_DELAY = 1000; // 1 second |
|
48 } |
|
49 |
|
50 #ifdef Q_OS_SYMBIAN |
|
51 namespace |
|
52 { |
|
53 inline QString convertTDesC2QString(const TDesC& aDescriptor) |
|
54 { |
|
55 #ifdef QT_NO_UNICODE |
|
56 return QString::fromLocal8Bit(aDescriptor.Ptr(), aDescriptor.Length()); |
|
57 #else |
|
58 return QString::fromUtf16(aDescriptor.Ptr(), aDescriptor.Length()); |
|
59 #endif |
|
60 } |
|
61 |
|
62 inline QString windowGroupName(RWsSession& ws, int id) |
|
63 { |
|
64 TBuf<CApaWindowGroupName::EMaxLength> name; |
|
65 ws.GetWindowGroupNameFromIdentifier(id, name); |
|
66 // Window group name contains "null" characters, |
|
67 // which are considered end-of-string if not replaced. |
|
68 for (int i=0; i < name.Length(); i++) { |
|
69 if (name[i] == NULL) { |
|
70 name[i] = ' '; |
|
71 } |
|
72 } |
|
73 return convertTDesC2QString(name); |
|
74 } |
|
75 |
|
76 inline QString bitString(int number, char fill = '0', int width = 32) |
|
77 { |
|
78 return QString::number(number, 2).rightJustified(width, fill); |
|
79 } |
|
80 |
|
81 //!@todo: Avkon UIDs not needed once device dialogs fully implemented in Orbit. |
|
82 |
|
83 // AknCapServer |
|
84 static const unsigned int UID_AKNCAPSERVER = 0x10207218; |
|
85 |
|
86 // Phone ui |
|
87 static const unsigned int UID_PHONEUI = 0x100058B3; |
|
88 // Task switcher |
|
89 static const unsigned int UID_TASKSWITCHER = 0x2002677D; |
|
90 // Dialog server |
|
91 static const unsigned int UID_DIALOGAPPSERVER = 0x20022FC5; |
|
92 |
|
93 // Log event types |
|
94 static const char *EVENT_USB = "usb"; |
|
95 static const char *EVENT_FOREGROUND = "foreground"; |
|
96 } |
|
97 #endif // Q_OS_SYMBIAN |
|
98 |
|
99 |
|
100 /*! |
|
101 * Constructor |
|
102 */ |
|
103 CxuiApplicationFrameworkMonitorPrivate::CxuiApplicationFrameworkMonitorPrivate(CxuiApplicationFrameworkMonitor *parent, |
|
104 CxuiApplication &application, |
|
105 CxeSettings& settings) |
|
106 : q(parent), |
|
107 mApplication(application), |
|
108 mSettings(settings), |
|
109 #ifdef Q_OS_SYMBIAN |
|
110 mWsSession(CCoeEnv::Static()->WsSession()), |
|
111 mWindowGroup(CCoeEnv::Static()->RootWin()), |
|
112 mWindowGroupId(mWindowGroup.Identifier()), |
|
113 mWindowGroupName(), |
|
114 mKeyLockState(EKeyguardNotActive), |
|
115 mBatteryStatus(EBatteryStatusUnknown), |
|
116 mUsbPersonality(0), |
|
117 mUsbModeCheckTimer(this), |
|
118 mEventLog(NULL), |
|
119 #endif // Q_OS_SYMBIAN |
|
120 mState(CxuiApplicationFrameworkMonitor::ForegroundOwned) |
|
121 { |
|
122 CX_DEBUG_ENTER_FUNCTION(); |
|
123 #ifdef Q_OS_SYMBIAN |
|
124 mWindowGroup.EnableFocusChangeEvents(); |
|
125 mWindowGroupName = windowGroupName(mWsSession, mWindowGroupId); |
|
126 mEventLog = new CxuiEventLog("CxuiApplicationFrameworkMonitorPrivate"); |
|
127 init(); |
|
128 mUsbModeCheckTimer.setSingleShot(true); |
|
129 mUsbModeCheckTimer.setInterval(CXUI_USB_MODE_CHECK_TIMER_DELAY); |
|
130 connect(&mUsbModeCheckTimer, SIGNAL(timeout()), |
|
131 this, SLOT(usbModeCheckTimerCallback())); |
|
132 #endif // Q_OS_SYMBIAN |
|
133 CX_DEBUG_EXIT_FUNCTION(); |
|
134 } |
|
135 |
|
136 /*! |
|
137 * Destructor |
|
138 */ |
|
139 CxuiApplicationFrameworkMonitorPrivate::~CxuiApplicationFrameworkMonitorPrivate() |
|
140 { |
|
141 CX_DEBUG_ENTER_FUNCTION(); |
|
142 #ifdef Q_OS_SYMBIAN |
|
143 delete mEventLog; |
|
144 #endif // Q_OS_SYMBIAN |
|
145 CX_DEBUG_EXIT_FUNCTION(); |
|
146 } |
|
147 |
|
148 /*! |
|
149 * Current foreground owning state of this application. |
|
150 * @return Foreground owning state. |
|
151 */ |
|
152 CxuiApplicationFrameworkMonitor::ForegroundState CxuiApplicationFrameworkMonitorPrivate::foregroundState() const |
|
153 { |
|
154 return mState; |
|
155 } |
|
156 |
|
157 /*! |
|
158 * Is USB connected in mass memory mode? |
|
159 * @return True if USB mass memory mode is active and connected, false otherwise. |
|
160 */ |
|
161 bool CxuiApplicationFrameworkMonitorPrivate::isUsbMassMemoryModeActive() const |
|
162 { |
|
163 bool active(false); |
|
164 #ifdef Q_OS_SYMBIAN |
|
165 // Mass memory mode activity can be seen from the KUsbWatcherSelectedPersonality property. |
|
166 // When USB is connected in Mass Memory Mode, we get KUsbPersonalityIdMS as personality id. |
|
167 // If USB is not connected, personality id is KUsbWatcherSelectedPersonalityNone. |
|
168 active = (mUsbPersonality == KUsbPersonalityIdMS); |
|
169 #endif // Q_OS_SYMBIAN |
|
170 return active; |
|
171 } |
|
172 |
|
173 |
|
174 |
|
175 #ifdef Q_OS_SYMBIAN |
|
176 /*! |
|
177 * Slot to handle Symbian event. |
|
178 * @param event Symbian event to be handled. (Ownership not taken.) |
|
179 */ |
|
180 void CxuiApplicationFrameworkMonitorPrivate::handleEvent(const QSymbianEvent *event) |
|
181 { |
|
182 // We receive tons of these events, so function start and end traces |
|
183 // are intentionally left out. |
|
184 |
|
185 if (event) { |
|
186 switch (event->type()) { |
|
187 case QSymbianEvent::WindowServerEvent: |
|
188 handleWindowServerEvent(event); |
|
189 break; |
|
190 } |
|
191 } |
|
192 } |
|
193 |
|
194 /*! |
|
195 * Handle changes in RProperty values of keylock state and battery status. |
|
196 * @param uid Category uid of the changed property. |
|
197 * @param key Integer key of the changed property. |
|
198 * @param value New value of the changed property. |
|
199 */ |
|
200 void CxuiApplicationFrameworkMonitorPrivate::handlePropertyEvent(long int uid, unsigned long int key, QVariant value) |
|
201 { |
|
202 CX_DEBUG_ENTER_FUNCTION(); |
|
203 |
|
204 if (uid == KPSUidAvkonDomain.iUid && key == KAknKeyguardStatus) { |
|
205 CX_DEBUG(("CxuiApplicationFrameworkMonitor - keylock status changed: %d -> %d", mKeyLockState, value.toInt())); |
|
206 |
|
207 // Check if the keylock value has actually changed |
|
208 const int newKeyLockState = value.toInt(); |
|
209 if (newKeyLockState != mKeyLockState) { |
|
210 mKeyLockState = newKeyLockState; |
|
211 // Set foreground state based on keylock status and focused application info. |
|
212 setState(getCurrentState()); |
|
213 } |
|
214 } else if (uid == KPSUidHWRMPowerState.iUid && key == KHWRMBatteryStatus ) { |
|
215 CX_DEBUG(("CxuiApplicationFrameworkMonitor - battery status changed: %d -> %d", mBatteryStatus, value.toInt() )); |
|
216 |
|
217 // If status changed, check if battery is going empty. |
|
218 const int newBatteryStatus = value.toInt(); |
|
219 if (newBatteryStatus != mBatteryStatus) { |
|
220 mBatteryStatus = newBatteryStatus; |
|
221 |
|
222 // Notify that battery is almost empty, |
|
223 // need to stop any recordings etc. |
|
224 if(mBatteryStatus == EBatteryStatusEmpty) { |
|
225 emit q->batteryEmpty(); |
|
226 } |
|
227 } |
|
228 } else if (uid == KPSUidUsbWatcher.iUid && key == KUsbWatcherSelectedPersonality) { |
|
229 CX_DEBUG(("CxuiApplicationFrameworkMonitor - usb personality changed: %d -> %d", mUsbPersonality, value.toInt())); |
|
230 |
|
231 const int newUsbPersonality(value.toInt()); |
|
232 if (newUsbPersonality != mUsbPersonality) { |
|
233 // Check before saving the new state if mass memory mode was active, |
|
234 // so we know when to emit the unactivated signal. |
|
235 const bool wasUsbMassMemoryModeActive(isUsbMassMemoryModeActive()); |
|
236 // Store new state. |
|
237 mUsbPersonality = newUsbPersonality; |
|
238 |
|
239 // Save state to log. |
|
240 if (mEventLog) { |
|
241 mEventLog->append(EVENT_USB, QString::number(mUsbPersonality)); |
|
242 } |
|
243 |
|
244 // Check if mass memory mode activity changed. |
|
245 if (wasUsbMassMemoryModeActive != isUsbMassMemoryModeActive()) { |
|
246 |
|
247 // If the massmemory mode switched from on to off, |
|
248 // the signal is emitted immediately. |
|
249 // If the switch is from off to on, we need to use a timer |
|
250 // as a workaround because plugging in the USB charger |
|
251 // sends a mass memory mode change event. |
|
252 if (wasUsbMassMemoryModeActive) { |
|
253 emit q->usbMassMemoryModeToggled(isUsbMassMemoryModeActive()); |
|
254 } else { |
|
255 // (Re)starting the timer |
|
256 mUsbModeCheckTimer.stop(); |
|
257 mUsbModeCheckTimer.start(); |
|
258 } |
|
259 |
|
260 } |
|
261 } |
|
262 } |
|
263 |
|
264 CX_DEBUG_EXIT_FUNCTION(); |
|
265 } |
|
266 |
|
267 /*! |
|
268 * Callback function for the timer used to seperate USB charging |
|
269 * from USB mass memory mode |
|
270 */ |
|
271 void CxuiApplicationFrameworkMonitorPrivate::usbModeCheckTimerCallback() |
|
272 { |
|
273 CX_DEBUG_ENTER_FUNCTION(); |
|
274 |
|
275 // if the device is still in mass memory mode after the timer has finished, |
|
276 // the device really is in massmemory mode and not plugged into the charger |
|
277 if (isUsbMassMemoryModeActive()){ |
|
278 emit q->usbMassMemoryModeToggled(isUsbMassMemoryModeActive()); |
|
279 } |
|
280 |
|
281 CX_DEBUG_EXIT_FUNCTION(); |
|
282 } |
|
283 |
|
284 /*! |
|
285 * Set initial values. |
|
286 */ |
|
287 void CxuiApplicationFrameworkMonitorPrivate::init() |
|
288 { |
|
289 CX_DEBUG_ENTER_FUNCTION(); |
|
290 |
|
291 // Connect to application (window server) events. |
|
292 connect(&mApplication, SIGNAL(symbianEvent(const QSymbianEvent *)), this, SLOT(handleEvent(const QSymbianEvent *))); |
|
293 |
|
294 QVariant value; |
|
295 |
|
296 // Get initial battery status. |
|
297 mSettings.get(KPSUidHWRMPowerState.iUid, KHWRMBatteryStatus, Cxe::PublishAndSubscribe, value); |
|
298 mBatteryStatus = value.toInt(); |
|
299 |
|
300 // Get initial keylock status. |
|
301 mSettings.get(KPSUidAvkonDomain.iUid, KAknKeyguardStatus, Cxe::PublishAndSubscribe, value); |
|
302 mKeyLockState = value.toInt(); |
|
303 |
|
304 // Get current USB personality |
|
305 mSettings.get(KPSUidUsbWatcher.iUid, KUsbWatcherSelectedPersonality, Cxe::PublishAndSubscribe, value); |
|
306 mUsbPersonality = value.toInt(); |
|
307 |
|
308 bool ok = connect(&mSettings, SIGNAL(settingValueChanged(long int, unsigned long int, QVariant)), |
|
309 this, SLOT(handlePropertyEvent(long int, unsigned long int, QVariant))); |
|
310 CX_DEBUG_ASSERT(ok); |
|
311 |
|
312 // Get foreground state. Depends on keyguard status, so that needs to be read first. |
|
313 mState = getCurrentState(); |
|
314 |
|
315 CX_DEBUG_EXIT_FUNCTION(); |
|
316 } |
|
317 |
|
318 /*! |
|
319 * Helper method to handle Symbian event that specificly is of type QSymbianEvent::WindowServerEvent. |
|
320 * @param event Symbian event to be handled. (Ownership not taken.) |
|
321 */ |
|
322 void CxuiApplicationFrameworkMonitorPrivate::handleWindowServerEvent(const QSymbianEvent *event) |
|
323 { |
|
324 // We receive tons of these events, so function start and end traces |
|
325 // are intentionally left out. |
|
326 |
|
327 const TWsEvent *wsEvent = event->windowServerEvent(); |
|
328 if (wsEvent) { |
|
329 switch (wsEvent->Type()) { |
|
330 case EEventFocusGroupChanged: { |
|
331 CX_DEBUG(("CxuiApplicationFrameworkMonitor - EEventFocusGroupChanged event")); |
|
332 setState(getCurrentState()); |
|
333 break; |
|
334 } |
|
335 case EEventFocusGained: { |
|
336 CX_DEBUG(("CxuiApplicationFrameworkMonitor - EEventFocusGained event")); |
|
337 setState(getCurrentState()); |
|
338 break; |
|
339 } |
|
340 case EEventFocusLost: { |
|
341 CX_DEBUG(("CxuiApplicationFrameworkMonitor - EEventFocusLost event")); |
|
342 setState(getCurrentState()); |
|
343 break; |
|
344 } |
|
345 case EEventWindowVisibilityChanged: { |
|
346 const TWsVisibilityChangedEvent *visibilityEvent = wsEvent->VisibilityChanged(); |
|
347 if (visibilityEvent) { |
|
348 CX_DEBUG(("CxuiApplicationFrameworkMonitor - EFullyVisible: bits[%s]", |
|
349 bitString(TWsVisibilityChangedEvent::EFullyVisible).toAscii().constData() )); |
|
350 CX_DEBUG(("CxuiApplicationFrameworkMonitor - EPartiallyVisible: bits[%s]", |
|
351 bitString(TWsVisibilityChangedEvent::EPartiallyVisible).toAscii().constData() )); |
|
352 CX_DEBUG(("CxuiApplicationFrameworkMonitor - ENotVisible: bits[%s]", |
|
353 bitString(TWsVisibilityChangedEvent::ENotVisible).toAscii().constData() )); |
|
354 CX_DEBUG(("CxuiApplicationFrameworkMonitor - event: bits[%s]", |
|
355 bitString(visibilityEvent->iFlags).toAscii().constData() )); |
|
356 } |
|
357 break; |
|
358 } |
|
359 default: |
|
360 break; |
|
361 } |
|
362 } |
|
363 } |
|
364 |
|
365 /*! |
|
366 * Set state and emit signal if state really changes. |
|
367 * @param state New state. |
|
368 */ |
|
369 void CxuiApplicationFrameworkMonitorPrivate::setState(CxuiApplicationFrameworkMonitor::ForegroundState state) |
|
370 { |
|
371 if (mState != state) { |
|
372 const CxuiApplicationFrameworkMonitor::ForegroundState original(mState); |
|
373 |
|
374 // Check if state transition is acceptable in current state. |
|
375 switch (mState) { |
|
376 case CxuiApplicationFrameworkMonitor::ForegroundOwned: |
|
377 case CxuiApplicationFrameworkMonitor::ForegroundPartiallyLost: |
|
378 // All changes accepted. |
|
379 mState = state; |
|
380 break; |
|
381 case CxuiApplicationFrameworkMonitor::ForegroundFullyLost: |
|
382 // If foreground application is changed to note when we are already |
|
383 // fully in background, cannot accept state change to "partial foreground". |
|
384 if (state != CxuiApplicationFrameworkMonitor::ForegroundPartiallyLost) { |
|
385 mState = state; |
|
386 } else { |
|
387 CX_DEBUG(("CxuiApplicationFrameworkMonitor - state change full bg -> partial bg ignored")); |
|
388 } |
|
389 } |
|
390 |
|
391 if (mState != original) { |
|
392 // Print the event log with this foreground event included. |
|
393 if (mEventLog) { |
|
394 mEventLog->append( |
|
395 EVENT_FOREGROUND, |
|
396 CxuiApplicationFrameworkMonitor::staticMetaObject.enumerator( |
|
397 CxuiApplicationFrameworkMonitor::staticMetaObject.indexOfEnumerator("ForegroundState")).valueToKey(mState)); |
|
398 mEventLog->print(); |
|
399 } |
|
400 |
|
401 // If state was changed, signal it to listeners. |
|
402 emit q->foregroundStateChanged(mState); |
|
403 } |
|
404 } |
|
405 } |
|
406 |
|
407 /*! |
|
408 * Get the current foreground state. |
|
409 * @return Current state for foreground ownership. |
|
410 */ |
|
411 CxuiApplicationFrameworkMonitor::ForegroundState CxuiApplicationFrameworkMonitorPrivate::getCurrentState() |
|
412 { |
|
413 CX_DEBUG_ENTER_FUNCTION(); |
|
414 |
|
415 CxuiApplicationFrameworkMonitor::ForegroundState state(CxuiApplicationFrameworkMonitor::ForegroundOwned); |
|
416 int focusWindowGroupId(mWsSession.GetFocusWindowGroup()); |
|
417 |
|
418 if (mKeyLockState != EKeyguardNotActive) { |
|
419 // Keylock enabled is the same as if other application is in foreground. |
|
420 CX_DEBUG(("CxuiApplicationFrameworkMonitor - key lock on")); |
|
421 state = CxuiApplicationFrameworkMonitor::ForegroundFullyLost; |
|
422 } else if (focusWindowGroupId == mWindowGroupId) { |
|
423 // If our window group has focus, we clearly are the foreground owning application. |
|
424 CX_DEBUG(("CxuiApplicationFrameworkMonitor - Foreground window group matches ours.")); |
|
425 state = CxuiApplicationFrameworkMonitor::ForegroundOwned; |
|
426 |
|
427 } else { |
|
428 // Need to check if foreground is owned by known apps. |
|
429 unsigned int uid(focusedApplicationUid()); |
|
430 |
|
431 // Check the app uid. |
|
432 switch (uid) { |
|
433 case UID_AKNCAPSERVER: |
|
434 case UID_TASKSWITCHER: |
|
435 case UID_DIALOGAPPSERVER: |
|
436 // Note or task switcher in foreground. |
|
437 state = CxuiApplicationFrameworkMonitor::ForegroundPartiallyLost; |
|
438 break; |
|
439 case UID_PHONEUI: |
|
440 default: |
|
441 // Foreground owned by other app. |
|
442 state = CxuiApplicationFrameworkMonitor::ForegroundFullyLost; |
|
443 break; |
|
444 } |
|
445 } |
|
446 |
|
447 CX_DEBUG_EXIT_FUNCTION(); |
|
448 return state; |
|
449 } |
|
450 |
|
451 /*! |
|
452 * Get the uid of application in foreground. |
|
453 * @return Application uid for the foreground application. |
|
454 */ |
|
455 unsigned int CxuiApplicationFrameworkMonitorPrivate::focusedApplicationUid() |
|
456 { |
|
457 unsigned int uid(0); |
|
458 int focusWgId(mWsSession.GetFocusWindowGroup()); |
|
459 |
|
460 TRAP_IGNORE({ |
|
461 CApaWindowGroupName* wgn = CApaWindowGroupName::NewLC(mWsSession, focusWgId); |
|
462 uid = wgn->AppUid().iUid; |
|
463 CleanupStack::PopAndDestroy(wgn); |
|
464 }); |
|
465 |
|
466 // If the window group identifier does not have the application uid set, |
|
467 // get it via thread secure id. |
|
468 if (uid == 0) { |
|
469 TApaTask task(mWsSession); |
|
470 task.SetWgId(focusWgId); |
|
471 |
|
472 RThread t; |
|
473 int err = t.Open(task.ThreadId()); |
|
474 if (err == KErrNone) { |
|
475 uid = t.SecureId().iId; |
|
476 CX_DEBUG(("CxuiApplicationFrameworkMonitor - uid resolved from thread")); |
|
477 } |
|
478 t.Close(); |
|
479 } |
|
480 |
|
481 #ifdef CX_DEBUG |
|
482 QString name(windowGroupName(mWsSession, focusWgId)); |
|
483 CX_DEBUG(("CxuiApplicationFrameworkMonitor - Own window group id: 0x%08x", mWindowGroupId)); |
|
484 CX_DEBUG(("CxuiApplicationFrameworkMonitor - Focused window group id: 0x%08x", focusWgId)); |
|
485 CX_DEBUG(("CxuiApplicationFrameworkMonitor - Own window group name: [%s]", mWindowGroupName.toAscii().constData())); |
|
486 CX_DEBUG(("CxuiApplicationFrameworkMonitor - Focused window group name: [%s]", name.toAscii().constData())); |
|
487 CX_DEBUG(("CxuiApplicationFrameworkMonitor - Focused application uid: 0x%08x", uid)); |
|
488 #endif |
|
489 |
|
490 return uid; |
|
491 } |
|
492 |
|
493 #endif // Q_OS_SYMBIAN |
|
494 |
|
495 // end of file |
|