Open mvglasow opened 10 years ago
Wow, thank you for your detailed report...
Could you provide the build you are using to reproduce the issue? I had some trouble getting a good test app!
The location provider still provides the last result, but results are timestamped. There are 2 clocks for expiring cells: A measurement based one and a time based.
15 measurements is the maximum to keep a captured cell. This is quite small if you are moving.
The time based one is huge (6h) as you might otherwise drop all known cells if you drop your phone on a table. (Had this problem if you have your phone lying around with good gsm reception while you sleep)
Airplane mode stops the measurement based expire, and the huge time based expire kicks in. This is wrong. The time based cleanup should drop dramatically (10min?) if there is no reception or if the phone goes into airplane mode.
This expire system is used to smoothen the location.
Besides that I have been playing around with LAC/MCC mode, I'll open another ticket for it as it's clearly an improvement. However it's not as easy to give a good accurancy as you might not know all towers inside the LAC and you'd thus have to be a bit more conservative. Another idea was to check the "tower density" and reduce the radius if the density is high....
The satstat-location branch is at https://github.com/mvglasow/satstat/tree/satstat-location. I don't build binaries for that, but you can just drop it into Eclipse or an F-Droid server. As an alternative, I can just attach an APK signed with a debug key here. (I'm currently doing some debugging, requiring me to make some experimental tweaks to the code, but will build a clean version when I finish.)
The latest stable version of the master branch does not do visualization of location providers but will show you the current cell ID on the radio network page. It is available on F-Droid.
What exactly do you mean by “drop all known cells if you drop your phone on a table”? That location updates would stop when the cell does not change? I had to cope with that issue in SatStat as well. Here's how I solved it:
I register a PhoneStateListener as follows:
mTelephonyManager.listen(mPhoneStateListener, (LISTEN_CELL_INFO | LISTEN_CELL_LOCATION | LISTEN_SIGNAL_STRENGTHS));
The PhoneStateListener processes changes to the cell location and signal strength. (Cell info currently isn't implemented as it requires API 17 and I want to support earlier ones.)
When I create the radio network Activity, I populate the cell ID field by doing:
CellLocation cellLocation = mTelephonyManager.getCellLocation();
showCellLocation(cellLocation);
List<NeighboringCellInfo> neighboringCells = mTelephonyManager.getNeighboringCellInfo();
showNeighboringCellInfo(neighboringCells);
The first call will return the serving cell. The second should return all nearby cells but it doesn't work on the devices on which I've tried it (Nexus S and Galaxy Nexus, both on Cyanogenmod). You can do that as soon as a LocationListener attaches to the provider to get the current cell info. Then use the PhoneStateListener to:
Note: the API doc says that TelephonyManager.getCellLocation()
will get deprecated in the future, but for the moment it still works and I have yet to find a substitute.
I am primarily testing on a Galaxy Nexus and I do receive nearby cells but it's tricky.
The docs say you'll only get the neighbouring cells if available. They don't tell you when they are available. But if you think about it you're listening to modem activity (signal strength update), you thus know that the modem must have been doing at least something.
So here is how to get neighbour cells: Listen as you do
mTelephonyManager.listen(mPhoneStateListener, (LISTEN_CELL_INFO | LISTEN_CELL_LOCATION | LISTEN_SIGNAL_STRENGTHS));
But whenever you get an update poll the modem for everything it has lying around (here is how I do it: https://github.com/rtreffer/LocalGSMLocationProvider/blob/master/src/org/gfd/gsmlocation/CellbasedLocationProvider.java#L362 - handle gets called from every callback)
So getting much information only works if your modem is doing something. Which means if you leave it on your desk and if you have good connectivity you're starving on updates. This is OK, and intended. I found that you'll usually receive some updates within 6h, but that's purely empiric.
The problem is what should I do if I don't receive updates? If you are connected, fine. You're not moving. Keep the cell list "forever" (6h).
But I'm currently doing that if you are not connected, which might mean you are on-the-go and I should drop the cells.
+--------------------------------------+
|Current flow |
| |
| +------------------+ |
| | | |
| | Receiving events | |
| | | |
| ++--------------+--+ |
| | | |
| YES | | No |
| | | |
| +--------v--------+ +---v----------+ |
| | | | | |
| | Update celllist | | Expire cells | |
| | | | after 6h | |
| +-----------------+ +--------------+ |
| |
+--------------------------------------+--------+
|Correct flow |
| |
| +------------------+ |
| | | |
| | Receiving events | |
| | | |
| ++--------------+--+ |
| | | |
| YES | | No |
| | | |
| +--------v--------+ +---v--------------+ |
| | | | | |
| | Update celllist | | Are we connected | |
| | | | | |
| +-----------------+ +-+----------+-----+ |
| | | |
| YES | | NO |
| +------v-----+ +-v----------+ |
| | Drop cells | | Drop cells | |
| | after 6h | | after 5m | |
| +------------+ +------------+ |
| |
+-----------------------------------------------+
OK, I think I'm getting it.
The issue currently is that the provider continues to send the same location not only when the connection is lost, but also when the serving cell changes to one that is not in the database.
When in doubt about the current cell, you can poll that information from the phone by simply calling the methods mentioned – e.g. TelephonyManager.getCellLocation()
for the serving cell. You could start a timer with each update (resetting the previous timer with each update, so the timer will only fire if no update is received for the specified period) that polls the network info.
Also, when disconnected, I would stop sending updates immediately rather then sending out potentially stale locations for five more minutes. As soon as we disconnect from a cell (whether that is due to a handoff or because we have no service), location estimates based on the old cell are no longer accurate.
I'm not even sure if sending locations with an “old” timestamp is legitimate behavior on Android. I would assume applications to expect only current locations delivered to their LocationListeners. Also, examining the timestamp of a Location
is tricky business: getElapsedRealtimeNamos()
is not available prior to API level 17, and getTime()
may use different references (GPS uses GPS time, Network presumably uses the system's wall clock time), so there may not be an accurate way of telling how old the Location really is.
For getting the last known location from a provider, there is LocationManager.getLastKnownLocation()
. The API explicitly states that locations obtained this way could be out-of-date.
Sending only known-to-be-current locations would allow applications to control more easily what they want to get: registering for location updates may take some time, but will give up-to-date locations, while using getLastKnownLocation()
will return a result instantly (if one is available) but it may be outdated.
ps: I'd be interested to know how you get the Galaxy Nexus to deliver nearby cells. Which of the API calls does actually return something? On what carrier? What ROM?
pps: I have a SatStat APK (with map support, signed with test key) here. Unfortunately the bug tracker does not support uploading it. How can I get it to you? (Unless you prefer building it from source.)
I have noticed that the location provider continues sending the last known location in situations in which it shoud really stop sending updates.
To reproduce:
You will observe the following:
The latter is not correct:
A somewhat more advanced approach when the cell is not in the DB would be to drop the cell ID and look just for the same MCC/MNC/LAC combination. Bigger cities have multiple LACs, thus the radius of a LAC should be somewhere in the 2–3 km range. The actual accuracy is best inferred from the database (half the longest distance between any two cells having that LAC, plus the average coverage radius of a cell). If the MCC/MNC/LAC combination still does not yield a result, try by MCC/MNC and finally just by MCC. (In some places, such as in the US, carriers use different MNCs in different regions, hence MCC/MNC may allow for a narrower radius than the MCC alone.)
Of course, accuracy will decrease dramatically if we don't have a cell ID. However, even an inaccurate location will still be useful for some purposes: MCC/MNC/LAC would result in a radius of 2–3 km, which is fully sufficient to get the local weather forecast or a list of nearby events. In small countries (such as Andorra or San Marino) even the MCC would be sufficient for that purpose.