레이블이 gps인 게시물을 표시합니다. 모든 게시물 표시
레이블이 gps인 게시물을 표시합니다. 모든 게시물 표시

2017년 2월 12일 일요일

using mock gps, fake gps ( Mock GPS 사용하기 : GPS 위치 조작 ) in android

이 글은 안드로이드 6.0 M OS 에서 테스트 되어졌습니다.

Mock GPS는 GPS 어플을 개발할때 모의로 테스트 해볼 수 있게 API가 마련되어있습니다.
하지만 구글 문서에서는 예제를 정확히 찾기가 어려워서 예제를 만들어 보았습니다.
이글은 정상적으로 Mock GPS API를 사용하는 예제입니다.
아래 첫번째 소스 코드는 Mock GPS를 사용하지 않았는지 속이기 위한 Fake GPS 소스 코드가 아닙니다. (isFromMockProvider()를 이용하면 Mock 위치 정보인지 알 수 있습니다.)
두번째 소스는 isFromMockProvider() 값이 false가 리턴됩니다.

여기에 있는 소스들 실행시 실행후 취소키를 누르게 되면 앱이 종료됩니다.
홈키를 눌러 thread가 살아 있을수 있게 해서 테스트 해야 합니다.

사용하기 위해서는 아래와 같이 설정을 미리 해야합니다.

(삼성폰예) Mock GPS를 이용하기 전에 개발자 옵션를 on해야하며 (환경설정>디바이스정보>소프트웨어 정보>빌드번호 연속으로 터치)
(환경설정>개발자옵션)모위 위치 앱을 설정(모의 위치 앱으로 사용하고자 하는 앱 설정 한번에 하나만 사용가능) 해야 합니다.
이부분은 fly GPS로 마켓을 검색해보면 세팅하는 방법이 자세히 나와있습니다.

참고
http://programmingjungle.blogspot.kr/2012/08/how-to-use-mocking-location-on-android.html

권한은 아래 권한이 필요합니다.

<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
사용할 Provider를 아래 메소드를 이용하여 등록하고
addTestProvider
setTestProviderEnabled
아래 메소드는 위치 정보를 변경할때 사용합니다.
setTestProviderLocation
그리고 필요없으면 삭제하는 메소드로 구성되어 있습니다.
removeTestProvider

Mock 소스

package com.example.xxx.mockgps;

import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;


public class MainActivity extends AppCompatActivity {
    Button btn1;
    Button btn2;
    Button btn3;
    Button btn4;
    EditText editText;
    MockUpdateGPSThread thread;
    FakeUpdateGPSThread fakethread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setupButton();

    }
    private void setupButton() {
        editText = (EditText) findViewById(R.id.editText);
        btn1 = (Button) findViewById(R.id.button);
        btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startMockGps();
                editText.append("Start\n");
            }
        });
        btn2 = (Button) findViewById(R.id.button2);
        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                stopMockGps();
                editText.append("Stop\n");
            }
        });
        btn3 = (Button) findViewById(R.id.button3);
        btn3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startFakeGps();
            }
        });
        btn4 = (Button) findViewById(R.id.button4);
        btn4.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                stopFakeGps();
            }
        });
    }


    class MockUpdateGPSThread extends Thread {
        final int TIME_UPDATES_MS = 5000;
        public boolean Running;
        private double curLat = 34.526456 , curLong = 127.3298;

        @Override
        public void run() {
            Log.i("zzz", "Starting Mock GPSThread");
            Running = true;

            LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

            addSetProvider(locationManager,"gps");
            addSetProvider(locationManager,"network");
            while (Running) {
                setPLocation(locationManager ,"gps", curLat, curLong);
                setPLocation(locationManager ,"network", curLat, curLong);
                try {
                    Thread.sleep(TIME_UPDATES_MS);
                } catch (Exception e) {
                    Running = false;
                    break;
                }
                curLat = curLat + 0.01;
            }
            delProvider(locationManager, "gps");
            delProvider(locationManager, "network");
            Log.i("zzz", "Ending Mock GPSThread");
        }
    }

    private void addSetProvider(LocationManager locationManager,String provider) {
        locationManager.addTestProvider(provider, false, false, false, false, false, false, false, 1, 1);
        if( !locationManager.isProviderEnabled(provider) )
            locationManager.setTestProviderEnabled(provider, true);
    }
    public void setPLocation(LocationManager locationManager,String provider, double curLat, double curLong) {
        Location loc = new Location(provider);
        loc.setTime(System.currentTimeMillis());
        loc.setLatitude(curLat);
        loc.setLongitude(curLong);
        loc.setBearing(0);
        loc.setAccuracy(1.0f);
        loc.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
        loc.setSpeed((float) 0);
        locationManager.setTestProviderLocation(provider, loc);
    }
    public void delProvider(LocationManager locationManager, String provider) {
        if( locationManager.isProviderEnabled(provider) )
            locationManager.setTestProviderEnabled(provider, false);
        locationManager.removeTestProvider(provider);
    }
    private void startMockGps() {
        if( thread == null ) {
            thread = new MockUpdateGPSThread();
            thread.start();
        }
    }
    private void stopMockGps() {
        if( thread!= null) {
            thread.interrupt();
        }
    }
}


AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.xxxx.mockgps">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
</manifest>




-------------------------------------------------------------------

Fake GPS


이 글은 안드로이드 6.0 M OS 에서 테스트 되어졌습니다.

Fake 모드란 App에서 Mock인지 속인다는 의미에서 이름을 지었습니다. 대부분 fake gps앱들이 아래형태로 제작되어지고 있고, 안드로이드 버전에 따라 동작이 안 될 수도 있습니다.
그 이유는 버그를 이용하기 때문입니다.
아래가 핵심 부분입니다.
provider는 network을 만들고 setTestProviderLocation 함수 인자를 넘겨줄때 provider는 network을 넘겨주지만 Location으로 만든 Provider는 GPS를 넘겨주는 방법을 사용합니다.
아래 코드 참조
addSetProvider(locationManager,"network");
setFakeLocation(locationManager ,"gps","network", curLat, curLong);

    public void setFakeLocation(LocationManager locationManager,String provider1, String provider2, double curLat, double curLong) {
        Location loc = new Location("");
        loc.setProvider(provider1);
        loc.setTime(System.currentTimeMillis());
        loc.setLatitude(curLat);
        loc.setLongitude(curLong);
        loc.setBearing(0);
        loc.setAccuracy(1.0f);
        loc.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
        loc.setSpeed((float) 0);
        locationManager.setTestProviderLocation(provider2, loc);
    }

그리고 제대로 사용하려면 network에서 위치정보 신호가 들어오면 안됩니다. 왜냐하면 network은 test provider설정이 되어있기 때문입니다. 따라서 GPS 환경 설정에서 위치 인식 방법중 GPS만 사용 설정을 해야합니다. 또한 GPS쪽으로도 위치정보 신호가 들어오면 안됩니다. 순간 이동을 하게 됩니다.





여기에 있는 소스들 실행시 실행후 취소키를 누르게 되면 앱이 종료됩니다.
홈키를 눌러 thread가 살아 있을수 있게 해서 테스트 해야 합니다.

동작 화면


Fake 모드 소스

package com.example.xxxx.mockgps;

import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;


