Writing an Active Backup Client Tutorial

A backup client application is often referred to as a data owner as it owns the data. This section describes how to write an active backup client which works with the Backup Engine and the Backup Server for data backup and restore.

Before you start, you must understand:

In an active backup and restore, the Backup Engine calls the active backup client to supply the data for the backup. Writing an active backup client means to:


  1. Write an active backup client (data owner) class which must subscribe for the KUidBackupRestoreKey property published by the backup server. When a backup server receives a request from a client (a host PC) for a backup or a restore, it reads backup registration files for a list of data owners (refer to step 3). It then calls the CSBEClient::SetBURModeL() function with the registered types (base or incremental, full or partial). This function signals start or completion of a backup or restore to all data owners. Data owners must subscribe the following publish and subscribe property:

    1. Key–KUidBackupRestoreKey defined in the epoc32\include\connect\sbdefs.h header file. This key belongs to the KUidSystemCategoryValue category.

    2. Value–TBURPartType or TBackupIncType.
    The backup client must check the value of the property (flag) on startup, in case the device is already in a backup or restore mode. A conn::CActiveBackupClient object can only be created once the publish and subscribe flag has transitioned into a backup or restore mode. The backup client can be deleted once the backup or restore operation has finished. This releases valuable system resources. CActiveBackupClient::ConfirmReadyForBURL() is called to indicate that the backup client is ready for the data supply to the Backup Engine.
    class CReferenceActiveDataOwner : public CActive
        {
        // Methods
    public:
        static CReferenceActiveDataOwner* NewL();
        ~CReferenceActiveDataOwner();
        
        // From CActive
        void RunL();
        TInt RunError(TInt aError);
        void DoCancel();
    
    private:
        CReferenceActiveDataOwner() 
        void ConstructL();
        void SubscribeToPSFlag();
        void ProcessBackupStateL(TInt aBackupStateValue);
    
        // Attributes
    private:
        //Active Backup Client
        CActiveBackupClient* iActiveBackupClient;
        
        //The publish and subscribe flag from the Backup Engine
        RProperty iProperty;
        
        //Pointer to the callback implementation
        CReferenceCallbackImplementation* iCallbackImpl;
        };
        
    /** Subscribe to the publish and subscribe flag.
        Call RunL() when the flag changes.
    */
    void CReferenceActiveDataOwner::SubscribeToPSFlag()
        {
        iStatus = KRequestPending;
        iProperty.Subscribe(iStatus);
        SetActive();
        }
    
    /** Check the flag in case the data owner has been started for a backup or restore, 
        or in case the publish and subscribe transition is missed.
    */
    void CReferenceActiveDataOwner::ConstructL()
        {
        // Set up the property to catch the flag.
        TInt backupStateValue = 0;
        iProperty.Attach(KUidSystemCategory, KUidBackupRestoreKey);
    
        // Process the mode change accordingly
        iProperty.Get(backupStateValue);
        ProcessBackupStateL(backupStateValue);
    
        // Add to the active scheduler
        CActiveScheduler::Add(this);
    
        // Subscribe to the flag to catch transitions
        SubscribeToPSFlag();    
    }
    
    /** Create the CActiveBackupClient and the callback implementation
        based on the flag.
    @param aBackupStateValue the new backup state value
    */
    void CReferenceActiveDataOwner::ProcessBackupStateL(TInt aBackupStateValue)
        {
        TInt type = aBackupStateValue & KBURPartTypeMask;
        
        // Create the the CActiveBackupClient and the callback implementation if
        // the device is in a backup or a restore mode.
        if (type == EBURBackupFull || type == EBURRestoreFull ||
            type == EBURBackupPartial || type == EBURRestorePartial)
            if (iCallbackImpl == NULL)
                {
                iCallbackImpl = CReferenceCallbackImplementation::NewL();
                }
            if (iActiveBackupClient == NULL)
                {
                iActiveBackupClient = 
                          CActiveBackupClient::NewL(iCallbackImpl);
                }
            }
    
        // Confirm the readiness of the active backup client.
        if ((type == EBURBackupFull || type == EBURRestoreFull) ||
            ((type == EBURBackupPartial || type == EBURRestorePartial) &&
             iActiveBackupClient->DoesPartialBURAffectMeL()))
            {
    
             .... // Things handled by a device vendor.
    
            //Inform the Backup Engine that the client is ready for data supply.
            iActiveBackupClient->ConfirmReadyForBURL(KErrNone);
            }
        else        
            {
            // CActiveBackupClient and the callback implementation 
            // if it is not an active backup.
            if (iActiveBackupClient != NULL)
                {
                delete iActiveBackupClient;
                iActiveBackupClient = NULL;
                }
    
            // The callback implementation must be deleted after 
            // the CActiveBackupClient is deleted.
            if (iCallbackImpl != NULL)
                {
                delete iCallbackImpl;
                iCallbackImpl = NULL;
                }
            }
        }
    
    /** When the flag changes, RunL() is called.
    */
    void CReferenceActiveDataOwner::RunL()
        {
     
        TInt backupStateValue = 0;   // re-set the flag value
    
        // re-subscribe to the flag to monitor future changes
        SubscribeToPSFlag();
    
        iProperty.Get(backupStateValue);
    
        // Process the mode change accordingly
        ProcessBackupStateL(backupStateValue);
        }
    
    ...
    

  2. Implement conn::MActiveBackupDataClient to write a callback handler. The callback handler prepares the data to the Backup Engine for a backup or a restore. For more information about each function, refer to the conn::MActiveBackupDataClient API reference. The callback implementation must fulfill the following requirements:

    1. The Initialise function is usually called before the actual backup or restore function is called. For example, InitialiseGetBackupDataL() is called before GetBackupDataSectionL(). The Initialise function gives a chance to a device vendor to prepare the data before the actual data supply.

    2. In an incremental backup or restore, a snapshot is the dataset after each incremental backup or restore. If MActiveBackupDataClient::AllSnapshotsSuppliedL() is called, it means that all snapshots have been supplied to the conn::CActiveBackupClient. If conn::CActiveBackupClient does not receive a snapshot, it performs a base backup.

    3. For a backup function, a TBool reference parameter named aFinished is passed as an argument. This indicates to the Backup Engine if all of the data for a backup is supplied. Setting this to EFalse causes the Backup Engine to repeatedly call the backup function for more data.

    4. For a restore function, aFinished is passed from the Backup Engine to indicate to the data owner if all of the restore data is supplied. The Backup Engine sets aFinished to ETrue after the final call of the restore function.

    5. Any leaves in the callback are propagated over Inter-Processor Communications (IPC) to the Backup Engine and potentially to the PC as errors. For this reason, any internal calls must be trapped in the callback.
    class CReferenceCallbackImplementation :
        public CBase,
        public MActiveBackupDataClient
        {
        // Methods
    public:
        static CReferenceCallbackImplementation* NewL();
        ~CReferenceCallbackImplementation();
    
        // The following functions are from the MActiveBackupDataClient  
        void AllSnapshotsSuppliedL();
        void ReceiveSnapshotDataL(TDriveNumber aDrive, TDesC8& aBuffer, 
                                    TBool aLastSection);
        TUint GetExpectedDataSize(TDriveNumber aDrive);
        void GetSnapshotDataL(TDriveNumber aDrive, TPtr8& aBuffer, 
                                TBool& aFinished);
        void InitialiseGetBackupDataL(TDriveNumber aDrive);
        void GetBackupDataSectionL(TPtr8& aBuffer, TBool& aFinished);
        void InitialiseRestoreBaseDataL(TDriveNumber aDrive);
        void RestoreBaseDataSectionL(TDesC8& aBuffer, TBool aFinished);
        void InitialiseRestoreIncrementDataL(TDriveNumber aDrive);
        void RestoreIncrementDataSectionL(TDesC8& aBuffer, TBool aFinished);
        void RestoreComplete(TDriveNumber aDrive);
        void TerminateMultiStageOperation();
        TUint GetDataChecksum(TDriveNumber aDrive);
    
        //The following two functions are for the proxy backup client only and 
        // Symbian provides a default implementation for each function.
        void InitialiseGetProxyBackupDataL(TSecureId aSID, TDriveNumber aDrive);
        void InitialiseRestoreProxyBaseDataL(TSecureId aSID, TDriveNumber aDrive);
        
    private:
        CReferenceCallbackImplementation() {}        
        CMyABData* iSnapshot;
        CMyABData* iData;
        TBool iProxy;
        TInt iOffset;
        TInt iSourceSize;
        TUint iFillChar;
        TSecureId iID;
        };
    
    void CReferenceCallbackImplementation::AllSnapshotsSuppliedL()
        {
        __LOG1("[0x%08x]: CReferenceCallbackImplementation::AllSnapshotsSuppliedL()", 
             iID.iId);
        }
        
    void CReferenceCallbackImplementation::ReceiveSnapshotDataL(
                                               TDriveNumber aDrive, 
                                               TDesC8& aBuffer,
                                               TBool /*aLastSection*/)
        {
        __LOG1("[0x%08x]: CReferenceCallbackImplementation::ReceiveSnapshotDataL()", iID.iId);
        // Create or append a buffer containing the snapshot
        if (!iSnapshot)
            {
            iSnapshot = CMyABData::NewL(aDrive);
            }
    
        iSnapshot->AddDataL(aBuffer);
        }
        
    TUint CReferenceCallbackImplementation::GetExpectedDataSize(TDriveNumber /*aDrive*/)
        {
        __LOG1("[0x%08x]: CReferenceCallbackImplementation::GetExpectedDataSize()", iID.iId);
            
        return iSourceSize;
        }
        
    void CReferenceCallbackImplementation::GetSnapshotDataL(
                                               TDriveNumber /*aDrive*/,
                                               TPtr8& aBuffer,
                                               TBool& aFinished)
        {
        __LOG1("[0x%08x]: CReferenceCallbackImplementation::GetSnapshotDataL()", iID.iId);
        aBuffer.Append(KABTestSnapshot());
        
        aFinished = ETrue;
        }
        
    void CReferenceCallbackImplementation::InitialiseGetBackupDataL(TDriveNumber /*aDrive*/)
        {
        __LOG1("[0x%08x]: CReferenceCallbackImplementation::InitialiseGetBackupData()", iID.iId);
        iOffset = 0;
    
        if (!iSnapshot)
            {
            iFillChar = 66;
            }
        else
            {
            iFillChar = 73;
            }
        }
        
    void CReferenceCallbackImplementation::GetBackupDataSectionL(
                                                TPtr8& aBuffer,
                                                TBool& aFinished)
        {
        __LOG1("[0x%08x]: CReferenceCallbackImplementation::GetBackupDataSectionL()", iID.iId);
        
        FillBuffer(aBuffer, aFinished);
        }
        
    void CReferenceCallbackImplementation::InitialiseRestoreBaseDataL(TDriveNumber aDrive)
        {
        __LOG("CReferenceCallbackImplementation::InitialiseRestoreBaseDataL()");
    
        iData = CMyABData::NewL(aDrive);
        }
        
    void CReferenceCallbackImplementation::RestoreBaseDataSectionL(
                                               TDesC8& aBuffer,
                                               TBool aFinished)
        {
        __LOG1("[0x%08x]: CReferenceCallbackImplementation::RestoreBaseDataSectionL()", iID.iId);
    
        // append a buffer containing the base data
        iData->AddDataL(aBuffer);
        
        if (aFinished)
            {
            ValidateRestoredData();
        
            delete iData;
            iData = NULL;
            }
        }
        
    void CReferenceCallbackImplementation::InitialiseRestoreIncrementDataL(TDriveNumber aDrive)
        {
        __LOG1("[0x%08x]: CReferenceCallbackImplementation::InitialiseRestoreIncrementDataL()", iID.iId);
        if (!iSnapshot)
            {
            User::Leave(KErrCorrupt);
            }
    
        if (!iData)
            {
            iData = CMyABData::NewL(aDrive);
            }
        }
        
    void CReferenceCallbackImplementation::RestoreIncrementDataSectionL(
                                               TDesC8& aBuffer,
                                               TBool aFinished)
        {
        __LOG1("[0x%08x]: CReferenceCallbackImplementation::RestoreIncrementDataSectionL()", iID.iId);
    
        iData->AddDataL(aBuffer);
        
        if (aFinished)
            {
            ValidateRestoredData();
        
            delete iData;
            iData = NULL;
            }
        }
        
    void CReferenceCallbackImplementation::RestoreComplete(TDriveNumber /*aDrive*/)
        {
        __LOG1("[0x%08x]: CReferenceCallbackImplementation::RestoreComplete()", iID.iId);
        }
        
    void CReferenceCallbackImplementation::TerminateMultiStageOperation()
        {
        __LOG1("[0x%08x]: CReferenceCallbackImplementation::TerminateMultiStageOperation()", iID.iId);
        // We also don't do anything here until we start testing multipart?
        }
        
    TUint CReferenceCallbackImplementation::GetDataChecksum(TDriveNumber /*aDrive*/)
        {
        __LOG1("[0x%08x]: CReferenceCallbackImplementation::GetDataChecksum()", iID.iId);
        return 0;
        }
    
    void CReferenceCallbackImplementation::InitialiseGetProxyBackupDataL(
                                               TSecureId aSID, 
                                               TDriveNumber /*aDrive*/)
        {
        __LOG1("[0x%08x]: CReferenceCallbackImplementation::InitialiseGetProxyBackupDataL()", iID.iId);
        __LOG2("[0x%08x]: Proxy data about to be requested for SID 0x%08x", iID.iId, aSID.iId);
        iProxy = ETrue;
        iOffset = 0;
        
        iFillChar = 80;        // 'P'
        }
    
    void CReferenceCallbackImplementation::InitialiseRestoreProxyBaseDataL(
                                                TSecureId aSID, 
                                                TDriveNumber aDrive)
        {
        __LOG1("[0x%08x]: CReferenceCallbackImplementation::InitialiseRestoreProxyBaseDataL()", iID.iId);
        __LOG2("[0x%08x]: Proxy data about to be restored for SID 0x%08x", iID.iId, aSID.iId);
        iProxy = ETrue;
        iData = CMyABData::NewL(aDrive);
        }
    ...

  3. Create a backup registration file to set attribute values for the data owner application. The Backup Engine parses backup registration files to get the data own list. The list includes details of data owners and data for backup and restore. Create the file using a text editor and save it as backup_registration.xml under the private folder of the data owner application on the Symbian device. Refer to Backup Registration Files for more information.

    1. process_name is the name of the data owner application.

    2. active_type is set to activeonly for an active backup.

    3. supports_incremental can be set to false to disable the incremental backup support from the active backup client.
    <!-- backup_registration.xml file for referencetestdataowner.exe -->
    
    <?xml version="1.0" standalone="yes"?>
    
    <backup_registration version="1.0">
        <active_backup process_name = "referencetestdataowner.exe"
                 active_type = "activeonly"
                       requires_delay_to_prepare_data = "yes" <!--deprecated-->
                       supports_incremental = "yes"> 
        </active_backup>
    
        <restore requires_reboot = "no"/>  <!--Currently only support no-->
    </backup_registration>
    

    Finally, test the data owner application. The logging information appends to the securebackup.txt log file. To enable the log generation, the C:\logs\connect directory must be created on the device.