Android gps数据上报(基于gps_quem.c)

在网上找了好久关于这方面的东西,可都是介绍启动的,现在我把我理解的其数据上报的流程写下来,以作记录:

 

(主要基于gps_qemu.c的数据上报)

 

hardware/libhardware_legacy/gps/gps_qemu.c(HAL层)

 

数据从串口传上来主要是通过函数static void* gps_state_thread( void*  arg )接收,并解析上报给

android_location_GpsLocationProvider.cpp(JNI层)的,下面首先来看gps_state_thread中数据抓取及数据解析:

 

 

              else if (fd == gps_fd)

                {

                    char  buff[32];

                    D("gps fd event");

                    for (;;) {

                        int  nn, ret;

 

                        ret = read( fd, buff, sizeof(buff) );//从串口获取数据)

                        if (ret < 0) {

                            if (errno == EINTR)

                                continue;

                            if (errno != EWOULDBLOCK)

                                LOGE("error while reading from gps daemon socket: %s:", strerror(errno));

                            break;

                        }

                        D("received %d bytes: %.*s", ret, ret, buff);

                        for (nn = 0; nn < ret; nn++)

                            nmea_reader_addc( reader, buff[nn] );//解析数据并上报)

                    }

                    D("gps fd event end");

                }

接下来看nmea_reader_addc函数(在gps_qemu.c中):

   if (c == '/n') {

        nmea_reader_parse( r ); //解析数据并上报

        r->pos = 0;

    }

函数nmea_reader_parse( NmeaReader*  r )(在gps_qemu.c中)

        if (r->callback) {

            r->callback( &r->fix ); //这句就是上报数据了

            r->fix.flags = 0;

        }

        else {

            D("no callback, keeping data until needed !");

        }

=====================================注释一===================================

这里有个回调函数r->callback,这个回调函数是在gps_state_thread函数中通过nmea_reader_set_callback函数设置的,nmea_reader_set_callback中有这样的语句:

r->callback = cb;//这就是设定了

而在gps_state_thread中传给nmea_reader_set_callback的state->callbacks.location_cb参数是在这里设定的

static int qemu_gps_init(GpsCallbacks* callbacks)

{

    GpsState*  s = _gps_state;

    if (!s->init)

        gps_state_init(s);

    if (s->fd < 0)

        return -1;

    s->callbacks = *callbacks;

    return 0;

}

而函数qemu_gps_init是在qemuGpsInterface结构体中被JNI(c/c++部分)层调用的,在JNI(c/c++部分)层怎样调用,这将在注释二中解释

 

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

在这里,数据在HAL层的路已经走完,下面就进入到JNI(c/c++部分)层android_location_GpsLocationProvider.cpp中了

在这层中首先接收到数据的是函数location_callback:

   memcpy(&sGpsLocation, location, sizeof(sGpsLocation));//这里把接收到的数据传给sGpsLocation结构体

接着再在android_location_GpsLocationProvider_wait_for_event中:

    if (pendingCallbacks & kLocation)

        memcpy(&sGpsLocationCopy, &sGpsLocation, sizeof(sGpsLocationCopy));//把数据传给sGpsLocationCopy结构体

。。。。。。。这里省略了几行。。。。。。

    if (pendingCallbacks & kLocation) {

        env->CallVoidMethod(obj, method_reportLocation, sGpsLocationCopy.flags,           //把数据上报给上层(注释二中解释相关函数)

                (jdouble)sGpsLocationCopy.latitude, (jdouble)sGpsLocationCopy.longitude,

                (jdouble)sGpsLocationCopy.altitude,

                (jfloat)sGpsLocationCopy.speed, (jfloat)sGpsLocationCopy.bearing,

                (jfloat)sGpsLocationCopy.accuracy, (jlong)sGpsLocationCopy.timestamp);

    }

=====================================注释二===================================

 

在这一层里location_callback,是怎样接收到数据呢?

其实这个函数是作为一个回调函数,在HAL层中调用的,其是通过调用qemu_gps_init函数把location_callback注册到HAL层中的

我没看下调用qemu_gps_init,在函数android_location_GpsLocationProvider_init(JNI层):

    if (!sGpsInterface)

        sGpsInterface = gps_get_interface();//这句是获得在HAN层定义的GpsInterface指针,gps_get_interface

                                                                   //定义在hardware/libhardware_legacy/gps/gps.cpp

    if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0)      //这里调用的init就是调用qemu_gps_init了

        return false;                                                            //参数sGpsCallbacks就是包含location_callback指针的结构体了

sGpsCallbacks定义如下:

GpsCallbacks sGpsCallbacks = {

    location_callback,

    status_callback,

    sv_status_callback,

    nmea_callback

};

 

***********************我是分隔线************************

android_location_GpsLocationProvider_wait_for_event函数就注册成了java层中的接口了,env是由上层传下来的参数,具体请参考JNI的相关知识

