tzservices/tzserver/Server/Source/readonlytzdb.cpp
author William Roberts <williamr@symbian.org>
Fri, 23 Apr 2010 14:37:17 +0100
branchRCL_3
changeset 22 c82a39b81a38
parent 0 2e3d3ce01487
child 81 676b6116ca93
permissions -rw-r--r--
Rework addition of Symbian splash screen to reduce the source impact (uses SVG from Bug 2414) Notes: by using the OPTION SOURCEDIR parameter in the mifconv extension instructions, I can arrange to use the same source file name in sfimage, without having to export over the original Nokia file. This means that the name inside splashscreen.mbg is the same, which removes the need for the conditional compilation in SplashScreen.cpp, and gets rid of sf_splashscreen.mmp.

// Copyright (c) 2004-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 <tz.h>
#include "ReadOnlyTzDb.h"
#include <f32file.h>
#include "dataprovider.h"
#include <vtzrules.h>

const TUint KDefaultTzNumericId = 0;

// TZ Database is stored in the timezone server's own private area
_LIT(KTzDbFileNameRom,"Z:\\private\\1020383E\\TZDB.DBZ");
_LIT(KTzDbFileNameFlash,"C:\\private\\1020383E\\TZDB.DBZ");
_LIT(KTzDbFileNameFlashCorrupt,"C:\\private\\1020383E\\TZDB.DB~");

