Common mis-use and abuse of Symbian OS

Login to reply to this topic.
Tue, 2006-03-28 17:56
Joined: 2006-03-04
Forum posts: 194
I'm going to start my own thread and post responses to it over time of common mis-uses / mis-understandings / bad advice that I see on here regarding the use of Symbian OS.

Number1:
-------------

TRequestStatus status;
status = KRequestPending;
AsynchronousFunction(status);

You do not and should not be setting the value of status to KRequestPending, that is not your responsibility, it is the responsibility of AsynchronousFunction().

However in practice its not very serious and 99% of the time shouldn't cause any problems - but if you didn't write AsynchronousFunction() and don't have the code how do you know its not got the following as its first line for example?

__ASSERT_ALWAYS(aStatus != KRequestPending, Panic(shouldn't be already set to KRequestPending)) 


Tue, 2006-03-28 17:57
Joined: 2006-03-04
Forum posts: 194
Re: Common mis-use (abuse) of Symbian
 
Number 2:
------------

People putting calls to User::WaitForRequest(iStatus) in an active object.

DONT.




Tue, 2006-03-28 17:58
Joined: 2006-03-04
Forum posts: 194
Re: Common mis-use (abuse) of Symbian
Number 3
-------------

People declaring an additional TRequestStatus in an active object then using that instead of iRequestStatus in combination** with SetActive().


Don't declare an additional TRequestStatus in an active object, CActive already has one, you should use that.



[** Actually it is possible to do this sort of thing in an active object:
TRequestStatus status
AsynchronousCall(status);
User::WaitForRequest(status);
ADifferentAsynchronousCall(iStatus);
SetActive()

Assuming AsynchronousCall() gets the chance to execute that is, however if you are doing this the chance is you don't really know what you are doing and are just trying stuff at random to try and get your program to work]
Tue, 2006-03-28 18:41
Joined: 2006-03-04
Forum posts: 194
Re: RTFM - Common mis-use and abuse of Symbian
Number 4
-------------

How to return descriptors from functions.

First off, a mis-use: some of the descriptor classes are abstract, stop trying to declare a TDesC as a variable.

Now, how to use them in combination with functions:

If you are returning data from a function as a descriptor then:
- if its an entire descriptor and which will still exist while the caller uses it you should return const TDesC& or TDes& (if the caller is allowed to modify it).

- if you are returning data which is not an entire descriptor (i.e. it lives in a buffer or is part of another descriptor), which will still exist while the caller uses it, return TPtrC or a TPtr (if the caller is allowed to modify it).

- if the data has to be constructed, but has a known (not too big) maximum length, return a TBufC<> or TBuf<>

-if the data has to be constructed, and could have variable or long length, there are two basic options:

a) allocate an HBufC for the data and return HBufC*, the caller is then responsible for deletion of the object. Note: this requires allocation and free, which have a run time overhead.

b) have the caller pass a TDes& as a parameter and write back to that descriptor, ensuring that what happens if this is not "large enough" is well defined. This is expecially good for when the caller should specify how much data it wants returned--the descriptor's MaxLength() provides this.

Tue, 2006-03-28 19:30
Joined: 2006-03-04
Forum posts: 194
Re: RTFM - Common mis-use and abuse of Symbian
Number 5
-------------
Stop using a granularity of 1 for arrays and buffers.

CBufBase* buffer = CBufFlat::NewL(1);
buffer->InsertL(something big lots of times);

Everybody seems to do this but its a horrendous thing to do.


Here's what the SDK says about it:

"The granularity of buffer expansion. Additional space, when required, is always allocated in multiples of this number. Note: although a value of zero is permitted by this interface, it has no meaning, and risks raising panics later during execution. We suggest that you pass a positive value".

Did you see the bit about "Additional space, when required, is always allocated in multiples of this number"?
Mon, 2006-04-10 17:39
Joined: 2006-03-04
Forum posts: 194
Re: Common mis-use and abuse of Symbian OS (RTFM)
Number 6
-------------

Back to active objects again, specifically cancelling. Here's some examples of what not to do that is seen frequently.

a) Calling DoCancel() directly - don't try to do this. Just call Cancel(), DoCancel() gets called automatically via Cancel()

