关于CELL基站和WIFI定位的资料

**********************************************************************************************************************************************************************************

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.csdn.net/NewMap/archive/2009/03/17/3999337.aspx


*******************************************************************************************************************************************************************

http://blog.csdn.net/sunrock/article/details/6536996



修改 frameworks/base/core/res/res/values/config.xml

[xhtml] view plain copy
  1. <!-- Component name of the service providing network location support. -->  
  2. <string name="config_networkLocationProvider">com.google.android.location.NetworkLocationProvider</string>  
  3. <!-- Component name of the service providing geocoder API support. -->  
  4. <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

2009-01-28 18:48 lyyle | 分类:高考 | 浏览1151次
77E和77W分别是第几时区??!!  告诉我方法!!
我来帮他解答
提问者采纳
2009-01-30 21:11
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/ | 萝卜白菜的博客

====================================================================================================================================



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值