tenfourfox/dom/system/gonk/mozstumbler/MozStumbler.cpp
Cameron Kaiser c9b2922b70 hello FPR
2017-04-19 00:56:45 -07:00

427 lines
13 KiB
C++

/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 "MozStumbler.h"
#include "nsDataHashtable.h"
#include "nsGeoPosition.h"
#include "nsNetCID.h"
#include "nsPrintfCString.h"
#include "StumblerLogging.h"
#include "WriteStumbleOnThread.h"
#include "../GeolocationUtil.h"
#include "nsIInterfaceRequestor.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIMobileConnectionInfo.h"
#include "nsIMobileConnectionService.h"
#include "nsIMobileCellInfo.h"
#include "nsIMobileNetworkInfo.h"
#include "nsINetworkInterface.h"
#include "nsIRadioInterfaceLayer.h"
using namespace mozilla;
using namespace mozilla::dom;
NS_IMPL_ISUPPORTS(StumblerInfo, nsICellInfoListCallback, nsIWifiScanResultsReady)
class RequestCellInfoEvent : public nsRunnable {
public:
RequestCellInfoEvent(StumblerInfo *callback)
: mRequestCallback(callback)
{}
NS_IMETHOD Run() {
MOZ_ASSERT(NS_IsMainThread());
// Get Cell Info
nsCOMPtr<nsIMobileConnectionService> service =
do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
if (!service) {
STUMBLER_ERR("Stumbler-can not get nsIMobileConnectionService \n");
return NS_OK;
}
nsCOMPtr<nsIMobileConnection> connection;
uint32_t numberOfRilServices = 1, cellInfoNum = 0;
service->GetNumItems(&numberOfRilServices);
for (uint32_t rilNum = 0; rilNum < numberOfRilServices; rilNum++) {
service->GetItemByServiceId(rilNum /* Client Id */, getter_AddRefs(connection));
if (!connection) {
STUMBLER_ERR("Stumbler-can not get nsIMobileConnection by ServiceId %d \n", rilNum);
} else {
cellInfoNum++;
connection->GetCellInfoList(mRequestCallback);
}
}
mRequestCallback->SetCellInfoResponsesExpected(cellInfoNum);
// Get Wifi AP Info
nsCOMPtr<nsIInterfaceRequestor> ir = do_GetService("@mozilla.org/telephony/system-worker-manager;1");
nsCOMPtr<nsIWifi> wifi = do_GetInterface(ir);
if (!wifi) {
mRequestCallback->SetWifiInfoResponseReceived();
STUMBLER_ERR("Stumbler-can not get nsIWifi interface\n");
return NS_OK;
}
wifi->GetWifiScanResults(mRequestCallback);
return NS_OK;
}
private:
RefPtr<StumblerInfo> mRequestCallback;
};
void
MozStumble(nsGeoPosition* position)
{
if (WriteStumbleOnThread::IsFileWaitingForUpload()) {
nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
MOZ_ASSERT(target);
// Knowing that file is waiting to upload, and no collection will take place,
// just trigger the thread with an empty string.
nsCOMPtr<nsIRunnable> event = new WriteStumbleOnThread(EmptyCString());
target->Dispatch(event, NS_DISPATCH_NORMAL);
return;
}
nsCOMPtr<nsIDOMGeoPositionCoords> coords;
position->GetCoords(getter_AddRefs(coords));
if (!coords) {
return;
}
double latitude, longitude;
coords->GetLatitude(&latitude);
coords->GetLongitude(&longitude);
const double kMinChangeInMeters = 30;
static int64_t lastTime_ms = 0;
static double sLastLat = 0;
static double sLastLon = 0;
double delta = -1.0;
int64_t timediff = (PR_Now() / PR_USEC_PER_MSEC) - lastTime_ms;
if (0 != sLastLon || 0 != sLastLat) {
delta = CalculateDeltaInMeter(latitude, longitude, sLastLat, sLastLon);
}
STUMBLER_DBG("Stumbler-Location. [%f , %f] time_diff:%lld, delta : %f\n",
longitude, latitude, timediff, delta);
// Consecutive GPS locations must be 30 meters and 3 seconds apart
if (lastTime_ms == 0 || ((timediff >= STUMBLE_INTERVAL_MS) && (delta > kMinChangeInMeters))){
lastTime_ms = (PR_Now() / PR_USEC_PER_MSEC);
sLastLat = latitude;
sLastLon = longitude;
RefPtr<StumblerInfo> requestCallback = new StumblerInfo(position);
RefPtr<RequestCellInfoEvent> runnable = new RequestCellInfoEvent(requestCallback);
NS_DispatchToMainThread(runnable);
} else {
STUMBLER_DBG("Stumbler-GPS locations less than 30 meters and 3 seconds. Ignore!\n");
}
}
void
StumblerInfo::SetWifiInfoResponseReceived()
{
mIsWifiInfoResponseReceived = true;
if (mIsWifiInfoResponseReceived && mCellInfoResponsesReceived == mCellInfoResponsesExpected) {
STUMBLER_DBG("Call DumpStumblerInfo from SetWifiInfoResponseReceived\n");
DumpStumblerInfo();
}
}
void
StumblerInfo::SetCellInfoResponsesExpected(uint8_t count)
{
mCellInfoResponsesExpected = count;
STUMBLER_DBG("SetCellInfoNum (%d)\n", count);
if (mIsWifiInfoResponseReceived && mCellInfoResponsesReceived == mCellInfoResponsesExpected) {
STUMBLER_DBG("Call DumpStumblerInfo from SetCellInfoResponsesExpected\n");
DumpStumblerInfo();
}
}
#define TEXT_LAT NS_LITERAL_CSTRING("latitude")
#define TEXT_LON NS_LITERAL_CSTRING("longitude")
#define TEXT_ACC NS_LITERAL_CSTRING("accuracy")
#define TEXT_ALT NS_LITERAL_CSTRING("altitude")
#define TEXT_ALTACC NS_LITERAL_CSTRING("altitudeAccuracy")
#define TEXT_HEAD NS_LITERAL_CSTRING("heading")
#define TEXT_SPD NS_LITERAL_CSTRING("speed")
nsresult
StumblerInfo::LocationInfoToString(nsACString& aLocDesc)
{
nsCOMPtr<nsIDOMGeoPositionCoords> coords;
mPosition->GetCoords(getter_AddRefs(coords));
if (!coords) {
return NS_ERROR_FAILURE;
}
nsDataHashtable<nsCStringHashKey, double> info;
double val;
coords->GetLatitude(&val);
info.Put(TEXT_LAT, val);
coords->GetLongitude(&val);
info.Put(TEXT_LON, val);
coords->GetAccuracy(&val);
info.Put(TEXT_ACC, val);
coords->GetAltitude(&val);
info.Put(TEXT_ALT, val);
coords->GetAltitudeAccuracy(&val);
info.Put(TEXT_ALTACC, val);
coords->GetHeading(&val);
info.Put(TEXT_HEAD, val);
coords->GetSpeed(&val);
info.Put(TEXT_SPD, val);
for (auto it = info.Iter(); !it.Done(); it.Next()) {
const nsACString& key = it.Key();
val = it.UserData();
if (!IsNaN(val)) {
aLocDesc += nsPrintfCString("\"%s\":%f,", key.BeginReading(), val);
}
}
aLocDesc += nsPrintfCString("\"timestamp\":%lld,", PR_Now() / PR_USEC_PER_MSEC).get();
return NS_OK;
}
#define TEXT_RADIOTYPE NS_LITERAL_CSTRING("radioType")
#define TEXT_MCC NS_LITERAL_CSTRING("mobileCountryCode")
#define TEXT_MNC NS_LITERAL_CSTRING("mobileNetworkCode")
#define TEXT_LAC NS_LITERAL_CSTRING("locationAreaCode")
#define TEXT_CID NS_LITERAL_CSTRING("cellId")
#define TEXT_PSC NS_LITERAL_CSTRING("psc")
#define TEXT_STRENGTH_ASU NS_LITERAL_CSTRING("asu")
#define TEXT_STRENGTH_DBM NS_LITERAL_CSTRING("signalStrength")
#define TEXT_REGISTERED NS_LITERAL_CSTRING("serving")
#define TEXT_TIMEING_ADVANCE NS_LITERAL_CSTRING("timingAdvance")
template <class T> void
ExtractCommonNonCDMACellInfoItems(nsCOMPtr<T>& cell, nsDataHashtable<nsCStringHashKey, int32_t>& info)
{
int32_t mcc, mnc, cid, sig;
cell->GetMcc(&mcc);
cell->GetMnc(&mnc);
cell->GetCid(&cid);
cell->GetSignalStrength(&sig);
info.Put(TEXT_MCC, mcc);
info.Put(TEXT_MNC, mnc);
info.Put(TEXT_CID, cid);
info.Put(TEXT_STRENGTH_ASU, sig);
}
void
StumblerInfo::CellNetworkInfoToString(nsACString& aCellDesc)
{
aCellDesc += "\"cellTowers\": [";
for (uint32_t idx = 0; idx < mCellInfo.Length() ; idx++) {
const char* radioType = 0;
int32_t type;
mCellInfo[idx]->GetType(&type);
bool registered;
mCellInfo[idx]->GetRegistered(&registered);
if (idx) {
aCellDesc += ",{";
} else {
aCellDesc += "{";
}
STUMBLER_DBG("type=%d\n", type);
nsDataHashtable<nsCStringHashKey, int32_t> info;
info.Put(TEXT_REGISTERED, registered);
if(type == nsICellInfo::CELL_INFO_TYPE_GSM) {
radioType = "gsm";
nsCOMPtr<nsIGsmCellInfo> gsmCellInfo = do_QueryInterface(mCellInfo[idx]);
ExtractCommonNonCDMACellInfoItems(gsmCellInfo, info);
int32_t lac;
gsmCellInfo->GetLac(&lac);
info.Put(TEXT_LAC, lac);
} else if (type == nsICellInfo::CELL_INFO_TYPE_WCDMA) {
radioType = "wcdma";
nsCOMPtr<nsIWcdmaCellInfo> wcdmaCellInfo = do_QueryInterface(mCellInfo[idx]);
ExtractCommonNonCDMACellInfoItems(wcdmaCellInfo, info);
int32_t lac, psc;
wcdmaCellInfo->GetLac(&lac);
wcdmaCellInfo->GetPsc(&psc);
info.Put(TEXT_LAC, lac);
info.Put(TEXT_PSC, psc);
} else if (type == nsICellInfo::CELL_INFO_TYPE_CDMA) {
radioType = "cdma";
nsCOMPtr<nsICdmaCellInfo> cdmaCellInfo = do_QueryInterface(mCellInfo[idx]);
int32_t mnc, lac, cid, sig;
cdmaCellInfo->GetSystemId(&mnc);
cdmaCellInfo->GetNetworkId(&lac);
cdmaCellInfo->GetBaseStationId(&cid);
info.Put(TEXT_MNC, mnc);
info.Put(TEXT_LAC, lac);
info.Put(TEXT_CID, cid);
cdmaCellInfo->GetEvdoDbm(&sig);
if (sig < 0 || sig == nsICellInfo::UNKNOWN_VALUE) {
cdmaCellInfo->GetCdmaDbm(&sig);
}
if (sig > -1 && sig != nsICellInfo::UNKNOWN_VALUE) {
sig *= -1;
info.Put(TEXT_STRENGTH_DBM, sig);
}
} else if (type == nsICellInfo::CELL_INFO_TYPE_LTE) {
radioType = "lte";
nsCOMPtr<nsILteCellInfo> lteCellInfo = do_QueryInterface(mCellInfo[idx]);
ExtractCommonNonCDMACellInfoItems(lteCellInfo, info);
int32_t lac, timingAdvance, pcid, rsrp;
lteCellInfo->GetTac(&lac);
lteCellInfo->GetTimingAdvance(&timingAdvance);
lteCellInfo->GetPcid(&pcid);
lteCellInfo->GetRsrp(&rsrp);
info.Put(TEXT_LAC, lac);
info.Put(TEXT_TIMEING_ADVANCE, timingAdvance);
info.Put(TEXT_PSC, pcid);
if (rsrp != nsICellInfo::UNKNOWN_VALUE) {
info.Put(TEXT_STRENGTH_DBM, rsrp * -1);
}
}
aCellDesc += nsPrintfCString("\"%s\":\"%s\"", TEXT_RADIOTYPE.get(), radioType);
for (auto it = info.Iter(); !it.Done(); it.Next()) {
const nsACString& key = it.Key();
int32_t value = it.UserData();
if (value != nsICellInfo::UNKNOWN_VALUE) {
aCellDesc += nsPrintfCString(",\"%s\":%d", key.BeginReading(), value);
}
}
aCellDesc += "}";
}
aCellDesc += "]";
}
void
StumblerInfo::DumpStumblerInfo()
{
if (!mIsWifiInfoResponseReceived || mCellInfoResponsesReceived != mCellInfoResponsesExpected) {
STUMBLER_DBG("CellInfoReceived=%d (Expected=%d), WifiInfoResponseReceived=%d\n",
mCellInfoResponsesReceived, mCellInfoResponsesExpected, mIsWifiInfoResponseReceived);
return;
}
mIsWifiInfoResponseReceived = false;
mCellInfoResponsesReceived = 0;
nsAutoCString desc;
nsresult rv = LocationInfoToString(desc);
if (NS_WARN_IF(NS_FAILED(rv))) {
STUMBLER_ERR("LocationInfoToString failed, skip this dump");
return;
}
CellNetworkInfoToString(desc);
desc += mWifiDesc;
STUMBLER_DBG("dispatch write event to thread\n");
nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
MOZ_ASSERT(target);
nsCOMPtr<nsIRunnable> event = new WriteStumbleOnThread(desc);
target->Dispatch(event, NS_DISPATCH_NORMAL);
}
NS_IMETHODIMP
StumblerInfo::NotifyGetCellInfoList(uint32_t count, nsICellInfo** aCellInfos)
{
MOZ_ASSERT(NS_IsMainThread());
STUMBLER_DBG("There are %d cellinfo in the result\n", count);
for (uint32_t i = 0; i < count; i++) {
mCellInfo.AppendElement(aCellInfos[i]);
}
mCellInfoResponsesReceived++;
DumpStumblerInfo();
return NS_OK;
}
NS_IMETHODIMP StumblerInfo::NotifyGetCellInfoListFailed(const nsAString& error)
{
MOZ_ASSERT(NS_IsMainThread());
mCellInfoResponsesReceived++;
STUMBLER_ERR("NotifyGetCellInfoListFailedm CellInfoReadyNum=%d, mCellInfoResponsesExpected=%d, mIsWifiInfoResponseReceived=%d",
mCellInfoResponsesReceived, mCellInfoResponsesExpected, mIsWifiInfoResponseReceived);
DumpStumblerInfo();
return NS_OK;
}
NS_IMETHODIMP
StumblerInfo::Onready(uint32_t count, nsIWifiScanResult** results)
{
MOZ_ASSERT(NS_IsMainThread());
STUMBLER_DBG("There are %d wifiAPinfo in the result\n",count);
mWifiDesc += ",\"wifiAccessPoints\": [";
bool firstItem = true;
for (uint32_t i = 0 ; i < count ; i++) {
nsString ssid;
results[i]->GetSsid(ssid);
if (ssid.IsEmpty()) {
STUMBLER_DBG("no ssid, skip this AP\n");
continue;
}
if (ssid.Length() >= 6) {
if (StringEndsWith(ssid, NS_LITERAL_STRING("_nomap"))) {
STUMBLER_DBG("end with _nomap. skip this AP(ssid :%s)\n", ssid.get());
continue;
}
}
if (firstItem) {
mWifiDesc += "{";
firstItem = false;
} else {
mWifiDesc += ",{";
}
// mac address
nsString bssid;
results[i]->GetBssid(bssid);
// 00:00:00:00:00:00 --> 000000000000
bssid.StripChars(":");
mWifiDesc += "\"macAddress\":\"";
mWifiDesc += NS_ConvertUTF16toUTF8(bssid);
uint32_t signal;
results[i]->GetSignalStrength(&signal);
mWifiDesc += "\",\"signalStrength\":";
mWifiDesc.AppendInt(signal);
mWifiDesc += "}";
}
mWifiDesc += "]";
mIsWifiInfoResponseReceived = true;
DumpStumblerInfo();
return NS_OK;
}
NS_IMETHODIMP
StumblerInfo::Onfailure()
{
MOZ_ASSERT(NS_IsMainThread());
STUMBLER_ERR("GetWifiScanResults Onfailure\n");
mIsWifiInfoResponseReceived = true;
DumpStumblerInfo();
return NS_OK;
}