b) Not calling Cancel() - if you've got an outstanding asynchronous request* and your active object is being deleted for example then you must call Cancel().


c) if (IsActive())
      Cancel()

There's no need to have this check that the object is active before calling Cancel(). It is safe to call Cancel() even if the active object isn't active, the active object base class code makes this check so there's no need for you to also.

d) Finally, you'd think this would be obvious but apparently not seeing as quite a few people do it, or rather don't do it:
Calling Cancel() but not adding any code to the body of the overridden DoCancel() to actually cancel anything.
If your DoCancel() function is empty how can any outstanding asynchronous request get canceled?


* However don't forget, some active objects are self scheduling.
Thu, 2006-04-20 22:21
Joined: 2006-03-04
Forum posts: 194
Re: Common mis-use and abuse of Symbian OS
Number 7
------------

Not a mis-use of Symbian for number 7. Instead a  history lesson for the people that are always complaining that Symbian OS doesn't support some standard C++ features etc.

Q:When do you think Symbian OS was written?
A: It started in 1994.

Q: Have you ever used the compilers from 1994?
A: Probably not. See if you can find MSCVC++ 2.0 or an early Boreland compiler for example, now try and write a program using exceptions or templates or a few other features that you take for granted and expect your compiler to be capable of dealing with and prepare to be shocked (the Gnu compiler also didn't support some C++ features, however one thing I never understood was why Symbian didn't request that they be added, after all they were requesting other features and indeed paying for their development I believe, mind you if the PC compilers didn't support those features in the first place then as thats where all the development gets done it didn't matter if Gnu did or didn't).


Two main reasons why Symbian OS doesn't support some standard features:
1) There was no choice for certain features, the compilers simply didn't support those features or did but incorrectly.
2) Efficiency and size. ER1 had to fit into 4Mg I think it was. That's tiny. You try and write an entire operating system, plus a whole suite of programs (agenda, spreadsheet, contacts, drawing, word processor etc. .....) along with a UI and get it to fit into 4Mg.


And here's a nice soundbite timeline I saw somebody post somewhere regarding Symbian OS and C++ standardization:

1) Writing of Epoc32 ER1 begins: September 1994
2) Psion Series 5 PDA ships containing the first release of the Symbian OS in a product: June 1997
3) ISO/IEC publication of the C++ standard: September 1998
Fri, 2006-04-21 17:55
Joined: 2006-03-04
Forum posts: 194
Re: Common mis-use and abuse of Symbian OS
Number 8
-------------

"My program hangs when I call CActiveScheduler::Start() and never reaches the next line  Huh"

A lot of people who have never developed for a real-time environment have trouble initially getting their heads round the concepts involved. This is especially the case if they've previously only written simple programs where there is a sequence of steps and the code starts at the first step, goes through them all, gets to the last one then the program finishes. So they've previously only written a simple program, try to program on SYmbian and find out they have to use active objects and then it starts to fall apart (it not just Symbian, they'd have similar problems using threads or similar on any OS if they've never used them before).

