// 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 );
	}
