qthighway/xqservice/src/xqaiwutils.cpp
branchRCL_3
changeset 10 cd2778e5acfe
parent 9 5d007b20cfd0
child 11 19a54be74e5e
equal deleted inserted replaced
9:5d007b20cfd0 10:cd2778e5acfe
     1 /*
       
     2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 *
       
     5 * This program is free software: you can redistribute it and/or modify
       
     6 * it under the terms of the GNU Lesser General Public License as published by
       
     7 * the Free Software Foundation, version 2.1 of the License.
       
     8 * 
       
     9 * This program is distributed in the hope that it will be useful,
       
    10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    12 * GNU Lesser General Public License for more details.
       
    13 *
       
    14 * You should have received a copy of the GNU Lesser General Public License
       
    15 * along with this program.  If not, 
       
    16 * see "http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html/".
       
    17 *
       
    18 * Description:
       
    19 *
       
    20 */
       
    21 
       
    22 #include <e32std.h>
       
    23 #include <apgcli.h>
       
    24 #include <apacmdln.h> 
       
    25 #include <apparc.h>
       
    26 #include <apmstd.h>
       
    27 #include <w32std.h>
       
    28 #include <apgtask.h>
       
    29 #include <caf/content.h>
       
    30 
       
    31 #include "xqaiwdecl.h"
       
    32 #include "xqservicelog.h"
       
    33 #include <xqserviceglobal.h>  // Error codes
       
    34 #include <xqserviceipcconst.h>
       
    35 #include <xqapplicationmanager.h>
       
    36 #include "xqaiwutils.h"
       
    37 
       
    38 
       
    39 class XQAiwUtilsPrivate : public QObject
       
    40 {
       
    41     public:
       
    42 
       
    43         XQAiwUtilsPrivate();
       
    44         virtual ~XQAiwUtilsPrivate();
       
    45         
       
    46         void launchApplicationL(int applicationId, const QString &cmdArguments);
       
    47         int launchFile(const QVariant &file);
       
    48         int findApplicationFromApa(const QString &file, int &applicationId, QString &mimeType);
       
    49         int findApplicationFromApa(const XQSharableFile &file, int &applicationId, QString &mimeType);
       
    50         bool applicationExists(int applicationId);
       
    51         int toIntFromHex(const QString &str, bool *ok);
       
    52         void GetDrmAttributesL(ContentAccess::CContent *c, const QList<int> & attributes, QVariantList &result);
       
    53         
       
    54     public:
       
    55         RApaLsSession apaSession;
       
    56     
       
    57 };
       
    58 
       
    59 
       
    60 XQAiwUtils::XQAiwUtils()
       
    61    : d(NULL)
       
    62 {
       
    63     XQSERVICE_DEBUG_PRINT("XQAiwUtils::XQAiwUtils");
       
    64     d = new XQAiwUtilsPrivate();
       
    65 }
       
    66 
       
    67 XQAiwUtils::~XQAiwUtils()
       
    68 {
       
    69     XQSERVICE_DEBUG_PRINT("XQAiwUtils::~XQAiwUtils");
       
    70     delete d;
       
    71 };
       
    72 
       
    73 int XQAiwUtils::launchApplication(int applicationId, const QList<QVariant> &arguments)
       
    74 {
       
    75     TInt error = KErrNone;
       
    76     
       
    77     // Create space separated command line args
       
    78     QString args = createCmdlineArgs(arguments);
       
    79     XQSERVICE_DEBUG_PRINT("args %s", qPrintable(args));
       
    80     
       
    81     TRAP(error, d->launchApplicationL(applicationId, args));
       
    82     return mapError(error);
       
    83 }
       
    84 
       
    85 int XQAiwUtils::launchFile(int applicationId, const QVariant &file)
       
    86 {
       
    87     Q_UNUSED(applicationId);
       
    88     TInt error = KErrNone;
       
    89     error=d->launchFile(file);
       
    90     return mapError(error);
       
    91 }
       
    92 
       
    93 
       
    94 int XQAiwUtils::mapError(int symbianError)
       
    95 {
       
    96     XQSERVICE_DEBUG_PRINT("XQAiwUtils::doMapErrors");
       
    97     XQSERVICE_DEBUG_PRINT("error: %d", symbianError);
       
    98     int error(XQService::ENoError);
       
    99     switch (symbianError)
       
   100     {
       
   101         case KErrNone:
       
   102         {
       
   103             error = XQService::ENoError;
       
   104             break;
       
   105         }
       
   106         
       
   107         case KErrPermissionDenied:
       
   108         case KErrServerTerminated:
       
   109         {
       
   110             error = XQService::EConnectionClosed;
       
   111             break;
       
   112         }
       
   113         case KErrServerBusy:
       
   114         {
       
   115             error = XQService::EConnectionError;
       
   116             break;
       
   117         }
       
   118         case KErrArgument:
       
   119         {
       
   120             error = XQService::EArgumentError;
       
   121             break;
       
   122         }
       
   123         case KErrNoMemory:
       
   124         {
       
   125             error = XQService::EIPCError;
       
   126             break;
       
   127         }
       
   128         case KErrNotFound:
       
   129         {
       
   130             error = XQService::EServerNotFound;
       
   131             break;
       
   132         }
       
   133         
       
   134         default:
       
   135         {
       
   136             error = XQService::EUnknownError;
       
   137             break;
       
   138         }
       
   139     }
       
   140     XQSERVICE_DEBUG_PRINT("error: %d", error);
       
   141     return error;
       
   142     
       
   143 }
       
   144 
       
   145 int XQAiwUtils::findApplication(const QFile &file, int &applicationId)
       
   146 {
       
   147     XQSERVICE_DEBUG_PRINT("XQAiwUtils::findApplication %s", qPrintable(file.fileName()));
       
   148     TInt error = KErrNone;
       
   149     int appId = 0;
       
   150     QString mimeType;
       
   151     error = d->findApplicationFromApa(file.fileName(), appId, mimeType);
       
   152     if (!error)
       
   153     {
       
   154         applicationId = appId;
       
   155     }
       
   156     return error;
       
   157 
       
   158 }
       
   159 
       
   160 int XQAiwUtils::findApplication(const XQSharableFile &file, int &applicationId)
       
   161 {
       
   162     XQSERVICE_DEBUG_PRINT("XQAiwUtils::findApplication (handle)");
       
   163     TInt error = KErrNone;
       
   164     int appId = 0;
       
   165     QString mimeType;
       
   166     error = d->findApplicationFromApa(file, appId, mimeType);
       
   167     if (!error)
       
   168     {
       
   169         applicationId = appId;
       
   170     }
       
   171     return error;
       
   172 
       
   173 }
       
   174 
       
   175 
       
   176 int XQAiwUtils::findApplication(const QUrl &uri, int &applicationId)
       
   177 {
       
   178     XQSERVICE_DEBUG_PRINT("XQAiwUtils::findapplication %s", qPrintable(uri.toString()));
       
   179     int appId = 0;
       
   180     bool idOk = false;
       
   181     if (uri.scheme() == XQURI_SCHEME_ACTIVITY)  // application://uid3
       
   182     {
       
   183         QString uid = uri.authority(); 
       
   184         XQSERVICE_DEBUG_PRINT("findApplication::authority=%s", qPrintable(uid));
       
   185         appId = d->toIntFromHex(uid, &idOk);
       
   186         XQSERVICE_DEBUG_PRINT("XQAiwUriDriver::appid=%x,%d", appId, idOk);
       
   187 
       
   188         if (idOk)
       
   189         {
       
   190             idOk = d->applicationExists(appId);
       
   191         }
       
   192     }
       
   193     else if (uri.scheme() == XQURI_SCHEME_FILE)  // file://
       
   194     {
       
   195         QString mimeType;
       
   196         TInt err = d->findApplicationFromApa(uri.toLocalFile(), appId, mimeType);
       
   197         idOk = (err == KErrNone);
       
   198     }
       
   199 
       
   200     if (idOk)
       
   201     {
       
   202         applicationId = appId;
       
   203         return mapError(KErrNone);
       
   204     }
       
   205 
       
   206     return mapError(KErrNotFound);
       
   207 
       
   208 }
       
   209 
       
   210 // Create space separated command line args
       
   211 QString XQAiwUtils::createCmdlineArgs(const QList<QVariant> &args)
       
   212 {
       
   213     XQSERVICE_DEBUG_PRINT("XQAiwUtils::createCmdlineArgs");
       
   214     
       
   215     QString argsStr = "";
       
   216     for ( int i = 0; i < args.size(); ++i )
       
   217     {
       
   218         QVariant v = args.at(i);
       
   219         QString s = v.toString();
       
   220         if (!s.isEmpty())
       
   221         {
       
   222             argsStr += (i==0 ? "" : " ");
       
   223             argsStr += s;
       
   224         }
       
   225     }
       
   226 
       
   227     return argsStr;
       
   228     
       
   229 }
       
   230 
       
   231 
       
   232 // Error error message for R&D purposes
       
   233 QString XQAiwUtils::createErrorMessage(int errorCode, const QString context, const QString detail)
       
   234 {
       
   235     QString txt;
       
   236     switch (errorCode)
       
   237     {
       
   238         case XQService::ENoError:
       
   239                 txt =  "ENoError";
       
   240         break;
       
   241 
       
   242         case XQService::EConnectionError:
       
   243                 txt ="EConnectionError";
       
   244         break;
       
   245 
       
   246         case XQService::EConnectionClosed:
       
   247                 txt = "EConnectionClosed";
       
   248         break;
       
   249 
       
   250         case XQService::EServerNotFound:
       
   251                 txt = "EServerNotFound";
       
   252         break;
       
   253 
       
   254         case XQService::EIPCError:
       
   255                 txt = "EIPCError";
       
   256         break;
       
   257 
       
   258         case XQService::EUnknownError:
       
   259                 txt = "EUnknownError";
       
   260         break;
       
   261 
       
   262         case XQService::ERequestPending:
       
   263                 txt = "ERequestPending";
       
   264         break;
       
   265 
       
   266         case XQService::EMessageNotFound:
       
   267                 txt = "EMessageNotFound";
       
   268         break;
       
   269 
       
   270         case XQService::EArgumentError:
       
   271                 txt = "EArgumentError";
       
   272         break;
       
   273 
       
   274         default:
       
   275             txt = QString("AIW error: %1").arg(errorCode);
       
   276             break;
       
   277 
       
   278     }
       
   279 
       
   280     QString ret = "AIW error: ";
       
   281     ret += txt;
       
   282     ret += " (";
       
   283     ret += context;
       
   284     ret += ",";
       
   285     ret += detail;
       
   286     ret += ")";
       
   287 
       
   288     return ret;
       
   289 }
       
   290 
       
   291 bool XQAiwUtils::getDrmAttributes(const QString &file, const QList<int> & attributes, QVariantList &result)
       
   292 {
       
   293 
       
   294     QString fileName = file;
       
   295     fileName.replace("/", "\\");  // Normalize
       
   296     
       
   297     XQSERVICE_DEBUG_PRINT("XQAiwUtilsPrivate::getDrmAttributes %s", qPrintable(fileName));
       
   298     
       
   299     TPtrC fileNameSymbian( reinterpret_cast<const TUint16*>(fileName.utf16()));
       
   300     
       
   301     TInt err=KErrNone;
       
   302     ContentAccess::CContent* c = 0;
       
   303 
       
   304     TRAP(err,c = ContentAccess::CContent::NewL(fileNameSymbian));
       
   305     if (err != KErrNone)
       
   306     {
       
   307         XQSERVICE_DEBUG_PRINT("XQAiwUtilsPrivate::getDrmAttributes leave %d", err);
       
   308         return false;
       
   309     }
       
   310     CleanupStack::PushL(c);
       
   311 
       
   312     d->GetDrmAttributesL(c, attributes, result);
       
   313 
       
   314     CleanupStack::PopAndDestroy();  // c
       
   315 
       
   316     return true;
       
   317 }
       
   318 
       
   319 
       
   320 bool XQAiwUtils::getDrmAttributes(const XQSharableFile &file, const QList<int> & attributes, QVariantList &result)
       
   321 {
       
   322     XQSERVICE_DEBUG_PRINT("XQAiwUtilsPrivate::getDrmAttributes (handle) %s", qPrintable(file.fileName()));
       
   323 
       
   324     RFile fileHandle;
       
   325     if (!file.getHandle(fileHandle))
       
   326     {
       
   327         XQSERVICE_DEBUG_PRINT("\tInvalid handle");
       
   328         return false;
       
   329     }
       
   330     TInt err=KErrNone;
       
   331     ContentAccess::CContent* c = 0;
       
   332     TRAP(err,c = ContentAccess::CContent::NewL(fileHandle));
       
   333     if (err != KErrNone)
       
   334     {
       
   335         XQSERVICE_DEBUG_PRINT("XQAiwUtilsPrivate::getDrmAttributes leave %d", err);
       
   336         return false;
       
   337     }
       
   338     CleanupStack::PushL(c);
       
   339 
       
   340     d->GetDrmAttributesL(c, attributes, result);
       
   341 
       
   342     CleanupStack::PopAndDestroy();  // c
       
   343 
       
   344     return true;
       
   345 }
       
   346 
       
   347 int XQAiwUtils::toIntFromHex(const QString &str, bool *ok)
       
   348 {
       
   349     return d->toIntFromHex(str,ok);
       
   350     
       
   351 }
       
   352 
       
   353 
       
   354 // --- XQAiwUtilsPrivate--
       
   355 
       
   356 XQAiwUtilsPrivate::XQAiwUtilsPrivate()
       
   357 {
       
   358     apaSession.Connect();
       
   359 }
       
   360 
       
   361 XQAiwUtilsPrivate::~XQAiwUtilsPrivate()
       
   362 {
       
   363     apaSession.Close();
       
   364 }
       
   365 
       
   366 void XQAiwUtilsPrivate::launchApplicationL(int applicationId, const QString &cmdArguments)
       
   367 {
       
   368     XQSERVICE_DEBUG_PRINT("XQAiwUtils::launchApplication");
       
   369     XQSERVICE_DEBUG_PRINT("applicationId=%x, cmdArguments %s", applicationId, qPrintable(cmdArguments));
       
   370 
       
   371     TPtrC cmdArgs( reinterpret_cast<const TUint16*>(cmdArguments.utf16()) );
       
   372     TUid uid;
       
   373     uid.iUid = applicationId;
       
   374 
       
   375     RWsSession wsSession;
       
   376     User::LeaveIfError(wsSession.Connect());
       
   377     CleanupClosePushL(wsSession);
       
   378 
       
   379     TApaTaskList taskList( wsSession );
       
   380     TApaTask task = taskList.FindApp( uid );
       
   381 
       
   382     if ( task.Exists() )
       
   383     {
       
   384         // Switching
       
   385         XQSERVICE_DEBUG_PRINT("XQAiwUtils::launchApplication: switch to existing");
       
   386         // TODO: How to pass new aguments to  running process ? Use SendMessage ?
       
   387         task.BringToForeground();
       
   388         CleanupStack::PopAndDestroy();  // wsSession
       
   389     }
       
   390     else
       
   391     {
       
   392         // Start application
       
   393         TApaAppInfo aInfo;
       
   394         User::LeaveIfError( apaSession.GetAppInfo( aInfo, uid ) );
       
   395         CApaCommandLine* cmdLine = CApaCommandLine::NewLC();
       
   396         cmdLine->SetExecutableNameL( aInfo.iFullName );
       
   397         RProcess newApp;
       
   398         User::LeaveIfError(newApp.Create(aInfo.iFullName, cmdArgs));
       
   399         cmdLine->SetProcessEnvironmentL(newApp);
       
   400         newApp.Resume();
       
   401         newApp.Close(); // Close the handle (not the app)
       
   402         CleanupStack::PopAndDestroy(2);  // cmdLine, wsSession
       
   403     }
       
   404 
       
   405     XQSERVICE_DEBUG_PRINT("application started");
       
   406 
       
   407 }
       
   408 
       
   409 
       
   410 int  XQAiwUtilsPrivate::launchFile(const QVariant &file)
       
   411 {
       
   412     XQSERVICE_DEBUG_PRINT("XQAiwUtils::launchFile");
       
   413 
       
   414     TThreadId startedApp;
       
   415     TInt err=KErrNone;
       
   416     if (file.typeName() == QString("XQSharableFile"))
       
   417     {
       
   418         XQSharableFile sharableFile = file.value<XQSharableFile>();
       
   419         RFile fileHandle;
       
   420         XQSERVICE_DEBUG_PRINT("\tStarting file by handle %s", qPrintable(sharableFile.fileName()));
       
   421         if (!sharableFile.getHandle(fileHandle))
       
   422         {
       
   423             err=KErrArgument;
       
   424         }
       
   425         else
       
   426         {
       
   427             err = apaSession.StartDocument(fileHandle, startedApp);
       
   428         }
       
   429     }
       
   430     else
       
   431     {
       
   432         QString fileName = file.toString();
       
   433         fileName.replace("/", "\\");  // Normalize just-in case
       
   434         XQSERVICE_DEBUG_PRINT("\tStarting file %s", qPrintable(fileName));
       
   435         TPtrC fname( reinterpret_cast<const TUint16*>(fileName.utf16()) );
       
   436         err=apaSession.StartDocument(fname, startedApp);
       
   437     }
       
   438 
       
   439     XQSERVICE_DEBUG_PRINT("XQAiwUtils::launchFile status=%d", err);
       
   440     return err;
       
   441 
       
   442 }
       
   443 
       
   444 
       
   445 
       
   446 
       
   447 int XQAiwUtilsPrivate::findApplicationFromApa(const QString &file, int &applicationId, QString &mimeType)
       
   448 {
       
   449     QString fileName = file;
       
   450     
       
   451     XQSERVICE_DEBUG_PRINT("XQAiwUtilsPrivate::::findApplicationFromApa file=%s", qPrintable(fileName));
       
   452 
       
   453     fileName.replace("/", "\\");  // Normalize
       
   454     
       
   455     TPtrC name( reinterpret_cast<const TUint16*>(fileName.utf16()) );
       
   456 
       
   457     // Get the UID and MIME type for the given file name.
       
   458     TUid uid;
       
   459     uid.iUid=0;
       
   460     TDataType dataType;
       
   461     TInt err = apaSession.AppForDocument(name, uid, dataType);
       
   462     XQSERVICE_DEBUG_PRINT("\tFind status %d,%x", err, uid.iUid);
       
   463     if (err || uid.iUid == 0)
       
   464     {
       
   465         XQSERVICE_DEBUG_PRINT("\tHandler not found");
       
   466         return KErrNotFound;
       
   467     }
       
   468 
       
   469     applicationId = uid.iUid;  // return value
       
   470     QString mime = QString::fromUtf16(dataType.Des().Ptr(), dataType.Des().Length());
       
   471     mimeType = mime;
       
   472     
       
   473     XQSERVICE_DEBUG_PRINT("\tapplicationId=%x,mime-type=%s", applicationId, qPrintable(mime));
       
   474     
       
   475     return KErrNone;
       
   476     
       
   477 }
       
   478 
       
   479 int XQAiwUtilsPrivate::findApplicationFromApa(const XQSharableFile &file, int &applicationId, QString &mimeType)
       
   480 {
       
   481     XQSERVICE_DEBUG_PRINT("XQAiwUtilsPrivate::findApplicationFromApa by handle, file=%s", qPrintable(file.fileName()));
       
   482     RFile fileHandle;
       
   483     if (!file.getHandle(fileHandle))
       
   484     {
       
   485         XQSERVICE_DEBUG_PRINT("\tInvalid handle");
       
   486         return KErrArgument;
       
   487     }
       
   488 
       
   489     // Get the UID and MIME type for the given file name.
       
   490     TUid uid;
       
   491     uid.iUid=0;
       
   492     TDataType dataType;
       
   493     TInt err = apaSession.AppForDocument(fileHandle, uid, dataType);
       
   494     XQSERVICE_DEBUG_PRINT("\tFind status %d,%x", err, uid.iUid);
       
   495     if (err || uid.iUid == 0)
       
   496     {
       
   497         XQSERVICE_DEBUG_PRINT("\tHandler not found");
       
   498         return KErrNotFound;
       
   499     }
       
   500 
       
   501     applicationId = uid.iUid;  // return value
       
   502     QString mime = QString::fromUtf16(dataType.Des().Ptr(), dataType.Des().Length());
       
   503     mimeType = mime;
       
   504     
       
   505     XQSERVICE_DEBUG_PRINT("\tapplicationId=%x,mime-type=%s", applicationId, qPrintable(mime));
       
   506     return KErrNone;
       
   507 
       
   508 }
       
   509 
       
   510 
       
   511 bool XQAiwUtilsPrivate::applicationExists(int applicationId)
       
   512 {
       
   513     TUid uid;
       
   514     uid.iUid = applicationId;
       
   515 
       
   516     TApaAppInfo aInfo;
       
   517     return apaSession.GetAppInfo( aInfo, uid ) == KErrNone;
       
   518     
       
   519 }
       
   520 
       
   521 //
       
   522 // For some reason QString::toInt(0,16) does not work...
       
   523 // Implement own converter
       
   524 //
       
   525 int XQAiwUtilsPrivate::toIntFromHex(const QString &str, bool *ok)
       
   526 {
       
   527     int result=0;
       
   528     int power = 0;
       
   529     int base=16;
       
   530     QString s = str.toUpper();
       
   531     s.replace("0X", "");  // Remove possible 0x
       
   532 
       
   533     XQSERVICE_DEBUG_PRINT("XQAiwUtilsPrivate::toIntFromHex=%s", qPrintable(s));
       
   534     
       
   535     for (int i=s.length()-1; i >= 0; i--)
       
   536     {
       
   537         int val = (int)s[i].toLatin1();
       
   538         int num;
       
   539         if ((val >= (int)'A') && (val <= (int)'F'))
       
   540             num = 10 + (val - (int)'A');
       
   541         else if ((val >= (int)'0') && (val <= (int)'9'))
       
   542             num = val - (int)'0';
       
   543         else
       
   544         {
       
   545             *ok = false;
       
   546             return 0;
       
   547         }
       
   548         
       
   549         int multiplier = 1;
       
   550         for (int j=0; j < power; j++) 
       
   551             multiplier *= base; // Calculate power
       
   552         
       
   553         result += multiplier*num;
       
   554         power++;
       
   555     }
       
   556 
       
   557     *ok = true;
       
   558 
       
   559     return result;
       
   560 }
       
   561 
       
   562 
       
   563 
       
   564 void XQAiwUtilsPrivate::GetDrmAttributesL(ContentAccess::CContent *c, const QList<int> & attributes, QVariantList &result)
       
   565 {
       
   566 
       
   567     XQSERVICE_DEBUG_PRINT("XQAiwUtilsPrivate::GetDrmAttributesL");
       
   568     
       
   569     HBufC* buffer = 0;
       
   570 
       
   571     foreach (int attrName, attributes)
       
   572     {
       
   573         QVariant v; // By default invalid
       
   574         bool isStringAttribute = attrName >= XQApplicationManager::DrmStringAttributeBase;
       
   575         if (isStringAttribute && !buffer)
       
   576         {
       
   577             // Assume 512 characters is enough
       
   578             buffer = HBufC::NewLC(512);
       
   579             XQSERVICE_DEBUG_PRINT("XQAiwUtilsPrivate::buffer allocated");
       
   580         }
       
   581         
       
   582         if (!isStringAttribute)
       
   583         {
       
   584             TInt value = 0;
       
   585             TInt err = c->GetAttribute(attrName, value);
       
   586             if(err == KErrNone)
       
   587             {
       
   588                 // Ok, set the value
       
   589                 v.setValue(value);
       
   590             }
       
   591             XQSERVICE_DEBUG_PRINT("XQAiwUtilsPrivate::GetDrmAttributesL (int):%d,%d=%d", err, attrName, value);
       
   592         }
       
   593         else
       
   594         {
       
   595             // String attribute
       
   596             attrName -= XQApplicationManager::DrmStringAttributeBase;  // CAF uses same values for int and string attributes 
       
   597             TPtr value( buffer->Des() );
       
   598             value.Zero();
       
   599             TInt err = c->GetStringAttribute(attrName, value);
       
   600             QString strValue;            
       
   601             if(err == KErrNone)
       
   602             {
       
   603                 // Ok, set the value
       
   604                 strValue = QString::fromUtf16(value.Ptr(), value.Length());
       
   605                 v.setValue(strValue);
       
   606             }
       
   607             XQSERVICE_DEBUG_PRINT("XQAiwUtilsPrivate::GetDrmAttributesL (string):%d,%d=%s", err, attrName, qPrintable(strValue));
       
   608 
       
   609         }
       
   610         // On error value remains invalid and client can check that
       
   611         // v.isValid()
       
   612         result.append(v);
       
   613     }
       
   614 
       
   615     if (buffer)
       
   616     {
       
   617         CleanupStack::PopAndDestroy();  // buffer
       
   618     }
       
   619 
       
   620 }