public class MainActivity extends AppCompatActivity {
    Button btn1;
    Button btn2;
    Button btn3;
    Button btn4;
    EditText editText;
    MockUpdateGPSThread thread;
    FakeUpdateGPSThread fakethread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setupButton();

    }
    private void setupButton() {
        editText = (EditText) findViewById(R.id.editText);
        btn1 = (Button) findViewById(R.id.button);
        btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startMockGps();
                editText.append("Start\n");
            }
        });
        btn2 = (Button) findViewById(R.id.button2);
        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                stopMockGps();
                editText.append("Stop\n");
            }
        });
        btn3 = (Button) findViewById(R.id.button3);
        btn3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startFakeGps();
                editText.append("Fake Start\n");
            }
        });
        btn4 = (Button) findViewById(R.id.button4);
        btn4.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                stopFakeGps();
                editText.append("Fake Stop\n");
            }
        });
    }


    class MockUpdateGPSThread extends Thread {
        final int TIME_UPDATES_MS = 5000;
        public boolean Running;
        private double curLat = 34.526456 , curLong = 127.3298;

        @Override
        public void run() {
            Log.i("zzz", "Starting Mock GPSThread");
            Running = true;

            LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

            addSetProvider(locationManager,"gps");
            addSetProvider(locationManager,"network");
            while (Running) {
                setPLocation(locationManager ,"gps", curLat, curLong);
                setPLocation(locationManager ,"network", curLat, curLong);
                try {
                    Thread.sleep(TIME_UPDATES_MS);
                } catch (Exception e) {
                    Running = false;
                    break;
                }
                curLat = curLat + 0.01;
            }
            delProvider(locationManager, "gps");
            delProvider(locationManager, "network");
            Log.i("zzz", "Ending Mock GPSThread");
        }
    }



    private void addSetProvider(LocationManager locationManager,String provider) {
        locationManager.addTestProvider(provider, false, false, false, false, false, false, false, 1, 1);
        if( !locationManager.isProviderEnabled(provider) )
            locationManager.setTestProviderEnabled(provider, true);
    }
    public void setPLocation(LocationManager locationManager,String provider, double curLat, double curLong) {
        Location loc = new Location(provider);
        loc.setTime(System.currentTimeMillis());
        loc.setLatitude(curLat);
        loc.setLongitude(curLong);
        loc.setBearing(0);
        loc.setAccuracy(1.0f);
        loc.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
        loc.setSpeed((float) 0);
        locationManager.setTestProviderLocation(provider, loc);
    }
    public void delProvider(LocationManager locationManager, String provider) {
        if( locationManager.isProviderEnabled(provider) )
            locationManager.setTestProviderEnabled(provider, false);
        locationManager.removeTestProvider(provider);
    }
    private void startMockGps() {
        if( thread == null ) {
            thread = new MockUpdateGPSThread();
            thread.start();
        }
    }
    private void stopMockGps() {
        if( thread!= null) {
            thread.interrupt();
        }
    }

    class FakeUpdateGPSThread extends Thread {
        final int TIME_UPDATES_MS = 5000;
        public boolean Running;
        private double curLat = 34.526456 , curLong = 127.3298;

        @Override
        public void run() {
            Log.i("zzz", "Starting fake GPSThread");
            Running = true;

            LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

            addSetProvider(locationManager,"network");
            while (Running) {
                setFakeLocation(locationManager ,"gps","network", curLat, curLong);
                try {
                    Thread.sleep(TIME_UPDATES_MS);
                } catch (Exception e) {
                    Running = false;
                    break;
                }
                curLat = curLat + 0.01;
            }
            delProvider(locationManager, "network");
            Log.i("zzz", "Ending fake GPSThread");
        }
    }
    public void setFakeLocation(LocationManager locationManager,String provider1, String provider2, double curLat, double curLong) {
        Location loc = new Location("");
        loc.setProvider(provider1);
        loc.setTime(System.currentTimeMillis());
        loc.setLatitude(curLat);
        loc.setLongitude(curLong);
        loc.setBearing(0);
        loc.setAccuracy(1.0f);
        loc.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
        loc.setSpeed((float) 0);
        locationManager.setTestProviderLocation(provider2, loc);
    }
    private void startFakeGps() {
        if( fakethread == null ) {
            fakethread = new FakeUpdateGPSThread();
            fakethread.start();
        }
    }
    private void stopFakeGps() {
        if( fakethread!= null) {
            fakethread.interrupt();
        }
    }

}