If you're using an active object then you have to have an active scheduler installed. If you're writing an application i.e. a user interface then there will already be one installed which has been started and you shouldn't be messing around with them. If you're not writing a UI application (i.e. you're writing an exe for example) then you have to install and start an active scheduler yourself. This invaraibly starts to cause problems with newbies, consider this abbreviated pseudo code snippet and simpilfied explanation to consider why:



main()
{
CActiveScheduler* scheduler = new CActiveScheduler;
CActiveScheduler::Install(scheduler);
CActiveScheduler::Start();
CMyActive* active = CMyActive::NewL();
active->DoStuff();
}

They code this and then step through it in the debugger but after they get to CActiveScheduler::Start() nothing happens, it doesn't reach the next line and they don't understand why their program has hung.

The reason is because the call to CActiveScheduler::Start() effectivey shifts the paradigm of your program, you've now stopped working with baby programs where there is a simple sequence of steps of execution and its easy to follow, you've now jumped into the realm of real-time programming, where your code flow is now dictated by events external to your program so you have to change your way of thinking and make sure you properly understand how it works.


The above code won't work because once a call to CActiveScheduler::Start() is made then imagine that an infinite loop is being executed inside that function (actually it effectively is). Suppose this function were implemented as this:

CActiveScheduler::Start()
{
   while (true)
   {
   }
}

Now can you see why the main() code will hang and the next line never executed after the call to CActiveScheudler::Start()?


Of course its not as simple as that, but now that you can see why it would hang then we'll move onto showing a bit more of the code.


The code for the active schuduler does something like this (note this isn't actually exactly what happens but for the purposes of explanation assume that it is)

CActiveScheduler::Start()
{
    while (true)
    {
    if (something has happened that an active object is interested in == TRUE)
        YourActiveObject->RunL();
    }
}


The "something has happened that an active object is interested in" is set by another piece of code 'signalling' a TRequestStatus that you passed to it in your active object code.

So lets consider if in your active object you wanted to open a socket and the socket API is SomeSocketClass::Open(TRequestStatus& aStatus). Then
when the socket code has opened the socket this will result in the TRequestStatus being signalled which results in "something has happened that an active object is interested in"
becomeing set to TRUE and thus the active object's RunL() gets called.

Now lets assume the call to open the socket is in the following function:

CMyActive::DoStuff()
{
SetActive();
iSocket->Open(iStatus);
}


With the code as it is in main() you can see that "something has happened that an active object is interested in" will never be able to be set to TRUE because the call to open the socket
doesn't happen until after the call to CActiveScheuler::Start(),and thats stuck in an infinite loop. So you have to rearange your code as:

main()
{
CActiveScheduler* scheduler = new CActiveScheduler;
CActiveScheduler::Install(scheduler);
CMyActive* active = CMyActive::NewL();
active->DoStuff();
CActiveScheduler::Start();
TInt ImANumpty = 5;
}


Now the call to open the socket is made before we enter CActiveScheduler::Start()'s infinite loop.
Now the important thing to bear in mind is that when we are executing through the loop the socket code is also executing effectively in parallel in another part
of the operating system, and when it has opened the socket the "something has happened that an active object is interested in" turns
from being FALSE to being TRUE and as a consequence your active object's RunL() gets called.


People still have problems with the above code however, wondering why after they call CActiveScheduler::Start(), and even though their active object's
RunL() get's called they wonder why ImANumpty never gets called. Well its because CMyActive::RunL() is called but as we're still inside the infinite loop then calling
RunL() results in "something has happened that an active object is interested in" being set back to FALSE. So after RunL() is called we're back inside the infinite
loop forever unless (a) "something has happened that an active object is interested in" gets set back to TRUE again somehow or (b) we can break out of the loop somehow.
For (a) to happen you have to send another TRequestStatus to something, so in the case of the socket example it might be to send some data over the socket, so you're
code might be:

CMyActive::RunL()
{
SetActive();
iSocket->SendData(_L("the data to send"), iStatus);
}

When the data has been sent "something has happened that an active object is interested in" gets set to TRUE so RunL() gets called again.
Suppose now we're finished and don't to do anything else what then? We have to break out of the infinite loop, and this can be done with
CActiveScheduler::Stop(), so our RunL() code might look like:


CMyActive::RunL()
{
if (iState == ENotSentAnyDataYet)
   {
   SetActive();
   iSocket->SendData(_L("the data to send"), iStatus);
   iState = ESentData; 
   }
   else
      if (iState == ESentData)
          CActiveSchuduler::Stop();
}


The first time RunL() is called iSocket->SendData() gets called, the second time the RunL() is called CActiveScheduler::Stop() is called
which causes the infinite loop to stop and thus execution of CActiveScheduler::Start() finishes and thus the line TInt ImANumpty = 5; gets executed and the
program finishes.


important point: in the example code it shows execution returning to the main part of the program after the active scheduler has stopped. You would usually write this sort of code in an exe which is test code for example and which only runs for a short time and has a predetermined life span. Programs (servers, applications) on a mobile device aren't like that however, who knows when they will end. You have to stop thinking in terms of a program running and stopping once all its lines have been executed and start thinking in terms of a program running potentially for ever and it reacting to various events whenever they might occur.
There are not many times when you need to use an active scheduler, writing test code is one as mentioned, the other main one is if you write a server. For most users it is sufficient for you to not know they even exist.



8b
----
When to use the active scheduler: if you are writing an application i.e. a UI then the UI framework creates and starts an active scheulder for you, so you should have no need to create one of your own*.
If you are writing a DLL that uses active objects and your DLL is intended to be used by another component then again you shouldn't add an active scheulder to your DLL.
AN example when you'd use one are if you are writing an exe which makes use of active objects or uses components which use them.


*This, and indeed this whole posting, is a simplified overview of the active scheduler, you might be in situations where you need to add your own to have nested active schedulers. But thats a more advanced area  than the simplified overview given here
Fri, 2006-08-04 23:39
Joined: 2006-03-04
Forum posts: 194
Re: Common mis-use and abuse of Symbian OS
Number 9 - Misunderstanding about the stack and the heap in relation to descriptors



A lot of people seem to get confused between the stack and the heap when it comes to descriptors. A main reason for the confusion is actually from documentation (including from Symbian themselves) that isn't clear or is misleading.

For example documentation that says TBuf descriptors go on the stack is incorrect.
Also documentation that says the data pointed to by a HBufC* goes on the heap but the HBufC pointer goes on the stack is also incorrect.

The above two statements are correct IF the TBuf and HBufC pointer are local variables i.e.

void Function()
{
TBuf<20> buf;
TPtr ptr;
HBufC* hBuf = HBufC::NewL(...)
}

buf, ptr and hBuf are all on the stack, while the data pointed to by hBuf is on the heap, however in this code:

class CClass : public CBase
{
...
TBuf<20> ibuf;
TPtr iptr;
HBufC* ihBuf;
}

CClass *class = CClass::NewLC();

Then ibuf, iptr, ihBuf and the data pointed to by hBuf are all on the heap.


Avoid putting large descriptors on the stack:

void Function()
{
TBuf<1000000> buf;
}

This will go on the stack and is too large so will cause stack overflow or the infamous __chkstk compiler warning.


So instead if you do this:

void Function()
{
TBuf<1000000>* buf = new(ELeave) TBuf<10000000>;
}

Now it will go on the heap but you should probably use RBuf or HBuf instead, (or for data this large look at CBufFlat or preferably CBugSeg).

In this example, the data is also on the heap:

class CClass : public CBase
{
...
TBuf<10000000) iBuf;
}

CClass* class = CClass::NewLC();
Thu, 2006-08-10 10:30
Joined: 2005-04-27
Forum posts: 28
reply : regarding stack/heap
Hi. Thanks for the post/thread. Actually i was also wondering about the stack / heap allocation regarding desc
types. Actually it follows the C++ convention only local var's on stack & class members on heap.
Thu, 2006-08-24 22:44
Joined: 2006-03-04
Forum posts: 194
Re: Common mis-use and abuse of Symbian OS
Number 10 - stuff about signing and capabilites
--------------------------------------------------------------

There's loads that could be written about this, here's a little to begin with, it could do with tidying, editing, adding much more but it'll do for a start:


Quotes taken a thread discussion on capabilites and its replies.


Quote
This method requires 'Network Services" capability which I have got with the developer certificate.

Quote
im giving the capabilities which you can get it free through developer certificate:


You don't get capabilites from a certificate. From a certificate you get the ability to sign and thus install software which uses a certain set of capabilites. It doesn't matter what the capabilites are in a certificate if the correct capabilites aren't in your code in the first place.

The capabilities are specified in the .mmp file. If you run your code on the emulator and look in the epocwind.out file you will be able to see if you have got the correct capabilites specified in your .mmp file or not as you will get an error message saying what capability is missing for a particular dll to be loaded or for a particular function to be called.

The certificate comes into play when installing the SIS onto the device. If you have some powerful capabilites in your .mmp file then you will need a certificate endorsing those powerful capabilites before you are able to install the software. And don't confuse a developer certificate with a real certificate. A developer certificate is an aid when you are developing your software, to release your software to the world you need a real certificate. Developer Certificates are issued for a particular device or small number of specified devices, if an attempt is made to install to any other device installation will fail.


Quote
Have you modified .mmp file ? Try to add following line in .mmp:
CAPABILITY ALL -tcb

You shouldn't just add capabilites in the blind hope that you will be able to access APIs.

If you have All -TCB then go along to the certificate provider and say excuse me can I have a certificate that will allow me to install my app on the device with All - TCB capabilities please? they're not too likely to say "Yes of course you can have AllFiles which will allow your program to erase the entire contents of the device, no problemo. Yes you can have PowerMgmt so you can kill all the running processes or shut down and start up the phone at random to irritate the user. We don't mind giving you a certificate with these capabilites at all as you can't do much damage to the phone having All-TCB".  Tongue

The more powerful the set of capabilites you request, then in theory, the more stringent the controls and checking there should be before you will be granted them in a certificate (real certificate, not developer certificate). And you might not be granted some powerful ones at all. Certianly nobody would be granted TCB. 

Capabilites are divided into two sets - user and system.  If your code only uses user certificates then you may be allowed to install it onto the device without any certificate depending upon how the phone manufacturer/phone operator has configured the device. However I think Nokia has configured all their devices to require signing even if you are only using user capabilites, though getting a certificate with just user certificates ought to be easier than if it contains system capabilites.

The standard set of user capabilities is:

UserServices
NetworkService
UserEnvironment
ReadUserData
WriteUserData
Location

(However a device manufacturer can customise this list if they wish to though this is unlikly).

If you are installing software which uses any system capabilites then it MUST be signed with a certificate which endorses those capabilites (i.e. allows the software signed with that certificate to be installable).


P.S.
Top tip when changing the capabilities in a .mmp file. Delete everything you can find associated with your project, dlls, exes, directories in epoc32's build folder. Delete everything.
There seems to be a problem with residual information lying around which isn't tidied up by regenerating your project files from the updated .mmp or by doing abld clean or reallyclean or bldmake clean. So you may find you add a new capability to a .mmp, regenerate the project file, re-build everything but the code still behaves as if it hasn't got the new capability. If this happens to you make sure you have a totally clean environment and try again.
Fri, 2006-08-25 00:40
Joined: 2006-03-04
Forum posts: 194
Re: Common mis-use and abuse of Symbian OS
Number 11 - reasons why your SIS file may not install
--------------------------------------------------------------------

(These apply specifially to Symbian OS9.x/nokia 3.x. Some will apply to earlier versions, but many won't).


1) Your SIS might not be signed when it has to be. It might not be signed with the correct capabilites. It may not be signed with the correct certificate (Lots of rules here, see above for some of them).

2) Your certificate may have expired. Your device's date may not be correctly set so the device thinks the certificate has expired.

