commonservices/sysutil/src/sysutil.cpp
changeset 0 4e1aa6a622a0
child 21 ccb4f6b3db21
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/commonservices/sysutil/src/sysutil.cpp	Tue Feb 02 00:53:00 2010 +0200
@@ -0,0 +1,2458 @@
+// Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+
+
+// INCLUDE FILES
+#include "sysutil.h"
+#include <f32file.h>
+#include <bsul/ccacheddriveinfo.h>
+#include "sysutildomaincrkeys.h"	// for disc space
+#include "sysutilinternalpskeys.h"	// for disc space
+#include "sysutildebug.h"			// debug helper
+#include "sysutilpatchdata.h"		// patchable data defining the MMC drive integer identifer
+#include <baflpan.h>
+#include <e32property.h>
+#include <e32capability.h>
+#include <bsul/inifile.h>
+#include <bautils.h>
+#include "sysutilsetup.h"
+#include "sysutilplugindef.h"
+
+// File names of the files which store version and device type information.
+_LIT( KSWVersionFileName,			"sw.txt" );
+_LIT( KLangSWVersionFileName,		"langsw.txt" );
+_LIT( KLangVersionFileName,			"lang.txt" );
+_LIT( KDeviceAttributesFileName,	"deviceattributes.ini" );
+_LIT( KPRInformationFileName,       "purpose.txt" );
+
+// UID of sysutilsetup.exe.
+const TUid KSysUtilSetupUid = {0x10285B3B};
+
+// Publish and Subscribe keys.
+const TUid KTestFilePathsUid = {0x10285B40};
+const TUid KSWVersionUid = {0x10285B3C};
+const TUid KLangSWVersionUid = {0x10285B3D};
+const TUid KLangVersionUid = {0x10285B3E};
+const TUid KDeviceTypeInfoUid = {0x10285B3F};
+const TUid KPRInformationUid = {0x20027BC4};
+
+// Device Type Information attribute UIDs.
+const TUid KManufacturerNameUid = {0x10286358};
+const TUid KModelNameUid = {0x10286359};
+const TUid KModelCodeUid = {0x1028635A};
+const TUid KRevisionIDUid = {0x1028635B};
+const TUid KDefaultDeviceNameUid = {0x1028635C};
+const TUid KUIPlatformNameUid = {0x1028635D};
+const TUid KUIPlatformVersionMajorUid = {0x1028635E};
+const TUid KUIPlatformVersionMinorUid = {0x1028635F};
+const TUid KUIPlatformVersionUid = {0x10286360};
+const TUid KOSVersionMajorUid = {0x10286361};
+const TUid KOSVersionMinorUid = {0x10286362};
+const TUid KOSVersionUid = {0x10286363};
+
+// This is the maximum number of Device Type Information attributes that can be 
+// stored in the Device Type Information Publish and Subscribe property.
+const TUint16 KAttributeLimit = 450;
+
+// Default values that will be used if no plugin is provided, the 
+// deviceattributes.ini file is missing or if a device attribute is not 
+// specified by a licensee.
+_LIT16( KDefaultManufacturer, "Symbian" );
+_LIT16( KDefaultModelName, "Symbian" );
+_LIT16( KDefaultModelCode, "Symbian" );
+_LIT16( KDefaultRevisionID, "00.00" );
+_LIT16( KDefaultDeviceName, "Symbian" );
+_LIT16( KDefaultUIPlatform, "Symbian" );
+_LIT16( KDefaultUIPlatformVersionMajor, "0" );
+_LIT16( KDefaultUIPlatformVersionMinor, "0" );
+_LIT16( KDefaultUIPlatformVersion, "0.0" );
+_LIT16( KDefaultOSVersionMajor, "0" );
+_LIT16( KDefaultOSVersionMinor, "0" );
+_LIT16( KDefaultOSVersion, "0.0" );
+
+
+// Security policies for the Publish and Subscribe properties.
+_LIT_SECURITY_POLICY_C1( KSecurityPolicyNone, ECapability_None );
+_LIT_SECURITY_POLICY_S1( KSecurityPolicyWriteDeviceData, 0x10285B3B, ECapabilityWriteDeviceData );
+
+_LIT( KNewLinePattern, "\\n" );
+_LIT( KNewline, "\n" );
+
+
+/**
+Dummy sysutil implementation object - never instantiated.
+@internalComponent
+*/
+class CDeviceTypeInformation::TImpl
+	{
+	TUint32 iDummy;
+	};
+
+/**
+Stores a copy of the device type information attributes. For details of the 
+memory layout see SGL.TS0017.201 BAFL Component Design Document.doc.
+@internalComponent 
+*/
+typedef struct _SDeviceAttributes
+	{
+	TUint16 Length() const;
+	TUint16 Count() const;
+	TInt32 Uid( const TInt aAttributeIndex ) const;
+	TInt16 Error( const TInt aAttributeIndex ) const;
+	TInt AttributeLength( const TInt aAttributeIndex ) const;
+	TUint16* AttributePtr( const TInt aAttributeIndex ) const;
+	TUint16* iDeviceAttributes;
+	} SDeviceAttributes;
+	
+/**
+@return TInt16 The number of elements in the memory block.
+*/
+inline TUint16 SDeviceAttributes::Length() const
+	{
+	return iDeviceAttributes[0];
+	}
+
+/**
+@return TInt16 The number of device type information attributes.
+*/
+inline TUint16 SDeviceAttributes::Count() const
+	{
+	return iDeviceAttributes[1];
+	}
+
+/**
+Returns the UID which matches the attribute with the given index.
+
+@param 	aAttribIndex	The index of the attribute in iDeviceAttributes.
+@return TInt32 			The UID of the attribute with the given index.
+*/
+inline TInt32 SDeviceAttributes::Uid( const TInt aAttribIndex ) const
+	{
+	return (iDeviceAttributes[2 + (2 * aAttribIndex)] << 16) + iDeviceAttributes[2 + (2 * aAttribIndex) + 1];
+	}
+
+/**
+Returns an error code associated with the device type information attribute with 
+the given index. This error code is generated when the attribute is retrieved 
+from a plugin/deviceattributes.ini. It indicates if a value was successfully 
+read, no value was provided or if an error occured.
+
+@param 	aAttribIndex	The index of the attribute in iDeviceAttributes.
+@return TInt16 			The error of the attribute with the given index.
+*/
+inline TInt16 SDeviceAttributes::Error( const TInt aAttribIndex) const
+	{
+	return static_cast<TInt16>(iDeviceAttributes[2 + (3 * Count()) + 1 + aAttribIndex]);
+	}
+
+/**
+Returns the length of the attribute with the given index.
+
+@param 	aAttribIndex	The index of the attribute in iDeviceAttributes.
+@return TInt			The length of the attribute with the given index.
+*/
+inline TInt SDeviceAttributes::AttributeLength( const TInt aAttribIndex ) const
+	{
+	return iDeviceAttributes[2 + (2 * Count()) + aAttribIndex + 1] - iDeviceAttributes[2 + (2 * Count()) + aAttribIndex];
+	}
+
+/**
+Returns a pointer to the attribute value in iDeviceAttributes with the given 
+index.
+
+@param 	aAttribIndex	The index of the attribute in iDeviceAttributes.
+@return TUint16*		A pointer to the attribute value with the given index.
+*/
+inline TUint16* SDeviceAttributes::AttributePtr( const TInt aAttribIndex ) const
+	{
+	return iDeviceAttributes + iDeviceAttributes[2 + (2 * Count()) + aAttribIndex];
+	}
+
+// ======== LOCAL FUNCTIONS ======== 
+
+/**
+Returns the total length of all the device type information attribute values stored 
+in aValues.
+
+@param	aKeys	An array which contains a list of device type information attribute
+				UIDs. The UIDs corrospond to the values in aValues.
+@param	aValues	An array which contains a list of device type information attribute
+				values. The values corrospond to the UIDs in aKeys.
+@return TInt	The total length of all the device type information attribute values 
+				stored in aValues.
+@internalComponent
+*/
+static TInt AttributesTotalLength( RArray<TInt32>&, CDesC16ArraySeg& aValues )
+	{
+	TUint attributesTotalLength = 0;
+	TInt numAttributes = aValues.Count();
+	for( TInt attributeIndex = 0; attributeIndex < numAttributes; attributeIndex++ )
+		{
+		TInt attributeLength = aValues[attributeIndex].Length();
+		if( attributeLength < CDeviceTypeInformation::KMaxAttributeLength )
+			{
+			attributesTotalLength += attributeLength;
+			}
+		else
+			{
+			attributesTotalLength += CDeviceTypeInformation::KMaxAttributeLength;
+			}
+		}
+	
+	return attributesTotalLength;
+	}
+
+/**
+Returns a pointer to some memory which contains the device type information attribute 
+UIDs and values (these are contained in aKeys and aValues) in a compact format. The 
+layout of the memory can be found in the Design Document 
+(SGL.TS0017.201 BAFL Component Design Document.doc). 
+
+@param	aKeys		An array which contains a list of device type information 
+					attribute UIDs. The UIDs corrospond to the values in aValues.
+@param	aValues		An array which contains a list of device type information 
+					attribute values. The values corrospond to the UIDs in aKeys.
+@leave	-			One of the system-wide error codes.
+@return	TUint16*	A pointer to the memory which contains the device type 
+					information attribute UIDs and values in a compact format.
+@internalComponent
+*/
+static TUint16* CreateDeviceTypeInfoPSDataL( RArray<TInt32>& aKeys, CDesC16ArraySeg& aValues )
+	{
+	// See SGL.TS0017.201 BAFL Component Design Document.doc for more information on the
+	// memory requirements and layout.
+	TInt memoryElements = 3 + (4 * aKeys.Count()) + AttributesTotalLength( aKeys, aValues );
+	
+	// Allocate memory to store the device type information attribute keys and values
+	TUint16* deviceAttributes = static_cast<TUint16*>( User::AllocL( sizeof(TUint16) * memoryElements ) );
+	CleanupStack::PushL( deviceAttributes );
+	
+	// Store the attributes length and count
+	deviceAttributes[0] = memoryElements;
+	deviceAttributes[1] = aKeys.Count();
+	
+	// Store the attribute UIDs, offsets of the attributes in the memory, error codes and attribute values.
+	if( deviceAttributes[1] > 0 )
+		{
+		// Set the first offset to point to the start of the attributes
+		deviceAttributes[2 + (2 * deviceAttributes[1])] = 2 + 1 + (4 * deviceAttributes[1]);
+		
+		for( TInt attributeNumber = 0; attributeNumber < deviceAttributes[1]; attributeNumber++ )
+			{
+			// Store the UID as two TUint16s
+			deviceAttributes[2 + (2 * attributeNumber)] = static_cast<TUint16>(aKeys[attributeNumber] >> 16); 
+			deviceAttributes[2 + (2 * attributeNumber) + 1] = static_cast<TUint16>(aKeys[attributeNumber]); 
+			
+			if( KUIPlatformVersionMajorUid.iUid == aKeys[attributeNumber] || KUIPlatformVersionMinorUid.iUid == aKeys[attributeNumber] 
+				|| KOSVersionMajorUid.iUid == aKeys[attributeNumber] || KOSVersionMinorUid.iUid == aKeys[attributeNumber] )
+				{
+				// If the values were present in the INI/plugin (as we have the attribute keys) and the values
+				// are KNullDesC16 then the values were corrupt.
+				if( aValues[attributeNumber].Compare( KNullDesC16 ) == 0 )
+					{
+					deviceAttributes[2 + (2 * deviceAttributes[1]) + 1 + attributeNumber] = deviceAttributes[2 + (2 * deviceAttributes[1]) + 1 + attributeNumber - 1];
+					deviceAttributes[2 + (3 * deviceAttributes[1]) + 1 + attributeNumber] = static_cast<TUint16>(KErrCorrupt);
+					continue;
+					}
+				}
+			
+			// If the attribute value is too long we need to truncate it and set the error as KErrOverflow.
+			if( aValues[attributeNumber].Length() > CDeviceTypeInformation::KMaxAttributeLength )
+				{
+				deviceAttributes[2 + (2 * deviceAttributes[1]) + 1 + attributeNumber] = deviceAttributes[2 + (2 * deviceAttributes[1]) + attributeNumber] + CDeviceTypeInformation::KMaxAttributeLength;
+				deviceAttributes[2 + (3 * deviceAttributes[1]) + 1 + attributeNumber] = static_cast<TUint16>(KErrOverflow);
+				Mem::Copy( deviceAttributes + deviceAttributes[2 + (2 * deviceAttributes[1]) + attributeNumber], aValues[attributeNumber].Ptr(), CDeviceTypeInformation::KMaxAttributeLength * 2 );
+				}
+			else
+				{
+				deviceAttributes[2 + (2 * deviceAttributes[1]) + 1 + attributeNumber] = deviceAttributes[2 + (2 * deviceAttributes[1]) + attributeNumber] + aValues[attributeNumber].Length();
+				deviceAttributes[2 + (3 * deviceAttributes[1]) + 1 + attributeNumber] = KErrNone;
+				Mem::Copy( deviceAttributes + deviceAttributes[2 + (2 * deviceAttributes[1]) + attributeNumber], aValues[attributeNumber].Ptr(), aValues[attributeNumber].Size() );
+				}
+			}
+		}
+	CleanupStack::Pop( 1 );
+	
+	return deviceAttributes;
+	}
+
+/**
+Helper function which deletes the Version and Device Type Information Publish and 
+Subscribe properties. This is meant for testing purposes only and requires the 
+KSysUtilTestModeEnabled constant to be patched to ETrue.
+
+@leave	-			One of the system-wide error codes.
+@internalComponent
+*/
+EXPORT_C void DeletePSPropertiesL()
+	{
+	if( KSysUtilTestModeEnabled )
+		{
+		TInt err = RProperty::Delete(KSWVersionUid.iUid);
+		if(err != KErrNotFound)
+			{
+			User::LeaveIfError(err);
+			}
+		
+		err = RProperty::Delete(KLangSWVersionUid.iUid);
+		if(err != KErrNotFound)
+			{
+			User::LeaveIfError(err);
+			}
+		
+		err = RProperty::Delete(KLangVersionUid.iUid);
+		if(err != KErrNotFound)
+			{
+			User::LeaveIfError(err);
+			}
+		
+		err = RProperty::Delete(KDeviceTypeInfoUid.iUid);
+		if(err != KErrNotFound)
+			{
+			User::LeaveIfError(err);
+			}
+		
+		err = RProperty::Delete(KPRInformationUid.iUid);
+        if(err != KErrNotFound)
+            {
+            User::LeaveIfError(err);
+            }
+		}
+	}
+
+/**
+Finds and returns the device type information attribute number in aAttr. This 
+number is used as an index in a populated instance of SDeviceAttributes.
+
+@param aAttr A reference to SDeviceAttributes which contains a copy of the device type information attributes.
+@param aKey The UID of the device type information attribute which needs to be found.
+@return The attribute index number (position) in aAttr.
+@return KErrNotFound aKey is not present in aAttr.
+@internalComponent
+*/
+static TInt FindAttributeNumber( const SDeviceAttributes& aAttr, const TUid& aKey )
+	{
+	TInt bottom = 0;
+	TInt top = aAttr.Count() - 1;
+	
+	while( bottom <= top )
+		{
+		TUint middle = bottom + ( top - bottom ) / 2;
+		if( static_cast<TUint32>(aKey.iUid) < aAttr.Uid( middle ) )
+			{
+			top = middle - 1;
+			}
+		else if( static_cast<TUint32>(aKey.iUid) > aAttr.Uid( middle ) )
+			{
+			bottom = middle + 1;
+			}
+		else
+			{
+			return middle;
+			}
+		}
+	return KErrNotFound;
+	}
+
+/**
+Find a value from the patchable data. If that fails try Publish&Subscribe,
+or if that fails, from Central Repository.
+
+@internalComponent
+@param aPCKey identifies the value to get from P&S.
+@param aCRKey identifies the value to get from Centrep
+@param aPatchableData patchable data set at compile time, may have been patched via rom patching
+@return Disk level threshold
+*/
+static TInt FindValueL(
+    const TUint aPCKey,
+    const TUint aCRKey,
+    const TInt aPatchableData )
+    {
+    TInt val( 0 );
+    
+    if (aPatchableData != -1)
+    	{
+    	__SYSUTIL_TRACE1("SysUtil: CL treshold value found from patchable data %d",aPatchableData);	
+    	return aPatchableData;
+    	}
+	
+	// Patchable data not set, try to get it from P & S Keys.
+	TInt errorCode = RProperty::Get( KPSUidDiskLevel, aPCKey, val );
+    if ( errorCode != KErrNone )
+        {
+        __SYSUTIL_TRACE1("SysUtil: CL treshold value not found from PS: %d, trying CR",errorCode);
+        
+        CRepository* repository = CRepository::NewLC(KCRUidDiskLevel);
+        User::LeaveIfError(repository->Get(aCRKey,val));
+        CleanupStack::PopAndDestroy( repository );
+        }	
+    	
+	return val;
+    }
+
+/**
+Find out the critical level treashold (in bytes) for disk drives of type
+aMediaType. Different media types may have different tresholds. If adding
+a non-default treshold for some media type, implementation should go here.
+
+@internalComponent
+@param aMediaType identifies the media type of the drive
+@return Disk level threshold
+*/
+static TInt64 FindCriticalLevelTresholdL( const TMediaType aMediaType )
+    {
+    if ( aMediaType == EMediaRam ) // RAM drives have different critical level than others
+        {
+        return (TInt64) FindValueL(KRamDiskCriticalThreshold, KRamDiskCriticalLevel, KSysUtilRamDiskCriticalThreshold  );
+        }
+    else // Some other media type
+        {
+        return (TInt64) FindValueL(KOtherDiskCriticalThreshold, KDiskCriticalThreshold, KSysUtilOtherDiskCriticalThreshold );
+        }
+    }
+
+/**
+Retrieves a string containing the location of sysutilplugin.dll. The location  
+which is retrieved is dependent upon two things. The first is a patchable constant 
+KSysUtilTestModeEnabled. This must be set to ETrue in order to use the test location instead 
+of the standard path. This is set to EFalse by default for hardware builds.
+
+The second is a Publish and Subscribe property, which has a UID of
+0x10285B40. If KSysUtilTestModeEnabled is set to ETrue then this property will be defined 
+and set to ETrue. This means that the test location is being used instead of the 
+standard location. If the client wishes to switch to the standard location they 
+can set this property to EFalse.
+
+It may be the case that the standard location needs to be used before the property 
+is defined. If this is needed then the client can define and set the property. 
+Alternatively, a call can be made to SysUtil::GetSWVersion, 
+SysUtil::GetLangSWVersion, SysUtil::GetLangVersion or SysUtil::GetDeviceTypeInfoL. 
+It can then call RProperty::Set with a value of EFalse.
+
+@param aLocation	On return contains the location of sysutilplugin.dll.
+@leave	-			One of the system-wide error codes.
+@internalComponent
+*/
+static void GetDllLocationL( TDes& aLocation )
+	{
+	_LIT( KDeviceAttributeDll,	"Z:\\sys\\bin\\sysutilplugin.dll" );
+
+	if( KSysUtilTestModeEnabled )
+		{
+		TBool testPaths = EFalse;
+		TInt err = RProperty::Get( KUidSystemCategory, KTestFilePathsUid.iUid, testPaths );
+		if( KErrNone != err )
+            testPaths = EFalse;
+		
+		if( testPaths )
+			{
+			TDriveUnit systemDrive( static_cast<TUint>(RFs::GetSystemDrive()) );
+			TParse path;
+			path.Set( systemDrive.Name(), &KDeviceAttributeDll, NULL );
+			
+			aLocation = path.FullName();
+			__SYSUTIL_TRACE1("DLL Location: %S", &aLocation);
+			return;
+			}
+		}
+	
+	__SYSUTIL_TRACE1("DLL Location: %S", &KDeviceAttributeDll);
+	aLocation = KDeviceAttributeDll;
+	}
+
+/**
+Retrieves a string containing the path to deviceattributes.ini. The path which 
+is retrieved is dependent upon two things. The first is a patchable constant 
+KSysUtilTestModeEnabled. This must be set to ETrue in order to use the test path instead 
+of the standard path. This is set to EFalse by default for hardware builds.
+
+The second is a Publish and Subscribe property, which has a UID of
+0x10285B40. If KSysUtilTestModeEnabled is set to ETrue then this property will be defined 
+and set to ETrue. This means that the test path is being used instead of the 
+standard path. If the client wishes to switch to the standard path they can set 
+this property to EFalse.
+
+It may be the case that the standard path needs to be used before the property 
+is defined. If this is needed then the client can define and set the property. 
+Alternatively, a call can be made to SysUtil::GetSWVersion, 
+SysUtil::GetLangSWVersion, SysUtil::GetLangVersion SysUtil::GetDeviceTypeInfoL or SysUtil::GetPRInformation. 
+It can then call RProperty::Set with a value of EFalse.
+
+@param 	aPath	On return contains the path to deviceattributes.ini.
+@return	-		One of the system-wide error codes.
+@internalComponent
+*/
+static TInt GetFilePath( TDes& aPath )
+	{
+	_LIT( KDebugPath, "Z:\\versions\\" );
+	_LIT( KPath, "Z:\\resource\\versions\\" );
+
+	if( KSysUtilTestModeEnabled )
+		{
+		TBool testPaths = EFalse;
+		
+		TInt err = RProperty::Get( KUidSystemCategory, KTestFilePathsUid.iUid, testPaths );
+		if( KErrNone != err )
+            testPaths = EFalse;
+	
+		if( testPaths )
+			{
+			TDriveUnit systemDrive( static_cast<TUint>(RFs::GetSystemDrive()) );
+			TParse path;
+			path.Set( systemDrive.Name(), &KDebugPath, NULL );
+			
+			aPath = path.DriveAndPath();
+			__SYSUTIL_TRACE1("File Location: %S", &aPath);
+			return KErrNone;
+			}
+		}
+	
+	__SYSUTIL_TRACE1("File Location: %S", &KPath);
+	aPath = KPath;
+	return KErrNone;
+	}
+
+/**
+Fetch text from specified files. The algorithm first reads the file and puts
+the string into a buffer. Then it searches for these "\n" sequences in
+the buffer. To search for "\n" in a string the search string is specified as "\\n"
+with the extra slash in front to mean that we are really searching for "\n" and not
+the newline character - 0x000A. Once the "\n" is found in the buffer it is replaced
+with the newline character - 0x000A. So since this input string is unicode, it is
+actually searching for the byte sequence of 0x005C, 0x006E. These 4 bytes are
+replaced by the two byte sequence of 0x000A.
+
+@internalComponent
+@param aFilename File name to fetch string from.
+@param aValue On return, contains the requested version string. If the buffer is
+insufficient the descriptor is filled to its maximum length. If a buffer is provided
+that is longer than 64 characters, and the file content is larger than 64 characters,
+the returned buffer is truncated at 64 characters (see error codes below).
+@param aRemoveNewLines Replaces new line patterns if set to ETrue
+@return KErrNone on success.
+	    KErrTooBig if the maximum length of the descriptor is insufficient
+to hold the file content, or has been truncated (see above). In both cases new-line processing
+is done on the buffers if needed. If the file is empty KErrEof is returned, and the length of the
+buffer is set to zero. If none of these cases apply then one of the Symbian error codes is
+returned if reading the version string fails.
+*/
+static TInt GetTextFromFile(
+    const TDesC& aFilename,
+    TDes& aValue,
+    TBool aRemoveNewLines )
+    {
+    RFs fs;
+    TInt err;
+    err = fs.Connect();
+    if (err != KErrNone)
+        return err;
+
+    RFile file;
+    err = file.Open( fs, aFilename,
+                     EFileRead | EFileStreamText | EFileShareReadersOnly );
+    if (err != KErrNone)
+        {
+        fs.Close();
+        return err;
+        }
+
+    TBuf8<2> characters;
+    err =  file.Read(characters);
+    
+    if (err == KErrNone || err == KErrTooBig)
+    	{
+	    // This means that we have an ANSI file (without the header bytes)
+	    if( characters.Length() == 0 || characters.Length() == 1 )
+	    	{
+		    file.Close();
+		    fs.Close();
+			return KErrCorrupt;
+	    	}
+	    else 
+	    	{
+			TUint8 firstByte = characters[0];
+			TUint8 secondByte = characters[1];
+			
+			// Heading byte values for unicode files
+			const TInt KFFByte = 255;
+			const TInt KFEByte = 254;
+			
+		    // If file isn't unicode KErrCorrupt is returned
+			if( (firstByte!=KFFByte && secondByte!=KFEByte) && (secondByte!=KFFByte && firstByte!=KFEByte) )
+				{
+			    file.Close();
+			    fs.Close();
+				return KErrCorrupt;
+				} 
+	    	} 
+    	}
+    
+    TFileText tf;
+    tf.Set(file);
+    err = tf.Read(aValue);
+    // If the maximum length of the descriptor is insufficient to hold the record,
+    // the Read() function returns KErrTooBig and the descriptor is filled to its maximum length.
+    //
+    // If Read() is called when the current position is the end of the file (that is, after
+    // the last line delimiter in the file), KErrEof is returned, and the length of the buffer
+    // is set to zero. In this case, this would mean an empty file, as this code always reads
+    // from the beginning of the file.
+    
+    if (err == KErrNone || err == KErrTooBig)
+    	{
+    	if (aValue.Length() > KSysUtilVersionTextLength)
+    		{
+    		// File content is larger than 64 characters. Truncate to 64 characters.
+    		aValue.Delete(KSysUtilVersionTextLength,aValue.Length() - KSysUtilVersionTextLength);
+    		err = KErrTooBig;
+    		}
+    	if (aRemoveNewLines)
+    		{
+    		// Replace new-line patterns with real ones
+            TInt error = aValue.Find(KNewLinePattern);
+            while (error != KErrNotFound)
+                {
+                // error is a position
+                aValue.Replace(error, KNewLinePattern().Length(), KNewline );
+                error = aValue.Find(KNewLinePattern);
+                }
+    		}
+    	}
+
+    file.Close();
+    fs.Close();
+    
+    return err;
+    }
+
+/**
+Launches sysutilsetup.exe which is a helper executable. It should be launched 
+when the current client does not have the correct capabilities to define Publish 
+and Subscribe properties.
+
+Flags are used to determine which Publish and Subscribe properties are defined 
+and set. The supported flags can be found in sysutilsetup.h.
+
+@param aFlags 			Used to determine which Publish and Subscribe properties 
+						should be defined and set. See sysutilsetup.h for 
+						supported flags.
+@return KErrNotReady	The excutable has panicked or was not found.
+@return -				Otherwise, one of the other system-wide error codes.
+@internalComponent
+*/
+static TInt LaunchSetupExecutable( const TDesC& aFlags )
+	{
+	_LIT( KSysUtilSetupExeName, "Z:\\sys\\bin\\sysutilsetup.exe" );
+	const TUidType KSetupUid( KNullUid, KNullUid, KSysUtilSetupUid );
+	
+	RProcess setupProcess;
+	TInt err = setupProcess.Create( KSysUtilSetupExeName, aFlags, KSetupUid );
+	if( KErrNone != err )
+		{
+		return KErrNotFound == err ? KErrNotReady : err;
+		}
+	
+	TRequestStatus status;
+	setupProcess.Rendezvous( status );
+	if( KRequestPending != status.Int() )
+		{
+		setupProcess.Kill( 0 );		// Abort startup
+		}
+	else
+		{
+		setupProcess.Resume();		// Logon OK - start the server
+		}
+	
+	User::WaitForRequest( status );	// Wait for start or death
+	
+	// We can't use the 'exit reason' if the server panicked as this
+	// is the panic 'reason' and may be '0' which cannot be distinguished
+	// from KErrNone
+	err = setupProcess.ExitType() == EExitPanic ? KErrNotReady : status.Int();
+	setupProcess.Close();
+	
+	return err;
+	}
+
+/**
+Retrieves a string containing the version information for the given UID.
+
+@param aVersionUid	The UID that corresponds to the version information which is required.
+@param aValue		On return, contains the version string for the given aVersionUid.
+@return -			One of the system-wide error codes.
+@internalComponent
+*/
+static TInt GetVersionPropertyData( const TUid& aVersionUid, TDes& aValue )
+	{
+	// The P&S property contains the version data (max length 64) and the error code
+	TBuf16<KSysUtilVersionTextLength + 1> temp;
+	TInt16 err = RProperty::Get( KSysUtilSetupUid, aVersionUid.iUid, temp );
+	
+	// If the P&S properties are not set we need to set them
+	if ( KErrNotFound == err )
+		{
+		__SYSUTIL_TRACE("Launching sysutilsetup.exe");
+		err = LaunchSetupExecutable( KSetVersionPropertiesFlag );
+		__SYSUTIL_TRACE("sysutilsetup.exe terminated");
+		if( KErrNone != err )
+			{
+			return err;
+			}
+		
+		err = RProperty::Get( KSysUtilSetupUid, aVersionUid.iUid, temp );
+		if( KErrNone != err )
+			{
+			return err;
+			}
+		}
+	
+	if( aValue.MaxLength() < temp.Length() - 1 )
+		{
+		aValue = temp.MidTPtr( 1, aValue.MaxLength() );
+		return KErrTooBig;
+		}
+	aValue = temp.MidTPtr( 1, temp.Length()  - 1 );
+	
+	return static_cast<TInt16>( temp[0] );
+	}
+
+/**
+Retrieves the version information from aFileName and stores it in a Publish and 
+Subscribe property which has a key of aKey.
+
+@param aFileName	The name of the file which contains the version information.
+@param aKey			The key to be used for the Publish and Subscribe property.
+@leave -			One of the system-wide error codes.
+@internalComponent
+*/
+static void SetVersionPropertyL( const TDesC& aFileName, const TUid& aKey )
+	{
+	TFileName filePath;
+	User::LeaveIfError( GetFilePath( filePath ) );
+	filePath.Append( aFileName );
+	
+	// The P&S property must store the version data (max length 64) and the error code.
+	TUint16* versionData = static_cast<TUint16*>( User::AllocL( sizeof(TUint16) * (KSysUtilVersionTextLength + 1) ) );
+	CleanupDeletePushL( versionData );
+	
+	// The error code is store in the first TUint16 and the rest is for data.
+	TPtr16 versionDataPtr( versionData + 1, KSysUtilVersionTextLength );
+	TInt err = GetTextFromFile( filePath, versionDataPtr, ETrue );
+	if ( err != KErrNone )
+		{
+		__SYSUTIL_TRACE2( "Error: %d, while processing: %S", err, &filePath );
+		}
+	
+	// Store the error code from GetTextFromFile in the first TUint16.
+	versionData[0] = static_cast<TUint16>( err );
+	
+	err = RProperty::Define( aKey.iUid, RProperty::EByteArray, KSecurityPolicyNone, 
+							 KSecurityPolicyWriteDeviceData, KSysUtilVersionTextLength + 1 );
+	if ( KErrAlreadyExists != err )
+		{
+		User::LeaveIfError( err );
+		}
+	
+	versionDataPtr.Set( versionData, versionDataPtr.Length() + 1, versionDataPtr.Length() + 1 );
+	User::LeaveIfError( RProperty::Set( KSysUtilSetupUid, aKey.iUid, versionDataPtr ) );
+	CleanupStack::PopAndDestroy( 1 );
+	}
+
+/**
+Sets the version information Publish and Subscribe properties.
+
+@leave -			One of the system-wide error codes.
+@internalComponent
+*/
+EXPORT_C void SetVersionPropertiesL()
+	{
+	SetVersionPropertyL( KSWVersionFileName, KSWVersionUid );
+	SetVersionPropertyL( KLangSWVersionFileName, KLangSWVersionUid );
+	SetVersionPropertyL( KLangVersionFileName, KLangVersionUid);
+	SetVersionPropertyL( KPRInformationFileName, KPRInformationUid );
+	}
+
+/**
+Compares two UIDs.
+
+@param 	aLeft	The first of two UIDs to compare.
+@param	aRight	The second of two UIDs to compare.
+@return	TInt	Zero if both UIDs are the same. Positive if aLeft is greater 
+				than aRight. Negative if aLeft is less than aRight.
+@leave -		One of the system-wide error codes.
+@internalComponent
+*/
+static TInt CompareUids( const TInt32& aLeft, const TInt32& aRight )
+	{
+	TUint32 left = static_cast<TUint32>(aLeft);
+	TUint32 right = static_cast<TUint32>(aRight);
+	if( left == right )
+		{
+		return 0;
+		}
+	else if ( left < right )
+		{
+		return -1;
+		}
+	return 1;
+	}
+
+/**
+Finds the deviceattributes.ini file which best match the current locale. If more 
+than one INI file is found for the current locale (this is a result of having 
+different versions of the INI file in different ROFS sections) then the contents 
+of the INI file are merged. If a attribute appears more than once then the most 
+recent value is taken. On return akeys and aValues will contain the merged 
+results.
+
+Note: The keys and values are in UID order from low to high.
+
+@param	aKeys		On return, contains the attribute UIDs.
+@param  aValues		On return, contains the attribute values.
+@leave	KErrGeneral A UID in the INI file is missing the '0x' hex prefix.
+@leave	-			Otherwise, one of the other system-wide error codes.
+@internalComponent
+*/
+static void ReadDeviceAttribFilesAsArraysL( 
+	RArray<TInt32>& aKeys, 
+	CDesC16ArraySeg& aValues )
+	{
+	_LIT( KWildCardChar, "*" );
+	
+	RFs fs;
+	fs.Connect();
+	CleanupClosePushL( fs );
+	
+	// Get the localised version of deviceattributes.ini, if available, and use
+	// it to create a search pattern which will be used to find versions of the
+	// file that may be available in additional ROFS sections.
+	TFileName matchPattern;
+	User::LeaveIfError( GetFilePath( matchPattern ) );
+	matchPattern.Append( KDeviceAttributesFileName );
+	BaflUtils::NearestLanguageFile( fs, matchPattern );
+	matchPattern.Append( KWildCardChar );
+	
+	// Get a list of all the versions of the localised deviceattributes.ini
+	// that may be available in additional ROFS sections. If an error occurs
+	// the default values will be used.
+	CDir* iniFileList = NULL;
+	TInt err = fs.GetDir( matchPattern, KEntryAttReadOnly | KEntryAttHidden | 
+							KEntryAttSystem | KEntryAttArchive, ESortByName, iniFileList );
+	
+	__SYSUTIL_TRACE1( "Error: %d, while getting a list of deviceattribute.ini files", err );
+	
+	if( err == KErrNone )
+		{
+		CleanupStack::PushL( iniFileList );
+		// Go through each INI file (starting with the last file first) extracting 
+		// the keys and values which are then stored in the two provided arrays. 
+		// This keys and values are stored in key order from low to high. Only the 
+		// most recent version of each value is stored i.e. the value in the INI 
+		// file which is stored in the newest ROFS section.	
+		TLinearOrder<TInt32> orderer( CompareUids );
+		for( TInt iniNumber = iniFileList->Count() - 1; iniNumber >= 0; iniNumber-- )
+			{
+			// Get next INI file
+			TFileName iniPath;
+			User::LeaveIfError( GetFilePath( iniPath ) );
+			iniPath.Append( (*iniFileList)[iniNumber].iName );
+			
+			BSUL::CIniDocument16* iniFile = NULL;
+			TRAPD( err, iniFile = BSUL::CIniDocument16::NewL( fs, iniPath ) );
+			__SYSUTIL_TRACE1("BSUL::CIniDocument16::NewL error = %d", err);
+			if( err == KErrNone )
+				{
+				CleanupStack::PushL( iniFile );
+				
+				RArray<TPtrC16> iniFileSections;
+				iniFile->GetSectionList( iniFileSections );
+				CleanupClosePushL( iniFileSections );
+				
+				// For each section get the key/value pairs and insert in order to 
+				// the provided arrays. If a key already exists do not insert the 
+				// value in aValue.
+				TInt numIniFileSections = iniFileSections.Count();
+				for( TInt sectionNum = 0; sectionNum < numIniFileSections; sectionNum++ )
+					{
+					BSUL::CIniSecIter16* sectionIter = BSUL::CIniSecIter16::NewL( iniFileSections[sectionNum], iniFile );
+					CleanupStack::PushL( sectionIter );
+					
+					TPtrC16 key;
+					TPtrC16 value;
+					while( sectionIter->Next( key, value ) )
+						{
+						const TChar char0('0');
+						const TChar charX('x');
+						
+						TUint32 tempUid;
+						TLex16 lex( key );
+						
+						// Skip over the '0' and 'x' hex number prefix
+						if( lex.Get() != char0 || lex.Get() != charX )
+							{
+							User::Leave(KErrGeneral);
+							}
+						User::LeaveIfError( lex.Val( tempUid, EHex ) );	
+						
+						TInt err = aKeys.InsertInOrder( static_cast<TInt32>(tempUid), orderer );
+						if( KErrAlreadyExists != err )
+							{
+							User::LeaveIfError( err );
+							
+							TInt index = aKeys.FindInOrder( static_cast<TInt32>(tempUid), orderer );
+							aValues.InsertL( index, value );
+							}
+						}
+					CleanupStack::PopAndDestroy( 1 ); // sectionIter
+					}
+				CleanupStack::PopAndDestroy( 2 ); // iniFile and iniFileSections
+				}
+			}
+		CleanupStack::PopAndDestroy( 1 ); // iniFileList
+		}
+	CleanupStack::PopAndDestroy( 1 ); // fs
+	}
+
+/**
+Retrieves the provisioned device type information attribute key/value pairs 
+which are stored in aKeys and aValues.
+
+Note: The keys and values are in UID order from low to high.
+
+@param	aKeys		On return, contains the attribute UIDs.
+@param	aValues		On return, contains the attribute values.
+@leave	KErrCorrupt	There is a mismatch between the number of keys and values or
+					an invalid key has been provided.
+@leave	-			Otherwise, one of the other system-wide error codes.
+@panic KErrTooBig	More than 450 attributes where provisioned.
+@internalComponent
+*/
+static void GetDeviceAttributesL( RArray<TInt32>& aKeys, CDesC16ArraySeg& aValues )
+	{
+	RArray<TInt32> iniKeys;
+	CleanupClosePushL( iniKeys );
+	
+	CDesC16ArraySeg* iniValues = new (ELeave) CDesC16ArraySeg( 8 );
+	CleanupStack::PushL( iniValues );
+	
+	// First try to get the device type information attributes from the INI file/s.
+	ReadDeviceAttribFilesAsArraysL( iniKeys, *iniValues );
+	
+	if( !KSysUtilDisableDeviceTypeInfoSetupExe )
+		{
+		// Now try to get the device type information attributes from a DLL.
+		TFileName deviceAttributeDllLocation;
+		GetDllLocationL(deviceAttributeDllLocation);
+		
+		RLibrary deviceAttributeDll;
+		TInt err = deviceAttributeDll.Load( deviceAttributeDllLocation );
+		CleanupClosePushL( deviceAttributeDll );
+		
+		if( KErrNone == err )
+			{
+			SysUtilPlugin::GetDeviceAttributesAsArraysFuncL GetDeviceAttributesAsArraysL = reinterpret_cast<SysUtilPlugin::GetDeviceAttributesAsArraysFuncL>( deviceAttributeDll.Lookup( SysUtilPlugin::EGetDeviceAttributesAsArraysLOrdinal ) );
+			User::LeaveIfNull( &GetDeviceAttributesAsArraysL );
+			GetDeviceAttributesAsArraysL( aKeys, aValues );
+			}
+		else
+			{
+			__SYSUTIL_TRACE1("Could not load sysutilplug.dll with error: %d ", err);
+			}
+		CleanupStack::PopAndDestroy( 1 );
+		}
+	
+	// Merge results with the INI taking lowest priority i.e. if the value already
+	// exists in aKeys/aValues then ignore the one in iniKeys/iniValues.
+	TLinearOrder<TInt32> orderer( CompareUids );
+	TInt numIniAttribs = iniKeys.Count();
+	for( TInt keyNum = 0; keyNum < numIniAttribs; keyNum++ )
+		{
+		TInt err = aKeys.InsertInOrder( iniKeys[keyNum], orderer );
+		if( KErrAlreadyExists != err )
+			{
+			User::LeaveIfError( err );
+			
+			TInt index = aKeys.FindInOrder( iniKeys[keyNum], orderer );
+			aValues.InsertL( index, (*iniValues)[keyNum] );
+			}
+		}	
+	CleanupStack::PopAndDestroy( 2 );
+	
+	// Check we have the same number of keys and values and that we don't have too many keys
+	if( aKeys.Count() != aValues.Count() )
+		{
+		User::Leave( KErrCorrupt );
+		}
+	else if( aKeys.Count() > KAttributeLimit )
+		{
+		_LIT( KPanicReason, "KAttributeLimit exceeded" );
+		User::Panic( KPanicReason, KErrTooBig );
+		}
+	}
+
+/**
+Validates major and minor version numbers. This means both values must be 
+within the range 0 to KMaxTUint16. If the major, minor or both numbers are 
+invalid their values will be replaced by KNullDesC16.
+
+@param	aKeys	An array which contains a list of device type information attribute
+				UIDs. The UIDs corrospond to the values in aValues.
+@param	aValues	An array which contains a list of device type information attribute
+				values. The values corrospond to the UIDs in aKeys.
+@internalComponent
+*/
+static void ValidateVersionNumbersL( RArray<TInt32>& aKeys, CDesC16ArraySeg& aValues, const TUid& aMajorVersionUid, const TUid& aMinorVersionUid)
+	{
+	TLinearOrder<TInt32> orderer( CompareUids );
+	TInt pos = aKeys.FindInOrder( aMajorVersionUid.iUid, orderer );
+	if( KErrNotFound != pos )
+		{
+		if( aKeys[pos + 1] == aMinorVersionUid.iUid )
+			{		
+			TInt32 value;
+			TLex16 lex( aValues[pos] );
+			TInt err = lex.Val( value );
+				
+			if( KErrNone == err && value >= 0 && value <= KMaxTUint16 )
+				{
+				lex = aValues[pos+ 1];
+				err = lex.Val( value );
+				
+				if( KErrNone == err && value >= 0 && value <= KMaxTUint16 )
+					{
+					return;
+					}
+				}
+			aValues.Delete( pos + 1 );
+			aValues.InsertL( pos + 1, KNullDesC16 );
+			}
+		aValues.Delete( pos );
+		aValues.InsertL( pos, KNullDesC16 );
+		return;
+		}
+
+	pos = aKeys.FindInOrder( aMinorVersionUid.iUid, orderer );
+	if( KErrNotFound != pos )
+		{
+		aValues.Delete( pos );
+		aValues.InsertL( pos, KNullDesC16 );
+		}
+	}
+
+/**
+Gets the device type information attributes and stores them in their formatted form.
+
+@return	TUint16*	Formatted device type information attributes.
+@internalComponent
+*/
+static TUint16* FormattedDeviceTypeInfoL()
+	{
+	RArray<TInt32> keys;
+	CleanupClosePushL( keys );
+	
+	CDesC16ArraySeg* values = new (ELeave) CDesC16ArraySeg( 8 );
+	CleanupStack::PushL( values );
+	
+	GetDeviceAttributesL( keys, *values );
+	
+	// Validate the major and minor UI and OS version numbers. This should be done now to avoid repeated processing
+	// later on when the client calls either of the GetUIPlatformVersion APIs. An added advantage of doing 
+	// this now is that we save memory space if the version numbers are corrupt.
+	ValidateVersionNumbersL( keys, *values, KUIPlatformVersionMajorUid, KUIPlatformVersionMinorUid );
+	ValidateVersionNumbersL( keys, *values, KOSVersionMajorUid, KOSVersionMinorUid );
+	
+	TUint16* deviceAttributes = CreateDeviceTypeInfoPSDataL( keys, *values );
+
+	CleanupStack::PopAndDestroy( 2 );
+	
+	return deviceAttributes;
+	}
+
+/**
+Sets the Device Type Information attributes Publish and Subscribe property 
+value. If the property does not already exist it will define it. Otherwise, it 
+will set the value again.
+
+@leave -	One of the system-wide error codes.
+@internalComponent
+*/
+EXPORT_C void SetDeviceTypeInfoPropertyL()
+	{
+	TUint16* deviceAttributes = FormattedDeviceTypeInfoL();
+	CleanupStack::PushL( deviceAttributes );
+
+	// Set the Publish and Subscribe property
+	TPtrC16 deviceAttributesTPtrC( deviceAttributes, deviceAttributes[0] );
+	TInt propertyType = deviceAttributesTPtrC.Size() < 512 ? RProperty::EByteArray : RProperty::ELargeByteArray;
+	
+	TInt err = RProperty::Define( KDeviceTypeInfoUid.iUid, propertyType, KSecurityPolicyNone, 
+								  KSecurityPolicyWriteDeviceData, deviceAttributesTPtrC.Size() );
+	if ( KErrAlreadyExists != err )
+		{
+		User::LeaveIfError( err );
+		}	
+	User::LeaveIfError( RProperty::Set( KSysUtilSetupUid, KDeviceTypeInfoUid.iUid, deviceAttributesTPtrC ) );
+
+	CleanupStack::PopAndDestroy( 1 );
+	}
+
+// ========================= SysUtil MEMBER FUNCTIONS ==========================
+
+/**
+Obtains the displayable software version string. 
+
+Usage example:
+@code
+TBuf<KSysUtilVersionTextLength> version;
+if ( SysUtil::GetSWVersion( version ) == KErrNone )
+	{
+	// Use the version string.
+	...
+	}
+@endcode
+The software version is provisioned by the device creator into the ROM
+as a Unicode UTF16 format displayable string, for example:
+
+<code>V 1.0\\n29-07-07\\nBuild12345\\n(c) Symbian Software</code>
+
+This provisioned text string contains only one line of text with "\n" sequences
+in the text which indicate a new line. This API will parse the
+text and remove any "\n" sequences that it finds and replace it with the 
+Unicode newline sequence - 0x000A. The resulting
+buffer is then a unicode string with newline sequences built in. This
+then can for example, allow the buffer to be displayed directly to the
+screen already formatted on multiple lines.
+
+@param aValue On return, contains the software version string. The buffer should
+have space for KSysUtilVersionTextLength characters. If the buffer is 
+insufficient the descriptor is filled to its maximum length. If a buffer is 
+provided that is longer than 64 characters, and the provisioned text is larger 
+than 64 characters, the returned buffer is truncated at 64 characters (see error 
+codes below).
+@return KErrNone on success, KErrTooBig if the maximum length of the descriptor 
+is insufficient to hold the provisioned text, or has been truncated (see above). 
+In both cases new-line processing is done on the buffers. If a zero length
+text string has been provisioned KErrEof is returned, and the length of the 
+buffer is set to zero. If none of these cases apply then one of the Symbian 
+error codes is returned if reading the version string fails.
+*/
+EXPORT_C TInt SysUtil::GetSWVersion( TDes& aValue )
+	{
+	if( KSysUtilDisableVersionSetupExe )
+		{
+		__SYSUTIL_TRACE("GetSWVersion caching disabled");
+		
+		TFileName filePath;
+		TInt err = GetFilePath( filePath );
+		if( KErrNone != err)
+			{
+			return err;
+			}
+		
+		filePath.Append( KSWVersionFileName );
+		
+		err = GetTextFromFile( filePath, aValue, ETrue );
+		if ( err != KErrNone )
+			{
+			__SYSUTIL_TRACE2("Error: %d, while processing: %S",err, &filePath);
+			}
+		
+		return err;
+		}
+	else
+		{
+		__SYSUTIL_TRACE("GetSWVersion caching enabled");
+		return GetVersionPropertyData( KSWVersionUid, aValue );
+		}
+	}
+
+/**
+Returns displayable software version which the currently installed language 
+package is compatible with. 
+
+This version text is provisioned by the device creator into the ROM
+as a Unicode UTF16 format displayable string, for example:
+
+<code>V 1.0\\n29-07-07\\nBuild12345\\n(c) Symbian Software</code>
+
+This provisioned text string contains only one line of text with "\n" sequences
+in the text which indicate a new line. This API will parse the
+text and remove any "\n" sequences that it finds and replace it with the 
+Unicode newline sequence - 0x000A. The resulting
+buffer is then a unicode string with newline sequences built in. This
+then can for example, allow the buffer to be displayed directly to the
+screen already formatted on multiple lines.
+
+@param aValue On return, contains the software version string. The buffer should
+have space for KSysUtilVersionTextLength characters. If the buffer is 
+insufficient the descriptor is filled to its maximum length. If a buffer is 
+provided that is longer than 64 characters, and the provisioned text is larger 
+than 64 characters, the returned buffer is truncated at 64 characters (see error 
+codes below).
+@return KErrNone on success, KErrTooBig if the maximum length of the descriptor 
+is insufficient to hold the provisioned text, or has been truncated (see above). 
+In both cases new-line processing is done on the buffers. If a zero length
+text string has been provisioned KErrEof is returned, and the length of the 
+buffer is set to zero. If none of these cases apply then one of the Symbian 
+error codes is returned if reading the version string fails.
+*/
+EXPORT_C TInt SysUtil::GetLangSWVersion( TDes& aValue )
+	{
+	if( KSysUtilDisableVersionSetupExe )
+		{
+		__SYSUTIL_TRACE("GetLangSWVersion caching disabled");
+		
+		TFileName filePath;
+		TInt err = GetFilePath( filePath );
+		if( KErrNone != err)
+			{
+			return err;
+			}
+		
+		filePath.Append( KLangSWVersionFileName );
+		
+		err = GetTextFromFile( filePath, aValue, ETrue );
+		if ( err != KErrNone )
+			{
+			__SYSUTIL_TRACE2("Error: %d, while processing: %S",err, &filePath);
+			}
+		
+		return err;
+		}
+	else
+		{
+		__SYSUTIL_TRACE("GetLangSWVersion caching enabled");
+		return GetVersionPropertyData( KLangSWVersionUid, aValue );
+		}
+	}
+
+/**
+Obtains the displayable version of the currently installed language package.
+This does NOT do any newline processing on the version text.
+(unlike, for example GetLangSWVersion() or GetSWVersion()).
+
+This version text is provisioned by the device creator into the ROM
+as a Unicode UTF16 format displayable string.
+
+@param aValue On return, contains the software version string. The buffer should
+have space for KSysUtilVersionTextLength characters. If the buffer is 
+insufficient the descriptor is filled to its maximum length. If a buffer is 
+provided that is longer than 64 characters, and the provisioned text is larger 
+than 64 characters, the returned buffer is truncated at 64 characters (see error 
+codes below).
+@return KErrNone on success, KErrTooBig if the maximum length of the descriptor 
+is insufficient to hold the provisioned text, or has been truncated (see above). 
+In both cases new-line processing is done on the buffers. If a zero length
+text string has been provisioned KErrEof is returned, and the length of the 
+buffer is set to zero. If none of these cases apply then one of the Symbian 
+error codes is returned if reading the version string fails.
+*/
+EXPORT_C TInt SysUtil::GetLangVersion( TDes& aValue )
+	{
+	if( KSysUtilDisableVersionSetupExe )
+		{
+		__SYSUTIL_TRACE("GetLangVersion caching disabled");
+		
+		TFileName filePath;
+		TInt err = GetFilePath( filePath );
+		if( KErrNone != err)
+			{
+			return err;
+			}
+		
+		filePath.Append( KLangVersionFileName );
+		
+		err = GetTextFromFile( filePath, aValue, ETrue );
+		if ( err != KErrNone )
+			{
+			__SYSUTIL_TRACE2("Error: %d, while processing: %S",err, &filePath);
+			}
+		
+		return err;
+		}
+	else
+		{
+		__SYSUTIL_TRACE("GetLangVersion caching enabled");
+		return GetVersionPropertyData( KLangVersionUid, aValue );
+		}
+	}
+
+/**
+Obtains the displayable product release information string. 
+
+Usage example:
+@code
+TBuf<KSysUtilVersionTextLength> prInfo;
+if ( SysUtil::GetPRInformation( prInfo ) == KErrNone )
+    {
+    // Use the version string.
+    ...
+    }
+@endcode
+The product release information is provisioned by the device creator into the ROM
+as a Unicode UTF16 format displayable string, for example:
+
+<code>custom build\n20090626\nusb fix xyz</code>
+or
+<code>PR1.0</code>
+or
+<code>PR1.1</code>
+
+This provisioned text string contains only one line of text with "\n" sequences
+in the text which indicate a new line. This API will parse the
+text and remove any "\n" sequences that it finds and replace it with the 
+Unicode newline sequence - 0x000A. The resulting
+buffer is then a unicode string with newline sequences built in. This
+then can for example, allow the buffer to be displayed directly to the
+screen already formatted on multiple lines.
+
+@param aValue On return, contains the product release information string. The buffer should
+have space for KSysUtilVersionTextLength characters. If the buffer is 
+insufficient the descriptor is filled to its maximum length. If a buffer is 
+provided that is longer than 64 characters, and the provisioned text is larger 
+than 64 characters, the returned buffer is truncated at 64 characters.
+@return KErrNone on success, KErrTooBig if the maximum length of the descriptor 
+is insufficient to hold the provisioned text, or has been truncated. 
+In both cases new-line processing is done on the buffers. If a zero length
+text string has been provisioned KErrEof is returned, and the length of the 
+buffer is set to zero. If none of these cases apply then one of the Symbian 
+error codes is returned if reading the product release string fails.
+*/
+EXPORT_C TInt SysUtil::GetPRInformation( TDes& aValue )
+    {
+	TInt err = KErrNone;
+    if( KSysUtilDisableVersionSetupExe )
+        {
+        __SYSUTIL_TRACE("GetPRInformation caching disabled");
+        TFileName filePath;
+        GetFilePath( filePath );
+		filePath.Append( KPRInformationFileName );
+		err = GetTextFromFile( filePath, aValue, ETrue );
+		if ( err != KErrNone )
+			{
+			__SYSUTIL_TRACE2("Error: %d, while processing: %S",err, &filePath);
+			}
+		}
+    else
+        {
+        __SYSUTIL_TRACE("GetPRInformation caching enabled");
+        err = GetVersionPropertyData( KPRInformationUid, aValue );
+        }
+	return err;
+    }
+    
+/**
+Checks if free system drive storage space is or will fall below critical level.
+The system drive (internal, read/write, persistent drive) is also known
+as the FFS (internal flash file system).
+
+To calculate if a critical level has been reached the critical level threshold
+setting will be used. This setting is available in the patchable data.
+
+RAM drives and non-RAM drives have different threshold levels, so the corresponding
+setting will be used for the calculation depending on the drive type.
+
+RAM drives are defined to be drives that have the media type of EMediaRam
+as returned by RFs. @see RFs::Drive
+
+To set the patchdata for the RAM drive threshold set the MACRO 
+SYMBIAN_BAFL_SYSUTIL_RAM_DRIVE_CRITICAL_THRESHOLD at ROM build time to an
+appropriate level. To set the patchdata for the non-RAM drive threshold
+set the MACRO SYMBIAN_BAFL_SYSUTIL_OTHER_DISK_CRITICAL_THRESHOLD at ROM 
+build time to an appropriate level.
+
+This method also needs to determine which drive is the FFS drive. To do
+this patchable data has been provided. If the patchable data has not been
+set, then RFs::GetSystemDrive() will be used to determine the FFS drive. 
+
+To set the patchdata to the FFS drive set the MACRO 
+SYMBIAN_BAFL_SYSUTIL_DEFAULT_FFS_DRIVE to the appropriate drive letter. 
+@see TDriveNumber
+
+This function exists here to maintain binary compatibility .
+@see  SysUtil::FFSSpaceBelowCriticalLevelL
+
+@param aFs File server session. Must be given if available, e.g. from
+EIKON environment. If NULL, this method will create a temporary session,
+which causes the method to consume more time and system resources.
+@param aBytesToWrite Number of bytes the caller is about to write to the
+system drive. If value 0 is given, this method checks if the current system
+drive space is already below critical level.
+@return ETrue if system drive space would go below critical level after writing
+aBytesToWrite more data, EFalse otherwise.
+@leave System wide error codes
+*/
+EXPORT_C TBool SysUtil::FFSSpaceBelowCriticalLevel_OldL(
+    RFs* aFs,
+    TInt aBytesToWrite )
+    {
+    __SYSUTIL_TRACE("SysUtil::FFSSpaceBelowCriticalLevel_OldL");
+    TBool retVal( EFalse );
+    
+    RFs fs;
+    if ( !aFs )
+        {
+        User::LeaveIfError( fs.Connect() );  // Create temp session.
+        CleanupClosePushL( fs );
+        }
+    else
+        {
+        if( aFs->Handle() == KNullHandle )
+        	{
+        	User::Panic(_L("BAFL"), EBafPanicRFsConnectArg );
+        	}
+        fs = *aFs;
+        }
+
+    TInt ffsDrive = GetFFSDriveLetter( fs );
+    retVal = DiskSpaceBelowCriticalLevel_OldL(&fs, aBytesToWrite, ffsDrive );
+    
+	if ( !aFs )
+	    {
+	    CleanupStack::PopAndDestroy(); // Close temp session
+	    }
+    
+    return retVal;
+    }
+
+/**
+Checks if free disk drive storage space is or will fall below critical
+level. 
+
+To calculate if a critical level has been reached the critical level threshold
+setting will be used. This setting is available in the patchable data.
+
+RAM drives  and non-RAM drives have different threshold levels, so the corresponding
+setting will be used for the calculation depending on the drive type.
+
+RAM drives are defined to be drives that have the media type of EMediaRam
+as returned by RFs. @see RFs::Drive
+ 
+To set the patchdata for the RAM drive threshold set the MACRO 
+SYMBIAN_BAFL_SYSUTIL_RAM_DRIVE_CRITICAL_THRESHOLD at ROM build time to an
+appropriate level. To set the patchdata for the non-RAM drive threshold
+set the MACRO SYMBIAN_BAFL_SYSUTIL_OTHER_DISK_CRITICAL_THRESHOLD at ROM 
+build time to an appropriate level.
+
+This function exists here to maintain binary compatibility .
+@see  SysUtil::DiskSpaceBelowCriticalLevelL
+
+@param aFs File server session. Must be given if available, e.g. from
+EIKON environment. If NULL, this method will create a temporary session,
+which causes the method to consume more time and system resources.
+@param aBytesToWrite Number of bytes the caller is about to write to
+disk. If value 0 is given, this method checks if the current disk space
+is already below critical level.
+@param aDrive Identifies the disk drive to be checked. Numeric values
+for identifying disk drives are defined in TDriveNumber enumeration.
+@see TDriveNumber in f32file.h.
+@return ETrue if disk space would go below critical level after writing
+aBytesToWrite more data, EFalse otherwise.
+@leave System wide error codes
+*/
+EXPORT_C TBool SysUtil::DiskSpaceBelowCriticalLevel_OldL(
+    RFs* aFs,
+    TInt aBytesToWrite,
+    TInt aDrive )
+    {
+    __SYSUTIL_TRACE1("SysUtil::DiskSpaceBelowCriticalLevel_OldL( %d )",aDrive);
+    
+    RFs fs;
+    if ( !aFs )
+        {
+        User::LeaveIfError( fs.Connect() );  // Create temp session.
+        CleanupClosePushL( fs );
+        }
+    else
+        {
+        if( aFs->Handle() == KNullHandle )
+        	{
+        	User::Panic(_L("BAFL"), EBafPanicRFsConnectArg );
+        	}
+        fs = *aFs;
+        }
+
+    TVolumeInfo vinfo;
+    // This may leave e.g. KErrNotReady if no drive
+    TInt errorCode = fs.Volume( vinfo, aDrive );
+    
+    if ( !aFs )
+        {
+        CleanupStack::PopAndDestroy(); // Close temp session
+        }
+        
+	__SYSUTIL_TRACE1("SysUtil::DiskSpaceBelowCriticalLevel_OldL RFs::Volume returned error code %d.",errorCode);
+    User::LeaveIfError( errorCode );
+
+    const TInt64 criticalLevel = FindCriticalLevelTresholdL( vinfo.iDrive.iType );
+    __SYSUTIL_TRACE3("SysUtil: CL treshold value: %Ld, Free: %Ld, Size: %Ld",criticalLevel,vinfo.iFree,vinfo.iSize);
+  
+    return ( vinfo.iFree - (TInt64)aBytesToWrite ) <= criticalLevel;
+    }
+
+/**
+Checks if free MMC storage space is or will fall below critical
+level. 
+
+To calculate if a critical level has been reached the critical level threshold
+setting will be used. This setting is available in the patchable data.
+
+RAM drives and non-RAM drives have different threshold levels, so the corresponding
+setting will be used for the calculation depending on the drive type.
+
+RAM drives are defined to be drives that have the media type of EMediaRam
+as returned by RFs. @see RFs::Drive
+
+This method also needs to determine which drive is the MMC drive. To do
+this a patchable data has been provided. If the patchable data
+has not been set, then the drive will be fetched using BSUL.
+@see CCachedDriveInfo
+
+To set the patchdata for the RAM drive threshold set the MACRO 
+SYMBIAN_BAFL_SYSUTIL_RAM_DRIVE_CRITICAL_THRESHOLD at ROM build time to an
+appropriate level. To set the patchdata for the non-RAM drive threshold
+set the MACRO SYMBIAN_BAFL_SYSUTIL_OTHER_DISK_CRITICAL_THRESHOLD at ROM 
+build time to an appropriate level.
+
+To set the patchdata to the MMC drive set the MACRO 
+SYMBIAN_BAFL_SYSUTIL_DEFAULT_MMC_DRIVE to the appropriate drive letter. 
+@see TDriveNumber
+
+This function exists here to maintain binary compatibility .
+@see  SysUtil::MMCSpaceBelowCriticalLevelL
+
+@param aFs File server session. Must be given if available, e.g. from
+EIKON environment. If NULL, this method will create a temporary session,
+which causes the method to consume more time and system resources.
+@param aBytesToWrite Number of bytes the caller is about to write to
+MMC. If value 0 is given, this method checks if the current MMC space
+is already below critical level.
+@return ETrue if MMC space would go below critical level after writing
+aBytesToWrite more data, EFalse otherwise.
+@leave KErrNotFound if the MMC drive cannot be found, otherwise one of the
+system-wide error codes.
+*/
+EXPORT_C TBool SysUtil::MMCSpaceBelowCriticalLevel_OldL(RFs* aFs, TInt aBytesToWrite)
+	{
+	__SYSUTIL_TRACE("SysUtil::MMCSpaceBelowCriticalLevel_OldL");
+    TBool retVal(EFalse);
+
+    RFs fs;
+    if ( !aFs )
+        {
+        User::LeaveIfError( fs.Connect() );  // Create temp session.
+        CleanupClosePushL( fs );
+        }
+    else
+        {
+        if( aFs->Handle() == KNullHandle )
+        	{
+        	User::Panic(_L("BAFL"), EBafPanicRFsConnectArg );
+        	}
+        fs = *aFs;
+        } 
+    
+    TInt mmcDrive = GetMMCDriveLetter( fs );
+    retVal = DiskSpaceBelowCriticalLevel_OldL(&fs, aBytesToWrite, mmcDrive );
+ 	
+    if ( !aFs )
+        {
+        CleanupStack::PopAndDestroy(); // Close temp session
+        }
+ 			
+    return retVal;
+    }
+
+/**
+Checks if free system drive storage space is or will fall below critical level.
+The system drive (internal, read/write, persistent drive) is also known
+as the FFS (internal flash file system).
+
+To calculate if a critical level has been reached the critical level threshold
+setting will be used. This setting is available in the patchable data.
+
+RAM drives and non-RAM drives have different threshold levels, so the corresponding
+setting will be used for the calculation depending on the drive type.
+
+RAM drives are defined to be drives that have the media type of EMediaRam
+as returned by RFs. @see RFs::Drive
+
+To set the patchdata for the RAM drive threshold set the MACRO 
+SYMBIAN_BAFL_SYSUTIL_RAM_DRIVE_CRITICAL_THRESHOLD at ROM build time to an
+appropriate level. To set the patchdata for the non-RAM drive threshold
+set the MACRO SYMBIAN_BAFL_SYSUTIL_OTHER_DISK_CRITICAL_THRESHOLD at ROM 
+build time to an appropriate level.
+
+This method also needs to determine which drive is the FFS drive. To do
+this patchable data has been provided. If the patchable data has not been
+set, then RFs::GetSystemDrive() will be used to determine the FFS drive. 
+
+To set the patchdata to the FFS drive set the MACRO 
+SYMBIAN_BAFL_SYSUTIL_DEFAULT_FFS_DRIVE to the appropriate drive letter. 
+@see TDriveNumber
+
+@param aFs File server session. Must be given if available, e.g. from
+EIKON environment. If NULL, this method will create a temporary session,
+which causes the method to consume more time and system resources.
+@param aBytesToWrite Number of bytes the caller is about to write to the
+system drive. If value 0 is given, this method checks if the current system
+drive space is already below critical level.
+@return ETrue if system drive space would go below critical level after writing
+aBytesToWrite more data, EFalse otherwise.
+@leave System wide error codes
+*/
+
+EXPORT_C TBool SysUtil::FFSSpaceBelowCriticalLevelL(
+    RFs* aFs,
+    TInt64 aBytesToWrite )
+    {
+    __SYSUTIL_TRACE("SysUtil::FFSSpaceBelowCriticalLevelL");
+    TBool retVal( EFalse );
+    
+    RFs fs;
+    if ( !aFs )
+        {
+        User::LeaveIfError( fs.Connect() );  // Create temp session.
+        CleanupClosePushL( fs );
+        }
+    else
+        {
+        if( aFs->Handle() == KNullHandle )
+        	{
+        	User::Panic(_L("BAFL"), EBafPanicRFsConnectArg );
+        	}
+        fs = *aFs;
+        }
+
+    TInt ffsDrive = GetFFSDriveLetter( fs );
+    retVal = DiskSpaceBelowCriticalLevelL(&fs, aBytesToWrite, ffsDrive );
+    
+	if ( !aFs )
+	    {
+	    CleanupStack::PopAndDestroy(); // Close temp session
+	    }
+    
+    return retVal;
+    }
+
+/**
+Checks if free disk drive storage space is or will fall below critical
+level. 
+
+To calculate if a critical level has been reached the critical level threshold
+setting will be used. This setting is available in the patchable data.
+
+RAM drives  and non-RAM drives have different threshold levels, so the corresponding
+setting will be used for the calculation depending on the drive type.
+
+RAM drives are defined to be drives that have the media type of EMediaRam
+as returned by RFs. @see RFs::Drive
+ 
+To set the patchdata for the RAM drive threshold set the MACRO 
+SYMBIAN_BAFL_SYSUTIL_RAM_DRIVE_CRITICAL_THRESHOLD at ROM build time to an
+appropriate level. To set the patchdata for the non-RAM drive threshold
+set the MACRO SYMBIAN_BAFL_SYSUTIL_OTHER_DISK_CRITICAL_THRESHOLD at ROM 
+build time to an appropriate level.
+
+Usage example:
+@code
+TInt64 dataSize = 500000000;
+if ( SysUtil::DiskSpaceBelowCriticalLevelL( &iFsSession, dataSize, EDriveC ) )
+    {
+    // Can not write the data, there's not enough free space on disk.
+    ...
+    }
+else
+    {
+    // It's ok to actually write the data.
+    ...
+    }
+@endcode
+@param aFs File server session. Must be given if available, e.g. from
+EIKON environment. If NULL, this method will create a temporary session,
+which causes the method to consume more time and system resources.
+@param aBytesToWrite Number of bytes the caller is about to write to
+disk. If value 0 is given, this method checks if the current disk space
+is already below critical level.
+@param aDrive Identifies the disk drive to be checked. Numeric values
+for identifying disk drives are defined in TDriveNumber enumeration.
+@see TDriveNumber in f32file.h.
+@return ETrue if disk space would go below critical level after writing
+aBytesToWrite more data, EFalse otherwise.
+@leave System wide error codes
+*/
+
+EXPORT_C TBool SysUtil::DiskSpaceBelowCriticalLevelL(
+    RFs* aFs,
+    TInt64 aBytesToWrite,
+    TInt aDrive )
+    {
+    __SYSUTIL_TRACE1("SysUtil::DiskSpaceBelowCriticalLevelL( %d )",aDrive);
+    
+    RFs fs;
+    if ( !aFs )
+        {
+        User::LeaveIfError( fs.Connect() );  // Create temp session.
+        CleanupClosePushL( fs );
+        }
+    else
+        {
+        if( aFs->Handle() == KNullHandle )
+        	{
+        	User::Panic(_L("BAFL"), EBafPanicRFsConnectArg );
+        	}
+        fs = *aFs;
+        }
+
+    TVolumeInfo vinfo;
+    // This may leave e.g. KErrNotReady if no drive
+    TInt errorCode = fs.Volume( vinfo, aDrive );
+    
+    if ( !aFs )
+        {
+        CleanupStack::PopAndDestroy(); // Close temp session
+        }
+        
+	__SYSUTIL_TRACE1("SysUtil::DiskSpaceBelowCriticalLevelL RFs::Volume returned error code %d.",errorCode);
+    User::LeaveIfError( errorCode );
+
+    const TInt64 criticalLevel = FindCriticalLevelTresholdL( vinfo.iDrive.iType );
+    __SYSUTIL_TRACE3("SysUtil: CL treshold value: %Ld, Free: %Ld, Size: %Ld",criticalLevel,vinfo.iFree,vinfo.iSize);
+  
+    return ( vinfo.iFree - aBytesToWrite ) <= criticalLevel;
+    }
+
+/**
+Checks if free MMC storage space is or will fall below critical
+level. 
+
+To calculate if a critical level has been reached the critical level threshold
+setting will be used. This setting is available in the patchable data.
+
+RAM drives and non-RAM drives have different threshold levels, so the corresponding
+setting will be used for the calculation depending on the drive type.
+
+RAM drives are defined to be drives that have the media type of EMediaRam
+as returned by RFs. @see RFs::Drive
+
+This method also needs to determine which drive is the MMC drive. To do
+this a patchable data has been provided. If the patchable data
+has not been set, then the drive will be fetched using BSUL.
+@see CCachedDriveInfo
+
+To set the patchdata for the RAM drive threshold set the MACRO 
+SYMBIAN_BAFL_SYSUTIL_RAM_DRIVE_CRITICAL_THRESHOLD at ROM build time to an
+appropriate level. To set the patchdata for the non-RAM drive threshold
+set the MACRO SYMBIAN_BAFL_SYSUTIL_OTHER_DISK_CRITICAL_THRESHOLD at ROM 
+build time to an appropriate level.
+
+To set the patchdata to the MMC drive set the MACRO 
+SYMBIAN_BAFL_SYSUTIL_DEFAULT_MMC_DRIVE to the appropriate drive letter. 
+@see TDriveNumber
+
+@param aFs File server session. Must be given if available, e.g. from
+EIKON environment. If NULL, this method will create a temporary session,
+which causes the method to consume more time and system resources.
+@param aBytesToWrite Number of bytes the caller is about to write to
+MMC. If value 0 is given, this method checks if the current MMC space
+is already below critical level.
+@return ETrue if MMC space would go below critical level after writing
+aBytesToWrite more data, EFalse otherwise.
+@leave KErrNotFound if the MMC drive cannot be found, otherwise one of the
+system-wide error codes.
+*/
+EXPORT_C TBool SysUtil::MMCSpaceBelowCriticalLevelL(RFs* aFs, TInt64 aBytesToWrite)
+	{
+	__SYSUTIL_TRACE("SysUtil::MMCSpaceBelowCriticalLevelL");
+    TBool retVal(EFalse);
+
+    RFs fs;
+    if ( !aFs )
+        {
+        User::LeaveIfError( fs.Connect() );  // Create temp session.
+        CleanupClosePushL( fs );
+        }
+    else
+        {
+        if( aFs->Handle() == KNullHandle )
+        	{
+        	User::Panic(_L("BAFL"), EBafPanicRFsConnectArg );
+        	}
+        fs = *aFs;
+        } 
+    
+    TInt mmcDrive = GetMMCDriveLetter( fs );
+    retVal = DiskSpaceBelowCriticalLevelL(&fs, aBytesToWrite, mmcDrive );
+ 	
+    if ( !aFs )
+        {
+        CleanupStack::PopAndDestroy(); // Close temp session
+        }
+ 			
+    return retVal;
+    }
+
+
+/**
+Returns the FFS drive letter  
+
+This method determines which drive is the FFS drive. To do
+this a patchable data has been provided. If the patchable data
+has not been set, then the drive will be fetched via a call to
+GetSystemDrive().
+
+To set the patchdata to the FFS drive set the MACRO 
+SYMBIAN_BAFL_SYSUTIL_DEFAULT_FFS_DRIVE to the appropriate drive letter. 
+@see TDriveNumber
+
+@return TInt the integer value equivalent of the TDriveNumber
+*/
+EXPORT_C TInt SysUtil::GetFFSDriveLetter( RFs & aFs )
+	{
+	// Check the argument, and panic if necessary
+    if( aFs.Handle() == KNullHandle )
+    	{
+    	User::Panic(_L("KERN-EXEC"), 0);
+    	}
+	
+	// Check to see if FFS drive is set in patchable data
+	TInt ffsDrive = KSysUtilDefaultFFSDrive;
+	if( !(ffsDrive >= EDriveA && ffsDrive <= EDriveZ) )
+		{
+		ffsDrive =  aFs.GetSystemDrive();
+		} 
+
+	return ffsDrive;
+	}
+
+/**
+Returns the MMC drive letter  
+
+This method determines which drive is the MMC drive. To do
+this a patchable data has been provided. 
+
+To set the patchdata to the MMC drive set the MACRO 
+SYMBIAN_BAFL_SYSUTIL_DEFAULT_MMC_DRIVE to the appropriate drive letter. 
+@see TDriveNumber
+
+@return TInt the integer value equivalent of the TDriveNumber
+*/
+
+EXPORT_C TInt SysUtil::GetMMCDriveLetter( RFs & aFs )
+	{
+	// Check the argument, and panic if necessary
+    if( aFs.Handle() == KNullHandle )
+    	{
+    	User::Panic(_L("KERN-EXEC"), 0);
+    	}
+    
+    // Check to see if MMC drive is set in patchable data
+    TInt mmcDrive = KSysUtilDefaultMMCDrive;
+    
+	// If a valid mmcDrive is not currently found in patchable data, then search for one.
+    if( !(mmcDrive >= EDriveA && mmcDrive <= EDriveZ) )
+		{
+		__SYSUTIL_TRACE("SysUtil::MMCSpaceBelowCriticalLevelL - searching for MMC drive");
+	    // If mmcDrive is not set in patchable data, then fetch the MMC drive from BSUL.
+	    BSUL::CCachedDriveInfo *cachedDriveInfo = BSUL::CCachedDriveInfo::NewLC( aFs );
+	    // This will find valid MMC drive, leave if none available
+	    mmcDrive = cachedDriveInfo->GetDefaultRemovableMemoryCardDriveL();
+		__SYSUTIL_TRACE1("SysUtil::MMCSpaceBelowCriticalLevelL, Selected MMC drive %d.",mmcDrive);
+		CleanupStack::PopAndDestroy(cachedDriveInfo);
+		}
+
+	return mmcDrive;
+	}
+
+/**
+Creates and returns a populated CDeviceTypeInformation object which is used 
+to access the device type information attributes.
+
+All the attributes are provisioned by the device creator.
+
+The returned object is owned by the calling code and so it is the calling
+codes responsibility to delete this object when it no longer requires it. 
+
+@return	CDeviceTypeInformation*	A pointer to an instance of CDeviceTypeInformation.
+@leave	KErrNoMemory			If there is not enough memory to create the object. 
+@leave	KErrNotReady			If there was a problem retrieving the device type
+ 								information attributes.
+@leave	-			 			Otherwise one of the other system-wide error codes.
+@see CDeviceTypeInformation
+*/
+EXPORT_C CDeviceTypeInformation* SysUtil::GetDeviceTypeInfoL()
+	{
+	__SYSUTIL_TRACE("SysUtil::GetDeviceTypeInfoL");
+	return CDeviceTypeInformation::NewL();
+	}
+
+// ================= CDeviceTypeInformation MEMBER FUNCTIONS ===================
+
+/**
+Class destructor.
+
+Performs any clean up such as deleting memory on the heap.
+*/
+EXPORT_C CDeviceTypeInformation::~CDeviceTypeInformation()
+	{
+	SDeviceAttributes* attr = reinterpret_cast<SDeviceAttributes*>( iImpl );
+	if( attr != NULL )
+		{
+		delete attr->iDeviceAttributes;
+		delete attr;
+		}
+	}
+
+/**
+Allocates and constructs an instance of CDeviceTypeInformation.
+
+@return A pointer to an instance of CDeviceTypeInformation.
+@leave KErrNoMemory, if there is not enough memory to create the object.
+@leave -			 Otherwise one of the other system-wide error codes.
+*/
+CDeviceTypeInformation* CDeviceTypeInformation::NewL()
+	{
+	CDeviceTypeInformation* self = new (ELeave) CDeviceTypeInformation();
+	CleanupStack::PushL( self );
+	self->ConstructL();
+	CleanupStack::Pop( self );
+	return self;
+	}
+
+/**
+Class constructor.
+
+Performs any class construction tasks that will not cause a leave.
+*/
+CDeviceTypeInformation::CDeviceTypeInformation() : iImpl( NULL )
+	{
+	}
+
+/**
+Performs class creation tasks as part of two phase construction.
+
+Performs any class construction tasks that will cause a leave.
+*/
+void CDeviceTypeInformation::ConstructL()
+	{
+	SDeviceAttributes* attr = new (ELeave) SDeviceAttributes;
+	attr->iDeviceAttributes = NULL;
+	iImpl = reinterpret_cast<TImpl*>( attr );
+	
+	if( !KSysUtilDisableDeviceTypeInfoSetupExe )
+		{
+		__SYSUTIL_TRACE("GetDeviceTypeInfoL caching enabled");
+		
+		// Check if the P&S property is already set. If it isn't then define and set it. If it 
+		// is we will most likely get KErrOverflow as we are only providing enough space to 
+		// get the first TUint16. This stores the length of the attribute.
+		TUint16 attributesLength;
+		TPtr16 attributesLengthTPtr( &attributesLength, 1 );
+		TInt err = RProperty::Get( KSysUtilSetupUid, KDeviceTypeInfoUid.iUid, attributesLengthTPtr );
+		if ( KErrNotFound == err )
+			{
+			__SYSUTIL_TRACE("Launching sysutilsetup.exe");
+			User::LeaveIfError( LaunchSetupExecutable( KSetDeviceTypeInfoPropertyFlag ) );
+			__SYSUTIL_TRACE("sysutilsetup.exe terminated");
+			
+			err = RProperty::Get( KSysUtilSetupUid, KDeviceTypeInfoUid.iUid, attributesLengthTPtr );
+			if( KErrOverflow != err )
+				{
+				User::LeaveIfError( err );
+				}
+			}
+		else if( KErrOverflow != err )
+			{
+			User::LeaveIfError( err );
+			}
+		
+		attr->iDeviceAttributes = static_cast<TUint16*>( User::AllocL( sizeof(TUint16) * attributesLength ) );
+		TPtr16 deviceAttributesTPtr( attr->iDeviceAttributes, attributesLength );
+		User::LeaveIfError( RProperty::Get( KSysUtilSetupUid, KDeviceTypeInfoUid.iUid, deviceAttributesTPtr ) );
+		}
+	else
+		{
+		__SYSUTIL_TRACE("GetDeviceTypeInfoL caching disabled");
+		attr->iDeviceAttributes = FormattedDeviceTypeInfoL();
+		}
+	}
+
+/**
+Retrieves a reference to the attribute string which matches the provided 
+UID. The attribute has a maximum length of KMaxAttributeLength UTF-16 
+characters. 
+
+The attribute is provisioned by the device creator. If the device creator 
+does not provide an attribute value for the given UID then KNullDesC16 
+will be supplied and KErrNotFound will be returned. If the device creator 
+has supplied an attribute value which is longer than KMaxAttributeLength 
+then the supplied value will be truncated. In this case KErrKErrOverflow 
+will be returned.
+
+@param	aAttributeUid	The UID of the required attribute.
+@param	aValue			On return, contains the attribute value if successful 
+						and KNullDesC16 otherwise.
+@return KErrNone		Successful, the provisioned value has been returned.
+@return KErrNotFound	A value for the given UID was not provisioned.
+@return KErrOverflow	The provisioned attribute value has been truncated.
+@return -				Otherwise, one of the other system-wide error codes.
+@publishedPartner
+@released
+*/
+EXPORT_C TInt CDeviceTypeInformation::GetAttribute( const TUid& aAttributeUid, TPtrC16& aValue ) const
+	{
+	SDeviceAttributes* attr = reinterpret_cast<SDeviceAttributes*>( this->iImpl );
+
+	TInt attributeNumber = FindAttributeNumber( *attr, aAttributeUid );
+	if( KErrNotFound != attributeNumber )
+		{
+		aValue.Set( attr->AttributePtr( attributeNumber ), attr->AttributeLength( attributeNumber ) );
+		return attr->Error( attributeNumber );
+		}
+
+	aValue.Set( KNullDesC16 );
+	return KErrNotFound;
+	}
+
+/**
+Retrieves a reference to the Manufacturer Name string. This value conveys 
+the name of the device manufacturer. The Manufacturer Name has a maximum 
+length of KMaxAttributeLength UTF-16 characters. 
+
+This is a standard device type information attribute (it is common to all 
+device creators) as such it can be assumed that an attribute value will 
+always be retrieved.
+
+The Manufacturer Name is provisioned by the device creator. If the device 
+creator does not provide a value for this attribute then a default value 
+will be supplied instead and KDefaultValue will be returned. If the device 
+creator has supplied a value and it is longer than KMaxAttributeLength 
+then the provisioned value will be truncated. In this case KErrOverflow 
+will be returned.
+
+Note: This attribute is represented by a UID of 0x10286358. 
+
+@param	aValue 			On return, contains the Manufacturer Name UTF-16 string.
+@return KErrNone		Successful, the provisioned value has been returned.
+@return KDefaultValue	Successful, the default value has been returned.
+@return KErrOverflow	The provisioned attribute value has been truncated
+@return -				Otherwise one of the other system-wide error codes.
+*/
+EXPORT_C TInt CDeviceTypeInformation::GetManufacturerName( TPtrC16& aValue ) const
+	{
+	TInt err = GetAttribute( KManufacturerNameUid, aValue );
+	if( KErrNotFound == err )
+		{
+		aValue.Set( KDefaultManufacturer );
+		return CDeviceTypeInformation::KDefaultValue;
+		}
+	
+	return err;
+	}
+
+/**
+Retrieves a reference to the Model Name string. This value conveys the 
+model name of the device as recognisable by the end-user i.e. the consumer. 
+The Model Name has a maximum length of KMaxAttributeLength UTF-16 
+characters.  
+
+This is a standard device type information attribute (it is common to all 
+device creators) as such it can be assumed that an attribute value will 
+always be retrieved.
+
+The Model Name is provisioned by the device creator. If the device creator 
+does not provide a value for this attribute then a default value will be 
+supplied instead and KDefaultValue will be returned. If the device creator 
+has supplied a value and it is longer than KMaxAttributeLength then the 
+provisioned value will be truncated. In this case KErrKErrOverflow will be 
+returned.
+
+Note: This attribute is represented by a UID of 0x10286359. 
+
+@param	aValue			On return, contains the Model Name.
+@return KErrNone		Successful, the provisioned value has been returned.
+@return KDefaultValue	Successful, the default value has been returned.
+@return KErrOverflow	The provisioned attribute value has been truncated.
+@return -				Otherwise, one of the other system-wide error codes.
+*/
+EXPORT_C TInt CDeviceTypeInformation::GetModelName( TPtrC16& aValue ) const
+	{
+	TInt err = GetAttribute( KModelNameUid, aValue );
+	if( KErrNotFound == err )
+		{
+		aValue.Set( KDefaultModelName );
+		return CDeviceTypeInformation::KDefaultValue;
+		}
+	
+	return err;
+	}
+
+/**
+Retrieves a reference to the Model Code string. This value conveys the 
+internal model name or part number by which this model is known to the 
+manufacturer. The Model Code has a maximum length of KMaxAttributeLength 
+UTF-16 characters. 
+
+This is a standard device type information attribute (it is common to all 
+device creators) as such it can be assumed that an attribute value will 
+always be retrieved.
+
+The Model Code is provisioned by the device creator. If the device creator 
+does not provide a value for this attribute then a default value will be 
+supplied instead and KDefaultValue will be returned. If the device creator 
+has supplied a value and it is longer than KMaxAttributeLength then the 
+provisioned value will be truncated. In this case KErrKErrOverflow will be 
+returned.
+
+Note: This attribute is represented by a UID of 0x1028635A. 
+
+@param	aValue			On return, contains the Model Code.
+@return KErrNone		Successful, the provisioned value has been returned.
+@return KDefaultValue	Successful, the default value has been returned.
+@return KErrOverflow	The provisioned attribute value has been truncated.
+@return -				Otherwise, one of the other system-wide error codes.
+*/
+EXPORT_C TInt CDeviceTypeInformation::GetModelCode( TPtrC16& aValue ) const
+	{
+	TInt err = GetAttribute( KModelCodeUid, aValue );
+	if( KErrNotFound == err )
+		{
+		aValue.Set( KDefaultModelCode );
+		return CDeviceTypeInformation::KDefaultValue;
+		}
+	
+	return err;
+	}
+
+/**
+Retrieves a reference to the Revision ID string. This value contains the 
+device revision and/or variant identification string and conveys the 
+specific version of the hardware and software used in the device. The 
+Revision ID has a maximum length of KMaxAttributeLength UTF-16
+characters. 
+
+This is a standard device type information attribute (it is common to all 
+device creators) as such it can be assumed that an attribute value will 
+always be retrieved.
+
+The Revision ID is provisioned by the device creator. If the device 
+creator does not provide a value for this attribute then a default value 
+will be supplied instead and KDefaultValue will be returned. If the device 
+creator has supplied a value and it is longer than KMaxAttributeLength 
+then the provisioned value will be truncated. In this case 
+KErrKErrOverflow will be returned.
+
+Note: This attribute is represented by a UID of 0x1028635B. 
+
+@param	aValue			On return, contains the Revision ID (this also 
+						includes the Variant ID).
+@return KErrNone		Successful, the provisioned value has been returned.
+@return KDefaultValue	Successful, the default value has been returned.
+@return KErrOverflow	The provisioned attribute value has been truncated.
+@return -				Otherwise, one of the other system-wide error codes.
+*/
+EXPORT_C TInt CDeviceTypeInformation::GetRevisionID( TPtrC16& aValue ) const
+	{
+	TInt err = GetAttribute( KRevisionIDUid, aValue );
+	if( KErrNotFound == err )
+		{
+		aValue.Set( KDefaultRevisionID );
+		return CDeviceTypeInformation::KDefaultValue;
+		}
+	
+	return err;
+	}
+
+/**
+Retrieves a copy of the default Device Name string. This value conveys 
+the default name for the device as might be used for network identification 
+e.g. Bluetooth nickname. The Device Name has a maximum length of 
+KMaxAttributeLength UTF-16 characters. 
+
+This is a standard device type information attribute (it is common to all 
+device creators) as such it can be assumed that an attribute value will 
+always be retrieved.
+
+The Default Device Name is provisioned by the device creator. If the device 
+creator does not provide a value for this attribute then a default value 
+will be supplied instead and KDefaultValue will be returned. If the device 
+creator has supplied a value and it is longer than KMaxAttributeLength or 
+the user supplied descriptors length is less than KMaxAttributeLength then 
+the provioned value will be truncated. In this case KErrKErrOverflow will 
+be returned.
+
+Note: This attribute is represented by a UID of 0x1028635C. 
+
+@param	aValue			On return, contains the Default Device Name.
+@return KErrNone		Successful, the provisioned value has been returned.
+@return KDefaultValue	Successful, the default value has been returned.
+@return KErrOverflow	The provisioned attribute value has been truncated.
+@return -				Otherwise, one of the other system-wide error codes.
+*/
+EXPORT_C TInt CDeviceTypeInformation::GetDefaultDeviceName( TPtrC16& aValue ) const
+	{
+	TInt err = GetAttribute( KDefaultDeviceNameUid, aValue );
+	if( KErrNotFound == err )
+		{
+		aValue.Set( KDefaultDeviceName );
+		return CDeviceTypeInformation::KDefaultValue;
+		}
+	
+	return err;
+	}
+
+/**
+Retrieves a reference to the name of the UI Platform software used 
+in the device. The UI Platform name has a maximum length of 
+KMaxAttributeLength UTF-16 characters. 
+
+This is a standard device type information attribute (it is common to all 
+device creators) as such it can be assumed that an attribute value will 
+always be retrieved.
+
+The UI Platform is provisioned by the device creator. If the device 
+creator does not provide a value for this attribute then a default value 
+will be supplied instead and KDefaultValue will be returned. If the device 
+creator has supplied a value and it is longer than KMaxAttributeLength 
+then the provisioned value will be truncated. In this case 
+KErrKErrOverflow will be returned.
+
+Note: This attribute is represented by a UID of 0x1028635D. 
+
+@param	aValue			On return, contains the UI Platform.
+@return KErrNone		Successful, the provisioned value has been returned.
+@return KDefaultValue	Successful, the default value has been returned.
+@return KErrOverflow	The provisioned attribute value has been truncated.
+@return -				Otherwise, one of the other system-wide error codes.
+*/
+EXPORT_C TInt CDeviceTypeInformation::GetUIPlatformName( TPtrC16& aValue ) const
+	{
+	TInt err = GetAttribute( KUIPlatformNameUid, aValue );
+	if( KErrNotFound == err )
+		{
+		aValue.Set( KDefaultUIPlatform );
+		return CDeviceTypeInformation::KDefaultValue;
+		}
+	
+	return err;
+	}
+
+/**
+Retrieves a reference to the UI Platform version used in the device for 
+display, transmission or tagging purposes. The UI Platform version has a maximum 
+length of KMaxAttributeLength UTF-16 characters. 
+
+The string should never be used for programmatic decisions based on assumed
+functionality present in the device as device creators can vary the content of
+the device firmware. Instead Feature Manager should be used to query the 
+functional capabilities of a device. @see CFeatureDiscovery
+
+The UI Platform version is provisioned by the device creator. If the device 
+creator does not provide a value for this attribute then a default value 
+indicating unknown version will be supplied instead and KDefaultValue will 
+be returned. If the device creator has supplied a value and it is longer than 
+KMaxAttributeLength then the provisioned value will be truncated. In this case 
+KErrOverflow will be returned. The format of the string is device dependent.
+
+Note: This attribute is represented by a UID of 0x10286360. 
+
+@param	aValue			On return, contains the UI version number of the 
+						current UI platform.
+@return KErrNone		Successful, the provisioned value has been returned.
+@return KDefaultValue	Successful, the default value has been returned.
+@return KErrOverflow	The provisioned attribute value has been truncated 
+						due to the provided descriptor being too small.
+@return -				Otherwise, one of the other system-wide error codes.
+*/
+EXPORT_C TInt CDeviceTypeInformation::GetUIPlatformVersion( TPtrC16& aValue ) const
+	{
+	TInt err = GetAttribute( KUIPlatformVersionUid, aValue );
+	if( KErrNotFound == err )
+		{
+		aValue.Set( KDefaultUIPlatformVersion );
+		return CDeviceTypeInformation::KDefaultValue;
+		}
+	
+	return err;
+	}
+
+/**
+Retrieves the UI platform major and minor version numbers as TUint16s. 
+
+The retrieved values are both standard device type information attributes 
+(they are common to all device creators) as such it can be assumed that 
+values will always be retrieved.
+
+The UI version is provisioned by the device creator. If the device creator 
+provides invalid major and minor UI version numbers the default version 
+numbers will be supplied and KErrCorrupt will be returned. If the device 
+creator does not provide values for these attributes then default values 
+will be supplied and KDefaultValue will be returned.
+
+Note: The major UI version number attribute is represented by a UID of 
+0x1028635E and minor UI version number is represented by a UID of 
+0x1028635F. 
+
+@param	aMajor			On return, contains the UI major version number.
+@param	aMinor			On return, contains the UI minor version number.
+@return KErrNone		Successful, the provisioned value has been returned.
+@return KDefaultValue	Successful, the default value has been returned.
+@return KErrCorrupt		The provisioned attribute value is invalid. The 
+						default value has been returned.
+@return -				Otherwise, one of the other system-wide error codes.
+*/
+EXPORT_C TInt CDeviceTypeInformation::GetUIPlatformVersion( 
+	TUint16& aMajor, 
+	TUint16& aMinor ) const
+	{
+	SDeviceAttributes* attr = reinterpret_cast<SDeviceAttributes*>( this->iImpl );
+	TPtrC16 majorVersionNum( KDefaultUIPlatformVersionMajor );
+	TPtrC16 minorVersionNum( KDefaultUIPlatformVersionMinor );
+	
+	// If we have valid major and minor numbers use these instead of the default values. 
+	// Otherwise check to see if the minor version number exists. If it does the error 
+	// number associated with this should be used instead of KDefaultValue as the 
+	// version number (in major/minor form) is corrupt.
+	TInt attributeNumber = FindAttributeNumber( *attr, KUIPlatformVersionMajorUid );
+	if( KErrNotFound != attributeNumber )
+		{
+		if( attr->Uid( attributeNumber + 1 ) == KUIPlatformVersionMinorUid.iUid )
+			{
+			if( KErrNone == attr->Error( attributeNumber ) && KErrNone == attr->Error( attributeNumber + 1 ) )
+				{
+				majorVersionNum.Set( attr->AttributePtr( attributeNumber ), attr->AttributeLength( attributeNumber ) );
+				minorVersionNum.Set( attr->AttributePtr( attributeNumber + 1 ), attr->AttributeLength( attributeNumber + 1 ) );
+				}
+			}
+		}
+	else
+		{
+		attributeNumber = FindAttributeNumber( *attr, KUIPlatformVersionMinorUid );
+		}
+	
+	TLex16 lex( majorVersionNum );
+	lex.Val( aMajor, EDecimal );
+	
+	lex.Assign( minorVersionNum );
+	lex.Val( aMinor, EDecimal );
+
+	return attributeNumber == KErrNotFound ? KDefaultValue : attr->Error( attributeNumber );
+	}
+
+/**
+Retrieves a reference to the Symbian OS version used in the device for 
+display, transmission or tagging purposes. The Symbian OS version has a maximum 
+length of KMaxAttributeLength UTF-16 characters. 
+
+The string should never be used for programmatic decisions based on assumed
+functionality present in the device as device creators can very the content of
+the device firmware. Instead Feature Manager should be used to query the 
+functional capabilities of a device. @see CFeatureDiscovery
+
+This is a standard device type information attribute (it is common to all 
+devices) as such it can be assumed that an attribute value will 
+always be retrieved. 
+ 
+The Symbian OS version is provisioned by the device creator. If the device 
+creator does not provide a value for this attribute then a default value 
+indicating unknown version will be supplied instead and KDefaultValue will 
+be returned. If the device creator has supplied a value and it is longer than 
+KMaxAttributeLength then the provisioned value will be truncated. In this case 
+KErrOverflow will be returned. The format of the string is device dependent.
+
+Note: This attribute is represented by a UID of 0x10286363. 
+
+@param	aValue			On return, contains the Symbian OS version number.
+@return KErrNone		Successful, the provisioned value has been returned.
+@return KDefaultValue	Successful, the default value has been returned.
+@return KErrOverflow	The provisioned attribute value has been truncated 
+						due to the provided descriptor being too small.
+@return -				Otherwise one of the other system-wide error codes.
+*/
+EXPORT_C TInt CDeviceTypeInformation::GetOSVersion( TPtrC16& aValue ) const
+	{
+	TInt err = GetAttribute( KOSVersionUid, aValue );
+	if( KErrNotFound == err )
+		{
+		aValue.Set( KDefaultOSVersion );
+		return CDeviceTypeInformation::KDefaultValue;
+		}
+	
+	return err;
+	}
+
+/**
+Retrieves the Symbian OS major and minor version numbers as TUint16s. 
+
+The retrieved values are both standard device type information attributes 
+(they are common to all device creators) as such it can be assumed that 
+values will always be retrieved.
+
+The OS version is provisioned by the device creator. If the device creator 
+provides invalid major and minor UI version numbers the default version 
+numbers will be supplied and KErrCorrupt will be returned. If the device 
+creator does not provide values for these attributes then default values 
+will be supplied and KDefaultValue will be returned.
+
+Note: The major UI version number attribute is represented by a UID of 
+0x10286361 and minor UI version number is represented by a UID of 
+0x10286362. 
+
+@param	aMajor			On return, contains the OS major version number.
+@param	aMinor			On return, contains the OS minor version number.
+@return KErrNone		Successful, the provisioned value has been returned.
+@return KDefaultValue	Successful, the default value has been returned.
+@return KErrCorrupt		The provisioned attribute value is invalid. The 
+						default value has been returned.
+@return -				Otherwise, one of the other system-wide error codes.
+*/
+EXPORT_C TInt CDeviceTypeInformation::GetOSVersion( TUint16& aMajor,
+	TUint16& aMinor ) const
+	{
+	SDeviceAttributes* attr = reinterpret_cast<SDeviceAttributes*>( this->iImpl );
+	TPtrC16 majorVersionNum( KDefaultOSVersionMajor );
+	TPtrC16 minorVersionNum( KDefaultOSVersionMinor );
+	
+	// If we have valid major and minor numbers use these instead of the default values. 
+	// Otherwise check to see if the minor version number exists. If it does the error 
+	// number associated with this should be used instead of KDefaultValue as the 
+	// version number (in major/minor form) is corrupt.
+	TInt attributeNumber = FindAttributeNumber( *attr, KOSVersionMajorUid );
+	if( KErrNotFound != attributeNumber )
+		{
+		if( attr->Uid( attributeNumber + 1 ) == KOSVersionMinorUid.iUid )
+			{
+			if( KErrNone == attr->Error( attributeNumber ) && KErrNone == attr->Error( attributeNumber + 1 ) )
+				{
+				majorVersionNum.Set( attr->AttributePtr( attributeNumber ), attr->AttributeLength( attributeNumber ) );
+				minorVersionNum.Set( attr->AttributePtr( attributeNumber + 1 ), attr->AttributeLength( attributeNumber + 1 ) );
+				}
+			}
+		}
+	else
+		{
+		attributeNumber = FindAttributeNumber( *attr, KOSVersionMinorUid );
+		}
+	
+	TLex16 lex( majorVersionNum );
+	lex.Val( aMajor, EDecimal );
+	
+	lex.Assign( minorVersionNum );
+	lex.Val( aMinor, EDecimal );
+	
+	return attributeNumber == KErrNotFound ? KDefaultValue : attr->Error( attributeNumber );
+	}