Broadcast receiver is not called on real devices - android

I have updated target version from Android 8 to Android 10 after that I am facing an issue that broadcast receiver is not being called on devices(I have tested on Samsung s9(Pie), Mi Note 5(Oreo)) accept Google Pixel 2 XL device(Android 10) but it's working fine on Genymotion Samsung s9 emulator or any emulator. Can anybody tell what could be the possible issue?
There is Service called SipService inside that it registering the Intent filters and we trigger one of the Intent filter from one of the activity. Some codes are as below.
ACtivity
inside onCreate() method
registerReceiver(registrationStateReceiver, new IntentFilter(SipManager.ACTION_SIP_REGISTRATION_CHANGED));
bindService(new Intent(mContext, SipService.class), connection, Context.BIND_AUTO_CREATE);
And after some Webservice calls and operation we are calling one of the registered Intent Filter as below.
Intent intent = new Intent(SipManager.ACTION_SIP_REQUEST_RESTART);
sendBroadcast(intent);
Inside SipService class
private void registerBroadcasts() {
// Register own broadcast receiver
if (deviceStateReceiver == null) {
IntentFilter intentfilter = new IntentFilter();
intentfilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
intentfilter.addAction(SipManager.ACTION_SIP_ACCOUNT_CHANGED);
intentfilter.addAction(SipManager.ACTION_SIP_ACCOUNT_DELETED);
intentfilter.addAction(SipManager.ACTION_SIP_CAN_BE_STOPPED);
intentfilter.addAction(SipManager.ACTION_SIP_REQUEST_RESTART);
intentfilter.addAction(DynamicReceiver4.ACTION_VPN_CONNECTIVITY);
if (Compatibility.isCompatible(5)) {
deviceStateReceiver = new DynamicReceiver5(this);
} else {
deviceStateReceiver = new DynamicReceiver4(this);
}
registerReceiver(deviceStateReceiver, intentfilter);
deviceStateReceiver.startMonitoring();
}
}
Receiver Class
public class DynamicReceiver4 extends BroadcastReceiver {
private static final String THIS_FILE = "DynamicReceiver";
// Comes from android.net.vpn.VpnManager.java
// Action for broadcasting a connectivity state.
public static final String ACTION_VPN_CONNECTIVITY = "vpn.connectivity";
/** Key to the connectivity state of a connectivity broadcast event. */
public static final String BROADCAST_CONNECTION_STATE = "connection_state";
private SipService service;
// Store current state
private String mNetworkType;
private boolean mConnected = false;
private String mRoutes = "";
private boolean hasStartedWifi = false;
private Timer pollingTimer;
/**
* Check if the intent received is a sticky broadcast one
* A compat way
* #param it intent received
* #return true if it's an initial sticky broadcast
*/
public boolean compatIsInitialStickyBroadcast(Intent it) {
if(ConnectivityManager.CONNECTIVITY_ACTION.equals(it.getAction())) {
if(!hasStartedWifi) {
hasStartedWifi = true;
return true;
}
}
return false;
}
public DynamicReceiver4(SipService aService) {
service = aService;
}
#Override
public void onReceive(final Context context, final Intent intent) {
// Run the handler in SipServiceExecutor to be protected by wake lock
service.getExecutor().execute(new SipService.SipRunnable() {
public void doRun() throws SipService.SameThreadException {
onReceiveInternal(context, intent, compatIsInitialStickyBroadcast(intent));
}
});
}
/**
* Internal receiver that will run on sip executor thread
* #param context Application context
* #param intent Intent received
* #throws SameThreadException
*/
private void onReceiveInternal(Context context, Intent intent, boolean isSticky) throws SipService.SameThreadException {
String action = intent.getAction();
Log.d(THIS_FILE, "Internal receive " + action);
if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
ConnectivityManager cm =
(ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
onConnectivityChanged(activeNetwork, isSticky);
} else if (action.equals(SipManager.ACTION_SIP_ACCOUNT_CHANGED)) {
final long accountId = intent.getLongExtra(SipProfile.FIELD_ID, SipProfile.INVALID_ID);
// Should that be threaded?
if (accountId != SipProfile.INVALID_ID) {
final SipProfile account = service.getAccount(accountId);
if (account != null) {
Log.d(THIS_FILE, "Enqueue set account registration");
service.setAccountRegistration(account, account.active ? 1 : 0, true);
}
}
} else if (action.equals(SipManager.ACTION_SIP_ACCOUNT_DELETED)){
final long accountId = intent.getLongExtra(SipProfile.FIELD_ID, SipProfile.INVALID_ID);
if(accountId != SipProfile.INVALID_ID) {
final SipProfile fakeProfile = new SipProfile();
fakeProfile.id = accountId;
service.setAccountRegistration(fakeProfile, 0, true);
}
} else if (action.equals(SipManager.ACTION_SIP_CAN_BE_STOPPED)) {
service.cleanStop();
} else if (action.equals(SipManager.ACTION_SIP_REQUEST_RESTART)){
service.restartSipStack();
} else if(action.equals(ACTION_VPN_CONNECTIVITY)) {
onConnectivityChanged(null, isSticky);
}
}

Actually, My bad it was basically ABI(Application Binary Interface) issue that causing the issue in bit 64 devices.

Related

How to stop IntentService Android

I have build an IntentService in Android.
So if I received a pushNotification message, I muse stopped this service.
public class DosimeterDataSensingService extends IntentService {
private static final String TAG = "DosimeterDataSensingService";
public static boolean isStarted = false;
private Context mContext;
private int mStatus;
private BluetoothDevice mDevice;
private BluetoothGatt mConnGatt;
private boolean notificationsEnabled;
private long dosimeterScanningTime;
private boolean isThreadStarted = false;
private List<DosimeterDataRequest.DataBean> dosimeterDataList;
private List<DosimeterDataRequest.DataBean> dosimeterDataListMqtt;
private DosimeterMqttParcel dosimeterMqttParcel = new DosimeterMqttParcel();
private DosimeterDataRequest dosimeterDataRequest;
private String macAddress;
private boolean isRecordingEnabled = false;
private String dose = "";
private int battery = -1;
private Boolean syncRadiactionWS = false;
private Boolean syncRadiactionMQTT = false;
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* #param name Used to name the worker thread, important only for debugging.
*/
public DosimeterDataSensingService(String name) {
super(name);
}
public DosimeterDataSensingService() {
super(null);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
Log.d("dosimetro", "ON HANDLE INTENT");
if(intent!=null){
String action = intent.getStringExtra(PreferenceHandler.ACTION_STOP);
Log.d("dosimetro", action!=null ? action : "no action");
if(action!=null && action.equals(PreferenceHandler.ACTION_STOP)){
Log.d("dosimetro", "fermo il servizio");
String syncScanTime = PreferenceHandler.readString(getApplicationContext(), PreferenceHandler.DOSIMETER_SCANNING_TIME, null);
Log.d("dosimetro", syncScanTime!=null ? syncScanTime : "nullo");
String syncRotTime = PreferenceHandler.readString(getApplicationContext(), PreferenceHandler.DOSIMETER_ROTATION_TIME, null);
Log.d("dosimetro", syncRotTime!=null ? syncRotTime : "nullo");
super.stopSelf();
Log.d("dosimetro", "ho stoppato");
onDestroy();
return;
}
}
}
#Override
public void onCreate() {
super.onCreate();
Paper.init(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = createNotificationChannel();
}
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, getString(R.string.default_notification_channel_id))
.setSmallIcon(R.drawable.logoxhdpi)
.setCategory(Notification.CATEGORY_SERVICE)
.setContentTitle("Cardio App")
.setContentText("Getting data from Dosimeter")
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
Notification notification = mBuilder.build();
startForeground((int) (System.currentTimeMillis() + 1), notification);
isStarted = true;
if (PreferenceHandler.readString(this, PreferenceHandler.TYPE_USER, null).equals("2")) {
checkDosage();
}
List<GetAssignedDevicesListResponse.Parameters> preferenzeParametri= Paper.book().read(PreferenceHandler.PARAMETRI_VITALI, new ArrayList<>());
if(preferenzeParametri!=null && preferenzeParametri.size()>0){
for (GetAssignedDevicesListResponse.Parameters p: preferenzeParametri) {
if(p.getIdParameter() == PreferenceHandler.ID_RADIACTION){
//VERIFICO COME L'RR DEVE ESSERE SINCRONIZZATO
syncRadiactionWS = p.getSyncWs()!=null ? p.getSyncWs() : false;
syncRadiactionMQTT = p.getSyncMqtt()!=null ? p.getSyncMqtt() : false;
Log.e("DOSIMETER", "syncRadiactionWS true");
}
}
}else{
Log.e("DOSIMETER", "paperi init false");
}
checkBattery();
Log.i("SCANNINGTIME", "SYNC WS STARTED");
syncRadiactionLevel();
syncMqtt();
}
#Override
public void onDestroy() {
Log.d("DOSIMETRO", "ondestroy");
isStarted = false;
disconnectDosimeter();
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#SuppressLint("LongLogTag")
public void disconnectDosimeter() {
if (mConnGatt != null) {
isThreadStarted = false;
if ((mStatus != BluetoothProfile.STATE_DISCONNECTING)
&& (mStatus != BluetoothProfile.STATE_DISCONNECTED)) {
mConnGatt.disconnect();
mConnGatt.close();
mConnGatt = null;
mStatus = BluetoothProfile.STATE_DISCONNECTED;
}
}
try {
Method m = mDevice.getClass().getMethod("removeBond", (Class[]) null);
m.invoke(mDevice, (Object[]) null);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
Log.v("Dosimeter", "isRecordingEnabled" + isRecordingEnabled);
Log.v("Dosimeter", "Disconnecteddd");
}
/**
* Enable recording of values
*/
private void startRecordingData() {
String dosimeterRotation = PreferenceHandler.readString(getApplicationContext(), PreferenceHandler.DOSIMETER_ROTATION_TIME, null);
Log.i("ROTATIONTIME", dosimeterRotation);
long dosimeterRotationTime = 0L;
if (dosimeterRotation != null) {
dosimeterRotationTime = Long.parseLong(dosimeterRotation);
new Handler().postDelayed(() -> {
isRecordingEnabled = true;
isThreadStarted = false;
}, dosimeterRotationTime);
}
}
}
To stop the service I m using this code:
Intent i = new Intent(this, DosimeterDataSensingService.class);
i.putExtra(PreferenceHandler.ACTION_STOP,PreferenceHandler.ACTION_STOP);
stopService(new Intent(this, DosimeterDataSensingService.class));
From my log I can see that the system call
super.stopSelf();
onDestroy();
method but the IntentService works always.
You need not call stopSelf() or stopService() for IntentService.
As per the description mentioned in Docs:
Because most of the started services don't need to handle multiple requests simultaneously (which can actually be a dangerous multi-threading scenario), it's best that you implement your service using the IntentService class.
The IntentService class does the following:
It creates a default worker thread that executes all of the intents that are delivered to onStartCommand(), separate from your application's main thread.
Creates a work queue that passes one intent at a time to your onHandleIntent() implementation, so you never have to worry about multi-threading.
Stops the service after all of the start requests are handled, **so you never have to call stopSelf().**
Provides a default implementation of onBind() that returns null.
Provides a default implementation of onStartCommand() that sends the intent to the work queue and then to your onHandleIntent() implementation.
If the service is still running may be some intents are running.
Hope this helps.

Android WiFi Direct android.net.wifi.p2p.PEERS_CHANGED received endlessly

community,
this is my first question, so please tell me if I accidentally don't match some norms or similiar.
I am trying to code an android application that communicates with WiFi direct.
Everything works smooth at this point, but my BroadcastReceiver receives the android.net.wifi.p2p.PEERS_CHANGED action again and again.
public class WifiP2PBroadcastReceiver extends BroadcastReceiver {
private final static String TAG = "WifiP2PBR";
private WifiP2pManager _manager;
private WifiP2pManager.Channel _channel;
private Activity _callbackActivity;
private static ArrayList<WifiBroadcastCallback> _arrayList_callbacks;
public WifiP2PBroadcastReceiver(WifiP2pManager manager, WifiP2pManager.Channel channel, Activity callback) {
_manager = manager;
_channel = channel;
_callbackActivity = callback;
}
/**
* add a callback to the receiver
*
* #param callback
*/
public static void addWifiCallback(WifiBroadcastCallback callback) {
if(!getCallbacksList().contains(callback))
{
getCallbacksList().add(callback);
}
}
public void registerReceiver()
{
Log.d(TAG,"registerReceiver called");
if(_callbackActivity != null) {
_callbackActivity.registerReceiver(this, getIntentFilter());
}
}
public void unregisterReceiver() {
if(_callbackActivity != null) {
_callbackActivity.unregisterReceiver(this);
}
}
#Override
public final void onReceive(Context context, Intent intent) {
Log.d(TAG, "action: "+intent.getAction());
switch(intent.getAction()) {
case WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION:
Log.d(TAG, "state changed action");
break;
case WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION:
Log.d(TAG, "peers changed action");
notifyPeerUpdate();
break;
case WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION:
Log.d(TAG, "wifi p2p connection changed action");
NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
Log.d(TAG,"network info available");
break;
case WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION:
Log.d(TAG, "wifi p2p this device changed action");
notifyDeviceUpdate();
WifiP2pDevice thisDevice = intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE);
Log.d(TAG,"this device acquired");
break;
}
}
private void notifyPeerUpdate()
{
for(WifiBroadcastCallback c : getCallbacksList())
{
c.onPeersChanged();
}
}
private void notifyDeviceUpdate()
{
for(WifiBroadcastCallback c : getCallbacksList())
{
c.onDeviceChanged();
}
}
/**
* get array list of callbacks
*
* #return
*/
private static ArrayList<WifiBroadcastCallback> getCallbacksList() {
if(_arrayList_callbacks == null) {
_arrayList_callbacks = new ArrayList<WifiBroadcastCallback>();
}
return _arrayList_callbacks;
}
/**
* interface for callbacks
*/
public interface WifiBroadcastCallback {
void onDeviceChanged();
void onPeersChanged();
}
private IntentFilter getIntentFilter()
{
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
return intentFilter;
}
}
this is my BroadcastReceiver code. Everything else works fine and I'm out of ideas, it started out of the nowhere.
That's how it works indeed. Basically, what you should do there, is to call requestPeers with your instance of WifiP2pManager. Then you onPeersAvailable from WifiP2pManager.PeerListListener would be called with the array of peers seen, and there you could then check whether the change is something that you want to report.
reference implementation can be found from my github projects

Android service calling onDestroy before workerthread ends

I have a service that I'm using to send SOAP Webservice calls. Everything is working perfectly and it never crashes, but I kinda think it should.
My problem is that when I have long running queries (10-50 sec.) onDestroy() is called before my workerthread is done (and I call stopSelfResult). Could it be that System.out.println isn't executed right away/out of sync (cached) in the LogCat window?
The is how a start the service through QueryBase class:
QueryBase someService = new QueryBase(myActivity);
someService.execute(...);
My QueryBase Class
public class QueryBase {
private WeakReference<Activity> currentActivity = null;
private static class ResponseHandler extends Handler {
private QueryBase mQueryBase;
public ResponseHandler(QueryBase vQueryBase) {
mQueryBase = vQueryBase;
};
public void handleMessage(Message message) {
Bundle extras = message.getData();
mQueryBase.handleResult(message.arg1,message.arg2,extras.getInt("FRAMEID"),extras.getString("RESPONSE"));
mQueryBase=null;
};
};
public QueryBase(Activity vActivity) {
currentActivity = new WeakReference<Activity>(vActivity);
}
/***************************************************************************
* Start the service
**************************************************************************/
public boolean execute(Activity vActivity, int cmdID, int frameID, String serverAddress, int requestType, String request) {
// Valid activity
if (vActivity==null) return false;
// Test to see if network is connected
if (!isOnline(vActivity)) return false;
Intent webService = new Intent(vActivity, WebService.class);
final ResponseHandler responseHD = new ResponseHandler(this);
Messenger messenger = new Messenger(responseHD);
webService.putExtra("QUERYRESULT_MESSENGER",messenger);
webService.putExtra("CMDID", cmdID);
webService.putExtra("FRAMEID",frameID);
webService.putExtra("SERVER_ADDRESS",serverAddress);
webService.putExtra("REQUEST_TYPE",requestType);
webService.putExtra("REQUEST",request);
vActivity.startService(webService);
return true;
}
/***************************************************************************
* Is my Android connected?
**************************************************************************/
private Boolean isOnline(Activity vActivity) {
ConnectivityManager connMgr = (ConnectivityManager) vActivity.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) return true;
else return false;
}
/***************************************************************************
* Get current Activity
**************************************************************************/
public Activity getCurrentActivity() {
Activity ac = currentActivity.get();
if (ac!=null) {
if ((ac.isFinishing()) || (ac.activityDestroyed)) {
return null;
};
}
return ac;
};
/***************************************************************************
* XML result from webservice
**************************************************************************/
public void handleResult(int resultCode, int cmdID, int frameID, String response) {
System.out.println("DEFAULT HANDLER: ResultCode: " + resultCode);
};
}
My WebService Class
public class WebService extends Service {
public static final int WS_RT_BLOOSOAP = 0;
public static final int WS_RT_RSS = 1;
public static final int WS_RESULT_OK = 0;
public static final int WS_RESULT_UNABLE_TO_CONNECT = 2;
public static final int WS_RESULT_INVALID_REQUEST = 3;
public static final int WS_RESULT_UNKNOWN_ERROR = 999;
static private SparseBooleanArray workList=null; // Only one job with the same frameID is allowed to run
#Override
public void onCreate() {
System.out.println("#### WebService onCreate");
if (workList==null) workList = new SparseBooleanArray();
}
#Override
public void onDestroy() {
System.out.println("#### WebService onDestroy");
}
/***************************************************************************
* Start working
**************************************************************************/
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("WebService Start ID=" + startId);
final int currentID = startId;
final Intent currentIntent = intent;
Runnable workerRunnable = new Runnable() {
public void run() {
System.out.println("WebService Thread Start - ID=" + currentID);
int resultCode;
Bundle responseExtras = new Bundle();
resultCode = serverRequest(currentIntent,responseExtras);
sendResponse(currentIntent,resultCode,responseExtras);
System.out.println("WebService Thread End - ID=" + currentID);
Bundle extras = currentIntent.getExtras();
if (extras != null) {
int frameID = extras.getInt("FRAMEID");
System.out.println(">>>>>>> PUT FALSE " + frameID);
workList.put(frameID, false);
};
stopSelfResult(currentID);
}
};
if (intent!=null) {
Bundle extras = intent.getExtras();
if (extras != null) {
int frameID = extras.getInt("FRAMEID");
Boolean found = workList.get(frameID,false);
if (!found) {
System.out.println(">>>>>>> PUT TRUE FRAMEID=" + frameID);
workList.put(frameID, true);
Thread workerThread = new Thread(workerRunnable);
workerThread.start();
} else {
System.out.println(">>>>>>> Allready running FRAMEID=" + frameID);
}
};
};
return Service.START_STICKY;
};
/***************************************************************************
* No binding
**************************************************************************/
#Override
public IBinder onBind(Intent intent) {
return null;
}
/***************************************************************************
* Send webservice request and return result in responseExtras
**************************************************************************/
private int serverRequest(Intent intent, Bundle responseExtras) {
...
};
/***************************************************************************
* Send response back to service caller using Messenger.send()
**************************************************************************/
private boolean sendResponse(Intent intent, int resultCode, Bundle responseExtras) {
...
};
Your service is stopped if you call stopSelfResult() with the latest startId. So if the service gets started with an intent for startId=1 and another intent with startId=2 and the second is finished before the first, you call stopSelfResult(2) before you finished for startId=1. The service gets destroyed immediately if you call stopSelfResult() with the latest startId and no other intents are pending.
Hold the latest startId. Add all startIds you wish to process in an array (e.g. List<Integer> runningStartIds) and remove them when you've finished processing them. After removing on finishing, compare the current startId with the latest one and do not call stopSelfResult() if runningStartIds is not empty. So you will end up calling stopSelfResult() only for the latest startId, when all intents were processed and no more intents are pending.
Should work, although I haven't posted an example.
.:EDIT:.
Explenation:
The next Intent may come in as fast as you return from onStartCommand() regardless of what you're doing in the background.
.:EDIT:.
Not an Improvement(Improvement:
Thinking about that, in fact you only have to keep the mLastStartId. Just skip calling stopSelfResult() until the finished startId matches mLastStartId.)
Unfortunately, it always can be happen. Actually, android application components' life cycle aren't synchronized w/ any type of worker threads as default.
So, you may need to check the status of Service manually, for example you can have one boolean flag to indicate if a service is working or not. Another handy approach is using IntentService instead of using normal service, it handles worker thread and life-cycle features by itself.
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService ");
}
#Override
protected void onHandleIntent(Intent intent) {
// This callback-method is called inside worker thread,
// so you can do some long-time network job here.
SystemClock.sleep(30000); // 30 seconds
// In this timing the service will be stopped automatically.
}
}