CReadOnlyTzDb* CReadOnlyTzDb::NewL(RFs& aFs, CTzSwiObserver& aSwiObserver, MTzDataObserver& aTzDataObserver)
	{
	CReadOnlyTzDb* self = new(ELeave) CReadOnlyTzDb(aFs, aSwiObserver, aTzDataObserver);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

CReadOnlyTzDb::CReadOnlyTzDb(RFs& aFs, CTzSwiObserver& aSwiObserver, MTzDataObserver& aTzDataObserver)
	: iFs(aFs), iSwiObserver(aSwiObserver), iTzDataObserver(aTzDataObserver), iTzDbDataFromFlash(EFalse)
	{
	}

void CReadOnlyTzDb::ConstructL()
	{
	// Open the database, map the file into memory and build the entity tables
	InitialiseL();
	// Add observer to watch SWI completion
	iSwiObserver.AddObserverL(this);
	}

//
// Returns the memory address of the start of RAM copy of TZ Database file
//
TInt CReadOnlyTzDb::StartAddress() const 
	{ 
	return iStartAddress; 
	}

//
// Loads the TZ Database from given file into RAM.
//
TInt CReadOnlyTzDb::CopyDatabaseToRam(const TDesC& aTzDbFileName)
  {
	TInt error = KErrNone;
	RFile file;

	TUint fileMode = EFileShareReadersOnly | EFileRead | EFileStream;

	error = file.Open(iFs, aTzDbFileName, fileMode);
	if (error == KErrNone)
		{
		TInt size = 0;
		error = file.Size(size);
		if ( (error == KErrNone) && (size <= 0) )
			{
			error = KErrCorrupt;
			}
		if (error == KErrNone)
			{
			if (iTzDbData) 
				{
				iChunk.Close();
				iTzDbData = NULL;
				}
	
			error = iChunk.CreateLocal(size,size) ;
			if (error == KErrNone )
				{
				// Allocate memory inside the memory chunk
				iTzDbData = new (iChunk.Base()) TUint8 [size];
				// Set a TPtr to "point" at the created memory
				TPtr8 flashDataPtr(iTzDbData, size);

				error = file.Read(0,flashDataPtr,size);
				if (error == KErrNone)
					{
					iStartAddress = reinterpret_cast<TInt>(iTzDbData);
					}
				}
			}
		}
	file.Close();
	return error;
	}
  
TInt CReadOnlyTzDb::InvalidateFlashDatabaseL()
	{
	RFile file;
	CleanupClosePushL(file);	
	TUint fileMode = EFileShareExclusive | EFileWrite | EFileStream;
	TInt error = file.Open(iFs, KTzDbFileNameFlash, fileMode);
	if (error == KErrNone)
		{
		error = file.Rename(KTzDbFileNameFlashCorrupt);
		}
	CleanupStack::PopAndDestroy(&file);

	return error;
	}

void CReadOnlyTzDb::InitialiseL()
	{
	// deny access to Flash-based database 
	// if it is being restored in this precise moment
	// We must not allow access to the ROM-based DB either, 
	// because this might contain outdated data.
	if (iRestoreInProcess)
		{
		User::Leave(KErrNotReady);
		}
		
	//
	// try to copy TZ Database from file in Flash drive
	//
	TInt error = CopyDatabaseToRam(KTzDbFileNameFlash);

	//
	// if it wasn't found or couldn't be opened, try to copy from file in ROM
	//

	if (error != KErrNone)
		{
		User::LeaveIfError(CopyDatabaseToRam(KTzDbFileNameRom));
		iTzDbDataFromFlash = false;
		}
	else
		{
		iTzDbDataFromFlash = true;
		}
		
	//
	// read header and initialise table offsets
	//
	
	TTzHeader* header(reinterpret_cast<TTzHeader*>(iStartAddress) );

	// check that the db version is the expected
	if (header->iVersion != KTzDbVersion)
		{
		if (iTzDbData)
			{
			ReleaseData();
			if (iTzDbDataFromFlash)
			  {
				InvalidateFlashDatabaseL();
			  }
			}
		User::Leave(KErrCorrupt);
		}
	
	const TInt KStringsBaseAddress				= iStartAddress + header->iOffsetToStringTable;
	const TInt KRegionsBaseAddress				= iStartAddress + header->iOffsetToRegionsTable;
	const TInt KRegionalZonesBaseAddress		= iStartAddress + header->iOffsetToRegionalZonesTable;
	const TInt KZonesDataBaseAddress			= iStartAddress + header->iOffsetToZones;
	const TInt KZonesBaseAddress				= iStartAddress + header->iOffsetToZonesTable;
	const TInt KLinksBaseAddress				= iStartAddress + header->iOffsetToLinksTable;
	const TInt KStdTimeAlignmentsBaseAddress	= iStartAddress + header->iOffsetToStdTimeAlignmentsTable;
	const TInt KRuleSetsBaseAddress				= iStartAddress + header->iOffsetToRuleSetsTable;
	const TInt KRuleUsesBaseAddress				= iStartAddress + header->iOffsetToRuleUsesTable;
	const TInt KRuleDefinitionsBaseAddress		= iStartAddress + header->iOffsetToRuleDefinitionsTable;

	//
	// create tables
	//
	iStringsTable = CTzDbStringsTable::NewL(*(reinterpret_cast<TTzStringsTable*>(KStringsBaseAddress)));
	iRegionsTable = CTzDbRegionsTable::NewL(*this, *(reinterpret_cast<TTzRegionsTable*>(KRegionsBaseAddress)));
	iRegionalZonesTable = CTzDbRegionalZonesTable::NewL(*this, *(reinterpret_cast<TTzRegionalZonesTable*>(KRegionalZonesBaseAddress)));
	iZonesTable = CTzDbZonesTable::NewL(*this, *(reinterpret_cast<TTzZonesTable*>(KZonesBaseAddress)), KZonesDataBaseAddress);
	iLinksTable	= CTzDbLinksTable::NewL(*this, *(reinterpret_cast<TTzLinksTable*>(KLinksBaseAddress)));
	iStdTimeAlignmentsTable	= CTzDbStdTimeAlignmentsTable::NewL(*(reinterpret_cast<TTzStdTimeAlignmentsTable*>(KStdTimeAlignmentsBaseAddress)));
	iRuleSetsTable = CTzDbRuleSetsTable::NewL(*this, *(reinterpret_cast<TTzRuleSetsTable*>(KRuleSetsBaseAddress)));
	iRuleUsesTable = CTzDbRuleUsesTable::NewL(*(reinterpret_cast<TTzRuleUsesTable*>(KRuleUsesBaseAddress)));
	iRuleDefinitionsTable = CTzDbRuleDefinitionsTable::NewL(*(reinterpret_cast<TTzRuleDefinitionsTable*>(KRuleDefinitionsBaseAddress)));	
	}

//
// Releases all RAM used by the database
//
void CReadOnlyTzDb::ReleaseData()
	{
	// delete all entity tables that are created at startup
	delete iStringsTable;			iStringsTable = NULL;
	delete iRegionsTable;			iRegionsTable = NULL;
	delete iRegionalZonesTable;		iRegionalZonesTable = NULL;
	delete iZonesTable;				iZonesTable = NULL;
	delete iLinksTable;				iLinksTable = NULL;
	delete iStdTimeAlignmentsTable;	iStdTimeAlignmentsTable = NULL;
	delete iRuleSetsTable;			iRuleSetsTable = NULL;
	delete iRuleUsesTable;			iRuleUsesTable = NULL;
	delete iRuleDefinitionsTable;	iRuleDefinitionsTable = NULL;

	iStartAddress = 0;
	iChunk.Close(); iTzDbData = NULL;
	}

CReadOnlyTzDb::~CReadOnlyTzDb()
	{
	iSwiObserver.RemoveObserver(this);
	ReleaseData();
	}

//
// Returns the CTzId object that corresponds to the location set as default in the database
//
CTzId* CReadOnlyTzDb::GetDefaultTimeZoneIdL()
	{
	if (iZonesTable == NULL)
		{
		InitialiseL();
		}

	CTzId* defTZID = NULL;
	CTzDbZone* defZone = GetZoneL((reinterpret_cast<TTzHeader*>(iStartAddress))->iOffsetToDefaultZone);

	if (defZone)
		{
		CleanupStack::PushL(defZone); // push #1

		HBufC8* fullZoneName = defZone->GetFullZoneNameLC(); // push #2
		defTZID = CTzId::NewL(defZone->LocationId());
		CleanupStack::PushL(defTZID); // push #3
		defTZID->SetIdL(*fullZoneName);

		CleanupStack::Pop(defTZID);
		CleanupStack::PopAndDestroy(2, defZone);
		}

	return defTZID;
	}

void CReadOnlyTzDb::GetTzRulesL(CTzRules& aTzRules, CTzId& aTzId)
	{
	if (iZonesTable == NULL)
		{
		InitialiseL();
		}
		
	TRAPD(err, DoGetTzRulesL(aTzRules, aTzId));
	if ( (err == KErrCorrupt) && (iTzDbData) )
		{
		ReleaseData();
		// if the corruption occurred while accessing the Flash DB, we must flag it 
		// as corrupt before propagating the leave
		if (iTzDbDataFromFlash)
		  {
			InvalidateFlashDatabaseL();
			}
		}
		
	User::LeaveIfError(err);
	}
	
void CReadOnlyTzDb::DoGetTzRulesL(CTzRules& aTzRules, CTzId& aTzId)
	{
	CTzDbZone* zone = FindZoneL(aTzId,ETrue);
	if (zone)
		{
		CleanupStack::PushL(zone);
		zone->GetRulesL(aTzRules);
		CleanupStack::PopAndDestroy(zone);
		}
	}

TBool CReadOnlyTzDb::IsIdInDbL(TUint aTzId)
	{
	if (iZonesTable == NULL)
		{
		InitialiseL();
		}
	CTzDbZone* zone = FindZoneByNumericIdL(aTzId);
	TBool ret = (zone)?ETrue:EFalse;
	delete zone;
	return ret;
	}

//
// Returns the CTzDbZone object corresponding to the location received (as CTzId)
// The supplied CTzId has two different IDs: a numeric one and a name (string ID).
// If the numeric ID is set (>0), it is used to perform the database search. Otherwise,
// the name is used to do the search.
//
// If aFillInLocationIDs is ETrue, it also fills in the unset zone ID in the supplied CTzId
//
CTzDbZone* CReadOnlyTzDb::FindZoneL(CTzId& aLocation, TBool aFillInLocationIDs)
	{
	CTzDbZone* zone = NULL;
	if (aLocation.TimeZoneNumericID() > KDefaultTzNumericId)
		{
		zone = FindZoneByNumericIdL(aLocation.TimeZoneNumericID());
		if (zone && aFillInLocationIDs)
			{
			CleanupStack::PushL(zone); // push #1
			HBufC8* fullZoneName = zone->GetFullZoneNameLC(); // push #2
			aLocation.SetIdL(*fullZoneName);
			CleanupStack::PopAndDestroy(fullZoneName); // pop #2
			CleanupStack::Pop(zone); // pop #1
			}
		}
	else
		{
		zone = FindZoneByNameL(aLocation.TimeZoneNameID());
		if (zone)
			{
			CleanupStack::PushL(zone); // push #1
			aLocation.SetId(zone->LocationId());
			CleanupStack::Pop(zone); // pop #1
			}
		}

	return zone;
	}

//
//
// Looks for a Zone database entity given a location name
// Returns the Zone as a CTzDbZone* (NULL if it can't find
// a Zone in the database that matches the received location)
//
CTzDbZone* CReadOnlyTzDb::FindZoneByNameL(const TDesC8& aLocation)
	{
	// first of all, look in links table
	CTzDbZone* retZone = iLinksTable->FindZoneL(aLocation);

	if (retZone == NULL) 
		{ // received location was the name of an actual zone
		// break up Location in Region and City names: e.g. "Europe/London" = "Europe" + "London"
		TInt separatorOffset = aLocation.Locate('/');
		HBufC8* regionName;
		HBufC8* cityName;
		if (separatorOffset >= 0)
			{
			regionName = aLocation.Left(separatorOffset).AllocLC();  // push #1
			cityName = aLocation.Right(aLocation.Length() - (separatorOffset+1)).AllocLC(); // push #2
			}
		else
			{
			_LIT8(KNullStr,"");
			regionName = KNullStr().AllocLC(); // push #1
			cityName = aLocation.AllocLC(); // push #2
			}
		CTzDbRegion* region = iRegionsTable->FindRegionL(*regionName);
		if (region)
			{
			CleanupStack::PushL(region); // push #3
			retZone = region->FindZoneL(*cityName);
			CleanupStack::PopAndDestroy(region); // pop #3
			} // if (region)  
		
		CleanupStack::PopAndDestroy(2,regionName); // pop #2, #1 - cityName, regionName
		}

	return retZone;
	}

CTzDbZone* CReadOnlyTzDb::FindZoneByNumericIdL(TUint aLocationId)
	{
	return iZonesTable->GetZoneByIdL(aLocationId);
	}

//
//
// The following methods provide access to the entity tables
//

TPtrC8 CReadOnlyTzDb::GetStringL(TUint aOffset) const
	{
	return iStringsTable->GetStringL(aOffset);
	}

const TTzRuleDefinition& CReadOnlyTzDb::GetTRuleDefinition(TUint aOffset) const
	{
	return iRuleDefinitionsTable->GetRuleDefinition(aOffset);
	}

const TTzRegionalZoneIndex& CReadOnlyTzDb::GetTRegionalZoneIndex(TUint aReference) const
	{
	return iRegionalZonesTable->GetTRegionalZoneIndex(aReference);
	}

CTzDbZone* CReadOnlyTzDb::GetZoneL(TUint aReference) const
	{
	return iZonesTable->GetZoneL(aReference);
	}

const TTzZone& CReadOnlyTzDb::GetTZone(TUint aReference) const
	{
	return iZonesTable->GetTZone(aReference);
	}

const TTzStdTimeAlignment& CReadOnlyTzDb::GetTStdTimeAlignment(TUint aReference) const
	{
	return iStdTimeAlignmentsTable->GetTStdTimeAlignment(aReference);
	}

const TTzRuleUse& CReadOnlyTzDb::GetTRuleUse(TUint aReference) const
	{
	return iRuleUsesTable->GetTRuleUse(aReference);
	}

CTzDbRuleSet* CReadOnlyTzDb::GetRuleSetL(TUint aReference) const
	{
	return iRuleSetsTable->GetRuleSetL(aReference);
	}

//
// The following methods (inherited from MBackupRestoreNotificationObserver)
// provide BackupRestore support

void CReadOnlyTzDb::BackupBeginningL()
	{
	// No action needed
	}

void CReadOnlyTzDb::BackupCompletedL()
	{
	// No action needed
	}

void CReadOnlyTzDb::RestoreBeginningL()
	{
	// set flag indicating that restoring has begun
	SetRestoreInProcess();
	}

void CReadOnlyTzDb::RestoreCompletedL()
	{
	// reset flag
	ResetRestoreInProcess();	
	ReleaseData();	
	InitialiseL();
	}

void CReadOnlyTzDb::HandleDatabaseChangeL(RTz::TTzChanges aChange)
	{
	//notify the TZ Data Observer that a change has occurred
	iTzDataObserver.NotifyTZDataStatusChangeL(aChange);

	ReleaseData();
	InitialiseL();
	}

/*
Retrieves the UTC offset for a given numeric time zone id.

@param aTime time at which the UTC offset for given time zone is required.
@param aTzId a numeric time zone id,

@return	TInt offset from UTC (in minutes).
*/
TInt CReadOnlyTzDb::GetOffsetForTimeZoneIdL(const TTime& aTime, TInt aTzId)
	{
	if (iZonesTable == NULL)
		{
		InitialiseL();
		}
	
	TInt offset = 0;
	TDateTime nowDate(aTime.DateTime() );
	
	CTzDbZone* zone = FindZoneByNumericIdL(aTzId);;
	CTzDbStdTimeAlignment* timeAlignment;

	if (zone != NULL)
		{
		//
		// zone has been found
		//			
		CleanupStack::PushL(zone);
		timeAlignment = zone->FindStdTimeAlignmentL
			(
			nowDate.Year(),
			nowDate.Month(),
			nowDate.Day(),
			nowDate.Hour(),
			nowDate.Minute(),
			ETzUtcTimeReference
			);
			
		if(timeAlignment)
			{
			offset = timeAlignment->UtcOffset();
			delete timeAlignment;
			}
		CleanupStack::PopAndDestroy(zone);
		}
			
	return offset;
	}

//
// Called by CTzDbChangeNotifier to notify of a TZ Database change
// 
void CReadOnlyTzDb::NotifyTZDataStatusChangeL(RTz::TTzChanges aChange)
	{
	HandleDatabaseChangeL(aChange);
	}