所以我在 github 上运行这个示例应用程序,它处理谷歌地图和地理围栏:https ://github.com/androidfu/GeofenceExample
当我运行代码时,我得到了这个错误: Caused by: java.lang.SecurityException: com.androidfu.example.geofences from uid 10170 not allowed to perform MOCK_LOCATION
这是该类的代码,后面是堆栈跟踪。希望大家能给我一些见解。我也在清单中有模拟位置。错误来自 onResume() 方法。
public class MapsActivity extends FragmentActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, View.OnClickListener, ResultCallback<Status> {
public static final String TAG = MapsActivity.class.getSimpleName();
private static final long LOCATION_ITERATION_PAUSE_TIME = 1000;
private static final int NUMBER_OF_LOCATION_ITERATIONS = 10;
private GoogleMap googleMap; // Might be null if Google Play services APK is not available.
private MyPlaces happyPlace;
private MyPlaces home;
private List<Geofence> myFences = new ArrayList<>();
private GoogleApiClient googleApiClient;
private PendingIntent geofencePendingIntent;
private UpdateLocationRunnable updateLocationRunnable;
private LocationManager locationManager;
private int marker = 0;
private Location lastLocation;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
ImageButton happyPlaceBtn = (ImageButton) findViewById(R.id.ib_happy_place);
happyPlaceBtn.setOnClickListener(this);
ImageButton homeBtn = (ImageButton) findViewById(R.id.ib_home);
homeBtn.setOnClickListener(this);
ImageButton resetBtn = (ImageButton) findViewById(R.id.ib_reset);
resetBtn.setOnClickListener(this);
setUpMapIfNeeded();
}
/**
* Called when a view has been clicked.
*
* @param v The view that was clicked.
*/
@Override
public void onClick(View v) {
MyPlaces place;
switch (v.getId()) {
case R.id.ib_happy_place:
Toast.makeText(this, "You Clicked Happy Place", Toast.LENGTH_SHORT).show();
place = happyPlace;
moveToLocation(place);
break;
case R.id.ib_home:
Toast.makeText(this, "You Clicked Home", Toast.LENGTH_SHORT).show();
place = home;
moveToLocation(place);
break;
case R.id.ib_reset:
Toast.makeText(this, "Resetting Our Map", Toast.LENGTH_SHORT).show();
if (updateLocationRunnable != null) {
updateLocationRunnable.interrupt();
}
googleApiClient.disconnect();
googleMap.clear();
myFences.clear();
setUpMap();
break;
}
}
@Override
protected void onResume() {
super.onResume();
setUpMapIfNeeded();
this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
Log.i(TAG, "Setup MOCK Location Providers");
locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
Log.i(TAG, "GPS Provider");
locationManager.addTestProvider(LocationManager.GPS_PROVIDER, false, true, false, false, false, false, false, Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
locationManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true);
Log.i(TAG, "Network Provider");
locationManager.addTestProvider(LocationManager.NETWORK_PROVIDER, true, false, true, false, false, false, false, Criteria.POWER_MEDIUM, Criteria.ACCURACY_FINE);
locationManager.setTestProviderEnabled(LocationManager.NETWORK_PROVIDER, true);
}
@Override
protected void onPause() {
this.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// Interrupt our runnable if we're going into the background or exiting
if (updateLocationRunnable != null) {
updateLocationRunnable.interrupt();
}
Log.i(TAG, "Cleanup Our Fields");
locationManager.removeTestProvider(LocationManager.GPS_PROVIDER);
locationManager.removeTestProvider(LocationManager.NETWORK_PROVIDER);
locationManager = null;
updateLocationRunnable = null;
super.onPause();
}
@Override
protected void onStop() {
googleApiClient.disconnect();
super.onStop();
}
/**
* Sets up the map if it is possible to do so (i.e., the Google Play services APK is correctly
* installed) and the map has not already been instantiated.. This will ensure that we only ever
* call {@link #setUpMap()} once when {@link #googleMap} is not null.
* <p/>
* If it isn't installed {@link SupportMapFragment} (and
* {@link com.google.android.gms.maps.MapView MapView}) will show a prompt for the user to
* install/update the Google Play services APK on their device.
* <p/>
* A user can return to this FragmentActivity after following the prompt and correctly
* installing/updating/enabling the Google Play services. Since the FragmentActivity may not
* have been completely destroyed during this process (it is likely that it would only be
* stopped or paused), {@link #onCreate(Bundle)} may not be called again so we should call this
* method in {@link #onResume()} to guarantee that it will be called.
*/
private void setUpMapIfNeeded() {
// Do a null check to confirm that we have not already instantiated the map.
if (googleMap == null) {
// Try to obtain the map from the SupportMapFragment.
googleMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
// Check if we were successful in obtaining the map.
if (googleMap != null) {
setUpMap();
}
}
}
/**
* This is where we can add markers or lines, add listeners or move the camera.
* <p/>
* This should only be called once and when we are sure that {@link #googleMap} is not null.
*/
private void setUpMap() {
googleMap.setBuildingsEnabled(true);
// PRES 1
/*
1. Create a "Place" that will become a Geofence
2. Add a place marker on our Map
3. Add our place to our list of Geofences
4. Repeat for each place
*/
// Add a place with a Geofence
happyPlace = new MyPlaces("Pier @ Folly Beach", "This is my Happy Place!", new LatLng(32.652411, -79.938063), 10000, 10, R.drawable.ic_palm_tree);
addPlaceMarker(happyPlace);
addFence(happyPlace);
// Add a place with a Geofence
// Work 39.3336585, -84.3146718
// Home 39.2697455, -84.269921
home = new MyPlaces("Home", "This is where I live.", new LatLng(39.3336585, -84.3146718), 10000, 10, R.drawable.ic_home);
addPlaceMarker(home);
addFence(home);
// Add a place w/o a Geofence
MyPlaces charleston = new MyPlaces("Charleston, SC", "This is where I want to live!", new LatLng(32.8210454, -79.9704779), 0, 10, R.drawable.ic_heart);
addPlaceMarker(charleston);
addFence(charleston);
/*
After all your places have been created and markers added you can monitor your fences.
*/
monitorFences(myFences);
googleMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
@Override
public void onMapClick(LatLng latLng) {
if (updateLocationRunnable != null && updateLocationRunnable.isAlive() && !updateLocationRunnable.isInterrupted()) {
updateLocationRunnable.interrupt();
}
updateLocationRunnable = new UpdateLocationRunnable(locationManager, latLng);
updateLocationRunnable.start();
MyPlaces touchedPlace = new MyPlaces(String.format("Marker %1$d", ++marker), "", latLng, 65, 12, 0);
addPlaceMarker(touchedPlace);
}
});
}
/**
* Add a map marker at the place specified.
*
* @param place the place to take action on
*/
private void addPlaceMarker(MyPlaces place) {
MarkerOptions markerOptions = new MarkerOptions();
markerOptions.position(place.getCoordinates())
.title(place.getTitle());
if (!TextUtils.isEmpty(place.getSnippet())) {
markerOptions.snippet(place.getSnippet());
}
if (place.getIconResourceId() > 0) {
markerOptions.icon(BitmapDescriptorFactory.fromResource(place.getIconResourceId()));
}
googleMap.addMarker(markerOptions);
drawGeofenceAroundTarget(place);
}
/**
* If our place has a fence radius greater than 0 then draw a circle around it.
*
* @param place the place to take action on
*/
private void drawGeofenceAroundTarget(MyPlaces place) {
if (place.getFenceRadius() <= 0) {
// Nothing to draw
return;
}
CircleOptions circleOptions = new CircleOptions();
circleOptions.center(place.getCoordinates());
circleOptions.fillColor(Color.argb(0x55, 0x00, 0x00, 0xff));
circleOptions.strokeColor(Color.argb(0xaa, 0x00, 0x00, 0xff));
circleOptions.radius(place.getFenceRadius());
googleMap.addCircle(circleOptions);
}
/**
* Update our map's location to the place specified.
*
* @param place the place to take action on
*/
private void moveToLocation(final MyPlaces place) {
// Move the camera instantly to "place" with a zoom of 5.
if (place.getTitle().equals("Charleston, SC")) {
googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(place.getCoordinates(), place.getDefaultZoomLevel()));
}
// Fly to our new location and then set the correct zoom level for the given place.
googleMap.animateCamera(CameraUpdateFactory.newLatLng(place.getCoordinates()), new GoogleMap.CancelableCallback() {
@Override
public void onFinish() {
googleMap.animateCamera(CameraUpdateFactory.zoomTo(place.getDefaultZoomLevel()), 2000, null);
}
@Override
public void onCancel() {
// Nothing to see here.
}
});
}
/**
* If our place has a fence radius > 0 then add it to our monitored fences.
*
* @param place the place to take action on
*/
private void addFence(MyPlaces place) {
if (place.getFenceRadius() <= 0) {
// Nothing to monitor
return;
}
Geofence geofence = new Geofence.Builder()
.setCircularRegion(place.getCoordinates().latitude, place.getCoordinates().longitude, place.getFenceRadius())
.setRequestId(place.getTitle()) // every fence must have an ID
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT) // can also have DWELL
.setExpirationDuration(Geofence.NEVER_EXPIRE) // how long do we care about this geofence?
//.setLoiteringDelay(60000) // 1 min.
.build();
myFences.add(geofence);
}
/**
* Connect our GoogleApiClient so we can begin monitoring our fences.
*
* @param fences our list of Geofences to monitor
*/
private void monitorFences(List<Geofence> fences) {
if (fences.isEmpty()) {
throw new RuntimeException("No fences to monitor. Call addPlaceMarker() First.");
}
// PRES 2
googleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
googleApiClient.connect();
}
@Override
public void onConnected(Bundle bundle) {
/*
TODO
1. Display a spinner in the progress bar while we're waiting for location
2. When connected & not null update map position to location
3. If location null try again once every 10 seconds until we get an answer or quit after x minutes
4. ?
*/
Toast.makeText(this, "GoogleApiClient Connected", Toast.LENGTH_SHORT).show();
lastLocation = LocationServices.FusedLocationApi.getLastLocation(googleApiClient);
String lastLocationMessage;
if (lastLocation == null) {
lastLocationMessage = "Last Location is NULL";
moveToLocation(home);
} else {
lastLocationMessage = String.format("Last Location (%1$s, %2$s)", lastLocation.getLatitude(), lastLocation.getLongitude());
moveToLocation(new MyPlaces("Last Location", "I am here.", new LatLng(lastLocation.getLatitude(), lastLocation.getLongitude()), 0, 13, 0));
}
Toast.makeText(this, lastLocationMessage, Toast.LENGTH_SHORT).show();
// PRES 3
geofencePendingIntent = getRequestPendingIntent();
PendingResult<Status> result = LocationServices.GeofencingApi.addGeofences(googleApiClient, myFences, geofencePendingIntent);
result.setResultCallback(this);
}
@Override
public void onConnectionSuspended(int i) {
Toast.makeText(this, "GoogleApiClient Connection Suspended", Toast.LENGTH_SHORT).show();
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Toast.makeText(this, "GoogleApiClient Connection Failed", Toast.LENGTH_SHORT).show();
}
@Override
public void onResult(Status status) {
String toastMessage;
// PRES 4
if (status.isSuccess()) {
toastMessage = "Success: We Are Monitoring Our Fences";
} else {
toastMessage = "Error: We Are NOT Monitoring Our Fences";
}
Toast.makeText(this, toastMessage, Toast.LENGTH_SHORT).show();
}
/**
* Returns the current PendingIntent to the caller.
*
* @return The PendingIntent used to create the current set of geofences
*/
public PendingIntent getRequestPendingIntent() {
return createRequestPendingIntent();
}
/**
* Get a PendingIntent to send with the request to add Geofences. Location
* Services issues the Intent inside this PendingIntent whenever a geofence
* transition occurs for the current list of geofences.
*
* @return A PendingIntent for the IntentService that handles geofence
* transitions.
*/
private PendingIntent createRequestPendingIntent() {
if (geofencePendingIntent != null) {
return geofencePendingIntent;
} else {
Intent intent = new Intent(this, GeofenceTransitionReceiver.class);
intent.setAction("geofence_transition_action");
return PendingIntent.getBroadcast(this, R.id.geofence_transition_intent, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
}
// /////////////////////////////////////////////////////////////////////////////////////////
// // UpdateLocationRunnable //
// /////////////////////////////////////////////////////////////////////////////////////////
private Location createMockLocation(String locationProvider, double latitude, double longitude) {
Location location = new Location(locationProvider);
location.setLatitude(latitude);
location.setLongitude(longitude);
location.setAccuracy(1.0f);
location.setTime(System.currentTimeMillis());
/*
setElapsedRealtimeNanos() was added in API 17
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
}
try {
Method locationJellyBeanFixMethod = Location.class.getMethod("makeComplete");
if (locationJellyBeanFixMethod != null) {
locationJellyBeanFixMethod.invoke(location);
}
} catch (Exception e) {
// There's no action to take here. This is a fix for Jelly Bean and no reason to report a failure.
}
return location;
}
// /////////////////////////////////////////////////////////////////////////////////////////
// // CreateMockLocation //
// /////////////////////////////////////////////////////////////////////////////////////////
class UpdateLocationRunnable extends Thread {
private final LocationManager locMgr;
private final LatLng latlng;
Location mockGpsLocation;
Location mockNetworkLocation;
UpdateLocationRunnable(LocationManager locMgr, LatLng latlng) {
this.locMgr = locMgr;
this.latlng = latlng;
}
/**
* Starts executing the active part of the class' code. This method is
* called when a thread is started that has been created with a class which
* implements {@code Runnable}.
*/
@Override
public void run() {
try {
Log.i(TAG, String.format("Setting Mock Location to: %1$s, %2$s", latlng.latitude, latlng.longitude));
/*
Location can be finicky. Iterate over our desired location every second for
NUMBER_OF_LOCATION_ITERATIONS seconds to help it figure out where we want it to
be.
*/
for (int i = 0; !isInterrupted() && i <= NUMBER_OF_LOCATION_ITERATIONS; i++) {
mockGpsLocation = createMockLocation(LocationManager.GPS_PROVIDER, latlng.latitude, latlng.longitude);
locMgr.setTestProviderLocation(LocationManager.GPS_PROVIDER, mockGpsLocation);
mockNetworkLocation = createMockLocation(LocationManager.NETWORK_PROVIDER, latlng.latitude, latlng.longitude);
locMgr.setTestProviderLocation(LocationManager.NETWORK_PROVIDER, mockNetworkLocation);
Thread.sleep(LOCATION_ITERATION_PAUSE_TIME);
}
} catch (InterruptedException e) {
Log.i(TAG, "Interrupted.");
// Do nothing. We expect this to happen when location is successfully updated.
} finally {
Log.i(TAG, "Done moving location.");
}
}
}
}
堆栈跟踪:
09-26 11:08:07.870 10417-10417/com.androidfu.example.geofences E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.androidfu.example.geofences, PID: 10417
java.lang.RuntimeException: Unable to resume activity {com.androidfu.example.geofences/com.androidfu.example.geofences.GeofenceExampleLauncher}: java.lang.SecurityException: com.androidfu.example.geofences from uid 10170 not allowed to perform MOCK_LOCATION
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3121)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3152)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2499)
at android.app.ActivityThread.access$900(ActivityThread.java:157)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1356)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5525)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:730)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:620)
Caused by: java.lang.SecurityException: com.androidfu.example.geofences from uid 10170 not allowed to perform MOCK_LOCATION
at android.os.Parcel.readException(Parcel.java:1599)
at android.os.Parcel.readException(Parcel.java:1552)
at android.location.ILocationManager$Stub$Proxy.addTestProvider(ILocationManager.java:1096)
at android.location.LocationManager.addTestProvider(LocationManager.java:1298)
at com.androidfu.example.geofences.MapsActivity.onResume(MapsActivity.java:120)
at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1258)
at android.app.Activity.performResume(Activity.java:6347)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3110)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3152)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2499)
at android.app.ActivityThread.access$900(ActivityThread.java:157)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1356)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5525)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:730)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:620)