android background service static field send same msg twice

I have a big problem that i couldn't find solution. Need your help.
When i try to send just one message in activity using service , it works.
But when i try to send 2 message following another( that means one after other ) , sends the message twice with the same publishTopic2 and pushMsg2.
Code is :
PushService.actionPush(getApplicationContext(),publishTopic1,pushMsg1);
PushService.actionPush(getApplicationContext(),publishTopic2,pushMsg2);
1- is this from the wrong usage of syncronized keyword ?
2- is it possibe to send two msg after another without
public class PushService
extends Service
{
// this is the log tag
public static final String TAG = "DemoPushService";
public static String IncomingText = "";
private static String PushMesaj = "";
// the IP address, where your MQTT broker is running.
private static final String MQTT_HOST = "";
// the port at which the broker is running.
private static int MQTT_BROKER_PORT_NUM = 1883;
// Let's not use the MQTT persistence.
private static MqttClientPersistence MQTT_PERSISTENCE = null;
// We don't need to remember any state between the connections, so we use a
// clean start.
private static boolean MQTT_CLEAN_START = true;
// Let's set the internal keep alive for MQTT to 15 mins. I haven't tested
// this value much. It could probably be increased.
private static short MQTT_KEEP_ALIVE = 60 * 15;
// Set quality of services to 0 (at most once delivery), since we don't want
// push notifications
// arrive more than once. However, this means that some messages might get
// lost (delivery is not guaranteed)
// private static int[] MQTT_QUALITIES_OF_SERVICE = { 0 } ;
private static int MQTT_QUALITY_OF_SERVICE = 2;
// The broker should not retain any messages.
// private static boolean MQTT_RETAINED_PUBLISH = false;
// MQTT client ID, which is given the broker. In this example, I also use
// this for the topic header.
// You can use this to run push notifications for multiple apps with one
// MQTT broker.
public static String MQTT_CLIENT_ID = "begining";
public static String SUBSCRIBE_TOPIC = "begining";
private static String PUBLISH_TOPIC = "begining";
// These are the actions for the service (name are descriptive enough)
private static final String ACTION_START = MQTT_CLIENT_ID + ".START";
private static final String ACTION_STOP = MQTT_CLIENT_ID + ".STOP";
private static final String ACTION_SUBSCRIBE = MQTT_CLIENT_ID
+ ".SUBSCRIBE";
private static final String ACTION_PUBLISH = MQTT_CLIENT_ID + ".PUBLISH";
private static final String ACTION_KEEPALIVE = MQTT_CLIENT_ID
+ ".KEEP_ALIVE";
private static final String ACTION_PUSHMESSAGE = MQTT_CLIENT_ID
+ ".PUSH_MESSAGE";
private static final String ACTION_PUSHMESSAGE2 = MQTT_CLIENT_ID
+ ".PUSH_MESSAGE";
private static final String ACTION_RECONNECT = MQTT_CLIENT_ID
+ ".RECONNECT";
// Connection log for the push service. Good for debugging.
private ConnectionLog mLog;
// Connectivity manager to determining, when the phone loses connection
private ConnectivityManager mConnMan;
// Notification manager to displaying arrived push notifications
private NotificationManager mNotifMan;
// Whether or not the service has been started.
private boolean mStarted;
// This the application level keep-alive interval, that is used by the
// AlarmManager
// to keep the connection active, even when the device goes to sleep.
private static final long KEEP_ALIVE_INTERVAL = 1000 * 60 * 28;
// Retry intervals, when the connection is lost.
private static final long INITIAL_RETRY_INTERVAL = 1000 * 10;
private static final long MAXIMUM_RETRY_INTERVAL = 1000 * 60 * 30;
// Preferences instance
private SharedPreferences mPrefs;
// We store in the preferences, whether or not the service has been started
public static final String PREF_STARTED = "isStarted";
// We also store the deviceID (target)
public static final String PREF_DEVICE_ID = "deviceID";
// We store the last retry interval
public static final String PREF_RETRY = "retryInterval";
// Notification title
public static String NOTIF_TITLE = "Hey !!!";
// Notification id
private static final int NOTIF_CONNECTED = 0;
// This is the instance of an MQTT connection.
private MQTTConnection mConnection;
private long mStartTime;
private static clsUser yourInfo;
public static clsOnlineUsers onlineUsers;
private static clsUser myInfo;
// Static method to start the service
public static void actionStart(Context ctx)
{
Intent i = new Intent(ctx, PushService.class);
i.setAction(ACTION_START);
ctx.startService(i);
}
public static void actionSubscribe(Context ctx)
{
Intent i = new Intent(ctx, PushService.class);
i.setAction(ACTION_SUBSCRIBE);
ctx.startService(i);
}
public static void actionPublish(Context ctx)
{
Intent i = new Intent(ctx, PushService.class);
i.setAction(ACTION_PUBLISH);
ctx.startService(i);
}
// Static method to stop the service
public static void actionStop(Context ctx)
{
Intent i = new Intent(ctx, PushService.class);
i.setAction(ACTION_STOP);
ctx.startService(i);
}
// Static method to send a keep alive message
public static void actionPing(Context ctx)
{
Intent i = new Intent(ctx, PushService.class);
i.setAction(ACTION_KEEPALIVE);
ctx.startService(i);
}
public static void actionPush(Context ctx, String publishTopic,
String pushMsg)
{
PUBLISH_TOPIC = publishTopic;
PushMesaj = pushMsg;
Intent i = new Intent(ctx, PushService.class);
i.setAction(ACTION_PUSHMESSAGE);
ctx.startService(i);
}
#Override
public void onCreate()
{
super.onCreate();
log("Creating service");
mStartTime = System.currentTimeMillis();
try {
mLog = new ConnectionLog();
Log.i(TAG, "Opened log at " + mLog.getPath());
} catch (IOException e) {
Log.e(TAG, "Failed to open log", e);
}
// Get instances of preferences, connectivity manager and notification
// manager
mPrefs = getSharedPreferences(TAG, MODE_PRIVATE);
mConnMan = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
mNotifMan = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mPrefs.edit().putLong(PREF_RETRY, INITIAL_RETRY_INTERVAL).commit();
mPrefs.edit().putBoolean(PREF_STARTED, false).commit();
/*
* If our process was reaped by the system for any reason we need to
* restore our state with merely a call to onCreate. We record the last
* "started" value and restore it here if necessary.
*/
handleCrashedService();
}
// This method does any necessary clean-up need in case the server has been
// destroyed by the system
// and then restarted
private void handleCrashedService()
{
if (wasStarted() == true) {
log("Handling crashed service...");
// stop the keep alives
// stopKeepAlives();
// Do a clean start
start();
}
}
#Override
public void onDestroy()
{
log("Service destroyed (started=" + mStarted + ")");
// Stop the services, if it has been started
if (mStarted == true) {
stop();
}
try {
if (mLog != null)
mLog.close();
} catch (IOException e) {
}
}
#Override
public synchronized void onStart(Intent intent, int startId)
{
super.onStart(intent, startId);
log("Service started with intent=" + intent);
if (intent == null) {
start();
return;
}
// Do an appropriate action based on the intent.
if (intent.getAction().equals(ACTION_STOP) == true) {
stop();
stopSelf();
} else if (intent.getAction().equals(ACTION_START) == true) {
start();
} else if (intent.getAction().equals(ACTION_KEEPALIVE) == true) {
keepAlive();
} else if (intent.getAction().equals(ACTION_PUBLISH) == true) {
publish();
} else if (intent.getAction().equals(ACTION_SUBSCRIBE) == true) {
subscribe();
} else if (intent.getAction().equals(ACTION_PUSHMESSAGE) == true) {
sendMessage();
} else if (intent.getAction().equals(ACTION_RECONNECT) == true) {
if (isNetworkAvailable()) {
reconnectIfNecessary();
}
}
}
private void subscribe()
{
// TODO Auto-generated method stub
try {
mConnection.subscribeToTopic(SUBSCRIBE_TOPIC);
} catch (MqttException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void publish()
{
try {
// Send a keep alive, if there is a connection.
if (mStarted == true && mConnection != null) {
mConnection.sendPushMessage(1);
}
} catch (MqttException e) {
log("MqttException: "
+ (e.getMessage() != null ? e.getMessage() : "NULL"), e);
mConnection.disconnect();
mConnection = null;
cancelReconnect();
}
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
// log helper function
private void log(String message)
{
log(message, null);
}
private void log(String message, Throwable e)
{
if (e != null) {
Log.e(TAG, message, e);
} else {
Log.i(TAG, message);
}
if (mLog != null) {
try {
mLog.println(message);
} catch (IOException ex) {
}
}
}
// Reads whether or not the service has been started from the preferences
private boolean wasStarted()
{
return mPrefs.getBoolean(PREF_STARTED, false);
}
// Sets whether or not the services has been started in the preferences.
private void setStarted(boolean started)
{
mPrefs.edit().putBoolean(PREF_STARTED, started).commit();
mStarted = started;
}
private synchronized void start()
{
log("Starting service...");
// Do nothing, if the service is already running.
if (mStarted == true) {
Log.w(TAG, "Attempt to start connection that is already active");
return;
}
// Establish an MQTT connection
connect();
// Register a connectivity listener
registerReceiver(mConnectivityChanged, new IntentFilter(
ConnectivityManager.CONNECTIVITY_ACTION));
}
private synchronized void stop()
{
// Do nothing, if the service is not running.
if (mStarted == false) {
Log.w(TAG, "Attempt to stop connection not active.");
return;
}
// Save stopped state in the preferences
setStarted(false);
// Remove the connectivity receiver
unregisterReceiver(mConnectivityChanged);
// Any existing reconnect timers should be removed, since we explicitly
// stopping the service.
cancelReconnect();
// Destroy the MQTT connection if there is one
if (mConnection != null) {
mConnection.disconnect();
mConnection = null;
}
}
private clsUser setUserInfo(String deviceId)
{
clsUser u = new clsUser();
dbUser du = new dbUser(getApplicationContext(), dbUser.DATABASE_NAME,
null, dbUser.DATABASE_VERSION);
Cursor c1 = du.Getby(null, null, null, null, null, null, null, null,
null, deviceId, null, null, null);
u = du.setProperties(c1);
c1.close();
du.close();
return u;
}
//
private synchronized void connect()
{
log("Connecting...");
// Create a new connection only if the device id is not NULL
if (MQTT_CLIENT_ID == null) {
log("Device ID not found.");
} else {
try {
MQTT_CLIENT_ID = Config.id(getApplication());
myInfo = setUserInfo(MQTT_CLIENT_ID);
mConnection = new MQTTConnection(MQTT_HOST, "user/"
+ MQTT_CLIENT_ID + "/#");
} catch (MqttException e) {
// Schedule a reconnect, if we failed to connect
log("MqttException: "
+ (e.getMessage() != null ? e.getMessage() : "NULL"));
if (!isNetworkAvailable()) {
scheduleReconnect(mStartTime);
}
}
setStarted(true);
}
}
private synchronized void keepAlive()
{
try {
// Send a keep alive, if there is a connection.
if (mStarted == true && mConnection != null) {
mConnection.sendKeepAlive();
}
} catch (MqttException e) {
log("MqttException: "
+ (e.getMessage() != null ? e.getMessage() : "NULL"), e);
mConnection.disconnect();
mConnection = null;
cancelReconnect();
}
}
private void sendMessage()
{
try {
// Send a keep alive, if there is a connection.
if (mStarted == true && mConnection != null) {
mConnection.sendPushMessage(); // this does the job
}
} catch (MqttException e) {
log("MqttException: "
+ (e.getMessage() != null ? e.getMessage() : "NULL"), e);
mConnection.disconnect();
mConnection = null;
cancelReconnect();
}
}
// Schedule application level keep-alives using the AlarmManager
private void startKeepAlives()
{
Intent i = new Intent();
i.setClass(this, PushService.class);
i.setAction(ACTION_KEEPALIVE);
PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + KEEP_ALIVE_INTERVAL,
KEEP_ALIVE_INTERVAL, pi);
}
// Remove all scheduled keep alives
private void stopKeepAlives()
{
Intent i = new Intent();
i.setClass(this, PushService.class);
i.setAction(ACTION_KEEPALIVE);
PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmMgr.cancel(pi);
}
// We schedule a reconnect based on the starttime of the service
public void scheduleReconnect(long startTime)
{
// the last keep-alive interval
long interval = mPrefs.getLong(PREF_RETRY, INITIAL_RETRY_INTERVAL);
// Calculate the elapsed time since the start
long now = System.currentTimeMillis();
long elapsed = now - startTime;
log(String.valueOf(elapsed));
// Set an appropriate interval based on the elapsed time since start
if (elapsed < interval) {
interval = Math.min(interval * 4, MAXIMUM_RETRY_INTERVAL);
} else {
interval = INITIAL_RETRY_INTERVAL;
}
log("Rescheduling connection in " + interval + "ms.");
// Save the new internval
mPrefs.edit().putLong(PREF_RETRY, interval).commit();
// Schedule a reconnect using the alarm manager.
Intent i = new Intent();
i.setClass(this, PushService.class);
i.setAction(ACTION_RECONNECT);
PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmMgr.set(AlarmManager.RTC_WAKEUP, now + interval, pi);
}
// Remove the scheduled reconnect
public void cancelReconnect()
{
Intent i = new Intent();
i.setClass(this, PushService.class);
i.setAction(ACTION_RECONNECT);
PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
AlarmManager alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmMgr.cancel(pi);
}
private synchronized void reconnectIfNecessary()
{
if (mStarted == true && mConnection == null) {
log("Reconnecting...");
connect();
}
}
private class MQTTConnection
implements MqttCallback
{
MqttClient mqttClient = null;
MqttConnectOptions conOpt;
String myDeviceId;
// Creates a new connection given the broker address and initial topic
public MQTTConnection(String brokerHostName, String initTopic)
throws MqttException
{
// Create connection spec
}
private void publish(String topicName, int qos, String payload,
boolean retained) throws MqttException
{
}
/*
* Sends a message to the message broker, requesting that it be
* published to the specified topic.
*/
private void publishToTopic(String topicName, String message)
throws MqttException
{
if ((mqttClient == null) || (mqttClient.isConnected() == false)) {
// quick sanity check - don't try and publish if we don't have
// a connection
log("No connection to public to");
} else {
publish(topicName, MQTT_QUALITY_OF_SERVICE, message, false);
}
}
/*
* Send a request to the message broker to be sent messages published
* with the specified topic name. Wildcards are allowed.
*/
private void subscribeToTopic(String topicName1) throws MqttException
{
if ((mqttClient == null) || (mqttClient.isConnected() == false)) {
// quick sanity check - don't try and subscribe if we don't have
// a connection
log("Subscribtion failed : Connection error" + "No connection");
} else {
mqttClient.subscribe(topicName1, 0);
Log.v(TAG, "Subscribe To : " + topicName1);
}
}
public void sendPushMessage(int sira) throws MqttException
{
publishToTopic(PUBLISH_TOPIC, PushMesaj);
}
}
}
When you call actionPush(), it does the following:
PUBLISH_TOPIC = publishTopic;
PushMesaj = pushMsg;
which saves the arguments in private static variables and then calls startService. If you do this twice, you will overwrite the private static variables with the second set of arguments.
You need to understand that the call to startService() is not synchronous. It doesn't start the service immediately and call onStart() before the method returns. The service will get started and the call to onStart() will occur sometime later (timing isn't under your control here).
You want to put these parameters in the Intent that you use to start the service, and retrieve the arguments from the Intent in onStart(). You can't reliably use static variables to do this.
EDIT: ADD CODE DETAILS
Change actionPush() to this:
static final String EXTRA_TOPIC = "EXTRA_TOPIC";
static final String EXTRA_MESSAGE = "EXTRA_MESSAGE";
public static void actionPush(Context ctx, String publishTopic,
String pushMsg)
{
Intent i = new Intent(ctx, PushService.class);
i.setAction(ACTION_PUSHMESSAGE);
i.putExtra(EXTRA_TOPIC, publishTopic);
i.putExtra(EXTRA_MESSAGE, pushMsg);
ctx.startService(i);
}
and in onStart() you can get the arguments out of the Intent like this:
String topic = intent.getStringExtra(EXTRA_TOPIC);
String message = intent.getStringExtra(EXTRA_MESSAGE);
I'm not going to rewrite the entire program for you. You'll have to figure out now what to do with the arguments. Maybe you can now store them in private static variables, but I don't know enough about your program to be able to say if that will work. A better solution would be to pass the arguments through the various methods that you've defined until they get down to the methods where they are used.

onServiceConnected sometimes not called after bindService on some devices

I've looked at a number of other threads with similar titles, and none seem to cover my problem. So, here goes.
I'm using the Google market expansion files (apkx) library and sample code, with a few modifications. This code relies on receiving callbacks from a service which handles background downloading, licence checks etc.
I have a bug where the service doesn't get correctly attached, which results in a softlock. To make this more unhelpful, this bug never happens on some devices, but occurs about two thirds of the time on other devices. I believe it to be independent of Android version, certainly I have two devices running 2.3.4, one of which (a Nexus S) doesn't have the problem, the other (an HTC Evo 3D) does.
To attempt to connect to the service, bindService is called and returns true. OnBind then gets called as expected and returns a sensible value but (when the bug occurs) onServiceConnected doesn't happen (I've waited 20 minutes just in case).
Has anyone else seen anything like this? If not, any guesses for what I might have done to cause such behaviour? If no-one has any thoughts, I'll post some code tomorrow.
EDIT: Here's the relevant code. If I've missed anything, please ask.
Whilst adding this code, I found a minor bug. Fixing it caused the frequency of the problem I'm trying to solve to change from 2 times in 3 to about 1 time in 6 on the phone I'm testing it on; no idea about effects on other phones. This continues to suggest to me a race condition or similar, but I've no idea what with.
OurDownloaderActivity.java (copied and changed from Google sample code)
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
//Test the licence is up to date
//if (current stored licence has expired)
{
startLicenceCheck();
initializeDownloadUI();
return;
}
...
}
#Override
protected void onResume() {
if (null != mDownloaderClientStub) {
mDownloaderClientStub.connect(this);
}
super.onResume();
}
private void startLicenceCheck()
{
Intent launchIntent = OurDownloaderActivity.this
.getIntent();
Intent intentToLaunchThisActivityFromNotification = new Intent(OurDownloaderActivity
.this, OurDownloaderActivity.this.getClass());
intentToLaunchThisActivityFromNotification.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TOP);
intentToLaunchThisActivityFromNotification.setAction(launchIntent.getAction());
if (launchIntent.getCategories() != null) {
for (String category : launchIntent.getCategories()) {
intentToLaunchThisActivityFromNotification.addCategory(category);
}
}
// Build PendingIntent used to open this activity from Notification
PendingIntent pendingIntent = PendingIntent.getActivity(OurDownloaderActivity.this,
0, intentToLaunchThisActivityFromNotification,
PendingIntent.FLAG_UPDATE_CURRENT);
DownloaderService.startLicenceCheck(this, pendingIntent, OurDownloaderService.class);
}
initializeDownloadUI()
{
mDownloaderClientStub = DownloaderClientMarshaller.CreateStub
(this, OurDownloaderService.class);
//do a load of UI setup
...
}
//This should be called by the Stub's onServiceConnected method
/**
* Critical implementation detail. In onServiceConnected we create the
* remote service and marshaler. This is how we pass the client information
* back to the service so the client can be properly notified of changes. We
* must do this every time we reconnect to the service.
*/
#Override
public void onServiceConnected(Messenger m) {
mRemoteService = DownloaderServiceMarshaller.CreateProxy(m);
mRemoteService.onClientUpdated(mDownloaderClientStub.getMessenger());
}
DownloaderService.java (in Google market expansion library but somewhat edited )
//this is the onBind call that happens fine; the value it returns is definitely not null
#Override
public IBinder onBind(Intent paramIntent) {
return this.mServiceMessenger.getBinder();
}
final private IStub mServiceStub = DownloaderServiceMarshaller.CreateStub(this);
final private Messenger mServiceMessenger = mServiceStub.getMessenger();
//MY CODE, derived from Google's code
//I have seen the bug occur with a service started by Google's code too,
//but this code happens more often so is more repeatably related to the problem
public static void startLicenceCheck(Context context, PendingIntent pendingIntent, Class<?> serviceClass)
{
String packageName = serviceClass.getPackage().getName();
String className = serviceClass.getName();
Intent fileIntent = new Intent();
fileIntent.setClassName(packageName, className);
fileIntent.putExtra(EXTRA_LICENCE_EXPIRED, true);
fileIntent.putExtra(EXTRA_PENDING_INTENT, pendingIntent);
context.startService(fileIntent);
}
#Override
protected void onHandleIntent(Intent intent) {
setServiceRunning(true);
try {
final PendingIntent pendingIntent = (PendingIntent) intent
.getParcelableExtra(EXTRA_PENDING_INTENT);
if (null != pendingIntent)
{
mNotification.setClientIntent(pendingIntent);
mPendingIntent = pendingIntent;
} else if (null != mPendingIntent) {
mNotification.setClientIntent(mPendingIntent);
} else {
Log.e(LOG_TAG, "Downloader started in bad state without notification intent.");
return;
}
if(intent.getBooleanExtra(EXTRA_LICENCE_EXPIRED, false))
{
//we are here due to startLicenceCheck
updateExpiredLVL(this);
return;
}
...
}
}
//MY CODE, based on Google's, again
public void updateExpiredLVL(final Context context) {
Context c = context.getApplicationContext();
Handler h = new Handler(c.getMainLooper());
h.post(new LVLExpiredUpdateRunnable(c));
}
private class LVLExpiredUpdateRunnable implements Runnable
{
LVLExpiredUpdateRunnable(Context context) {
mContext = context;
}
final Context mContext;
#Override
public void run() {
setServiceRunning(true);
mNotification.onDownloadStateChanged(IDownloaderClient.STATE_LVL_UPDATING);
String deviceId = getDeviceId(mContext);
final APKExpansionPolicy aep = new APKExpansionPolicy(mContext,
new AESObfuscator(getSALT(), mContext.getPackageName(), deviceId));
// Construct the LicenseChecker with a Policy.
final LicenseChecker checker = new LicenseChecker(mContext, aep,
getPublicKey() // Your public licensing key.
);
checker.checkAccess(new LicenseCheckerCallback() {
...
});
}
}
DownloaderClientMarshaller.java (in Google market expansion library)
public static IStub CreateStub(IDownloaderClient itf, Class<?> downloaderService) {
return new Stub(itf, downloaderService);
}
and the Stub class from the same file:
private static class Stub implements IStub {
private IDownloaderClient mItf = null;
private Class<?> mDownloaderServiceClass;
private boolean mBound;
private Messenger mServiceMessenger;
private Context mContext;
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_ONDOWNLOADPROGRESS:
Bundle bun = msg.getData();
if ( null != mContext ) {
bun.setClassLoader(mContext.getClassLoader());
DownloadProgressInfo dpi = (DownloadProgressInfo) msg.getData()
.getParcelable(PARAM_PROGRESS);
mItf.onDownloadProgress(dpi);
}
break;
case MSG_ONDOWNLOADSTATE_CHANGED:
mItf.onDownloadStateChanged(msg.getData().getInt(PARAM_NEW_STATE));
break;
case MSG_ONSERVICECONNECTED:
mItf.onServiceConnected(
(Messenger) msg.getData().getParcelable(PARAM_MESSENGER));
break;
}
}
});
public Stub(IDownloaderClient itf, Class<?> downloaderService) {
mItf = itf;
mDownloaderServiceClass = downloaderService;
}
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
//this is the critical call that never happens
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the object we can use to
// interact with the service. We are communicating with the
// service using a Messenger, so here we get a client-side
// representation of that from the raw IBinder object.
mServiceMessenger = new Messenger(service);
mItf.onServiceConnected(
mServiceMessenger);
mBound = true;
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mServiceMessenger = null;
mBound = false;
}
};
#Override
public void connect(Context c) {
mContext = c;
Intent bindIntent = new Intent(c, mDownloaderServiceClass);
bindIntent.putExtra(PARAM_MESSENGER, mMessenger);
if ( !c.bindService(bindIntent, mConnection, 0) ) {
if ( Constants.LOGVV ) {
Log.d(Constants.TAG, "Service Unbound");
}
}
}
#Override
public void disconnect(Context c) {
if (mBound) {
c.unbindService(mConnection);
mBound = false;
}
mContext = null;
}
#Override
public Messenger getMessenger() {
return mMessenger;
}
}
DownloaderServiceMarshaller.java (in Google market expansion library, unchanged)
private static class Proxy implements IDownloaderService {
private Messenger mMsg;
private void send(int method, Bundle params) {
Message m = Message.obtain(null, method);
m.setData(params);
try {
mMsg.send(m);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public Proxy(Messenger msg) {
mMsg = msg;
}
#Override
public void requestAbortDownload() {
send(MSG_REQUEST_ABORT_DOWNLOAD, new Bundle());
}
#Override
public void requestPauseDownload() {
send(MSG_REQUEST_PAUSE_DOWNLOAD, new Bundle());
}
#Override
public void setDownloadFlags(int flags) {
Bundle params = new Bundle();
params.putInt(PARAMS_FLAGS, flags);
send(MSG_SET_DOWNLOAD_FLAGS, params);
}
#Override
public void requestContinueDownload() {
send(MSG_REQUEST_CONTINUE_DOWNLOAD, new Bundle());
}
#Override
public void requestDownloadStatus() {
send(MSG_REQUEST_DOWNLOAD_STATE, new Bundle());
}
#Override
public void onClientUpdated(Messenger clientMessenger) {
Bundle bundle = new Bundle(1);
bundle.putParcelable(PARAM_MESSENGER, clientMessenger);
send(MSG_REQUEST_CLIENT_UPDATE, bundle);
}
}
private static class Stub implements IStub {
private IDownloaderService mItf = null;
final Messenger mMessenger = new Messenger(new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REQUEST_ABORT_DOWNLOAD:
mItf.requestAbortDownload();
break;
case MSG_REQUEST_CONTINUE_DOWNLOAD:
mItf.requestContinueDownload();
break;
case MSG_REQUEST_PAUSE_DOWNLOAD:
mItf.requestPauseDownload();
break;
case MSG_SET_DOWNLOAD_FLAGS:
mItf.setDownloadFlags(msg.getData().getInt(PARAMS_FLAGS));
break;
case MSG_REQUEST_DOWNLOAD_STATE:
mItf.requestDownloadStatus();
break;
case MSG_REQUEST_CLIENT_UPDATE:
mItf.onClientUpdated((Messenger) msg.getData().getParcelable(
PARAM_MESSENGER));
break;
}
}
});
public Stub(IDownloaderService itf) {
mItf = itf;
}
#Override
public Messenger getMessenger() {
return mMessenger;
}
#Override
public void connect(Context c) {
}
#Override
public void disconnect(Context c) {
}
}
/**
* Returns a proxy that will marshall calls to IDownloaderService methods
*
* #param ctx
* #return
*/
public static IDownloaderService CreateProxy(Messenger msg) {
return new Proxy(msg);
}
/**
* Returns a stub object that, when connected, will listen for marshalled
* IDownloaderService methods and translate them into calls to the supplied
* interface.
*
* #param itf An implementation of IDownloaderService that will be called
* when remote method calls are unmarshalled.
* #return
*/
public static IStub CreateStub(IDownloaderService itf) {
return new Stub(itf);
}

Categories

Resources