3) Your certificate may have been revoked.

4) You might be installing onto a test device and it doesn't have any root certificates installed.

5) If your SIS is signed with a developer certificate:
- are you installing to a device specified by the dev cert - they are for particular devices only?
- have you signed it with more than one dev cert?

6) Does your certificate contain the capabilites that the programs you are trying to install uses?

7) The format of SIS files has changed with Symbian OS9.x / Nokia 3.x. You might have built it using an old SDK and thus its format is out of date.

8 ) Your sis file contains exes and dlls built for the emulator and not for the device.

9) Your .pkg may contain typos in the names of installtion paths etc.

10) Your program uses Dlls etc. which aren't also in the SIS or on the device

11) There might not be enough memory or disk space to install the stuff.

12) The stuff in the SIS may already be installed on the device (there are rules about what can or can't be replaced)


13) You might be attempting to install to a forbidden directory.
- You shouldn't attempt to write to /sys/
- You should write your executables to /sys/bin/
- You shouldn't write to a private folder that isn't yours,  unless its to its import directory provided that it previously exists
- You should put things like .ini files, .mbs, data files in your  private folder
- You should put things like resources & bitmaps in /resource/

A private folder is /private/0x1234565789/, where 0x123456789 is the SID or 3rd UID of the program.
An import folder is /private/0x1234565789/import