여기에 사용된 좌표는 임의의 좌표입니다.


2017년 2월 5일 일요일

gps example in android (안드로이드에서 gps 사용하기 예제)

Android GPS, location manager


기본 소스는 아래 링크를 참고하였습니다.
http://www.androidhive.info/2012/07/android-gps-location-manager-tutorial/
그러나 소스가 오래된거라 빌드가 안되어서 약간 손 본 부분과 분석하기위한 로그를 추가하였습니다.

GPSTracker.java 예제는 service를 상속 받았지만 실제 구현은 Service형태의 예제는 아니고 Activity를 종료시키면 같이 종료되는 형태로 작업이 되었습니다.

아래 예제는 LocationListener의 onLocationChanged, onProviderDisabled, onProviderEnabled, onStatusChanged 의 의미와 발생에 대해서 알아보도록 하겠습니다.

아래 소스는 Android 6.0 M OS 에서 테스트 되었습니다.

0. 전체적인 이해

https://developer.android.com/guide/topics/location/strategies.html

1. 권한

ACCESS_FINE_LOCATION(android.permission.ACCESS_FINE_LOCATION) 권한이 필요합니다. 이 권한은 GPS로 부터 정확한 위치를 얻기위한 것입니다.
ACCESS_COARSE_LOCATION 은 네트워크로부터 위치를 얻기위한 permission인데 FINE location 권한을 획득하면 포함하게됩니다.

Main 소스에서는 아래와 같은 runtime permission을 획득 하도록 하였습니다.
        if ( Build.VERSION.SDK_INT >= 23 &&
                ContextCompat.checkSelfPermission( this, android.Manifest.permission.ACCESS_FINE_LOCATION ) != PackageManager.PERMISSION_GRANTED ) {
            ActivityCompat.requestPermissions( this, new String[] {  android.Manifest.permission.ACCESS_FINE_LOCATION  },
                    0 );
        }

AndroidManifest.xml
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

2. Provider
위치 정보를 주는 곳이 두군데 있습니다.
위성을 이용하는 방식이 GPS_PROVIDER 이고 Network을 이용하는게 NETWORK_PROVIDER 입니다.
location manager 서비스를 이용해서 위치가 변경되거나 또는 주기적으로 위치정보가 업데이트 될때 정보를 보내도록 요청합니다.
해당코드는 아래와 같습니다. 이때 어떤 provider를 사용할지 인자로 넘기게 됩니다.

Network provider
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
MIN_TIME_BW_UPDATES,
MIN_DISTANCE_CHANGE_FOR_UPDATES, this);

GPS provider
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
MIN_TIME_BW_UPDATES,
MIN_DISTANCE_CHANGE_FOR_UPDATES, this);

그외에 인자로 넘어가는 아래값은 언제 업데이트 할지를 결정하는 인자입니다. 해당값에 따라서 거리가 변경되거나 일정 시간이 흐르거나 하면 위치정보를 받을 수 있습니다.
    // The minimum distance to change Updates in meters
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters

    // The minimum time between updates in milliseconds
    private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1; // 1 minute

지금 당장 위치를 구하려고 한다면 location provider의 마지막 location 정보를 이용해서 현재 좌표를 얻기위한 방법입니다.
                            location = locationManager
                                    .getLastKnownLocation(LocationManager.GPS_PROVIDER);
                            if (location != null) {
                                latitude = location.getLatitude();
                                longitude = location.getLongitude();
                            }

이후로는 주기적으로 아래 listener로 함수가 호출됩니다.

    @Override
    public void onLocationChanged(Location location) {
        if(location != null){
            double latitude= location.getLatitude();
            double longitude = location.getLongitude();
            //Toast.makeText(mContext, "onLocationChanged is - \nLat: " + latitude + "\nLong: " + longitude, Toast.LENGTH_SHORT).show();
            sendString("onLocationChanged is - \nLat: " + latitude + "\nLong: " + longitude + " provider:"+location.getProvider()+" mock:"+location.isFromMockProvider());
        }
    }




