Android Geofencing - No coming intents?

2019-04-07 12:23发布

问题:

I have a strange problem.

I implemented geofencing using Google Services. (Implementation below) On my devices (Samsung Galaxy S and Moto X) they work perfect. On some other devices (HTC Incredible S, Galaxy Note), I receive no transition intents. Never. With great, accurate GPS fix, standing in the middle of fence. Nothing. Nothing suspicious in logs. No errors, no warnings. No itents come, the service does not start.

Have anyone ever seen such a thing? (It is weird because I can't see any connection between devices which work and devices that does not. It's not manufacturer, it's not Android version, Play Services were up to date on all of them.)

Implementation:

Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="test.package"
android:installLocation="auto"
android:versionCode="17"
android:versionName="1.1" >

<uses-sdk
    android:minSdkVersion="7"
    android:targetSdkVersion="19" />

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="true" />


<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >

    <activity
        android:name="test.package.MainActivity"
        android:label="Compass"
        android:screenOrientation="portrait" />

    <service android:name="test.package.services.geofences.ShowNotificationService"
            android:label="@string/app_name"
            android:exported="false"/>

    <receiver android:name="test.package.services.geofences.UpdateGeofences">
        <intent-filter>
            <action android:name="test.package.updateGeofecnes"/>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>

Update Geofences:

 public class UpdateGeofences extends BroadcastReceiver implements
        GooglePlayServicesClient.ConnectionCallbacks,
        GooglePlayServicesClient.OnConnectionFailedListener,
        LocationClient.OnAddGeofencesResultListener,
        DataUpdateCallback {

    private LocationClient mLocationClient;
    private Context ctx;

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("NOTICE", "Updating Geofences");
        ctx = context;
        mLocationClient = new LocationClient(context, this, this);
        mLocationClient.connect();
    }

    @Override
    public void onConnected(Bundle bundle) {
        GeofenceProvider geofenceProvider = new GeofenceProvider(ctx);
        geofenceProvider.open();

        //reactivate geofences
        List<Geofence> geofences = geofenceProvider.getActiveGeofences(mLocationClient.getLastLocation());
        Intent intent = new Intent(ctx, ShowNotificationService.class);
        PendingIntent pendingIntent = PendingIntent.getService(ctx, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        if (geofences.size() > 0) mLocationClient.addGeofences(geofences, pendingIntent, this);
        else mLocationClient.disconnect();
    }

    @Override
    public void onAddGeofencesResult(int i, String[] strings) {
        // If adding the geofences was successful
        if (LocationStatusCodes.SUCCESS != i) {
            Log.e("Geofences", "Error adding geofences: "+ Arrays.toString(strings)+ " Code: "+i);
        }
        mLocationClient.disconnect();
    }    
}

Show notification service:

    public class ShowNotificationService extends IntentService {

    public ShowNotificationService() {
        super("ReceiveTransitionsIntentService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.d("NOTICE", "Android geofence notification!");
        if (LocationClient.hasError(intent)) {
            int errorCode = LocationClient.getErrorCode(intent);
            Log.e("ReceiveTransitionsIntentService", "Location Services error: " + Integer.toString(errorCode));
            Crashlytics.logException(new RuntimeException("Location Services error: "+errorCode));
        } else {
            int transitionType = LocationClient.getGeofenceTransition(intent);
            if (transitionType == Geofence.GEOFENCE_TRANSITION_ENTER) { //ak vstupujem pozriem sa ci uz sa mi to nevyhodilo dnes
                List<Geofence> triggerList = LocationClient.getTriggeringGeofences(intent);
                Log.d("NOTICE", "Transition Enter");
                GeofenceProvider mProvider = new GeofenceProvider(this);
                mProvider.open();
                try {
                    for (Geofence geofence : triggerList) {
                        String id = geofence.getRequestId();
                        String[] data = id.split(MyGeofence.delimiter);
                        Log.d("NOTICE", "Show notification: "+id);
                        if (data.length != 3) {
                            Crashlytics.logException(new RuntimeException("Invalid geofence id: " + id + "Data: "+ Arrays.toString(data)));
                            continue;
                        }
                        if (mProvider.updateLastFire(Integer.valueOf(data[2]))) {
                            NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
                            builder.setContentTitle(data[0]);
                            builder.setContentText(data[1]);
                            builder.setSmallIcon(R.drawable.noticon);
                            Intent result = new Intent(this, CompassActivity.class);
                            PendingIntent pendingResult = PendingIntent.getActivity(this, 0, result, PendingIntent.FLAG_UPDATE_CURRENT);
                            builder.setContentIntent(pendingResult);
                            builder.setOngoing(false);
                            builder.setAutoCancel(true);
                            Notification not = builder.build();
                            not.defaults = Notification.DEFAULT_ALL;
                            NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
                            int notID = Integer.valueOf(data[2]);
                            mNotificationManager.notify(notID, not);
                        }
                    }
                } catch (Exception e) {
                    Crashlytics.logException(e);
                    e.printStackTrace();
                } finally {
                    mProvider.close();
                }

            } else if(transitionType == Geofence.GEOFENCE_TRANSITION_EXIT) {    //ak odchadzam, oznacim to patricne v db
                List<Geofence> triggerList = LocationClient.getTriggeringGeofences(intent);
                Log.d("NOTICE", "Transition leave");
                GeofenceProvider mProvider = new GeofenceProvider(this);
                mProvider.open();
                try {
                    for (Geofence geofence : triggerList) {
                        String id = geofence.getRequestId();
                        String[] data = id.split(MyGeofence.delimiter);
                        if (data.length != 3) {
                            Crashlytics.logException(new RuntimeException("Invalid geofence id: " + id + "Data: "+ Arrays.toString(data)));
                            continue;
                        }
                        mProvider.invalidateLastFire(Integer.valueOf(data[2]));
                        Log.d("NOTICE", "Invalidate last fire: "+id);
                    }
                } catch (Exception e) {
                    Crashlytics.logException(e);
                    e.printStackTrace();
                } finally {
                    mProvider.close();
                }

            } else {    // An invalid transition was reported
                Log.e("ReceiveTransitionsIntentService", "Geofence transition error: " + Integer.toString(transitionType));
                Crashlytics.logException(new RuntimeException("Invalid geofence transition"));
            }
        }
    }
}

In UpdateGeofences I omitted methods required to connect to PlayServices, since they are basically copied from demo app and I am using them successfully in other places...

Main activity is irrelevant, it just sends broadcast to start UpdateGeofences every time it starts.

GeofenceProvider is also isn't a problem, I am using same code elsewhere with no problems.

回答1:

Eventualy, I managed to fix this by setting exported="true" in ShowNofiticationService.

Like this:

<service android:name="test.package.services.geofences.ShowNotificationService"
        android:label="@string/app_name"
        android:exported="true"/>

It seems the intents were fired by process with wrong id, and that coused security error. Not sure why would that happen, though...