14) Does your SIS have an SID (or 3rd uid) in the protected range (or a VID in the protected range. If you don't know what a VID is then you don't need to know). If it does it needs to be signed with an appropriate certificate.
For SIDs the protected range is 0-0x7fffffff and the unprotected range is 0x80000000-0xffffffff . For VIDs, the protected range is 0x00000001-0xffffffff and the unprotected range is 0x00000000.

15) Does your SIS file contain an exe with an SID (or 3rd UID) which is already being used on the device.

Fri, 2006-08-25 00:59
Joined: 2006-03-04
Forum posts: 194
Re: Common mis-use and abuse of Symbian OS
Number 12 - descriptors as function arguments
------------------------------------------------------------

1) If a function takes a descriptor as an input paramter you should declare it as const TDesC&

Whatever you do don't forget the &, if you do it will compile but you won't pass any data to the function.

Also don't forget the const either.

2) If the function takes an in-out parameter pass it as TDes&.

Again, whatever you do, don't forget the &.
This time, don't add the const by mistake.

Summary:
Do do const TDesC&
Do do TDes&
Don't do const TDesC
Don't do const TDes
Don't do TDesC&
Don't do const TDes&
Don't do TDes
Wed, 2007-10-10 00:37
Joined: 2007-09-23
Forum posts: 136
Re: Common mis-use and abuse of Symbian OS