Provider enable disable 은 설정에서 provider 설정을 변경하면 호출됩니다.
여기에서는 GPS update설정을 변경하도록 구현하였습니다.
    @Override
    public void onProviderDisabled(String provider) {
        //Toast.makeText(mContext, "onProviderDisabled " + provider, Toast.LENGTH_SHORT).show();
        mHandler.sendEmptyMessage(MainActivity.RENEW_GPS);
        sendString( "onProviderDisabled " + provider);
    }

    @Override
    public void onProviderEnabled(String provider) {
        //Toast.makeText(mContext, "onProviderEnabled " + provider, Toast.LENGTH_SHORT).show();
        mHandler.sendEmptyMessage(MainActivity.RENEW_GPS);
        sendString( "onProviderEnabled " + provider);
    }
뒤쪽 provider의 String은 gps, network 등이 출력됩니다. 아래 참고


참고로 location.isFromMockProvider 값을 출력해봤습니다. mock 위치라고 해서 GPS조작앱을 통한 값인지 여부를 판단할 수 있을지 알았는데요.
조작앱을 설치해서 실제 테스트해보니 항상 false값이 확인이 됩니다.
이 부분은 fake GPS 앱들이 특수한 방법으로 true가 안되게 하는것 같습니다. 기회가 닿으면 따로 분석 해보도록 하겠습니다.
http://swlock.blogspot.com/2017/02/using-mock-gps-mock-gps-gps-in-android.html

소스에서 onLocationChanged 될때 Bundle 값을 출력시켜 봤는데요.
satellites 숫자가 나오는데 이건 위성의 숫자라고 합니다.

3. 전체 소스는 아래와 같습니다.



MainActivity
package com.example.xxxx.gpstest;

import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.text.SimpleDateFormat;
import java.util.Date;

// http://www.androidhive.info/2012/07/android-gps-location-manager-tutorial/

public class MainActivity extends AppCompatActivity {
    Button btnShowLocation;
    EditText editText;

    // GPSTracker class
    GPSTracker gps = null;

    public Handler mHandler;

    public static int RENEW_GPS = 1;
    public static int SEND_PRINT = 2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);
        if ( Build.VERSION.SDK_INT >= 23 &&
                ContextCompat.checkSelfPermission( this, android.Manifest.permission.ACCESS_FINE_LOCATION ) != PackageManager.PERMISSION_GRANTED ) {
            ActivityCompat.requestPermissions( this, new String[] {  android.Manifest.permission.ACCESS_FINE_LOCATION  },
                    0 );
        }

        editText = (EditText) findViewById(R.id.editText);
        btnShowLocation = (Button) findViewById(R.id.btnShowLocation);

        mHandler = new Handler(){
            @Override
            public void handleMessage(Message msg){
                if(msg.what==RENEW_GPS){
                    makeNewGpsService();
                }
                if(msg.what==SEND_PRINT){
                    logPrint((String)msg.obj);
                }
            }
        };

        // show location button click event
        btnShowLocation.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                // create class object
                if(gps == null) {
                    gps = new GPSTracker(MainActivity.this,mHandler);
                }else{
                    gps.Update();
                }

                // check if GPS enabled
                if(gps.canGetLocation()){
                    double latitude = gps.getLatitude();
                    double longitude = gps.getLongitude();
                    // \n is for new line
                    Toast.makeText(getApplicationContext(), "Your Location is - \nLat: " + latitude + "\nLong: " + longitude, Toast.LENGTH_LONG).show();
                }else{
                    // can't get location
                    // GPS or Network is not enabled
                    // Ask user to enable GPS/network in settings
                    gps.showSettingsAlert();
                }
            }
        });
    }
    public void makeNewGpsService(){
        if(gps == null) {
            gps = new GPSTracker(MainActivity.this,mHandler);
        }else{
            gps.Update();
        }

    }
    public void logPrint(String str){
        editText.append(getTimeStr()+" "+str+"\n");
    }
    public String getTimeStr(){
        long now = System.currentTimeMillis();
        Date date = new Date(now);
        SimpleDateFormat sdfNow = new SimpleDateFormat("MM/dd HH:mm:ss");
        return sdfNow.format(date);
    }
}



