**********************************************************************************************************************************************************************************
http://itbbs.pconline.com.cn/pad/14885947.html
wifi定位是iphone升级到1.1.3之后新加的应用服务,拿到机器后开始试用一下wifi定位。在美国达拉斯,定位按钮被按下后,旋转了大约2秒钟,所在的街道就立刻闪现在屏幕中央了。难怪乔布斯也说:“It's really cool”,这个功能确实神奇呀。
以前用过GSM定位,那个误差能大到数公里,而wifi定位出的中心点距离我所在也就是50米。随后我到网上查了一下关于这个技术的文章,了解到了下面的情况。
做这项技术是由一家成立于2003年叫Skyhook Wireless(http://www.skyhookwireless.com/)的公司。在SkyHook主页上可以下载一个叫Loki的软件,是PC上用的,也支持wifi定位,不过好像只能在xp用。还没升到1.1.3的iphone用户可以先试试Loki。
这个技术的原理是利用下面三条事实:
- wifi热点(也就是AP,或者无线路由器)越来越多,在城市中更趋向于空间任何一点都能接收到至少一个AP的信号。(在美国,每个点收到3、5个AP信号的情况相当多见。中国也会越来越多的)
- 热点只要通电,不管它怎么加密的,都一定会向周围发射信号。信号中包含此热点的唯一全球ID。即使距离此热点比较远,无法建立连接,但还是可以侦听到它的存在。
- 热点一般都是很少变位置的,比较固定。
这样,定位端只要侦听一下附近都有哪些热点,检测一下每个热点的信号强弱,然后把这些信息发送给网络上的Skyhook的服务器。服务器根据这些信息,查询每个热点在数据库里记录的坐标,然后进行运算,就能知道客户端的具体位置了,最后坐标告诉客户端。容易理解的是,收到的AP信号越多,定位就会越准。
不过,一次成功的定位需要两个先决条件:
- 客户端能上网
- 侦听到的热点的坐标在Skyhook的数据库里有
第一条不消说了,不管是wifi还是edge,只要能连上Skyhook的服务器就行。
第二条是Skyhook的金矿所在。对于Skyhook如何知道每个AP的坐标信息有两种说法:
1. 有一种说法是靠网友自己搜集,然后发给Skyhook,Skyhook会付钱。
2. 不过官方网站上的说法是开着车满大街转悠,边走边采集AP信号,并用GPS定位,从而就有了坐标信息。而且他们会定期重新开车采集数据,以适应热点的变化。
相对之下,第2条更靠谱,而且成本并不高。比方说采集北京,设备上一个带GPS和wifi的PDA足以,然后装到出租车上,每月给司机200、300的,让他就正常拉客人。只要有3、5个司机合作,数据就采集下来了,并不断地更新。司机也一定会很乐呵,这纯粹是无成本的额外收入呀,还能享受一下GPS。
这里有一个此服务已经覆盖的区域的电子图:http://www.skyhookwireless.com/howitworks/coverage.php。可以看到我国的北京、香港和台北是有这个服务的。所以现阶段在大陆地区只有北京能用,不知道在中国有没有其他网络应用可以实现wifi定位。
wifi定位精度比GPS要低,受服务范围限制,而且没有方向、速度等数据,不能导航,更不能离线使用。不过它有比GPS更优越的地方,就是在人口、楼群越密集的地方,使用的效果会更好。GPS启动时间长,在室内是无效的,天气不好的时候表现也欠佳,楼群太密集的地方也不太好用。而这些因素都被wifi定位克服了。
我想这还不是真正的定位,这就是个玩具,真正的定位是用wifi信号定位,我正在研究这个
************************************************************************************************************************************************************************
文章出处:http://www.limodev.cn/blog
作者联系方式:李先静 <xianjimli at hotmail dot com>
Broncho A1还不支持基站和WIFI定位,Android的老版本里是有NetworkLocationProvider的,它实现了基站和WIFI定位,但从 android 1.5之后就被移除了。本来想在broncho A1里自己实现NetworkLocationProvider的,但一直没有时间去研究。我知道 gears(http://code.google.com/p/gears/)是有提供类似的功能,昨天研究了一下Gears的代码,看能不能移植到 android中来。
1.下载源代码
svn checkout http://gears.googlecode.com/svn/trunk/ gears-read-only
定位相关的源代码在gears/geolocation目录中。
2.关注android平台中的基站位置变化。
JAVA类AndroidRadioDataProvider是PhoneStateListener的子类,用来监听Android电话的状态变化。当服务状态、信号强度和基站变化时,就会用下面代码获取小区信息:
RadioData radioData = new RadioData();
GsmCellLocation gsmCellLocation = (GsmCellLocation) cellLocation;
// Extract the cell id, LAC, and signal strength.
radioData.cellId = gsmCellLocation.getCid();
radioData.locationAreaCode = gsmCellLocation.getLac();
radioData.signalStrength = signalStrength;
// Extract the home MCC and home MNC.
String operator = telephonyManager.getSimOperator();
radioData.setMobileCodes(operator, true);
if (serviceState != null) {
// Extract the carrier name.
radioData.carrierName = serviceState.getOperatorAlphaLong();
// Extract the MCC and MNC.
operator = serviceState.getOperatorNumeric();
radioData.setMobileCodes(operator, false);
}
// Finally get the radio type.
int type = telephonyManager.getNetworkType();
if (type == TelephonyManager.NETWORK_TYPE_UMTS) {
radioData.radioType = RADIO_TYPE_WCDMA;
} else if (type == TelephonyManager.NETWORK_TYPE_GPRS
|| type == TelephonyManager.NETWORK_TYPE_EDGE) {
radioData.radioType = RADIO_TYPE_GSM;
}
然后调用用C代码实现的onUpdateAvailable函数。
2.Native函数onUpdateAvailable是在radio_data_provider_android.cc里实现的。
声明Native函数
JNINativeMethod AndroidRadioDataProvider::native_methods_[] = {
{"onUpdateAvailable",
"(L" GEARS_JAVA_PACKAGE "/AndroidRadioDataProvider$RadioData;J)V",
reinterpret_cast<void*>(AndroidRadioDataProvider::OnUpdateAvailable)
},
};
JNI调用好像只能调用静态成员函数,把对象本身用一个参数传进来,然后再调用对象的成员函数。
void AndroidRadioDataProvider::OnUpdateAvailable(JNIEnv* env,
jclass cls,
jobject radio_data,
jlong self) {
assert(radio_data);
assert(self);
AndroidRadioDataProvider *self_ptr =
reinterpret_cast<AndroidRadioDataProvider*>(self);
RadioData new_radio_data;
if (InitFromJavaRadioData(env, radio_data, &new_radio_data)) {
self_ptr->NewRadioDataAvailable(&new_radio_data);
}
}
先判断基站信息有没有变化,如果有变化则通知相关的监听者。
void AndroidRadioDataProvider::NewRadioDataAvailable(
RadioData* new_radio_data) {
bool is_update_available = false;
data_mutex_.Lock();
if (new_radio_data && !radio_data_.Matches(*new_radio_data)) {
radio_data_ = *new_radio_data;
is_update_available = true;
}
// Avoid holding the mutex locked while notifying observers.
data_mutex_.Unlock();
if (is_update_available) {
NotifyListeners();
}
}
接下来的过程,基站定位和WIFI定位是一样的,后面我们再来介绍。下面我们先看WIFI定位。
3.关注android平台中的WIFI变化。
JAVA类AndroidWifiDataProvider扩展了BroadcastReceiver类,它关注WIFI扫描结果:
IntentFilter filter = new IntentFilter();
filter.addAction(mWifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
mContext.registerReceiver(this, filter, null, handler);
当收到WIFI扫描结果后,调用Native函数onUpdateAvailable,并把WIFI的扫描结果传递过去。
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(
mWifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
if (Config.LOGV) {
Log.v(TAG, "Wifi scan resulst available");
}
onUpdateAvailable(mWifiManager.getScanResults(), mNativeObject);
}
}
4.Native函数onUpdateAvailable是在wifi_data_provider_android.cc里实现的。
JNINativeMethod AndroidWifiDataProvider::native_methods_[] = {
{"onUpdateAvailable",
"(Ljava/util/List;J)V",
reinterpret_cast<void*>(AndroidWifiDataProvider::OnUpdateAvailable)
},
};
void AndroidWifiDataProvider::OnUpdateAvailable(JNIEnv* /* env */,
jclass /* cls */,
jobject wifi_data,
jlong self) {
assert(self);
AndroidWifiDataProvider *self_ptr =
reinterpret_cast<AndroidWifiDataProvider*>(self);
WifiData new_wifi_data;
if (wifi_data) {
InitFromJava(wifi_data, &new_wifi_data);
}
// We notify regardless of whether new_wifi_data is empty
// or not. The arbitrator will decide what to do with an empty
// WifiData object.
self_ptr->NewWifiDataAvailable(&new_wifi_data);
}
void AndroidWifiDataProvider::NewWifiDataAvailable(WifiData* new_wifi_data) {
assert(supported_);
assert(new_wifi_data);
bool is_update_available = false;
data_mutex_.Lock();
is_update_available = wifi_data_.DiffersSignificantly(*new_wifi_data);
wifi_data_ = *new_wifi_data;
// Avoid holding the mutex locked while notifying observers.
data_mutex_.Unlock();
if (is_update_available) {
is_first_scan_complete_ = true;
NotifyListeners();
}
#if USING_CCTESTS
// This is needed for running the WiFi test on the emulator.
// See wifi_data_provider_android.h for details.
if (!first_callback_made_ && wifi_data_.access_point_data.empty()) {
first_callback_made_ = true;
NotifyListeners();
}
#endif
}
从以上代码可以看出,WIFI定位和基站定位的逻辑差不多,只是前者获取的WIFI的扫描结果,而后者获取的基站信息。后面代码的基本上就统一起来了,接下来我们继续看。
5.把变化(WIFI/基站)通知给相应的监听者。
AndroidWifiDataProvider和AndroidRadioDataProvider都是继承了DeviceDataProviderImplBase,DeviceDataProviderImplBase的主要功能就是管理所有Listeners。
static DeviceDataProvider *Register(ListenerInterface *listener) {
MutexLock mutex(&instance_mutex_);
if (!instance_) {
instance_ = new DeviceDataProvider();
}
assert(instance_);
instance_->Ref();
instance_->AddListener(listener);
return instance_;
}
static bool Unregister(ListenerInterface *listener) {
MutexLock mutex(&instance_mutex_);
if (!instance_->RemoveListener(listener)) {
return false;
}
if (instance_->Unref()) {
delete instance_;
instance_ = NULL;
}
return true;
}
6.谁在监听变化(WIFI/基站)
NetworkLocationProvider在监听变化(WIFI/基站):
radio_data_provider_ = RadioDataProvider::Register(this);
wifi_data_provider_ = WifiDataProvider::Register(this);
当有变化时,会调用函数DeviceDataUpdateAvailable:
// DeviceDataProviderInterface::ListenerInterface implementation.
void NetworkLocationProvider::DeviceDataUpdateAvailable(
RadioDataProvider *provider) {
MutexLock lock(&data_mutex_);
assert(provider == radio_data_provider_);
is_radio_data_complete_ = radio_data_provider_->GetData(&radio_data_);
DeviceDataUpdateAvailableImpl();
}
void NetworkLocationProvider::DeviceDataUpdateAvailable(
WifiDataProvider *provider) {
assert(provider == wifi_data_provider_);
MutexLock lock(&data_mutex_);
is_wifi_data_complete_ = wifi_data_provider_->GetData(&wifi_data_);
DeviceDataUpdateAvailableImpl();
}
无论是WIFI还是基站变化,最后都会调用DeviceDataUpdateAvailableImpl:
void NetworkLocationProvider::DeviceDataUpdateAvailableImpl() {
timestamp_ = GetCurrentTimeMillis();
// Signal to the worker thread that new data is available.
is_new_data_available_ = true;
thread_notification_event_.Signal();
}
这里面只是发了一个signal,通知另外一个线程去处理。
7.谁在等待thread_notification_event_
线程函数NetworkLocationProvider::Run在一个循环中等待thread_notification_event,当有变化(WIFI/基站)时,就准备请求服务器查询位置。
先等待:
if (remaining_time > 0) {
thread_notification_event_.WaitWithTimeout(
static_cast<int>(remaining_time));
} else {
thread_notification_event_.Wait();
}
准备请求:
if (make_request) {
MakeRequest();
remaining_time = 1;
}
再来看MakeRequest的实现:
先从cache中查找位置:
const Position *cached_position =
position_cache_->FindPosition(radio_data_, wifi_data_);
data_mutex_.Unlock();
if (cached_position) {
assert(cached_position->IsGoodFix());
// Record the position and update its timestamp.
position_mutex_.Lock();
position_ = *cached_position;
position_.timestamp = timestamp_;
position_mutex_.Unlock();
// Let listeners know that we now have a position available.
UpdateListeners();
return true;
}
如果找不到,再做实际的请求
return request_->MakeRequest(access_token,
radio_data_,
wifi_data_,
request_address_,
address_language_,
kBadLatLng, // We don't have a position to pass
kBadLatLng, // to the server.
timestamp_);
7.客户端协议包装
前面的request_是NetworkLocationRequest实例,先看MakeRequest的实现:
先对参数进行打包:
if (!FormRequestBody(host_name_, access_token, radio_data, wifi_data,
request_address, address_language, latitude, longitude,
is_reverse_geocode_, &post_body_)) {
return false;
}
通知负责收发的线程
thread_event_.Signal();
8.负责收发的线程
void NetworkLocationRequest::Run() {
while (true) {
thread_event_.Wait();
if (is_shutting_down_) {
break;
}
MakeRequestImpl();
}
}
void NetworkLocationRequest::MakeRequestImpl() {
WebCacheDB::PayloadInfo payload;
把打包好的数据通过HTTP请求,发送给服务器
scoped_refptr<BlobInterface> payload_data;
bool result = HttpPost(url_.c_str(),
false, // Not capturing, so follow redirects
NULL, // reason_header_value
HttpConstants::kMimeApplicationJson, // Content-Type
NULL, // mod_since_date
NULL, // required_cookie
true, // disable_browser_cookies
post_body_.get(),
&payload,
&payload_data,
NULL, // was_redirected
NULL, // full_redirect_url
NULL); // error_message
MutexLock lock(&is_processing_response_mutex_);
// is_aborted_ may be true even if HttpPost succeeded.
if (is_aborted_) {
LOG(("NetworkLocationRequest::Run() : HttpPost request was cancelled.\n"));
return;
}
if (listener_) {
Position position;
std::string response_body;
if (result) {
// If HttpPost succeeded, payload_data is guaranteed to be non-NULL.
assert(payload_data.get());
if (!payload_data->Length() ||
!BlobToString(payload_data.get(), &response_body)) {
LOG(("NetworkLocationRequest::Run() : Failed to get response body.\n"));
}
}
解析出位置信息
std::string16 access_token;
GetLocationFromResponse(result, payload.status_code, response_body,
timestamp_, url_, is_reverse_geocode_,
&position, &access_token);
通知位置信息的监听者。
bool server_error =
!result || (payload.status_code >= 500 && payload.status_code < 600);
listener_->LocationResponseAvailable(position, server_error, access_token);
}
}
有人会问,请求是发哪个服务器的?当然是google了,缺省的URL是:
static const char16 *kDefaultLocationProviderUrl =
STRING16(L"https://www.google.com/loc/json");
回过头来,我们再总结一下:
1.WIFI和基站定位过程
2.NetworkLocationProvider和NetworkLocationRequest各有一个线程来异步处理请求。
3.这里的NetworkLocationProvider与android中的NetworkLocationProvider并不是同一个东西,这里是给gears用的,要在android的google map中使用,还得包装成android中的NetworkLocationProvider的接口。
4.WIFI和基站定位与平台无关,只要你能拿到WIFI扫描结果或基站信息,而且能访问google的定位服务器,不管你是Android平台,Windows Mobile平台还是传统的feature phone,你都可以实现WIFI和基站定位。
附: WIFI和基站定位原理
无论是WIFI的接入点,还是移动网络的基站设备,它们的位置基本上都是固定的。设备端(如手机)可以找到它们的ID,现在的问题就是如何通过这些ID找到对应的位置。网上的流行的说法是开车把所有每个位置都跑一遍,把这些设备的位置与GPS测试的位置关联起来。
参考资料:
Gears: http://gears.googlecode.com/
Google 地图 API: http://code.google.com/intl/zh-CN/apis/maps/documentation/reference.html
wifi定位技术: http://blog.youkuaiyun.com/NewMap/archive/2009/03/17/3999337.aspx
*******************************************************************************************************************************************************************
http://blog.youkuaiyun.com/sunrock/article/details/6536996
修改 frameworks/base/core/res/res/values/config.xml
- <!-- Component name of the service providing network location support. -->
- <string name="config_networkLocationProvider">com.google.android.location.NetworkLocationProvider</string>
- <!-- Component name of the service providing geocoder API support. -->
- <string name="config_geocodeProvider">com.google.android.location.GeocodeProvider</string>
把GoogleServicesFramework.apk和NetworkLocation.apk添加到/system/app目录下.
功能:
NetworkLocationProvider,网络定位功能;
GeocodeProvider,坐标地址转换功能。
***********************************************************************************************************************************************************************
http://www.netmite.com/android/mydroid/frameworks/base/location/java/com/android/internal/location/NetworkLocationProvider.java
/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.location; import com.android.internal.location.protocol.GDebugProfile; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import android.content.Context; import android.location.Criteria; import android.location.Location; import android.location.LocationManager; import android.location.LocationProviderImpl; import android.net.wifi.ScanResult; import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.SystemClock; import android.os.SystemProperties; import android.util.Log; /** * A location provider which gets approximate location from Google's * database of cell-tower and wi-fi locations. * * <p> It is the responsibility of the LocationManagerService to * notify this class of any changes in the radio information * by calling {@link #updateCellState} and of data state * changes by calling {@link #updateNetworkState} * * <p> The LocationManagerService must also notify the provider * of Wifi updates using the {@link #updateWifiScanResults} * and {@link #updateWifiEnabledState} * methods. * * <p> The provider uses whichever radio is available - Cell * or WiFi. If neither is available, it does NOT attempt to * switch them on. * * {@hide} */ public class NetworkLocationProvider extends LocationProviderImpl { private static final String TAG = "NetworkLocationProvider"; // Wait at least 60 seconds between network queries private static final int MIN_NETWORK_RETRY_MILLIS = 60000; // Max time to wait for radio update private static final long MAX_TIME_TO_WAIT_FOR_RADIO = 5 * 1000; // 5 seconds // State of entire provider private int mStatus = AVAILABLE; private long mStatusUpdateTime = 0; // Network state private int mNetworkState = TEMPORARILY_UNAVAILABLE; // Cell state private static final int MAX_CELL_HISTORY_TO_KEEP = 4; private LinkedList<CellState> mCellHistory = new LinkedList<CellState>(); private CellState mCellState = null; private long mLastCellStateChangeTime = 0; private long mLastCellLockTime = 0; // Wifi state private static final long MIN_TIME_BETWEEN_WIFI_REPORTS = 45 * 1000; // 45 seconds private List<ScanResult> mWifiLastScanResults = null; private long mLastWifiScanTriggerTime = 0; private long mLastWifiScanElapsedTime = 0; private long mLastWifiScanRealTime = 0; private long mWifiScanFrequency = MIN_TIME_BETWEEN_WIFI_REPORTS; private boolean mWifiEnabled = false; // Last known location state private Location mLocation = new Location(LocationManager.NETWORK_PROVIDER); private long mLastNetworkQueryTime = 0; // Last network request, successful or not private long mLastSuccessfulNetworkQueryTime = 0; // Last successful network query time // Is provider enabled by user -- ignored by this class private boolean mEnabled; // Is provider being used by an application private HashSet<String> mApplications = new HashSet<String>(); private boolean mTracking = false; // Location masf service private LocationMasfClient mMasfClient; // Context of location manager service private Context mContext; public static boolean isSupported() { // This class provides a Google-specific location feature, so it's enabled only // when the system property ro.com.google.enable_google_location_features is set. if (!SystemProperties.get("ro.com.google.enable_google_location_features").equals("1")) { return false; } // Otherwise, assume cell location should work if we are not running in the emulator return !SystemProperties.get("ro.kernel.qemu").equals("1"); } public NetworkLocationProvider(Context context, LocationMasfClient masfClient) { super(LocationManager.NETWORK_PROVIDER); mContext = context; mMasfClient = masfClient; } @Override public void updateNetworkState(int state) { if (state == mNetworkState) { return; } log("updateNetworkState(): Updating network state to " + state); mNetworkState = state; updateStatus(mNetworkState); } @Override public void updateCellState(CellState newState) { if (newState == null) { log("updateCellState(): Cell state is invalid"); return; } if (mCellState != null && mCellState.equals(newState)) { log("updateCellState(): Cell state is the same"); return; } // Add previous state to history if ((mCellState != null) && mCellState.isValid()) { if (mCellHistory.size() >= MAX_CELL_HISTORY_TO_KEEP) { mCellHistory.remove(0); } mCellHistory.add(mCellState); } mCellState = newState; log("updateCellState(): Received"); mLastCellLockTime = 0; mLastCellStateChangeTime = SystemClock.elapsedRealtime(); } public void updateCellLockStatus(boolean acquired) { if (acquired) { mLastCellLockTime = SystemClock.elapsedRealtime(); } else { mLastCellLockTime = 0; } } @Override public boolean requiresNetwork() { return true; } @Override public boolean requiresSatellite() { return false; } @Override public boolean requiresCell() { return true; } @Override public boolean hasMonetaryCost() { return true; } @Override public boolean supportsAltitude() { return false; } @Override public boolean supportsSpeed() { return false; } @Override public boolean supportsBearing() { return false; } @Override public int getPowerRequirement() { return Criteria.POWER_LOW; } @Override public void enable() { // Nothing else needs to be done mEnabled = true; } @Override public void disable() { // Nothing else needs to be done mEnabled = false; } @Override public boolean isEnabled() { return mEnabled; } @Override public int getAccuracy() { return Criteria.ACCURACY_COARSE; } @Override public int getStatus(Bundle extras) { return mStatus; } @Override public long getStatusUpdateTime() { return mStatusUpdateTime; } @Override public void setMinTime(long minTime) { if (minTime < MIN_TIME_BETWEEN_WIFI_REPORTS) { mWifiScanFrequency = MIN_TIME_BETWEEN_WIFI_REPORTS; } else { mWifiScanFrequency = minTime; } super.setMinTime(minTime); } @Override public boolean getLocation(Location l) { long now = SystemClock.elapsedRealtime(); // Trigger a wifi scan and wait for its results if necessary if ((mWifiEnabled) && (mWifiLastScanResults == null || ((now - mLastWifiScanElapsedTime) > mWifiScanFrequency))) { boolean fallback = false; // If scan has been recently triggered if (mLastWifiScanTriggerTime != 0 && ((now - mLastWifiScanTriggerTime) < mWifiScanFrequency)) { if ((now - mLastWifiScanTriggerTime) > MAX_TIME_TO_WAIT_FOR_RADIO) { // If no results from last trigger available, use cell results // This will also trigger a new scan log("getLocation(): falling back to cell"); fallback = true; } else { // Just wait for the Wifi results to be available return false; } } WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); log("getLocation(): triggering a wifi scan"); mLastWifiScanTriggerTime = now; boolean succeeded = wifiManager.startScan(); if (!succeeded) { log("getLocation(): wifi scan did not succeed"); // Wifi trigger failed, use cell results fallback = true; } // Wait for scan results if (!fallback) { return false; } } // If waiting for cell location if (mLastCellLockTime != 0 && ((now - mLastCellLockTime) < MAX_TIME_TO_WAIT_FOR_RADIO)) { return false; } // Update Location // 1) If there has been a cell state change // 2) If there was no successful reply for last network request if (mLastCellStateChangeTime > mLastNetworkQueryTime) { updateLocation(); return false; } else if ((mLastNetworkQueryTime != 0) && (mLastNetworkQueryTime > mLastSuccessfulNetworkQueryTime) && ((now - mLastNetworkQueryTime) > MIN_NETWORK_RETRY_MILLIS)) { updateLocation(); return false; } if (mLocation != null && mLocation.getAccuracy() > 0) { // We could have a Cell Id location which hasn't changed in a // while because we haven't switched towers so if the last location // time + mWifiScanFrequency is less than current time update the // locations time. long currentTime = System.currentTimeMillis(); if ((mLocation.getTime() + mWifiScanFrequency) < currentTime) { mLocation.setTime(currentTime); } l.set(mLocation); return true; } else { return false; } } @Override public void enableLocationTracking(boolean enable) { if (enable == mTracking) { return; } log("enableLocationTracking(): " + enable); mTracking = enable; if (!enable) { // When disabling the location provider, be sure to clear out old location clearLocation(); } else { // When enabling provider, force location forceLocation(); } } @Override public boolean isLocationTracking() { return mTracking; } /** * Notifies the provider that there are scan results available. * * @param scanResults list of wifi scan results */ public void updateWifiScanResults(List<ScanResult> scanResults) { if (!mTracking) { return; } long now = SystemClock.elapsedRealtime(); if (scanResults == null) { mWifiLastScanResults = null; mLastWifiScanElapsedTime = now; mLastWifiScanRealTime = System.currentTimeMillis(); log("updateWifIScanResults(): NULL APs"); // Force cell location since no wifi results available if (mWifiEnabled) { mLastCellLockTime = 0; mLastCellStateChangeTime = SystemClock.elapsedRealtime(); } } else if ((mWifiLastScanResults == null) || (mWifiLastScanResults.size() <= 2 && scanResults.size() > mWifiLastScanResults.size()) || ((now - mLastWifiScanElapsedTime) > mWifiScanFrequency)) { if (mWifiLastScanResults == null) { mWifiLastScanResults = new ArrayList<ScanResult>(); } else { mWifiLastScanResults.clear(); } mWifiLastScanResults.addAll(scanResults); mLastWifiScanElapsedTime = now; mLastWifiScanRealTime = System.currentTimeMillis(); log("updateWifIScanResults(): " + mWifiLastScanResults.size() + " APs"); updateLocation(); } } /** * Notifies the provider if Wifi has been enabled or disabled * by the user * * @param enabled true if wifi is enabled; false otherwise */ public void updateWifiEnabledState(boolean enabled) { mWifiEnabled = enabled; log("updateWifiEnabledState(): " + enabled); // Force location update forceLocation(); } public void addListener(String[] applications) { if (applications != null) { for (String app : applications) { String a = app.replaceAll("com.google.android.", ""); a = a.replaceAll("com.android.", ""); mApplications.add(a); log("addListener(): " + a); } } } public void removeListener(String[] applications) { if (applications != null) { for (String app : applications) { String a = app.replaceAll("com.google.android.", ""); a = a.replaceAll("com.android.", ""); mApplications.remove(a); log("removeListener(): " + a); } } } private void clearLocation() { mLocation.setAccuracy(-1); updateStatus(TEMPORARILY_UNAVAILABLE); } private void forceLocation() { if (mWifiEnabled) { // Force another wifi scan mWifiLastScanResults = null; mLastWifiScanTriggerTime = 0; mLastWifiScanElapsedTime = 0; mLastWifiScanRealTime = 0; } else { // Force another cell location request mLastCellLockTime = 0; mLastCellStateChangeTime = SystemClock.elapsedRealtime(); } } private void updateStatus(int status) { if (status != mStatus) { mStatus = status; mStatusUpdateTime = SystemClock.elapsedRealtime(); } } /** * Gets location from the server is applications are tracking this provider * */ private void updateLocation() { // If not being tracked, no need to do anything. if (!mTracking) { return; } // If network is not available, can't do anything if (mNetworkState != AVAILABLE) { return; } final long now = SystemClock.elapsedRealtime(); // There is a pending network request if ((mLastNetworkQueryTime != 0) && (mLastNetworkQueryTime > mLastSuccessfulNetworkQueryTime) && ((now - mLastNetworkQueryTime) <= MIN_NETWORK_RETRY_MILLIS)) { return; } // Don't include wifi points if they're too old List<ScanResult> scanResults = null; if (mWifiEnabled && (mWifiLastScanResults != null && ((now - mLastWifiScanElapsedTime) < (mWifiScanFrequency + MAX_TIME_TO_WAIT_FOR_RADIO)))) { scanResults = mWifiLastScanResults; } // If no valid cell information available boolean noCell = mCellState == null || !mCellState.isValid(); // If no valid wifi information available boolean noWifi = scanResults == null || (scanResults.size() == 0); // If no cell-id or wi-fi update, just return invalid location if (noCell && noWifi) { clearLocation(); return; } // What kind of a network location request was it int trigger; if (!mWifiEnabled) { if (!noCell) { trigger = GDebugProfile.TRIGGER_CELL_AND_WIFI_CHANGE; } else { trigger = GDebugProfile.TRIGGER_WIFI_CHANGE; } } else { trigger = GDebugProfile.TRIGGER_CELL_CHANGE; } try { mLastNetworkQueryTime = now; mMasfClient.getNetworkLocation(mApplications, trigger, mCellState, mCellHistory, scanResults, mLastWifiScanRealTime, new Callback() { public void locationReceived(Location location, boolean networkSuccessful) { // If location is valid and not the same as previously known location if ((location != null) && (location.getAccuracy() > 0) && (location.getTime() != mLocation.getTime())) { mLocation.set(location); updateStatus(AVAILABLE); } else { // Location is unavailable clearLocation(); } // Even if no location is available, network request could have succeeded if (networkSuccessful) { mLastSuccessfulNetworkQueryTime = SystemClock.elapsedRealtime(); } } }); } catch(Exception e) { Log.e(TAG, "updateLocation got exception:", e); } } public interface Callback { /** * Callback function to notify of a received network location * * @param location location object that is received. may be null if not a valid location * @param successful true if network query was successful, even if no location was found */ void locationReceived(Location location, boolean successful); } private void log(String log) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, log); } } }
**************************************************************************************************************************************************************************
http://www.cnblogs.com/qiuyi21/archive/2008/03/04/1089456.html
整个地球分为二十四时区,每个时区都有自己的本地时间。在国际无线电通信场合,为了统一起见,使用一个统一的时间,称为通用协调时(UTC,Universal Time Coordinated)。UTC与格林尼治平均时(GMT, Greenwich MeanTime)一样,都与英国伦敦的本地时相同。在本文中,UTC与GMT含义完全相同。
北京时区是东八区,领先UTC八个小时,在电子邮件信头的Date域记为+0800。如果在电子邮件的信头中有这么一行:
Date:Fri, 08 Nov 2002 09:42:22 +0800
说明信件的发送地的地方时间是二○○二年十一月八号,星期五,早上九点四十二分(二十二秒),这个地方的本地时领先UTC八个小时(+0800,就是东八区时间)。电子邮件信头的Date域使用二十四小时的时钟,而不使用AM和PM来标记上下午。
以这个电子邮件的发送时间为例,如果要把这个时间转化为UTC,可以使用一下公式:
UTC + 时区差 = 本地时间
时区差东为正,西为负。在此,把东八区时区差记为 +0800,
UTC + (+0800) = 本地(北京)时间 (1)
那么,UTC = 本地时间(北京时间))- 0800 (2)
0942 - 0800 = 0142
即UTC是当天凌晨一点四十二分二十二秒。如果结果是负数就意味着是UTC前一天,把这个负数加上2400就是UTC在前一天的时间。例如,本地(北京)时间是 0432 (凌晨四点三十二分),那么,UTC就是 0432 - 0800 = -0368,负号意味着是前一天, -0368 + 2400 =2032,既前一天的晚上八点三十二分。
纽约的时区是西五区,比UTC落后五个小时,记为 -0500:
UTC + (-0500)= 纽约时间 (3)
UTC = 纽约时间 + 0500 (4)
把(2)式 - (4)式相比较,
UTC =北京时间 - 0800 = 纽约时间 + 0500 (5)
即 北京时间 = 纽约时间 + 1300 (6)
即北京时间领先纽约时间十三个小时,由(6)式,
纽约时间 = 北京时间 - 1300 (7)
在四月下旬,纽约又换用夏令时,又称为日光节约时,比标准纽约时间提前一个小时,实际成为西四区的标准时间,成为 -0400。
UTC+ (-0400) = 纽约夏令时,套用以上公式,
北京时间 = 纽约夏令时 + 1200
纽约夏令时 = 北京时间 -1200
在这些转换中,最重要的公式就是
UTC + 时区差 = 本地时间
时区差东为正,西为负。例如,东八区(北京)是+0800,西五区(纽约)是-0500,加州是西八区,是-0800,美国中部时区是西六区,-0600,美国山地时区是西七区,-0700,太平洋时区是西八区,-0800,在夏天使用夏时制,成为-0700。德国时区是东一区,+0100,夏天变为+0200。
多数电子邮件程序,例如Outlook Express,在显示时间时,计算机程序把时间先转换成为本地时间再显示,例如,邮件的Date域为:
Date: Fri, 08 Nov 2002 09:42:22 +0800
Outlook Express在显示时就显示为:
Date: Thur, 07 Nov 2002 08:42:22pm,把北京时间转换成为了纽约时间,而且把二十四小时格式的时间转换成为了十二小时的格式。当然,为了时间转换正确,发送方和接受方的计算机的时区都要设置正确,在这里,发送方的时区要正确地设为北京时区东八区,而我的时区要设为西五区。
为了方便起见,我在这里放上纽约,加洲以及北京实时显示的时钟,以省去计算的麻烦。
*************************************************************************************************************************************************************************
http://zhidao.baidu.com/question/83764099.html
77E和77W分别是第几时区??!! 告诉我方法!!我来帮他解答
77E除以15 得5 余2 那就是东 五区 余数大于7.5 就加一个时区 77w除以15 得5 余2 西五区
http://www.2cto.com/kf/201107/98188.html
****************************************************************************************************************************************************************************
Android系统默认只能通过IP(10.0.2.2)单向访问PC电脑,而PC电脑不能通过IP来直接访问Android模拟器系统。要想实现PC电脑和Android模拟器系统以及Android模拟器之间相互通信必须借助端口重定向(redir)来实现。
先说说端口重定向所需要的telnet客户端安装:
windows:
安装telnet客户端。如果没有安装,可以在windows程序管理中的打开或关闭系统功能下找到telnet客户端菜单项来启用telnet客户端功能。
linux:
自行安装telnet客户端。
一、PC电脑不能直接访问Android模拟器系统的原因
Android系统为实现通信将PC电脑IP设置为10.0.2.2,自身为10.0.2.15/127.0.0.1。然而PC电脑并没有为Android模拟器系统指定IP,所以PC只能通过端口重定向来实现和Android模拟器的通信。
二、PC电脑和Android模拟器系统之间通信
1、运行模拟器
2、打开window 命令行,执行:
1
|
telnet localhost 5554
|
5554是模拟器的端口(位于Android模拟器窗口标题栏),执行之后会进入android console
3、在console下执行:
1
2 3 |
格式:redir add
< udp/tcp
>:
< pc端口
>:
< 模拟器端口
>
例如:redir add udp:2888:2888 redir add tcp:2888:2888 |
执行此命令之后,会把PC 2888 端口接收到的tcp/udp数据转到模拟器的2888端口。
三、多个Android模拟器系统之间通信
1、启动模拟器emulator-5554和emulator-5556
2、打开dos窗口执行telnet localhost 5554连接到模拟器5554
3、成功连接后,继续执行:redir add tcp:5000:6000将PC端口5000绑定到模拟器5554的端口6000上。
4、此时模拟器5556通过向PC电脑端口5000(即地址:10.0.2.2:5000)发送tcp/udp数据包跟模拟器5554通信。
5、同理根据步骤2、3来实现PC电脑对模拟器5556的端口转发。
添加成功后,我们可以用redir list命令来列出已经添加的映射端口,redir del可以进行删除。
相信只要理解了PC电脑和Android模拟器系统之间通信,便知道怎么实现多个模拟器之间通信。
转载请注明地址: http://orgcent.com/pc-android-emulator-socket/ | 萝卜白菜的博客