Number 13
----------------

Why are you using threads? You don't use threads in Symbian unless you need to, instead you use active objects.

When you need to use threads in Symbian is when you *need* pre-emptive multitasking, if you don't know what pre-emptive multitasking is then you probably don't need a thread.

Wed, 2007-10-31 14:02
Joined: 2007-05-17
Forum posts: 5
Re: Common mis-use and abuse of Symbian OS

Very common error while installing sis signed with devcert is wrong time defined on device. Devcerts has limited time and this leads to errors if time on devices exceeds this interval.


I like Symbian freeware. Do you?

Mon, 2007-12-03 21:24
Joined: 2007-09-23
Forum posts: 136
Re: Common mis-use and abuse of Symbian OS

Number 14 - Unnecessarily Cancelling within active objects
-----------------------------------------------------------------------------------

A lot of people write code within an active such as this:

void CMyActiveObject::StartProcessingL( )
{
// Cancel any outstanding request just in case
if ( IsActive() )
{
Cancel();
}

DoSomeStuff ....

}

CActive::Cancel() itself checks if the object is active or not, so wrapping Cancel() in an if statement in general is pointless.

"Cancel any outstanding request just in case" just in case what? If you're cancelling 'just in case' then you are admitting that the logic of your active oject is crap or it is being misused by a caller and you might get into a situation where an asynchronous request is going to be made when there is already an outstanding one. Don't put code in "just in case", design your object properly so this can't happen. If a client of your active object is calling StartProcessingL() twice then its probably a bug, so consider putting at least an ASSERT_DEBUG checking to see if the object is already active.

  • Login to reply to this topic.