GPSTracker.java
package com.example.xxxx.gpstest;

import android.Manifest;
import android.app.AlertDialog;
import android.app.Service;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.widget.Toast;

import java.util.Set;

/**
 * Created by xxxx on 2017-02-04.
 */
public class GPSTracker extends Service implements LocationListener {

    private final Context mContext;

    // flag for GPS status
    boolean isGPSEnabled = false;

    // flag for network status
    boolean isNetworkEnabled = false;

    // flag for GPS status
    boolean canGetLocation = false;

    Location location; // location
    double latitude; // latitude
    double longitude; // longitude

    // The minimum distance to change Updates in meters
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters

    // The minimum time between updates in milliseconds
    private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1; // 1 minute

    // Declaring a Location Manager
    protected LocationManager locationManager;

    private Handler mHandler;

    public GPSTracker(Context context, Handler handler) {
        this.mContext = context;
        this.mHandler = handler;
        getLocation();
    }
    public void Update(){
        getLocation();
    }
    public Location getLocation() {

        if ( Build.VERSION.SDK_INT >= 23 &&
                ContextCompat.checkSelfPermission( mContext, android.Manifest.permission.ACCESS_FINE_LOCATION ) != PackageManager.PERMISSION_GRANTED ) {
            return null;
        }
        try {
            locationManager = (LocationManager) mContext
                    .getSystemService(LOCATION_SERVICE);

            // getting GPS status
            isGPSEnabled = locationManager
                    .isProviderEnabled(LocationManager.GPS_PROVIDER);

            // getting network status
            isNetworkEnabled = locationManager
                    .isProviderEnabled(LocationManager.NETWORK_PROVIDER);

            if (!isGPSEnabled && !isNetworkEnabled) {
                // no network provider is enabled
            } else {
                this.canGetLocation = true;
                // First get location from Network Provider
                if (isNetworkEnabled) {
                    locationManager.requestLocationUpdates(
                            LocationManager.NETWORK_PROVIDER,
                            MIN_TIME_BW_UPDATES,
                            MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
                    Log.d("Network", "Network");
                    if (locationManager != null) {
                        location = locationManager
                                .getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                        if (location != null) {
                            latitude = location.getLatitude();
                            longitude = location.getLongitude();
                        }
                    }
                }
                // if GPS Enabled get lat/long using GPS Services
                if (isGPSEnabled) {
                    if (location == null) {
                        locationManager.requestLocationUpdates(
                                LocationManager.GPS_PROVIDER,
                                MIN_TIME_BW_UPDATES,
                                MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
                        Log.d("GPS Enabled", "GPS Enabled");
                        if (locationManager != null) {
                            location = locationManager
                                    .getLastKnownLocation(LocationManager.GPS_PROVIDER);
                            if (location != null) {
                                latitude = location.getLatitude();
                                longitude = location.getLongitude();
                            }
                        }
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return location;
    }

    /**
     * Stop using GPS listener
     * Calling this function will stop using GPS in your app
     * */
    public void stopUsingGPS(){
        if ( Build.VERSION.SDK_INT >= 23 &&
                ContextCompat.checkSelfPermission( mContext, android.Manifest.permission.ACCESS_FINE_LOCATION ) != PackageManager.PERMISSION_GRANTED ) {
            return;
        }
        if(locationManager != null){
            locationManager.removeUpdates(GPSTracker.this);
        }
    }

    /**
     * Function to get latitude
     * */
    public double getLatitude(){
        if(location != null){
            latitude = location.getLatitude();
        }

        // return latitude
        return latitude;
    }

    /**
     * Function to get longitude
     * */
    public double getLongitude(){
        if(location != null){
            longitude = location.getLongitude();
        }

        // return longitude
        return longitude;
    }

    /**
     * Function to check GPS/wifi enabled
     * @return boolean
     * */
    public boolean canGetLocation() {
        return this.canGetLocation;
    }

    /**
     * Function to show settings alert dialog
     * On pressing Settings button will lauch Settings Options
     * */
    public void showSettingsAlert(){
        AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);

        // Setting Dialog Title
        alertDialog.setTitle("GPS is settings");

        // Setting Dialog Message
        alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu?");

        // On pressing Settings button
        alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog,int which) {
                Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                mContext.startActivity(intent);
            }
        });

        // on pressing cancel button
        alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
            }
        });