下面的代码,完成了这个函数的注册:

static JNINativeMethod sMethods[] = {

    。。。省略。。。

    {"native_wait_for_event", "()V", (void*)android_location_GpsLocationProvider_wait_for_event},

    。。。省略。。。

};

int register_android_location_GpsLocationProvider(JNIEnv* env)

{

    return jniRegisterNativeMethods(env, "com/android/internal/location/GpsLocationProvider", sMethods, NELEM(sMethods));

}

****************我是分隔线********************

env->CallVoidMethod(obj, method_reportLocation, sGpsLocationCopy.flags,           //把数据上报给上层

                (jdouble)sGpsLocationCopy.latitude, (jdouble)sGpsLocationCopy.longitude,

                (jdouble)sGpsLocationCopy.altitude,

                (jfloat)sGpsLocationCopy.speed, (jfloat)sGpsLocationCopy.bearing,

                (jfloat)sGpsLocationCopy.accuracy, (jlong)sGpsLocationCopy.timestamp);

参数method_reportLocation,是在下面的函数(android_location_GpsLocationProvider)中被映射为java的reportLocation(在GpsLocationProvider.java中)方法:

static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {

    method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V");

    。。。省略。。。

}

在java层中GpsLocationProvider.java的reportLocation:

 private void reportLocation(int flags, double latitude, double longitude, double altitude,

            float speed, float bearing, float accuracy, long timestamp) {

        if (VERBOSE) Log.v(TAG, "reportLocation lat: " + latitude + " long: " + longitude +

                " timestamp: " + timestamp);

        mLastFixTime = System.currentTimeMillis();

       。。。省略。。。

}

这个函数是(JNI Java部分)java注册到JNI(c/C++部分)中的一个回调,我理解为只用来传递数据。

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

 

由以上流程可知,在JNI(c/c++)层的android_location_GpsLocationProvider_wait_for_event函数中调用了method_reportLocation方法,也就是调用了JNI(java)层的reportLocation方法,而数据在这里就传向了JNI(java部分)了,所以接下来就该讨论JNI(java)层了。

 

在JNI(java)层中的reportLocation方法中,可以看到数据先是被mLocation对象接收:

        synchronized (mLocation) {

            mLocationFlags = flags;

            if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {

                mLocation.setLatitude(latitude);

                mLocation.setLongitude(longitude);

                mLocation.setTime(timestamp);

            }

            if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {

                mLocation.setAltitude(altitude);

            } else {

                mLocation.removeAltitude();

            }

            if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {

                mLocation.setSpeed(speed);

            } else {

                mLocation.removeSpeed();

            }

            if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {

                mLocation.setBearing(bearing);

            } else {

                mLocation.removeBearing();

            }

            if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {

                mLocation.setAccuracy(accuracy);

            } else {

                mLocation.removeAccuracy();

            }

接着就是:

            try {

                mLocationManager.reportLocation(mLocation, false);

            } catch (RemoteException e) {

                Log.e(TAG, "RemoteException calling reportLocation");

            }

调用了mLocationManager.reportLocation,这个mLocationManager对象是接口ILocationManager的一个实例,其在GpsLocationProvider类的构造函数中被赋值:

 public GpsLocationProvider(Context context, ILocationManager locationManager) {

        mContext = context;

        mLocationManager = locationManager;

        mNIHandler = new GpsNetInitiatedHandler(context, this);

        。。。省略。。。

 }

通过调用关系可发现其会在server层中被调用,在LocationManagerService类的_loadProvidersLocked方法中调用,并且,传给其构造函数的参数是this,也就是说,mLocationManager是LocationManagerService类的实例,其在JNI(java)层中的reportLocation方法中调用的mLocationManager.reportLocation方法是LocationManagerService类实现的,所以数据流到了JNI(java)的reportLocation后就直接到了server层的LocationManagerService类中,_loadProvidersLocked方法代码如下:

    private void _loadProvidersLocked() {

        // Attempt to load "real" providers first

        if (GpsLocationProvider.isSupported()) {

            // Create a gps location provider

            GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this);

   。。。省略。。。

   }

 

 

接下来就来看server层(LocationManagerService.java)的LocationManagerService类

由JNI(java)层的可知,在这层中首先接受到数据的是reportLocation方法:

    public void reportLocation(Location location, boolean passive) {

        if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)

                != PackageManager.PERMISSION_GRANTED) {

            throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");

        }

 

        mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);

        Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);

        m.arg1 = (passive ? 1 : 0);

        mLocationHandler.sendMessageAtFrontOfQueue(m);//这句把location发送到句柄

    }

