Commit a0fcccc4 authored by Vyacheslav Koscheev's avatar Vyacheslav Koscheev

GLPv3+cell support

parent ba663d28
......@@ -2,9 +2,9 @@
Offscreen Android Views library for Qt
Authors:
Vyacheslav O. Koscheev <vok1980@gmail.com>
Ivan Avdeev marflon@gmail.com
Sergey A. Galin sergey.galin@gmail.com
Vyacheslav O. Koscheev <vok1980@gmail.com>
Ivan Avdeev marflon@gmail.com
Sergey A. Galin sergey.galin@gmail.com
Distrbuted under The BSD License
......@@ -41,53 +41,30 @@
namespace Mobility {
CellData::CellData()
{
clear();
}
bool CellData::compare( const CellData& other, bool compareSignalStrength ) const
{
if( cellId != other.cellId ||
locationAreaCode != other.locationAreaCode ||
mobileCountryCode != other.mobileCountryCode ||
mobileNetworkCode != other.mobileNetworkCode ||
timingAdvance != other.timingAdvance )
{
return false;
}
if( compareSignalStrength &&
signalStrength != other.signalStrength )
{
return false;
}
return true;
}
bool CellData::operator==(const CellData &other) const
{
return compare(other, true);
}
const int32_t CellData::java_integer_max_value = std::numeric_limits<int32_t>::max();
bool CellData::operator!=(const CellData &other) const
CellData::Data::Data(int32_t cell_id)
: cell_id_(cell_id)
, location_area_code_(java_integer_max_value)
, mobile_country_code_(java_integer_max_value)
, mobile_network_code_(java_integer_max_value)
, signal_strength_(java_integer_max_value)
, timing_advance_(java_integer_max_value)
{
return !compare(other, true);
}
// used as sign of validity
bool CellData::isEmpty() const
void CellData::Data::accept(DataOperation & operation) const
{
return !(cellId && locationAreaCode && mobileCountryCode && mobileNetworkCode);
operation.execute(QStringLiteral("cell_id"), cell_id_);
operation.execute(QStringLiteral("location_area_code"), location_area_code_);
operation.execute(QStringLiteral("mobile_country_code"), mobile_country_code_);
operation.execute(QStringLiteral("mobile_network_code"), mobile_network_code_);
operation.execute(QStringLiteral("signal_strength"), signal_strength_);
operation.execute(QStringLiteral("timing_advance"), timing_advance_);
operation.execute(QStringLiteral("radio_type"), radio_type_);
}
void CellData::clear()
{
memset(this, 0, sizeof(*this));
}
}
} // namespace Mobility
......@@ -2,9 +2,9 @@
Offscreen Android Views library for Qt
Authors:
Vyacheslav O. Koscheev <vok1980@gmail.com>
Ivan Avdeev marflon@gmail.com
Sergey A. Galin sergey.galin@gmail.com
Vyacheslav O. Koscheev <vok1980@gmail.com>
Ivan Avdeev marflon@gmail.com
Sergey A. Galin sergey.galin@gmail.com
Distrbuted under The BSD License
......@@ -45,39 +45,47 @@ namespace Mobility {
struct CellData
{
CellData();
bool compare( const CellData& other, bool compareSignalStrength ) const;
bool operator==(const CellData& other) const;
bool operator!=(const CellData& other) const;
bool isEmpty() const; // used as sign of validity
void clear();
// fields are described in http://code.google.com/intl/ru/apis/gears/geolocation_network_protocol.html
// Unique identifier of the cell. (CID for GSM, BID for CDMA)
int cellId;
// Location Area Code (LAC for GSM, NID for CDMA)
int locationAreaCode;
// Mobile Country Code (MCC for GSM and CDMA)
int mobileCountryCode;
// Mobile Network Code (MNC for GSM, SID for CDMA)
int mobileNetworkCode;
// Radio signal strength measured in dBm.
/* convert GSM asu (TS 27.007 8.5) to these as follows:
<rssi>:
0 -113 dBm or less
1 -111 dBm
2...30 -109... -53 dBm
31 -51 dBm or greater
99 not known or not detectable
*/
int signalStrength;
// Represents the distance from the cell tower. Each unit is roughly 550 meters.
int timingAdvance;
struct DataOperation
{
virtual void execute(const QString & key, int32_t value) = 0;
virtual void execute(const QString & key, const QString & value) = 0;
};
struct Data
{
Data(int32_t cell_id);
void accept(DataOperation & operation) const;
// required, int.
// Cell ID (CID) for GSM
// Base Station ID (BID) for CDMA
// UTRAN/GERAN Cell Identity (UC-Id) for WCDMA.
int32_t cell_id_;
// optional, int.
// Location Area Code (LAC) for GSM and WCDMA.
// Network ID (NID) for CDMA сетей. 0 <= LAC <= 65535.
int32_t location_area_code_;
// optional, int. 0 <= MCC < 1000
int32_t mobile_country_code_;
// optional, int. 0 <= MNC < 1000
int32_t mobile_network_code_;
// optional, int. RSSI в dBm.
int32_t signal_strength_;
// optional, int
int32_t timing_advance_;
// gsm, wcdma, lte, cdma
QString radio_type_;
};
typedef QList<Data> DataColl;
DataColl data_;
static const int32_t java_integer_max_value;
};
......
......@@ -2,9 +2,9 @@
Offscreen Android Views library for Qt
Authors:
Vyacheslav O. Koscheev <vok1980@gmail.com>
Ivan Avdeev marflon@gmail.com
Sergey A. Galin sergey.galin@gmail.com
Vyacheslav O. Koscheev <vok1980@gmail.com>
Ivan Avdeev marflon@gmail.com
Sergey A. Galin sergey.galin@gmail.com
Distrbuted under The BSD License
......@@ -37,23 +37,33 @@
*/
#include "QAndroidCellDataProvider.h"
#include <QtCore/QtDebug>
#include <QAndroidQPAPluginGap.h>
#include "TJniObjectLinker.h"
namespace Mobility {
Q_DECL_EXPORT void JNICALL Java_CellListener_cellUpdate(JNIEnv *, jobject, jlong native_ptr, jint cid, jint lac, jint mcc, jint mnc, jint rssi)
Q_DECL_EXPORT void JNICALL Java_CellListener_cellUpdate(JNIEnv *, jobject, jlong native_ptr, jstring type, jint cid, jint lac, jint mcc, jint mnc, jint rssi, jint ta)
{
JNI_LINKER_OBJECT(Mobility::QAndroidCellDataProvider, native_ptr, proxy)
proxy->cellUpdate(cid, lac, mcc, mnc, rssi);
proxy->cellUpdate(type, cid, lac, mcc, mnc, rssi, ta);
}
Q_DECL_EXPORT void JNICALL Java_CellListener_onSignalChanged(JNIEnv *, jobject, jlong native_ptr)
{
JNI_LINKER_OBJECT(Mobility::QAndroidCellDataProvider, native_ptr, proxy)
proxy->onSignalChanged();
}
static const JNINativeMethod methods[] = {
{"getContext", "()Landroid/content/Context;", reinterpret_cast<void*>(QAndroidQPAPluginGap::getCurrentContextNoThrow)},
// private native void cellUpdate(long native_ptr, int cid, int lac, int mcc, int mnc, int rssi);
{"cellUpdate", "(JIIIII)V", reinterpret_cast<void*>(Java_CellListener_cellUpdate)},
{"onSignalChanged", "(J)V", reinterpret_cast<void*>(Java_CellListener_onSignalChanged)},
{"cellUpdate", "(JLjava/lang/String;IIIIII)V", reinterpret_cast<void*>(Java_CellListener_cellUpdate)},
};
......@@ -74,48 +84,92 @@ QAndroidCellDataProvider::~QAndroidCellDataProvider()
void QAndroidCellDataProvider::start()
{
if (isJniReady())
try
{
if (isJniReady())
{
jni()->callBool("start");
}
}
catch (const std::exception & ex)
{
jni()->callBool("start");
}
qCritical() << "JNI exception in QAndroidCellDataProvider:" << ex.what();
}
}
void QAndroidCellDataProvider::stop()
{
if (isJniReady())
try
{
if (isJniReady())
{
jni()->callVoid("stop");
}
}
catch (const std::exception & ex)
{
qCritical() << "JNI exception in QAndroidCellDataProvider:" << ex.what();
}
}
void QAndroidCellDataProvider::onSignalChanged()
{
emit updated();
}
void QAndroidCellDataProvider::requestData()
{
current_data_ = CellDataPtr::create();
try
{
jni()->callVoid("stop");
if (isJniReady())
{
jni()->callVoid("requestData");
{
QWriteLocker locker(&lock_data_);
last_data_ = current_data_;
}
emit dataReady();
}
}
catch (const std::exception & ex)
{
qCritical() << "JNI exception in QAndroidCellDataProvider:" << ex.what();
}
}
void QAndroidCellDataProvider::cellUpdate(int cid, int lac, int mcc, int mnc, int rssi)
void QAndroidCellDataProvider::cellUpdate(jstring type, jint cid, jint lac, jint mcc, jint mnc, jint rssi, jint ta)
{
CellDataPtr new_data(new CellData);
new_data->cellId = cid;
new_data->locationAreaCode = lac;
new_data->mobileCountryCode = mcc;
new_data->mobileNetworkCode = mnc;
new_data->signalStrength = rssi;
new_data->timingAdvance = 0;
if (last_data_)
if (current_data_ && cid > 0 && cid != CellData::java_integer_max_value)
{
if (*new_data==*last_data_)
current_data_->data_.push_back(CellData::Data(cid));
if (isJniReady())
{
return;
QJniEnvPtr jep;
current_data_->data_.back().radio_type_ = jep.JStringToQString(type);
}
current_data_->data_.back().location_area_code_ = lac;
current_data_->data_.back().mobile_country_code_ = mcc;
current_data_->data_.back().mobile_network_code_ = mnc;
current_data_->data_.back().signal_strength_ = rssi;
current_data_->data_.back().timing_advance_ = ta;
}
last_data_ = new_data;
emit update();
}
CellDataPtr QAndroidCellDataProvider::getLastData()
{
QReadLocker locker(&lock_data_);
return last_data_;
}
}
} // namespace Mobility
......@@ -2,9 +2,9 @@
Offscreen Android Views library for Qt
Authors:
Vyacheslav O. Koscheev <vok1980@gmail.com>
Ivan Avdeev marflon@gmail.com
Sergey A. Galin sergey.galin@gmail.com
Vyacheslav O. Koscheev <vok1980@gmail.com>
Ivan Avdeev marflon@gmail.com
Sergey A. Galin sergey.galin@gmail.com
Distrbuted under The BSD License
......@@ -39,6 +39,7 @@
#pragma once
#include <QtCore/QObject>
#include <QtCore/QReadWriteLock>
#include <QJniHelpers.h>
#include "IJniObjectLinker.h"
#include "CellData.h"
......@@ -59,18 +60,24 @@ public:
CellDataPtr getLastData();
private:
friend void JNICALL Java_CellListener_cellUpdate(JNIEnv *, jobject, jlong native_ptr, jint cid, jint lac, jint mcc, jint mnc, jint rssi);
void cellUpdate(int cid, int lac, int mcc, int mnc, int rssi);
friend void JNICALL Java_CellListener_cellUpdate(JNIEnv *, jobject, jlong native_ptr, jstring type, jint cid, jint lac, jint mcc, jint mnc, jint rssi, jint ta);
friend void JNICALL JNICALL Java_CellListener_onSignalChanged(JNIEnv *, jobject, jlong native_ptr);
void cellUpdate(jstring type, jint cid, jint lac, jint mcc, jint mnc, jint rssi, jint ta);
void onSignalChanged();
public slots:
void start();
void stop();
void requestData();
signals:
void update();
void updated();
void dataReady();
private:
CellDataPtr last_data_;
CellDataPtr current_data_;
QReadWriteLock lock_data_;
};
}
......@@ -53,7 +53,6 @@ Q_DECL_EXPORT void JNICALL Java_WifiListener_scanUpdate(JNIEnv *, jobject, jlong
static const JNINativeMethod methods[] = {
{"getContext", "()Landroid/content/Context;", reinterpret_cast<void*>(QAndroidQPAPluginGap::getCurrentContextNoThrow)},
// private native void scanUpdate(long native_ptr);
{"scanUpdate", "(J)V", reinterpret_cast<void*>(Java_WifiListener_scanUpdate)},
};
......
/*
Offscreen Android Views library for Qt
Authors:
Vyacheslav O. Koscheev <vok1980@gmail.com>
Ivan Avdeev marflon@gmail.com
Sergey A. Galin sergey.galin@gmail.com
Distrbuted under The BSD License
Copyright (c) 2015, DoubleGIS, LLC.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the DoubleGIS, LLC nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
package ru.dublgis.androidhelpers.mobility;
public class CellData
{
public int cellId_;
public int locationAreaCode_;
public int mobileCountryCode_;
public int mobileNetworkCode_;
public int signalStrength_;
public int timingAdvance_;
public CellData(int cellId, int locationAreaCode,
int mobileCountryCode, int mobileNetworkCode,
int signalStrength, int timingAdvance )
{
cellId_ = cellId;
locationAreaCode_ = locationAreaCode;
mobileCountryCode_ = mobileCountryCode;
mobileNetworkCode_ = mobileNetworkCode;
signalStrength_ = signalStrength;
timingAdvance_ = timingAdvance;
}
@Override
public String toString()
{
return "[cid:"+cellId_+", lac:"+locationAreaCode_+
", mcc:"+mobileCountryCode_+", mnc:"+mobileNetworkCode_+
", rssi:"+signalStrength_+", ta:"+timingAdvance_+"]";
}
}
......@@ -2,9 +2,9 @@
Offscreen Android Views library for Qt
Authors:
Vyacheslav O. Koscheev <vok1980@gmail.com>
Ivan Avdeev marflon@gmail.com
Sergey A. Galin sergey.galin@gmail.com
Vyacheslav O. Koscheev <vok1980@gmail.com>
Ivan Avdeev marflon@gmail.com
Sergey A. Galin sergey.galin@gmail.com
Distrbuted under The BSD License
......@@ -39,255 +39,303 @@
package ru.dublgis.androidhelpers.mobility;
import android.content.Context;
import android.os.Build;
import android.os.Looper;
import android.telephony.TelephonyManager;
import android.telephony.PhoneStateListener;
import android.support.v4.app.ActivityCompat;
import android.telephony.CellInfo;
import android.telephony.CellInfoCdma;
import android.telephony.CellInfoGsm;
import android.telephony.CellInfoLte;
import android.telephony.CellInfoWcdma;
import android.telephony.CellLocation;
import android.telephony.gsm.GsmCellLocation;
import android.telephony.cdma.CdmaCellLocation;
import android.telephony.NeighboringCellInfo;
import android.telephony.PhoneStateListener;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
import android.telephony.cdma.CdmaCellLocation;
import android.telephony.gsm.GsmCellLocation;
import java.util.List;
import ru.dublgis.androidhelpers.Log;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
public class CellListener
{
final static String LOG_TAG = "Grym/CellListener";
final static boolean verbose = false;
private TelephonyManager mTelMan = null;
private CellLocation mLastLocation = null;
public class CellListener {
private final static String TAG = "Grym/CellListener";
private volatile long native_ptr_ = 0;
private final static boolean verbose = false;
private TrueCellListener mListener = null;
private TelephonyManager mManager = null;
private SignalStrength mSignalStrengthLast = null;
private CellListenerImpl mListener = null;
private Thread mListenerThread = null;
private Looper mListenerLooper = null;
public CellListener(long native_ptr)
{
public CellListener(long native_ptr) {
native_ptr_ = native_ptr;
}
//! Called from C++ to notify us that the associated C++ object is being destroyed.
public void cppDestroyed()
{
public void cppDestroyed() {
native_ptr_ = 0;
}
private class TrueCellListener extends PhoneStateListener
{
public synchronized void onCellLocationChanged(CellLocation location)
{
if (verbose)
{
Log.d(LOG_TAG,"CellListener onCellLocationChanged: "+location);
public synchronized boolean start() {
Log.d(TAG, "start");
try {
if (mManager == null) {
mManager = (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
}
mLastLocation = location;
Runnable listenRunnable = new Runnable() {
@Override
public void run() {
if (mManager != null) {
try {
Looper.prepare();
mListenerLooper = Looper.myLooper();
mListener = new CellListenerImpl();
mManager.listen(mListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
Looper.loop();
mManager.listen(mListener, PhoneStateListener.LISTEN_NONE);
}
catch (Throwable ex) {
Log.e(TAG, "Failed to start TelephonyManager listener", ex);
}
}
}
};
mListenerThread = new Thread(listenRunnable, "Listen TelephonyManager");
mListenerThread.start();
return true;
} catch (Throwable e) {
Log.e(TAG, "Exception while starting cell listener: ", e);
return false;
}
}
public synchronized void onSignalStrengthsChanged(SignalStrength signalStrength)
{
if (verbose)
{
Log.d(LOG_TAG,"CellListener onSignalStrengthsChanged: "+signalStrength);
public synchronized void stop() {
Log.d(TAG, "stop");
try {
if (mManager != null) {
mManager.listen(mListener, PhoneStateListener.LISTEN_NONE);
}
try
{
CellData data = null;
if (signalStrength.isGsm())
{
// Calculate dBm from asu
int dbm = signalStrength.getGsmSignalStrength();
if (dbm == 99)
{
dbm = -113;
}
else if ((dbm >= 0) && (dbm <= 31))
{
dbm = dbm * 2 - 113;
}
else
{
dbm = -51;
}
}
catch (SecurityException ex) {
Log.e(TAG, "Failed to stop listener", ex);
}
GsmCellLocation loc = (GsmCellLocation)mLastLocation;
if (loc != null)
{
data = new CellData(loc.getCid(), loc.getLac(), -1, -1, dbm, 0);
}
} else {
CdmaCellLocation loc = (CdmaCellLocation)mLastLocation;
if (loc != null)
{
data = new CellData(loc.getBaseStationId(), loc.getNetworkId(),
-1, loc.getSystemId(), signalStrength.getCdmaDbm(), 0);
}
}
if (null != mListenerLooper) {
mListenerLooper.quit();
}
if (data == null)
{
if (verbose)
{
Log.d(LOG_TAG,"CellListener onCellLocationChanged: null data!");
}
return;
}
try {
if (mListenerThread.isAlive()) {
mListenerThread.join(300);
}
} catch (InterruptedException e) {
Log.e(TAG, "Exception in cppDestroyed: ", e);
}
}
String op = mTelMan.getNetworkOperator();
try
{
if (op != null && op.length() > 0)
{
if (data.mobileCountryCode_ == -1)
{
try
{
data.mobileCountryCode_ = Integer.parseInt(op.substring(0, 3));
}
catch(Exception e)
{
Log.e(LOG_TAG, "Failed to parse mobile country code from: \""+op+"\"");
}
}
if (data.mobileNetworkCode_ == -1)
{
try
{
data.mobileNetworkCode_ = Integer.parseInt(op.substring(3));
}
catch(Exception e)
{
Log.e(LOG_TAG, "Failed to parse mobile network code from: \""+op+"\"");
}
}
}
else // (op is empty)
{
// (Normal situaution without SIM card.)
// Log.d(LOG_TAG, "Network operator string is empty.");
}
}
catch(Exception e)
{
Log.e(LOG_TAG, "Could not get network and country codes. CDMA?", e);
}