        // Showing Alert Message
        alertDialog.show();
    }

    @Override
    public void onLocationChanged(Location location) {
        if(location != null){
            double latitude= location.getLatitude();
            double longitude = location.getLongitude();
            //Toast.makeText(mContext, "onLocationChanged is - \nLat: " + latitude + "\nLong: " + longitude, Toast.LENGTH_SHORT).show();
            sendString("onLocationChanged is - \nLat: " + latitude + "\nLong: " + longitude + " provider:"+location.getProvider()+" mock:"+location.isFromMockProvider());
        }
    }

    @Override
    public void onProviderDisabled(String provider) {
        //Toast.makeText(mContext, "onProviderDisabled " + provider, Toast.LENGTH_SHORT).show();
        mHandler.sendEmptyMessage(MainActivity.RENEW_GPS);
        sendString( "onProviderDisabled " + provider);
    }

    @Override
    public void onProviderEnabled(String provider) {
        //Toast.makeText(mContext, "onProviderEnabled " + provider, Toast.LENGTH_SHORT).show();
        mHandler.sendEmptyMessage(MainActivity.RENEW_GPS);
        sendString( "onProviderEnabled " + provider);
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
        //Toast.makeText(mContext, "onStatusChanged " + provider + " : " + status, Toast.LENGTH_SHORT).show();
        mHandler.sendEmptyMessage(MainActivity.RENEW_GPS);
        sendString("onStatusChanged " + provider + " : " + status + ":" + printBundle(extras));
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

    private void sendString(String str){
        Message msg = mHandler.obtainMessage();
        msg.what = MainActivity.SEND_PRINT;
        msg.obj = new String(str);
        mHandler.sendMessage(msg);
    }

    public static String printBundle(Bundle extras) {
        StringBuilder sb = new StringBuilder();
        try {
                sb.append("extras = " + extras);
                sb.append("\n");
                if (extras != null) {
                    Set keys = extras.keySet();
                    sb.append("++ bundle key count = " + keys.size());
                    sb.append("\n");

                    for (String _key : extras.keySet()) {
                        sb.append("key=" + _key + " : " + extras.get(_key)+",");
                    }
                    sb.append("\n");
                }
        } catch (Exception e) {

        } finally {

        }
        return sb.toString();
    }

}



AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.xxxx.gpstest">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>


activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.darts.gpstest.MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="START"
        android:id="@+id/btnShowLocation"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true" />

    <EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/editText"
        android:layout_below="@+id/btnShowLocation"
        android:layout_alignParentBottom="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentEnd="true"
        android:editable="false"
        android:textSize="10dp"
        android:text="log print here\n" />
</RelativeLayout>


소스
https://drive.google.com/open?id=0B9vAKDzHthQIenZKLU1JeV9SY2c

APK
https://drive.google.com/open?id=0B9vAKDzHthQIOWhydXRGTjV6cWs