有其参数可知,数据是由参数location带入,这里请注意最后一句mLocationHandler.sendMessageAtFrontOfQueue(m),这句把消息发送到句柄,由句柄的handleMessage函数处理,这个handleMessage应该是该文件中LocationWorkerHandler类的方法,在这个方法中会继续把数据上报:

        public void handleMessage(Message msg) {

            。。。省略。。。

 

                        if (isAllowedBySettingsLocked(provider)) {

                            handleLocationChangedLocked(location, passive);//继续把数据上报

                        }

           。。。省略。。。

        }

该方法中的数据传给handleLocationChangedLocked函数,追踪可得这个方法是LocationManagerService类的方法:

    private void handleLocationChangedLocked(Location location, boolean passive) {

        String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());

        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);

        if (records == null || records.size() == 0) {

            return;

        }

 

        LocationProviderInterface p = mProvidersByName.get(provider);

        if (p == null) {

            return;

        }

 

        // Update last known location for provider

        Location lastLocation = mLastKnownLocation.get(provider);

        if (lastLocation == null) {

            mLastKnownLocation.put(provider, new Location(location));

        } else {

            lastLocation.set(location);//这句,把mLastKnownLocation表里的location对象设置成当前数据

        }

 

  。。。省略。。。

    }

这这个方法中可看到,有一个包含一个location对象的哈希表mLastKnownLocation,先从里面取出这个对象,如果为空的话,就把当前含数据的location放进去,如果已经有了的话就用当前含数据的location对象设置哈希表里的location对象,数据到这里就放到了用户可取的缓冲区里面了

 

 

到这里,数据就等待用户来取了,但是用户怎么取呢?

这里从应用程序的api开始看:

用户取数据一般应该调用下面Api(GPS的应用编程,参考其他文档,关于LocationManager对象的获得,请参考后面的注释三)

    private void getLocation()

    {

        // 获取位置管理服务

        LocationManager locationManager;

        String serviceName = Context.LOCATION_SERVICE;

        locationManager = (LocationManager) this.getSystemService(serviceName);

        // 查找到服务信息

        Criteria criteria = new Criteria();

        criteria.setAccuracy(Criteria.ACCURACY_FINE); // 高精度

        criteria.setAltitudeRequired(false);

        criteria.setBearingRequired(false);

        criteria.setCostAllowed(true);

        criteria.setPowerRequirement(Criteria.POWER_LOW); // 低功耗

 

        String provider = locationManager.getBestProvider(criteria, true); // 获取GPS信息

        Location location = locationManager.getLastKnownLocation(provider); // 通过GPS获取位置

        updateToNewLocation(location);

        // 设置监听器,自动更新的最小时间为间隔N秒(1秒为1*1000,这样写主要为了方便)或最小位移变化超过N米

        locationManager.requestLocationUpdates(provider, 100 * 1000, 500,

                locationListener);

    }

注意这一句:

Location location = locationManager.getLastKnownLocation(provider); // 通过GPS获取位置

这句调用了LocationManager类的getLastKnownLocation方法,看一下这个方法:

    public Location getLastKnownLocation(String provider) {

        if (provider == null) {

            throw new IllegalArgumentException("provider==null");

        }

        try {

            return mService.getLastKnownLocation(provider);//注意这句

        } catch (RemoteException ex) {

            Log.e(TAG, "getLastKnowLocation: RemoteException", ex);

            return null;

        }

    }

这句return mService.getLastKnownLocation(provider)就返回了包含数据的location对象,mService是ILocationManager接口的一个实例,查看源码可知,只有LocationManagerService类实现了这个接口,所以这里无疑是调用的LocationManagerService类的getLastKnownLocation方法:

    public Location getLastKnownLocation(String provider) {

        try {

            synchronized (mLock) {

                return _getLastKnownLocationLocked(provider);//。。。。。。。。。。。。。

            }

        } catch (SecurityException se) {

            throw se;

        } catch (IllegalArgumentException iae) {

            throw iae;

        } catch (Exception e) {

            Slog.e(TAG, "getLastKnownLocation got exception:", e);

            return null;

        }

    }

这个方法中,其调用了_getLastKnownLocationLocked方法,这个也是LocationManagerService类的一个方法:

    private Location _getLastKnownLocationLocked(String provider) {

        checkPermissionsSafe(provider);

 

        LocationProviderInterface p = mProvidersByName.get(provider);

        if (p == null) {

            throw new IllegalArgumentException("provider=" + provider);

        }

 

        if (!isAllowedBySettingsLocked(provider)) {

            return null;

        }

 

        return mLastKnownLocation.get(provider);//。。。。。。。

    }

这个方法中的return mLastKnownLocation.get(provider)从就从哈希表里面取出了含有数据的location对象,呵呵,到这里,数据就从底层,传送到了应用程序(完)

 

=====================================注释三===================================

LocationManager locationManager;

String serviceName = Context.LOCATION_SERVICE;

locationManager = (LocationManager) this.getSystemService(serviceName);

 

这三句,获得了LocationManager的对象,但getSystemService这个方法还不知道怎么调用,这里可能用错了。

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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值