diff --git a/b2g/chrome/content/settings.js b/b2g/chrome/content/settings.js index f14de8c78102..26d002db1d7f 100644 --- a/b2g/chrome/content/settings.js +++ b/b2g/chrome/content/settings.js @@ -179,6 +179,16 @@ SettingsListener.observe('language.current', 'en-US', function(value) { function(value) { Services.prefs.setBoolPref('ril.radio.disabled', value); }); + + SettingsListener.observe('wap.UAProf.url', '', + function(value) { + Services.prefs.setCharPref('wap.UAProf.url', value); + }); + + SettingsListener.observe('wap.UAProf.tagname', 'x-wap-profile', + function(value) { + Services.prefs.setCharPref('wap.UAProf.tagname', value); + }); })(); //=================== DeviceInfo ==================== diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 0e3cb4ed25d4..aab0deaa31d2 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "c59267134f80ccb38353a46c51b210009dba0391", + "revision": "61262845a414403a1507ceb1afebfd4f8139e0f5", "repo_path": "/integration/gaia-central" } diff --git a/dom/bluetooth/BluetoothOppManager.cpp b/dom/bluetooth/BluetoothOppManager.cpp index 61048c565ee6..016f99b538b0 100644 --- a/dom/bluetooth/BluetoothOppManager.cpp +++ b/dom/bluetooth/BluetoothOppManager.cpp @@ -47,22 +47,7 @@ static const uint32_t kUpdateProgressBase = 50 * 1024; static const uint32_t kPutRequestHeaderSize = 6; StaticRefPtr<BluetoothOppManager> sInstance; - -/* - * FIXME / Bug 806749 - * - * Currently Bluetooth*Manager inherits mozilla::ipc::UnixSocketConsumer, - * which means that each Bluetooth*Manager can handle only one socket - * connection at a time. We need to support concurrent multiple socket - * connections, and then we will be able to have multiple file transferring - * sessions at a time. - */ -static uint32_t sSentFileLength = 0; -static nsString sFileName; -static uint32_t sFileLength = 0; -static nsString sContentType; static bool sInShutdown = false; -static bool sWaitingToSendPutFinal = false; } NS_IMETHODIMP @@ -96,7 +81,6 @@ public: MOZ_ASSERT(NS_IsMainThread()); sInstance->SendPutRequest(mStream, mSize); - sSentFileLength += mSize; return NS_OK; } @@ -133,9 +117,7 @@ public: } if (numRead > 0) { - if (sSentFileLength + numRead >= sFileLength) { - sWaitingToSendPutFinal = true; - } + sInstance->CheckPutFinal(numRead); nsRefPtr<SendSocketDataTask> task = new SendSocketDataTask((uint8_t*)buf.forget(), numRead); @@ -190,6 +172,9 @@ BluetoothOppManager::BluetoothOppManager() : mConnected(false) , mSuccessFlag(false) , mIsServer(true) , mWaitingForConfirmationFlag(false) + , mFileLength(0) + , mSentFileLength(0) + , mWaitingToSendPutFinal(false) , mCurrentBlobIndex(-1) { mConnectedDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE); @@ -363,7 +348,7 @@ BluetoothOppManager::StartSendingNextFile() // file at the head of queue. SendConnectRequest(); } else { - SendPutHeaderRequest(sFileName, sFileLength); + SendPutHeaderRequest(mFileName, mFileLength); AfterFirstPut(); } } @@ -431,8 +416,8 @@ BluetoothOppManager::AfterFirstPut() mPutFinalFlag = false; mReceivedDataBufferOffset = 0; mSendTransferCompleteFlag = false; - sSentFileLength = 0; - sWaitingToSendPutFinal = false; + mSentFileLength = 0; + mWaitingToSendPutFinal = false; mSuccessFlag = false; mBodySegmentLength = 0; } @@ -516,7 +501,7 @@ BluetoothOppManager::CreateFile() nsString path; path.AssignLiteral(TARGET_SUBDIR); - path.Append(sFileName); + path.Append(mFileName); mDsFile = DeviceStorageFile::CreateUnique(path, nsIFile::NORMAL_FILE_TYPE, 0644); if (!mDsFile) { @@ -526,13 +511,13 @@ BluetoothOppManager::CreateFile() nsCOMPtr<nsIFile> f; mDsFile->mFile->Clone(getter_AddRefs(f)); - + /* * The function CreateUnique() may create a file with a different file - * name from the original sFileName. Therefore we have to retrieve + * name from the original mFileName. Therefore we have to retrieve * the file name again. */ - f->GetLeafName(sFileName); + f->GetLeafName(mFileName); NS_NewLocalFileOutputStream(getter_AddRefs(mOutputStream), f); NS_ENSURE_TRUE(mOutputStream, false); @@ -557,15 +542,15 @@ void BluetoothOppManager::ExtractPacketHeaders(const ObexHeaderSet& aHeader) { if (aHeader.Has(ObexHeaderId::Name)) { - aHeader.GetName(sFileName); + aHeader.GetName(mFileName); } if (aHeader.Has(ObexHeaderId::Type)) { - aHeader.GetContentType(sContentType); + aHeader.GetContentType(mContentType); } if (aHeader.Has(ObexHeaderId::Length)) { - aHeader.GetLength(&sFileLength); + aHeader.GetLength(&mFileLength); } if (aHeader.Has(ObexHeaderId::Body) || @@ -583,7 +568,7 @@ BluetoothOppManager::ExtractBlobHeaders() { RetrieveSentFileName(); - nsresult rv = mBlob->GetType(sContentType); + nsresult rv = mBlob->GetType(mContentType); if (NS_FAILED(rv)) { NS_WARNING("Can't get content type"); SendDisconnectRequest(); @@ -609,7 +594,7 @@ BluetoothOppManager::ExtractBlobHeaders() return false; } - sFileLength = fileLength; + mFileLength = fileLength; rv = NS_NewThread(getter_AddRefs(mReadFileThread)); if (NS_FAILED(rv)) { NS_WARNING("Can't create thread"); @@ -623,11 +608,11 @@ BluetoothOppManager::ExtractBlobHeaders() void BluetoothOppManager::RetrieveSentFileName() { - sFileName.Truncate(); + mFileName.Truncate(); nsCOMPtr<nsIDOMFile> file = do_QueryInterface(mBlob); if (file) { - file->GetName(sFileName); + file->GetName(mFileName); } /** @@ -636,16 +621,16 @@ BluetoothOppManager::RetrieveSentFileName() * information about the content type, sending a pre-defined file name without * extension would be fine. */ - if (sFileName.IsEmpty()) { - sFileName.AssignLiteral("Unknown"); + if (mFileName.IsEmpty()) { + mFileName.AssignLiteral("Unknown"); } - int32_t offset = sFileName.RFindChar('/'); + int32_t offset = mFileName.RFindChar('/'); if (offset != kNotFound) { - sFileName = Substring(sFileName, offset + 1); + mFileName = Substring(mFileName, offset + 1); } - offset = sFileName.RFindChar('.'); + offset = mFileName.RFindChar('.'); if (offset == kNotFound) { nsCOMPtr<nsIMIMEService> mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID); @@ -659,8 +644,8 @@ BluetoothOppManager::RetrieveSentFileName() EmptyCString(), extension); if (NS_SUCCEEDED(rv)) { - sFileName.AppendLiteral("."); - AppendUTF8toUTF16(extension, sFileName); + mFileName.AppendLiteral("."); + AppendUTF8toUTF16(extension, mFileName); } } } @@ -678,12 +663,12 @@ BluetoothOppManager::IsReservedChar(PRUnichar c) void BluetoothOppManager::ValidateFileName() { - int length = sFileName.Length(); + int length = mFileName.Length(); for (int i = 0; i < length; ++i) { // Replace reserved char of fat file system with '_' - if (IsReservedChar(sFileName.CharAt(i))) { - sFileName.Replace(i, 1, PRUnichar('_')); + if (IsReservedChar(mFileName.CharAt(i))) { + mFileName.Replace(i, 1, PRUnichar('_')); } } } @@ -786,7 +771,7 @@ BluetoothOppManager::ServerDataHandler(UnixSocketRawData* aMessage) // When we cancel the transfer, delete the file and notify completion if (mAbortFlag) { ReplyToPut(mPutFinalFlag, false); - sSentFileLength += mBodySegmentLength; + mSentFileLength += mBodySegmentLength; DeleteReceivedFile(); FileTransferComplete(); return; @@ -795,7 +780,7 @@ BluetoothOppManager::ServerDataHandler(UnixSocketRawData* aMessage) // Wait until get confirmation from user, then create file and write to it if (mWaitingForConfirmationFlag) { ReceivingFileConfirmation(); - sSentFileLength += mBodySegmentLength; + mSentFileLength += mBodySegmentLength; return; } @@ -818,10 +803,10 @@ BluetoothOppManager::ServerDataHandler(UnixSocketRawData* aMessage) ReplyToPut(mPutFinalFlag, true); // Send progress update - sSentFileLength += mBodySegmentLength; - if (sSentFileLength > kUpdateProgressBase * mUpdateProgressCounter) { + mSentFileLength += mBodySegmentLength; + if (mSentFileLength > kUpdateProgressBase * mUpdateProgressCounter) { UpdateProgress(); - mUpdateProgressCounter = sSentFileLength / kUpdateProgressBase + 1; + mUpdateProgressCounter = mSentFileLength / kUpdateProgressBase + 1; } // Success to receive a file and notify completion @@ -830,8 +815,14 @@ BluetoothOppManager::ServerDataHandler(UnixSocketRawData* aMessage) FileTransferComplete(); NotifyAboutFileChange(); } + } else if (opCode == ObexRequestCode::Get || + opCode == ObexRequestCode::GetFinal || + opCode == ObexRequestCode::SetPath) { + ReplyError(ObexResponseCode::BadRequest); + NS_WARNING("Unsupported ObexRequestCode"); } else { - NS_WARNING("Unhandled ObexRequestCode"); + ReplyError(ObexResponseCode::NotImplemented); + NS_WARNING("Unrecognized ObexRequestCode"); } } @@ -909,7 +900,7 @@ BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage) PostDelayedTask(FROM_HERE, new CloseSocketTask(mSocket), 1000); } } else if (mLastCommand == ObexRequestCode::Connect) { - MOZ_ASSERT(!sFileName.IsEmpty()); + MOZ_ASSERT(!mFileName.IsEmpty()); MOZ_ASSERT(mBlob); AfterOppConnected(); @@ -920,9 +911,9 @@ BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage) mRemoteMaxPacketLength = (((int)(aMessage->mData[5]) << 8) | aMessage->mData[6]); - sInstance->SendPutHeaderRequest(sFileName, sFileLength); + sInstance->SendPutHeaderRequest(mFileName, mFileLength); } else if (mLastCommand == ObexRequestCode::Put) { - if (sWaitingToSendPutFinal) { + if (mWaitingToSendPutFinal) { SendPutFinalRequest(); return; } @@ -932,9 +923,9 @@ BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage) return; } - if (kUpdateProgressBase * mUpdateProgressCounter < sSentFileLength) { + if (kUpdateProgressBase * mUpdateProgressCounter < mSentFileLength) { UpdateProgress(); - mUpdateProgressCounter = sSentFileLength / kUpdateProgressBase + 1; + mUpdateProgressCounter = mSentFileLength / kUpdateProgressBase + 1; } nsresult rv; @@ -1040,8 +1031,9 @@ BluetoothOppManager::SendPutRequest(uint8_t* aFileBody, index += AppendHeaderBody(&req[index], aFileBody, aFileBodyLength); SendObexData(req, ObexRequestCode::Put, index); - delete [] req; + + mSentFileLength += aFileBodyLength; } void @@ -1062,10 +1054,9 @@ BluetoothOppManager::SendPutFinalRequest() index += AppendHeaderEndOfBody(&req[index]); SendObexData(req, ObexRequestCode::PutFinal, index); - - sWaitingToSendPutFinal = false; - delete [] req; + + mWaitingToSendPutFinal = false; } void @@ -1094,6 +1085,14 @@ BluetoothOppManager::SendAbortRequest() SendObexData(req, ObexRequestCode::Abort, index); } +void +BluetoothOppManager::CheckPutFinal(uint32_t aNumRead) +{ + if (mSentFileLength + aNumRead >= mFileLength) { + mWaitingToSendPutFinal = true; + } +} + bool BluetoothOppManager::IsTransferring() { @@ -1161,6 +1160,19 @@ BluetoothOppManager::ReplyToPut(bool aFinal, bool aContinue) SendObexData(req, opcode, index); } +void +BluetoothOppManager::ReplyError(uint8_t aError) +{ + if (!mConnected) return; + + // Section 3.2 "Response Format", IrOBEX 1.2 + // [opcode:1][length:2][Headers:var] + uint8_t req[255]; + int index = 3; + + SendObexData(req, aError, index); +} + void BluetoothOppManager::SendObexData(uint8_t* aData, uint8_t aOpcode, int aSize) { @@ -1200,15 +1212,15 @@ BluetoothOppManager::FileTransferComplete() parameters.AppendElement(BluetoothNamedValue(name, v)); name.AssignLiteral("fileName"); - v = sFileName; + v = mFileName; parameters.AppendElement(BluetoothNamedValue(name, v)); name.AssignLiteral("fileLength"); - v = sSentFileLength; + v = mSentFileLength; parameters.AppendElement(BluetoothNamedValue(name, v)); name.AssignLiteral("contentType"); - v = sContentType; + v = mContentType; parameters.AppendElement(BluetoothNamedValue(name, v)); if (!BroadcastSystemMessage(type, parameters)) { @@ -1236,15 +1248,15 @@ BluetoothOppManager::StartFileTransfer() parameters.AppendElement(BluetoothNamedValue(name, v)); name.AssignLiteral("fileName"); - v = sFileName; + v = mFileName; parameters.AppendElement(BluetoothNamedValue(name, v)); name.AssignLiteral("fileLength"); - v = sFileLength; + v = mFileLength; parameters.AppendElement(BluetoothNamedValue(name, v)); name.AssignLiteral("contentType"); - v = sContentType; + v = mContentType; parameters.AppendElement(BluetoothNamedValue(name, v)); if (!BroadcastSystemMessage(type, parameters)) { @@ -1270,11 +1282,11 @@ BluetoothOppManager::UpdateProgress() parameters.AppendElement(BluetoothNamedValue(name, v)); name.AssignLiteral("processedLength"); - v = sSentFileLength; + v = mSentFileLength; parameters.AppendElement(BluetoothNamedValue(name, v)); name.AssignLiteral("fileLength"); - v = sFileLength; + v = mFileLength; parameters.AppendElement(BluetoothNamedValue(name, v)); if (!BroadcastSystemMessage(type, parameters)) { @@ -1296,15 +1308,15 @@ BluetoothOppManager::ReceivingFileConfirmation() parameters.AppendElement(BluetoothNamedValue(name, v)); name.AssignLiteral("fileName"); - v = sFileName; + v = mFileName; parameters.AppendElement(BluetoothNamedValue(name, v)); name.AssignLiteral("fileLength"); - v = sFileLength; + v = mFileLength; parameters.AppendElement(BluetoothNamedValue(name, v)); name.AssignLiteral("contentType"); - v = sContentType; + v = mContentType; parameters.AppendElement(BluetoothNamedValue(name, v)); if (!BroadcastSystemMessage(type, parameters)) { diff --git a/dom/bluetooth/BluetoothOppManager.h b/dom/bluetooth/BluetoothOppManager.h index aa63d2c5d84f..490e756ed18c 100644 --- a/dom/bluetooth/BluetoothOppManager.h +++ b/dom/bluetooth/BluetoothOppManager.h @@ -74,6 +74,7 @@ public: void ExtractPacketHeaders(const ObexHeaderSet& aHeader); bool ExtractBlobHeaders(); + void CheckPutFinal(uint32_t aNumRead); // Return true if there is an ongoing file-transfer session, please see // Bug 827267 for more information. @@ -109,6 +110,7 @@ private: void ReplyToConnect(); void ReplyToDisconnectOrAbort(); void ReplyToPut(bool aFinal, bool aContinue); + void ReplyError(uint8_t aError); void AfterOppConnected(); void AfterFirstPut(); void AfterOppDisconnected(); @@ -190,6 +192,12 @@ private: */ bool mWaitingForConfirmationFlag; + nsString mFileName; + nsString mContentType; + uint32_t mFileLength; + uint32_t mSentFileLength; + bool mWaitingToSendPutFinal; + nsAutoArrayPtr<uint8_t> mBodySegment; nsAutoArrayPtr<uint8_t> mReceivedDataBuffer; @@ -200,7 +208,6 @@ private: /** * A seperate member thread is required because our read calls can block * execution, which is not allowed to happen on the IOThread. - * */ nsCOMPtr<nsIThread> mReadFileThread; nsCOMPtr<nsIOutputStream> mOutputStream; diff --git a/dom/contacts/fallback/ContactDB.jsm b/dom/contacts/fallback/ContactDB.jsm index f1d6b15558fc..9ff4fc2339b7 100644 --- a/dom/contacts/fallback/ContactDB.jsm +++ b/dom/contacts/fallback/ContactDB.jsm @@ -25,6 +25,19 @@ const CHUNK_SIZE = 20; const REVISION_STORE = "revision"; const REVISION_KEY = "revision"; +function exportContact(aRecord) { + let contact = {}; + contact.properties = aRecord.properties; + + for (let field in aRecord.properties) + contact.properties[field] = aRecord.properties[field]; + + contact.updated = aRecord.updated; + contact.published = aRecord.published; + contact.id = aRecord.id; + return contact; +} + function ContactDispatcher(aContacts, aFullContacts, aCallback, aNewTxn, aClearDispatcher, aFailureCb) { let nextIndex = 0; @@ -54,7 +67,7 @@ function ContactDispatcher(aContacts, aFullContacts, aCallback, aNewTxn, aClearD aNewTxn("readonly", STORE_NAME, function(txn, store) { for (let i = start; i < Math.min(start+CHUNK_SIZE, aContacts.length); ++i) { store.get(aContacts[i]).onsuccess = function(e) { - chunk.push(e.target.result); + chunk.push(exportContact(e.target.result)); count++; if (count === aContacts.length) { aCallback(chunk); @@ -590,19 +603,6 @@ ContactDB.prototype = { return contact; }, - makeExport: function makeExport(aRecord) { - let contact = {}; - contact.properties = aRecord.properties; - - for (let field in aRecord.properties) - contact.properties[field] = aRecord.properties[field]; - - contact.updated = aRecord.updated; - contact.published = aRecord.published; - contact.id = aRecord.id; - return contact; - }, - updateRecordMetadata: function updateRecordMetadata(record) { if (!record.id) { Cu.reportError("Contact without ID"); @@ -979,7 +979,7 @@ ContactDB.prototype = { if (DEBUG) debug("Request successful. Record count: " + event.target.result.length); this.sortResults(event.target.result, options); for (let i in event.target.result) - txn.result[event.target.result[i].id] = this.makeExport(event.target.result[i]); + txn.result[event.target.result[i].id] = exportContact(event.target.result[i]); }.bind(this); } }, @@ -994,7 +994,7 @@ ContactDB.prototype = { if (DEBUG) debug("Request successful. Record count:" + event.target.result.length); this.sortResults(event.target.result, options); for (let i in event.target.result) { - txn.result[event.target.result[i].id] = this.makeExport(event.target.result[i]); + txn.result[event.target.result[i].id] = exportContact(event.target.result[i]); } }.bind(this); }, diff --git a/dom/system/gonk/AutoMounter.cpp b/dom/system/gonk/AutoMounter.cpp index 5406d42068ec..e9af00c8c7f0 100644 --- a/dom/system/gonk/AutoMounter.cpp +++ b/dom/system/gonk/AutoMounter.cpp @@ -28,6 +28,7 @@ #include "nsString.h" #include "nsThreadUtils.h" #include "nsXULAppAPI.h" +#include "OpenFileFinder.h" #include "Volume.h" #include "VolumeManager.h" @@ -71,8 +72,9 @@ using namespace mozilla::hal; #define USE_DEBUG 0 #undef LOG -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AutoMounter" , ## args) -#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "AutoMounter" , ## args) +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AutoMounter", ## args) +#define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "AutoMounter", ## args) +#define ERR(args...) __android_log_print(ANDROID_LOG_ERROR, "AutoMounter", ## args) #if USE_DEBUG #define DBG(args...) __android_log_print(ANDROID_LOG_DEBUG, "AutoMounter" , ## args) @@ -402,19 +404,50 @@ AutoMounter::UpdateState() if (vol->IsMountLocked()) { // The volume is currently locked, so leave it in the mounted // state. - DBG("UpdateState: Mounted volume %s is locked, leaving", - vol->NameStr()); + LOGW("UpdateState: Mounted volume %s is locked, not sharing", + vol->NameStr()); break; } + + // Check to see if there are any open files on the volume and + // don't initiate the unmount while there are open files. + OpenFileFinder::Info fileInfo; + OpenFileFinder fileFinder(vol->MountPoint()); + if (fileFinder.First(&fileInfo)) { + LOGW("The following files are open under '%s'", + vol->MountPoint().get()); + do { + LOGW(" PID: %d file: '%s' app: '%s' comm: '%s' exe: '%s'\n", + fileInfo.mPid, + fileInfo.mFileName.get(), + fileInfo.mAppName.get(), + fileInfo.mComm.get(), + fileInfo.mExe.get()); + } while (fileFinder.Next(&fileInfo)); + LOGW("UpdateState: Mounted volume %s has open files, not sharing", + vol->NameStr()); + + // Check again in 5 seconds to see if the files are closed. Since + // we're trying to share the volume, this implies that we're + // plugged into the PC via USB and this in turn implies that the + // battery is charging, so we don't need to be too concerned about + // wasting battery here. + MessageLoopForIO::current()-> + PostDelayedTask(FROM_HERE, + NewRunnableMethod(this, &AutoMounter::UpdateState), + 5000); + break; + } + // Volume is mounted, we need to unmount before // we can share. - DBG("UpdateState: Unmounting %s", vol->NameStr()); + LOG("UpdateState: Unmounting %s", vol->NameStr()); vol->StartUnmount(mResponseCallback); return; // UpdateState will be called again when the Unmount command completes } case nsIVolume::STATE_IDLE: { // Volume is unmounted. We can go ahead and share. - DBG("UpdateState: Sharing %s", vol->NameStr()); + LOG("UpdateState: Sharing %s", vol->NameStr()); vol->StartShare(mResponseCallback); return; // UpdateState will be called again when the Share command completes } @@ -428,14 +461,14 @@ AutoMounter::UpdateState() switch (volState) { case nsIVolume::STATE_SHARED: { // Volume is shared. We can go ahead and unshare. - DBG("UpdateState: Unsharing %s", vol->NameStr()); + LOG("UpdateState: Unsharing %s", vol->NameStr()); vol->StartUnshare(mResponseCallback); return; // UpdateState will be called again when the Unshare command completes } case nsIVolume::STATE_IDLE: { // Volume is unmounted, try to mount. - DBG("UpdateState: Mounting %s", vol->NameStr()); + LOG("UpdateState: Mounting %s", vol->NameStr()); vol->StartMount(mResponseCallback); return; // UpdateState will be called again when Mount command completes } diff --git a/dom/system/gonk/OpenFileFinder.cpp b/dom/system/gonk/OpenFileFinder.cpp new file mode 100644 index 000000000000..32a4f122b319 --- /dev/null +++ b/dom/system/gonk/OpenFileFinder.cpp @@ -0,0 +1,195 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "OpenFileFinder.h" + +#include "mozilla/FileUtils.h" +#include "nsPrintfCString.h" + +#include <sys/stat.h> +#include <errno.h> + +namespace mozilla { +namespace system { + +OpenFileFinder::OpenFileFinder(const nsACString& aPath) + : mPath(aPath), + mProcDir(nullptr), + mFdDir(nullptr), + mPid(0) +{ +} + +OpenFileFinder::~OpenFileFinder() +{ + Close(); +} + +bool +OpenFileFinder::First(OpenFileFinder::Info* aInfo) +{ + Close(); + + mProcDir = opendir("/proc"); + if (!mProcDir) { + return false; + } + mState = NEXT_PID; + return Next(aInfo); +} + +bool +OpenFileFinder::Next(OpenFileFinder::Info* aInfo) +{ + // NOTE: This function calls readdir and readlink, neither of which should + // block since we're using the proc filesystem, which is a purely + // kernel in-memory filesystem and doesn't depend on external driver + // behaviour. + while (mState != DONE) { + switch (mState) { + case NEXT_PID: { + struct dirent *pidEntry; + pidEntry = readdir(mProcDir); + if (!pidEntry) { + mState = DONE; + break; + } + char *endPtr; + mPid = strtol(pidEntry->d_name, &endPtr, 10); + if (mPid == 0 || *endPtr != '\0') { + // Not a +ve number - ignore + continue; + } + // We've found a /proc/PID directory. Scan open file descriptors. + if (mFdDir) { + closedir(mFdDir); + } + nsPrintfCString fdDirPath("/proc/%d/fd", mPid); + mFdDir = opendir(fdDirPath.get()); + if (!mFdDir) { + continue; + } + mState = CHECK_FDS; + } + // Fall through + case CHECK_FDS: { + struct dirent *fdEntry; + while((fdEntry = readdir(mFdDir))) { + if (!strcmp(fdEntry->d_name, ".") || + !strcmp(fdEntry->d_name, "..")) { + continue; + } + nsPrintfCString fdSymLink("/proc/%d/fd/%s", mPid, fdEntry->d_name); + nsCString resolvedPath; + if (ReadSymLink(fdSymLink, resolvedPath) && PathMatches(resolvedPath)) { + // We found an open file contained within the directory tree passed + // into the constructor. + FillInfo(aInfo, resolvedPath); + return true; + } + } + // We've checked all of the files for this pid, move onto the next one. + mState = NEXT_PID; + continue; + } + case DONE: + default: + mState = DONE; // covers the default case + break; + } + } + return false; +} + +void +OpenFileFinder::Close() +{ + if (mFdDir) { + closedir(mFdDir); + } + if (mProcDir) { + closedir(mProcDir); + } +} + +void +OpenFileFinder::FillInfo(OpenFileFinder::Info* aInfo, const nsACString& aPath) +{ + aInfo->mFileName = aPath; + aInfo->mPid = mPid; + nsPrintfCString exePath("/proc/%d/exe", mPid); + ReadSymLink(exePath, aInfo->mExe); + aInfo->mComm.Truncate(); + aInfo->mAppName.Truncate(); + nsPrintfCString statPath("/proc/%d/stat", mPid); + nsCString statString; + statString.SetLength(200); + char *stat = statString.BeginWriting(); + if (!stat) { + return; + } + ReadSysFile(statPath.get(), stat, statString.Length()); + // The stat line includes the comm field, surrounded by parenthesis. + // However, the contents of the comm field itself is arbitrary and + // and can include ')', so we search for the rightmost ) as being + // the end of the comm field. + char *closeParen = strrchr(stat, ')'); + if (!closeParen) { + return; + } + char *openParen = strchr(stat, '('); + if (!openParen) { + return; + } + if (openParen >= closeParen) { + return; + } + nsDependentCSubstring comm(&openParen[1], closeParen - openParen - 1); + aInfo->mComm = comm; + // There is a single character field after the comm and then + // the parent pid (the field we're interested in). + // ) X ppid + // 01234 + int ppid = atoi(&closeParen[4]); + // We assume that we're running in the parent process + if (ppid != getpid()) { + return; + } + // This looks like a content process. The comm field will be the + // app name. + aInfo->mAppName = aInfo->mComm; +} + +bool +OpenFileFinder::ReadSymLink(const nsACString& aSymLink, nsACString& aOutPath) +{ + aOutPath.Truncate(); + const char *symLink = aSymLink.BeginReading(); + + // Verify that we actually have a symlink. + struct stat st; + if (lstat(symLink, &st)) { + return false; + } + if ((st.st_mode & S_IFMT) != S_IFLNK) { + return false; + } + + // Contrary to the documentation st.st_size doesn't seem to be a reliable + // indication of the length when reading from /proc, so we use a fixed + // size buffer instead. + + char resolvedSymLink[PATH_MAX]; + ssize_t pathLength = readlink(symLink, resolvedSymLink, + sizeof(resolvedSymLink) - 1); + if (pathLength <= 0) { + return false; + } + resolvedSymLink[pathLength] = '\0'; + aOutPath.Assign(resolvedSymLink); + return true; +} + +} // system +} // mozilla diff --git a/dom/system/gonk/OpenFileFinder.h b/dom/system/gonk/OpenFileFinder.h new file mode 100644 index 000000000000..e00404248de4 --- /dev/null +++ b/dom/system/gonk/OpenFileFinder.h @@ -0,0 +1,60 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_system_openfilefinder_h__ +#define mozilla_system_openfilefinder_h__ + +#include "nsString.h" + +#include <dirent.h> + +namespace mozilla { +namespace system { + +class OpenFileFinder +{ +public: + enum State + { + NEXT_PID, + CHECK_FDS, + DONE + }; + class Info + { + public: + nsCString mFileName; // name of the the open file + nsCString mAppName; // App which has the file open (if it's a b2g app) + pid_t mPid; // pid of the process which has the file open + nsCString mComm; // comm associated with pid + nsCString mExe; // executable name associated with pid + }; + + OpenFileFinder(const nsACString& aPath); + ~OpenFileFinder(); + + bool First(Info* aInfo); // Return the first open file + bool Next(Info* aInfo); // Return the next open file + void Close(); + +private: + + void FillInfo(Info *aInfo, const nsACString& aPath); + bool ReadSymLink(const nsACString& aSymLink, nsACString& aOutPath); + bool PathMatches(const nsACString& aPath) + { + return Substring(aPath, 0, mPath.Length()).Equals(mPath); + } + + State mState; // Keeps track of what we're doing. + nsCString mPath; // Only report files contained within this directory tree + DIR* mProcDir; // Used for scanning /proc + DIR* mFdDir; // Used for scanning /proc/PID/fd + int mPid; // PID currently being processed +}; + +} // system +} // mozilla + +#endif // mozilla_system_nsvolume_h__ diff --git a/dom/system/gonk/moz.build b/dom/system/gonk/moz.build index 3f2cc95630bf..eeb9a109a5a7 100644 --- a/dom/system/gonk/moz.build +++ b/dom/system/gonk/moz.build @@ -52,6 +52,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': 'nsVolumeMountLock.cpp', 'nsVolumeService.cpp', 'nsVolumeStat.cpp', + 'OpenFileFinder.cpp', 'TimeZoneSettingObserver.cpp', 'Volume.cpp', 'VolumeCommand.cpp', diff --git a/widget/gonk/libdisplay/GonkDisplay.h b/widget/gonk/libdisplay/GonkDisplay.h index f883dba5b7f9..ab8bd13cf7e7 100644 --- a/widget/gonk/libdisplay/GonkDisplay.h +++ b/widget/gonk/libdisplay/GonkDisplay.h @@ -41,7 +41,7 @@ public: virtual bool QueueBuffer(ANativeWindowBuffer* buf) = 0; - uint32_t xdpi; + float xdpi; uint32_t surfaceformat; }; diff --git a/widget/gonk/libdisplay/GonkDisplayJB.cpp b/widget/gonk/libdisplay/GonkDisplayJB.cpp index 8337ff2d8db2..ce826e92f550 100644 --- a/widget/gonk/libdisplay/GonkDisplayJB.cpp +++ b/widget/gonk/libdisplay/GonkDisplayJB.cpp @@ -86,7 +86,7 @@ GonkDisplayJB::GonkDisplayJB() mWidth = values[0]; mHeight = values[1]; - xdpi = values[2]; + xdpi = values[2] / 1000.0f; surfaceformat = HAL_PIXEL_FORMAT_RGBA_8888; }