// Copyright (c) 1999-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 <btmanclient.h>
#include <e32math.h>
#include <utf.h>
#include "btmanclientserver.h"
#include "s32strm.h"
#include <bluetooth/logger.h>
#ifdef __FLOG_ACTIVE
_LIT8(KLogComponent, LOG_COMPONENT_BT_MANAGER_CLIENT);
#endif
const TInt KServerRetries = 2;
const TUint8 KDummyDevicePointer = 0xff;
/**
Start the btmanserver
@return Systemwide error code
@internalComponent
@released
*/
static TInt StartServer()
{
LOG_STATIC_FUNC
const TUidType serverUid(KNullUid,KNullUid,KBTManServerUid3);
#ifdef __BTMANSERVER_NO_PROCESSES__
//
// In EKA1 WINS the server is a DLL, the exported entrypoint returns a TInt
// which represents the real entry-point for the server thread
//
RLibrary lib;
TInt r=lib.Load(KBTManServerImg,serverUid);
if (r!=KErrNone)
return r;
TLibraryFunction ordinal1=lib.Lookup(1);
TThreadFunction serverFunc=reinterpret_cast<TThreadFunction>(ordinal1());
//
// To deal with the unique thread (+semaphore!) naming in EPOC, and that we may
// be trying to restart a server that has just exited we attempt to create a
// unique thread name for the server.
// This uses Math::Random() to generate a 32-bit random number for the name
//
TName name(KBTManServerName);
_LIT(KHexString, "0x");
name.Append(KHexString());
name.AppendNum(Math::Random(),EHex);
RThread server;
r=server.Create(name,serverFunc,
KBTManServerStackSize,
NULL,&lib,NULL,
KBTManServerInitHeapSize,KBTManServerMaxHeapSize,EOwnerProcess);
lib.Close(); // if successful, server thread has handle to library now
#else
//
// EPOC and EKA2 is easy, we just create a new server process. Simultaneous
// launching of two such processes should be detected when the second one
// attempts to create the server object, failing with KErrAlreadyExists.
//
RProcess server;
TInt r=server.Create(KBTManServerImg,KNullDesC,serverUid);
#endif
LOG1(_L("BTMan Server created, code %d"), r);
if (r!=KErrNone)
return r;
TRequestStatus stat;
server.Rendezvous(stat);
if (stat!=KRequestPending)
{
server.Kill(0); // abort startup
}
else
{
server.Resume(); // logon OK - start the server
LOG(_L("BTMan Server Resumed"));
}
User::WaitForRequest(stat); // 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
LOG1(_L("BTMan Server started, code %d (0=>success)"), stat.Int());
r=(server.ExitType()==EExitPanic) ? KErrGeneral : stat.Int();
server.Close();
LOG(_L("BTMan Server Closed"));
return r;
}
/**
Default Constructor.
@publishedAll
@released
*/
EXPORT_C RBTMan::RBTMan()
{
LOG_FUNC
}
/**
Connects to the btman server.
@return systemwide error code
@publishedAll
@released
*/
EXPORT_C TInt RBTMan::Connect()
{
LOG_FUNC
TInt retry=KServerRetries;
for (;;)
{
TInt r=CreateSession(KBTManServerName,Version()); //gives MessageSlots of -1
//this uses global pool rather
//than local pool
if (r!=KErrNotFound && r!=KErrServerTerminated)
return r;
if (--retry==0)
return r;
r=StartServer();
if (r!=KErrNone && r!=KErrAlreadyExists)
return r;
}
}
/**
Returns the version of the server.
@return version of btmanserver
*/
EXPORT_C TVersion RBTMan::Version() const
/**
**/
{
LOG_FUNC
return TVersion(KBTManServerMajorVersionNumber,
KBTManServerMinorVersionNumber,
KBTManServerBuildVersionNumber);
}
/**
Default constructor of base class for subsessions
@publishedAll
@released
*/
EXPORT_C RBTManSubSession::RBTManSubSession()
: iClientServerMsg()
{
LOG_FUNC
iClientServerMsg().iClientBusy = EFalse;
iClientServerMsg().iClientStatusToCancel = NULL;
}
/**
Cancel an outstanding request on btmanserver
@param aStatus the status of active object that made the original request
@publishedAll
@released
*/
EXPORT_C void RBTManSubSession::CancelRequest(TRequestStatus& aStatus)
{
LOG_FUNC
if (SubSessionHandle())
{
SendReceive(EBTManCancelRequest, TIpcArgs(&aStatus));
}
else
{
//Client has a bad handle therefore complete the call from here
LocalComplete(aStatus, KErrBadHandle);
}
}
/**
Completes a request locally without involving the server.
Called when a client-side error occurs.
@param aStatus: The TRequestStatus of the active object that made the request that needs to be completed.
@param aErr: The reason for completion. aErr will be written into the TRequestStatus of the active object.
@internalComponent
@released
**/
void RBTManSubSession::LocalComplete(TRequestStatus& aStatus, TInt aErr)
{
LOG_FUNC
aStatus = KRequestPending;
TRequestStatus* pS = &aStatus;
User::RequestComplete(pS, aErr);
}
TBool RBTManSubSession::IsBusy() const
{
LOG_FUNC
return iClientServerMsg().iClientBusy;
}
/**
To prevent clients using the server more than once
Business is cleared once the server has completed the job
@param aStatus the status of the client AO making a request on the server. this will can be used for cancelling later
@internalComponent
@released
**/
void RBTManSubSession::SetBusy(TRequestStatus& aStatus)
{
LOG_FUNC
iClientServerMsg().iClientBusy = ETrue;
iClientServerMsg().iClientStatusToCancel = &aStatus;
}
/**
Default constructor of registry session
@publishedAll
@released
*/
EXPORT_C RBTRegServ::RBTRegServ()
{
LOG_FUNC
}
/**
Connect to Registry Server
@return System-wide error code
@publishedAll
@released
@capability LocalServices
*/
EXPORT_C TInt RBTRegServ::Connect()
{
LOG_FUNC
// we provide the standard RBTMan session for now, but the client doesnt know this!
TInt r=RBTMan::Connect();
return r;
}
/**
Close session on Registry Server
@publishedAll
@released
*/
EXPORT_C void RBTRegServ::Close()
{
LOG_FUNC
RBTMan::Close();
}
/**
Return reference to the server session
@return reference to btman session
@publishedAll
@released
*/
EXPORT_C RBTMan& RBTRegServ::Session()
{
LOG_STATIC_FUNC
return static_cast<RBTMan&>(*this);
}
#ifndef _DEBUG
EXPORT_C TInt RBTRegServ::SetHeapFailure(TInt /*aType*/,TInt /*aRate*/)
#else
EXPORT_C TInt RBTRegServ::SetHeapFailure(TInt aType,TInt aRate)
#endif
/**
Send a message to the server to fail memory allocations (in debug builds)
@internalAll
@released
*/
{
LOG_FUNC
#ifdef _DEBUG
return SendReceive(EBTManSetHeapFailure,TIpcArgs(static_cast<RHeap::TAllocFail>(aType), aRate));
#else
return KErrNotSupported;
#endif
}
/**
Returns the number of open subsessions.
This should be used by clients to do resource leakage debugging checks
@publishedAll
@released
*/
EXPORT_C TInt RBTRegServ::ResourceCount()
{
LOG_FUNC
TInt count = 0;
TPckg<TInt> pckg(count);
SendReceive(EBTManSubSessionCount, TIpcArgs(&pckg));
return count;
}
/**
Default constructor for registry subsession
@publishedAll
@released
*/
EXPORT_C RBTRegistry::RBTRegistry()
: RBTManSubSession(), iDevicePckg(*reinterpret_cast<TBTNamelessDevice*>(KDummyDevicePointer))
{
LOG_FUNC
}
/**
Open a Bluetooth device subsession.
@param aSession The BTManager session to which this subsession is to be attached.
@return An error code depicting the outcome of the "open".
@publishedAll
@released
@capability LocalServices
*/
EXPORT_C TInt RBTRegistry::Open(RBTRegServ& aSession)
{
LOG_FUNC
iSendBuffer = NULL;
return CreateSubSession(aSession.Session(), EBTManCreateRegistrySubSession, TIpcArgs(NULL));
}
/** Create a constrained view of devices on the remote device table of the registry server.
This is in effect a registry search facility.
It is needed if the user wishes find data in the registry of uncertain size.
For example:-
1) a list of remote devices.
2) full details of a remote device including its name and/or friendly name.
Note 1: Some methods in the API (e.g. UnpairAllInView) require a view to have been created prior to their use.
Note 2: When a view has been generated, if details of the device(s) found are needed, these should be obtained using an instance of CBTRegistryResponse.
Note 3: A view must be closed before a new view can be created.
@see UnpairAllInView
@see DeleteAllInView
@see CloseView
@see NotifyViewChange
@see TBTRegistrySearch
@see CBTRegistryResponse
@param aSearch A search struct with details of devices to be contained in the result set
@param aStatus TRequestStatus supplied by caller
@return systemwide error
@publishedAll
@released
@capability LocalServices
@capability ReadDeviceData (LocalServices only if link key is not needed)
*/
EXPORT_C void RBTRegistry::CreateView(const TBTRegistrySearch& aSearch, TRequestStatus& aStatus)
{
LOG_FUNC
if (IsBusy())
{
LocalComplete(aStatus, KErrInUse);
}
else
{
if (SubSessionHandle())
{
SetBusy(aStatus);
iSearchPckg = aSearch;
SendReceive(EBTManRegistrySearch, TIpcArgs(&iSearchPckg, NULL, &iClientServerMsg), aStatus);
}
else
{
//Client has a bad handle therefore complete the call from here
LocalComplete(aStatus, KErrBadHandle);
}
}
}
/**
Close the subsession. Removes all server side resources pertaining to this subsession
@publishedAll
@released
*/
EXPORT_C void RBTRegistry::Close()
{
LOG_FUNC
delete iSendBuffer;
iSendBuffer = NULL;
RSubSessionBase::CloseSubSession(EBTManCloseSubSession);
}
/**
Close a previously created view on the registry.
Allows an app to use this subsession for other view or non-view operations
@return Systemwide error
@post Subsession is left open for further use - eg to create another view, or perform a viewless operation
@publishedAll
@released
*/
EXPORT_C TInt RBTRegistry::CloseView()
{
LOG_FUNC
if (SubSessionHandle())
{
return SendReceive(EBTRegistryCloseView, TIpcArgs(NULL, NULL, NULL)); // synchronous
}
else
{
return KErrBadHandle;
}
}
/**
Unpair a device in the registry
@param aAddress The device to unpair
@param aStatus reference to client AO's TRequestStatus
@publishedPartner
@released
@capability LocalServices
@capability WriteDeviceData
*/
EXPORT_C void RBTRegistry::UnpairDevice(const TBTDevAddr& aAddress, TRequestStatus& aStatus)
{
LOG_FUNC
if (IsBusy())
{
LocalComplete(aStatus, KErrInUse);
}
else
{
if (SubSessionHandle())
{
SetBusy(aStatus);
iAddrBuf = aAddress;
SendReceive(EBTRegistryDeleteLinkKey, TIpcArgs(&iAddrBuf, NULL, &iClientServerMsg), aStatus);
}
else
{
//Client has a bad handle therefore complete the call from here
LocalComplete(aStatus, KErrBadHandle);
}
}
}
/**
Unpair all the devices in the view - useful to unbond eg all devices (create a View with search All), or eg unbond all headsets
@param aStatus reference to client AO's TRequestStatus
@publishedPartner
@released
@capability LocalServices
@capability WriteDeviceData
*/
EXPORT_C void RBTRegistry::UnpairAllInView(TRequestStatus& aStatus)
{
LOG_FUNC
if (SubSessionHandle())
{
iClientServerMsg().iClientStatusToCancel = &aStatus;
SendReceive(EBTRegistryUnpairView, TIpcArgs(NULL, NULL, &iClientServerMsg), aStatus);
}
else
{
//Client has a bad handle therefore complete the call from here
LocalComplete(aStatus, KErrBadHandle);
}
}
/**
Add device to the registry
@leave OOM
@param aDevice reference to CBTDevice of details of device to add
@param aStatus reference to client AO's TRequestStatus
@publishedAll
@released
@capability LocalServices
*/
EXPORT_C void RBTRegistry::AddDeviceL(const CBTDevice& aDevice, TRequestStatus& aStatus)
{
LOG_FUNC
if (IsBusy())
{
LocalComplete(aStatus, KErrInUse);
}
else
{
if (SubSessionHandle())
{
if (iSendBuffer)
{
delete iSendBuffer;
iSendBuffer = NULL;
}
// need to contiguate CBTDevice into a buffer
SetBusy(aStatus);
iSendBuffer = CBufFlat::NewL(sizeof(CBTDevice)); //granularity
RBufWriteStream stream;
stream.Open(*iSendBuffer);
CleanupClosePushL(stream);
aDevice.ExternalizeL(stream);
const TPtr8 ptr=iSendBuffer->Ptr(0);
iSendBufferPtr.Set(ptr);
// now we can give the buffer to BTMan
SendReceive(EBTRegistryAddDevice, TIpcArgs(&iSendBufferPtr, NULL, &iClientServerMsg), aStatus);
CleanupStack::PopAndDestroy(1); // stream
}
else
{
//Client has a bad handle therefore complete the call from here
LocalComplete(aStatus, KErrBadHandle);
}
}
}
/**
Get a *nameless* device to the registry. To retrieve a full device with names a view should be created
@see CreateView
@pre Clients must ensure that they do not call this method while the same operation is already outstanding on the same RBTRegistry subsession
@param aDevice reference to TBTDevice (used as input and output). The input MUST contain the device address - this is used as the key. Once the method completes the reference will contain all other details found from the registry
@param aStatus reference to client AO's TRequestStatus
@publishedPartner
@released
@capability LocalServices
@capability ReadDeviceData (LocalServices only if link key is not needed)
*/
EXPORT_C void RBTRegistry::GetDevice(TBTNamelessDevice& aDevice, TRequestStatus& aStatus)
{
LOG_FUNC
if (IsBusy())
{
LocalComplete(aStatus, KErrInUse);
}
else
{
if (SubSessionHandle())
{
SetBusy(aStatus);
TPckg<TBTNamelessDevice> temp(aDevice);
iDevicePckg.Set(temp);
SendReceive(EBTRegistryGetNamelessDevice, TIpcArgs(&iDevicePckg, NULL, &iClientServerMsg), aStatus);
}
else
{
//Client has a bad handle therefore complete the call from here
LocalComplete(aStatus, KErrBadHandle);
}
}
}
/**
Modify the friendly name of a device
@leave OOM
@param aAddress The address of the device of which to change the name
@param aName The new name (note - not an 8bit descriptor)
@param aStatus reference to client AO's TRequestStatus
@publishedAll
@released
@capability LocalServices
*/
EXPORT_C void RBTRegistry::ModifyFriendlyDeviceNameL(const TBTDevAddr& aAddress,
const TDesC& aName,
TRequestStatus& aStatus)
{
LOG_FUNC
if (IsBusy())
{
LocalComplete(aStatus, KErrInUse);
}
else
{
if (SubSessionHandle())
{
SetBusy(aStatus);
TBuf8<KMaxFriendlyNameLen> tempBuf;
tempBuf.Zero();
if (CnvUtfConverter::ConvertFromUnicodeToUtf8(tempBuf, aName) != KErrNone)
{
LocalComplete(aStatus, KErrBadName);
return;
}
if (iSendBuffer)
{
delete iSendBuffer;
iSendBuffer = NULL;
}
iSendBuffer = CBufFlat::NewL(sizeof(TBuf8<KMaxFriendlyNameLen>)); //granularity
iSendBuffer->Reset();
iSendBuffer->InsertL(0, tempBuf);
const TPtr8 ptr=iSendBuffer->Ptr(0);
iSendBufferPtr.Set(ptr);
iAddrBuf = aAddress;
SendReceive(EBTRegistryModifyFriendlyName,
TIpcArgs(&iAddrBuf, &iSendBufferPtr, &iClientServerMsg), aStatus);
}
else
{
//Client has a bad handle therefore complete the call from here
LocalComplete(aStatus, KErrBadHandle);
}
}
}
/**
Modify the Bluetooth name of a device. This is not used beyond the stack
@param aAddress The address of the device of which to change the name
@param aName The new name (note - this is an 8bit descriptor)
@param aStatus reference to client AO's TRequestStatus
@internalAll
@released
@capability LocalServices
@capability WriteDeviceData (localServices only if friendly device name)
*/
EXPORT_C void RBTRegistry::ModifyBluetoothDeviceNameL(const TBTDevAddr& aAddress,
const TDesC8& aName,
TRequestStatus& aStatus)
{
LOG_FUNC
if (IsBusy())
{
LocalComplete(aStatus, KErrInUse);
}
else
{
if (SubSessionHandle())
{
SetBusy(aStatus);
if (iSendBuffer)
{
delete iSendBuffer;
iSendBuffer = NULL;
}
iSendBuffer = CBufFlat::NewL(sizeof(TBuf8<KMaxBluetoothNameLen>)); //granularity
iSendBuffer->Reset();
iSendBuffer->InsertL(0, aName);
const TPtr8 ptr=iSendBuffer->Ptr(0);
iSendBufferPtr.Set(ptr);
iAddrBuf = aAddress;
SendReceive(EBTRegistryModifyBluetoothName,
TIpcArgs(&iAddrBuf, &iSendBufferPtr, &iClientServerMsg), aStatus);
}
else
{
//Client has a bad handle therefore complete the call from here
LocalComplete(aStatus, KErrBadHandle);
}
}
}
/**
Update details of a device - other than its names: Not used beyond stack
@param aDeviceDetails The new details - the device address MUST be present in aDeviceDetails is used as the key
@param aStatus reference to client AO's TRequestStatus
@internalTechnology
@released
@capability LocalServices
@capability WriteDeviceData
*/
EXPORT_C void RBTRegistry::ModifyDevice(const TBTNamelessDevice& aDeviceDetails, TRequestStatus& aStatus)
{
LOG_FUNC
if (IsBusy())
{
LocalComplete(aStatus, KErrInUse);
}
else
{
if (SubSessionHandle())
{
SetBusy(aStatus);
TPckg<TBTNamelessDevice> temp(aDeviceDetails);
iDevicePckg.Set(temp);
SendReceive(EBTRegistryModifyNamelessDevice, TIpcArgs(&iDevicePckg, NULL, &iClientServerMsg), aStatus);
}
else
{
//Client has a bad handle therefore complete the call from here
LocalComplete(aStatus, KErrBadHandle);
}
}
}
/**
Notifies the client when a change has been made to the registry that affects the currently open view of devices.
@see CreateView
@see CloseView
@param aStatus reference to client AO's TRequestStatus
@publishedAll
@released
*/
EXPORT_C void RBTRegistry::NotifyViewChange(TRequestStatus& aStatus)
{
LOG_FUNC
if (SubSessionHandle())
{
// We don't set the session as busy because then no other commands can be sent
iClientServerMsg().iClientStatusToCancel = &aStatus;
SendReceive(EBTRegistryNotifyViewChange, TIpcArgs(NULL, NULL, &iClientServerMsg), aStatus);
}
else
{
//Client has a bad handle therefore complete the call from here
LocalComplete(aStatus, KErrBadHandle);
}
}
/**
@internalComponent
*/
void RBTRegistry::PreLoad(TRequestStatus& aStatus)
{
LOG_FUNC
if (SubSessionHandle())
{
iClientServerMsg().iClientStatusToCancel = &aStatus;
SendReceive(EBTManExtractRegistryDataIntoServer, TIpcArgs(NULL, NULL, &iClientServerMsg), aStatus);
}
else
{
//Client has a bad handle therefore complete the call from here
LocalComplete(aStatus, KErrBadHandle);
}
}
/**
@internalComponent
**/
void RBTRegistry::GetResults(TPtr8& aResults, TRequestStatus& aStatus)
{
LOG_FUNC
if (SubSessionHandle())
{
iClientServerMsg().iClientStatusToCancel = &aStatus;
SendReceive(EBTManRetrieveRegistryData, TIpcArgs(&aResults, NULL, &iClientServerMsg), aStatus);
}
else
{
//Client has a bad handle therefore complete the call from here
LocalComplete(aStatus, KErrBadHandle);
}
}
/**
Remove all devices in the view from the Registry
@pre View must be created first by client
@see CreateView
@param aStatus a TRequestStatus passed in by the caller
@publishedPartner
@released
@capability LocalServices
@capability WriteDeviceData (Only if different process than the one that created the device)
*/
EXPORT_C void RBTRegistry::DeleteAllInView(TRequestStatus& aStatus)
{
LOG_FUNC
if (SubSessionHandle())
{
iClientServerMsg().iClientStatusToCancel = &aStatus;
SendReceive(EBTRegistryDeleteDevices, TIpcArgs(NULL, NULL, &iClientServerMsg), aStatus);
}
else
{
//Client has a bad handle therefore complete the call from here
LocalComplete(aStatus, KErrBadHandle);
}
}
/**
default c'tor
@publishedAll
@released
*/
EXPORT_C RBTLocalDevice::RBTLocalDevice()
:RBTManSubSession(), iLocalDevicePckg(*reinterpret_cast<TBTLocalDevice*>(KDummyDevicePointer))
{
LOG_FUNC
}
/**
Open a local device subsession on the server, used for manipulating settings about the local device
@param aSession A server session
@return systemwide error
@publishedAll
@released
*/
EXPORT_C TInt RBTLocalDevice::Open(RBTRegServ& aSession)
{
LOG_FUNC
return CreateSubSession(aSession.Session(), EBTManCreateLocalDeviceSubSession, TIpcArgs(NULL));
}
/**
Close the subsession on the server
@publishedAll
@released
*/
EXPORT_C void RBTLocalDevice::Close()
{
LOG_FUNC
RSubSessionBase::CloseSubSession(EBTManCloseSubSession);
}
/**
Retrieve the local device details. Synchronous - intended mainly for Bluetooth stack use on startup
@param aLocalDeviceResult a reference to a TBTLocalDevice to store the results from the Registry
@return SystemWide error
@publishedAll
@released
*/
EXPORT_C TInt RBTLocalDevice::Get(TBTLocalDevice& aLocalDeviceResult)
{
LOG_FUNC
if (SubSessionHandle())
{
TPckg<TBTLocalDevice> pckg(aLocalDeviceResult);
return SendReceive(EBTRegistryGetLocalDevice, TIpcArgs(&pckg, NULL, NULL)); // synchronous
}
else
{
//Client has a bad handle therefore complete the call from here
return KErrBadHandle;
}
}
/**
Update the details about the local Bluetooth device
@param aLocalDevice New settings for local device
@param aStatus reference Client AO's TRequestStatus
@publishedPartner
@released
@capability LocalServices
@capability WriteDeviceData
*/
EXPORT_C void RBTLocalDevice::Modify(const TBTLocalDevice& aLocalDevice, TRequestStatus& aStatus)
{
LOG_FUNC
if (IsBusy())
{
LocalComplete(aStatus, KErrInUse);
}
else
{
if (SubSessionHandle())
{
SetBusy(aStatus);
TPckg<TBTLocalDevice> pckg(aLocalDevice);
iLocalDevicePckg.Set(pckg);
SendReceive(EBTRegistryUpdateLocalDevice, TIpcArgs(&iLocalDevicePckg, NULL, &iClientServerMsg), aStatus);
}
else
{
//Client has a bad handle therefore complete the call from here
LocalComplete(aStatus, KErrBadHandle);
}
}
}
/**
Synchronous overload of Modify (primarily for stack operations)
@see RBTLocalDevice::Modify(const TBTLocalDevice& aLocalDevice, TRequestStatus& aStatus)
@publishedPartner
@released
@capability LocalServices
@capability WriteDeviceData
*/
EXPORT_C TInt RBTLocalDevice::Modify(const TBTLocalDevice& aLocalDevice)
{
LOG_FUNC
// synchronous version for stack on close-down
TRequestStatus status;
Modify(aLocalDevice, status);
User::WaitForRequest(status);
return status.Int();
}
/**
Two-phase constructor to create a helper class to retrieve a set of results (a view) from the Registry
@leave OOM
@pre A non-empty view on the Registry task has been created
@param aView the Registry subsession that has had a View created on it
@return pointer to allocated object
@see CreateView()
@publishedAll
@released
*/
EXPORT_C CBTRegistryResponse* CBTRegistryResponse::NewL(RBTRegistry& aView)
{
LOG_STATIC_FUNC
return new (ELeave) CBTRegistryResponse(aView);
}
/**
Start fetching results from the Registry Server. When the request is complete, the results can be obtained via the Results() method.
@param aClientStatus a TRequestStatus passed in by the caller
@publishedAll
@released
*/
EXPORT_C void CBTRegistryResponse::Start(TRequestStatus& aClientStatus)
{
LOG_FUNC
// we do late construction on the heap buffer
__ASSERT_DEBUG(!IsActive(), User::Panic(KBTManClientPanic, EBTManClientResultRetrieveAlreadyActive));
iClientStatus = &aClientStatus;
*iClientStatus = KRequestPending;
CActiveScheduler::Add(this);
DoGet();
}
/** Get Results previously fetched from the Registry Server.
@pre The method Start needs to have been called, and notification of its completion received.
@return reference to the array of results
@publishedAll
@released
*/
EXPORT_C RBTDeviceArray& CBTRegistryResponse::Results()
{
LOG_FUNC
return iArray;
}
CBTRegistryResponse::CBTRegistryResponse(RBTRegistry& aView)
: CActive(EPriorityStandard), iView(aView), iResponsePtr(NULL,0)
{
LOG_FUNC
}
/**
Destructor
*/
EXPORT_C CBTRegistryResponse::~CBTRegistryResponse()
{
LOG_FUNC
Cancel();
iArray.ResetAndDestroy();
delete iResponseBuf;
}
void CBTRegistryResponse::DoGet()
{
LOG_FUNC
// create RPointerArray to point to n CBTDevices
// ask server async size of result set - this causes BTMan to extract from DBMS and return size
// we then allocate and in 2nd phase get the stuff from the server (could be synchronous
// we could wrap all this up in a helper object for client to use: CBTRegistryResponder
iArray.ResetAndDestroy();
iState = EGettingSize;
// Get the size of the entry
iView.PreLoad(iStatus);
SetActive();
}
void CBTRegistryResponse::DoCancel()
{
LOG_FUNC
//cancel the server
iView.CancelRequest(iStatus);
//notify our user
User::RequestComplete(iClientStatus, KErrCancel);
}
void CBTRegistryResponse::RunL()
{
LOG_FUNC
switch (iState)
{
case EGettingSize:
{
// get the results
if (iStatus.Int() >= KErrNone)
{
// we have a byte count of the response size
iResponseBuf = HBufC8::NewMaxL(iStatus.Int());
iResponsePtr.Set(iResponseBuf->Des());
iView.GetResults(iResponsePtr, iStatus);
iState = EGettingResults;
SetActive();
}
else
{
User::Leave(iStatus.Int());
}
}
break;
case EGettingResults:
{
if (iStatus.Int() >= KErrNone)
{
// got n CBTDevices to return
TInt c = iStatus.Int();
RDesReadStream stream(iResponsePtr);
CleanupClosePushL(stream);
while (c-->0)
{
CBTDevice* device = CBTDevice::NewLC();
device->InternalizeL(stream);
// place it into the result array
User::LeaveIfError(iArray.Append(device));
CleanupStack::Pop(); //device - don't destroy as 'owned' by array
}
CleanupStack::PopAndDestroy(); //stream
// free up memory:
delete iResponseBuf;
iResponseBuf = NULL;
User::RequestComplete(iClientStatus, KErrNone);
} // if
else
{
User::Leave(iStatus.Int());
}
break;
} // case
default:
User::Panic(KBTManClientPanic, EBTManClientBadResultRetrieveState);
}
}
TInt CBTRegistryResponse::RunError(TInt aError)
{
LOG_FUNC
//complete request if its pending
if (*iClientStatus == KRequestPending)
{
User::RequestComplete(iClientStatus, aError);
}
return KErrNone;
}
/**
Default constructor
@publishedAll
@released
*/
EXPORT_C TBTRegistrySearch::TBTRegistrySearch()
{
LOG_FUNC
Reset();
}
/**
Resets the search criteria
@publishedAll
@released
*/
EXPORT_C void TBTRegistrySearch::Reset()
{
LOG_FUNC
iSearchMask =0;
}
/**
Copy Constructor
@publishedAll
@released
*/
EXPORT_C TBTRegistrySearch::TBTRegistrySearch(const TBTRegistrySearch& aSearch)
{
LOG_FUNC
iSearchMask = aSearch.iSearchMask;
iDeviceClass = aSearch.iDeviceClass;
iDeviceAddress = aSearch.iDeviceAddress;
iLastSeen = aSearch.iLastSeen;
iLastUsed = aSearch.iLastUsed;
iCurrentProcessSID = aSearch.iCurrentProcessSID;
iBluetoothName.Set(aSearch.iBluetoothName);
iFriendlyName.Set(aSearch.iFriendlyName);
}
/**
Add search criterion for bonded devices
@publishedAll
@released
*/
EXPORT_C void TBTRegistrySearch::FindBonded()
{
LOG_FUNC
iSearchMask |= EBonded;
}
/**
Add search criterion for trusted devices
@publishedAll
@released
*/
EXPORT_C void TBTRegistrySearch::FindTrusted()
{
LOG_FUNC
iSearchMask |= ETrusted;
}
/**
Add search criterion to search for specific CoD
@param aClass The CoD to search
@see TBTDeviceClass
@publishedAll
@released
*/
EXPORT_C void TBTRegistrySearch::FindCoD(const TBTDeviceClass& aClass)
{
LOG_FUNC
iDeviceClass = aClass;
iSearchMask |= ECoD;
}
/**
Add search criterion to search for certain classes of device
@param aClass the device class to search for
@param aPref describes the type of device search to perform
@see TBTDeviceClass
@see enum TBTDeviceClassSearch
@publishedAll
@released
*/
EXPORT_C void TBTRegistrySearch::FindCoD(const TBTDeviceClass& aClass, TBTDeviceClassSearch aPref)
{
LOG_FUNC
iDeviceClass = aClass;
iSearchMask |= (aPref & EMajorDevice ? ECoDMajorDev : 0);
iSearchMask |= (aPref & EMinorDevice ? ECoDMinorDev : 0);
}
/**
Add search criterion to search for given device
@param aAddress address of device to search for
@publishedAll
@released
*/
EXPORT_C void TBTRegistrySearch::FindAddress(const TBTDevAddr& aAddress)
{
LOG_FUNC
iDeviceAddress = aAddress;
iSearchMask |= EAddress;
}
/**
Add search criterion to search for devices seen since given date
NOTE not currently implemented
@param aLastSeen date from which to find devices
@publishedAll
@released
*/
EXPORT_C void TBTRegistrySearch::FindSinceSeen(const TTime& aLastSeen)
{
LOG_FUNC
iLastSeen = aLastSeen;
iSearchMask |= ELastSeen;
}
/**
Add search criterion to search for devices used since given date
@param aLastUsed date from which to find devices
@publishedAll
@released
*/
EXPORT_C void TBTRegistrySearch::FindSinceUsed(const TTime& aLastUsed)
{
LOG_FUNC
iLastUsed = aLastUsed;
iSearchMask |= ELastUsed;
}
/**
Add search criterion to search for devices with some Bluetooth device name
@param aName device name to search for
@publishedAll
@released
*/
EXPORT_C void TBTRegistrySearch::FindBluetoothName(const TDesC8& aName)
/**
Add search criteria of specific Bluetooth name
*/
{
LOG_FUNC
iBluetoothName.Set(aName);
iSearchMask |= EBTName;
}
/**
Add search criterion to search for devices with some friendly name
@param aName device name to search for
@publishedAll
@released
*/
EXPORT_C void TBTRegistrySearch::FindFriendlyName(const TDesC& aName)
{
LOG_FUNC
iFriendlyName.Set(aName);
iSearchMask |= EFriendlyName;
}
/**
Add search criterion to search for finding all devices
@publishedAll
@released
*/
EXPORT_C void TBTRegistrySearch::FindAll()
{
LOG_FUNC
iSearchMask |= EAll;
}
/**
Add search criterion to search for devices added by the current process
@publishedAll
@released
*/
EXPORT_C void TBTRegistrySearch::FindCurrentProcessOwned()
{
LOG_FUNC
iCurrentProcessSID = User::Identity();
iSearchMask |= EProcess;
}
/**
Add search criterion to search for devices with a particular UI Cookie value.
@param aUiCookie The particular UI cookie value to search on.
@publishedPartner
@released
*/
EXPORT_C void TBTRegistrySearch::FindUiCookie(TUint32 aUiCookie)
{
LOG_FUNC
static const TUint32 KAllBitsMasked = 0xffffffff;
FindUiCookie(aUiCookie, KAllBitsMasked);
}
/**
Add search criterion to search for devices with a particular UI Cookie value.
@param aUiCookie The particular UI cookie value to search on.
@param aUiCookieMask The mask to indicate which bits of the cookie value are to be searched on.
@publishedPartner
@released
*/
EXPORT_C void TBTRegistrySearch::FindUiCookie(TUint32 aUiCookie, TUint32 aUiCookieMask)
{
LOG_FUNC
iUiCookie = aUiCookie;
iUiCookieMask = aUiCookieMask;
iSearchMask |= EUiCookie;
}
/**
Assignment operator
@param aSearch the search pattern to which to assign this
@publishedAll
@released
*/
EXPORT_C TBTRegistrySearch& TBTRegistrySearch::operator=(const TBTRegistrySearch& aSearch)
{
LOG_FUNC
if (this != &aSearch)
{
iSearchMask = aSearch.iSearchMask;
iDeviceClass = aSearch.iDeviceClass;
iDeviceAddress = aSearch.iDeviceAddress;
iLastSeen = aSearch.iLastSeen;
iLastUsed = aSearch.iLastUsed;
iCurrentProcessSID = aSearch.iCurrentProcessSID;
iUiCookie = aSearch.iUiCookie;
iUiCookieMask = aSearch.iUiCookieMask;
iBluetoothName.Set(aSearch.iBluetoothName);
iFriendlyName.Set(aSearch.iFriendlyName);
}
return *this;
}