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.
Related
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.
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.
I need to call the Google activity recognition service through a service (not activity) and run it in the background, of course when the user starts the app, which has an activity (But the service does not called directly from activity).
Therefore I have created a service class (ActivitySensor) and another class (ActivityRecognitionScan).
When I install the app on my Galaxy Nexus S device, the service starts calling onCreate and onDestroy automatically. Even without doing anything in the GUI
It is very strange behaviour. Does anybody has the same experience or solution for it?
I mean I get something as follows in the debug console:
Activity-Logging --- onCreate
Activity-Logging --- onDestroy
Activity-Logging --- onCreate
Activity-Logging --- onDestroy
Activity-Logging --- onCreate
Activity-Logging --- onDestroy
...
Here are my two classes:
public class ActivitySensor extends IntentService {
private ActivityRecognitionScan myascan;
private Intent inIntent;
private static long ACTIVITY_LOG_INTERVAL = 30000L;
private static JsonEncodeDecode jsonencoder = new JsonEncodeDecode();
public ActivitySensor() {
super("ActivitySensor");
}
#Override
public void onCreate(){
super.onCreate();
Log.d("Activity-Logging", "--- onCreate");
try {
myascan = new ActivityRecognitionScan(getApplicationContext());
myascan.startActivityRecognitionScan();
} catch (Exception e) {
Log.e("[Activity-Logging]","----------Error:"+e.getLocalizedMessage());
e.printStackTrace();
}
}
#Override
public void readSensor() {
// Log.e("Activity-Logging", "ActivityRecognitionResult.hasResult: "+String.valueOf(ActivityRecognitionResult.hasResult(inIntent)));
if (ActivityRecognitionResult.hasResult(inIntent)) {
ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(inIntent);
DetectedActivity activity = result.getMostProbableActivity();
final int type = activity.getType();
String strType = new String();
switch(type){
case DetectedActivity.IN_VEHICLE:
strType = "invehicle";
break;
case DetectedActivity.ON_BICYCLE:
strType ="onbicycle";
break;
case DetectedActivity.ON_FOOT:
strType = "onfoot";
break;
case DetectedActivity.STILL:
strType = "still";
break;
case DetectedActivity.TILTING:
strType ="tilting";
break;
case DetectedActivity.UNKNOWN:
strType ="unknown";
break;
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
Editor edt = prefs.edit();
String previousActv = prefs.getString("PREVIOUS_ACTIVIY","");
long previousDate = prefs.getLong("PREVIOUS_DATE", 0);
if (previousActv.length()==0){ // nothing was in the string and it is the first time just initialize
previousActv = strType;
previousDate = new Date().getTime();
// Log.e("-----FIRST TIME: type:", previousActv+" date:"+String.valueOf(previousDate));
edt.putString("PREVIOUS_ACTIVIY", strType);
edt.putLong("PREVIOUS_DATE", previousDate);
edt.commit();
}else {
if (!strType.equalsIgnoreCase(previousActv)){
Date readablePrevDate = new Date(previousDate);
Date nowDate = new Date();
String jsonstr = jsonencoder.EncodeActivity("Activity", readablePrevDate, nowDate, strType, activity.getConfidence());
// Log.e("[Activity-Logging] ----->",jsonstr);
edt.putString("PREVIOUS_ACTIVIY", strType);
edt.putLong("PREVIOUS_DATE", nowDate.getTime());
edt.commit();
DataAcquisitor.dataBuff.add(jsonstr);
}
}
}
}
#Override
protected void onHandleIntent(Intent intent) {
Log.d("Activity-Logging", "--- onHandleIntent"+ "---"+intent.getAction());
intent.putExtra("LOG_INTERVAL",ACTIVITY_LOG_INTERVAL );
intent.putExtra("STOP",false);
inIntent = intent;
readSensor();
}
#Override
public void onDestroy(){
Log.d("Activity-Logging", "--- onDestroy");
myascan.stopActivityRecognitionScan();
myascan=null;
//super.onDestroy();
}
}
This is the class that calls the Google Activity Recognition Service:
ActivityRecognitionScan implements GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener {
private Context ctx;
private static final String TAG = "ActivityRecognition";
private static ActivityRecognitionClient actrecClient;
private static PendingIntent callbackIntent;
private long ACTIVITY_LOG_INTERVAL=30000;
public ActivityRecognitionScan(Context context) {
ctx=context;
}
public void startActivityRecognitionScan(){
int resp = GooglePlayServicesUtil.isGooglePlayServicesAvailable(ctx);
if(resp == ConnectionResult.SUCCESS){
actrecClient = new ActivityRecognitionClient(ctx, this, this);
if (!actrecClient.isConnected()){
actrecClient.connect();
} else{
Log.e("ActivityRecognitionScan"," ---Activity recognition client is already connected");
}
}else{
Log.e("[Activity-Logging]", "Google Play Service hasn't installed");
}
}
public void stopActivityRecognitionScan(){
try{
if (actrecClient.isConnected() || actrecClient.isConnecting() ){
actrecClient.removeActivityUpdates(callbackIntent);
actrecClient.disconnect();
}
} catch (Exception e){
e.printStackTrace();
}
}
#Override
public void onConnectionFailed(ConnectionResult result) {
Log.e("[ActivityRecognitionScan]", "Connection Failed");
}
#Override
public void onConnected(Bundle connectionHint) {
try{
Intent intent = new Intent(ctx, ActivitySensor.class);
Bundle bundle = intent.getExtras();
callbackIntent = PendingIntent.getService(ctx, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
long interval = 5000;
if ( null!= bundle && bundle.containsKey("LOG_INTERVAL") ){
interval = bundle.getLong("LOG_INTERVAL");
}
actrecClient.requestActivityUpdates(interval, callbackIntent);
actrecClient.disconnect();
}catch(Exception ex){
Log.e("[Activity-Logging]","Error in requesting Activity update "+ex.getMessage());
ex.printStackTrace();
}
}
#Override
public void onDisconnected() {
callbackIntent.cancel();
actrecClient = null;
Log.e("[ActivityRecognitionScan]","---onDisconnected");
}
}
IntentService automatically stops itself on completion of onHandleIntent as per the source code (see ServiceHandler.handleMessage()) as per the description of an IntentService:
Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.
Use a Service if you want it to run continuously in the background.
You have 2 issues with your code that is causing the problem you are experiencing.
When activity is detected, the pending intent that is called calls (and creates, since it is an IntentService) ActivitySensor. The onCreate will connect another ActivityRecognitionClient, which is unnecessary. This causes another activity to be detected which causes your logging loop.
You should separate the creation of the ActivityRecognitionClient from the handling of the detected activity. You don't need to keep recreating it as subsequent detections will use the same PendingIntent. This will prevent the logging loop.
I've been using Android open source service example. I just need to use it to send notification to user, but strange, it allocates lots of memory. I checked in Running Services, and it is almost 20MB (if i set ACTION_BACKGROUND) or 30MB (if i set ACTION_FOREGROUND)...
What should i do to reduce this memory usage?
I've already read this discussion I have no bitmap or webview.
Here's my service:
/**
* This is an example of implementing an application service that can
* run in the "foreground". It shows how to code this to work well by using
* the improved Android 2.0 APIs when available and otherwise falling back
* to the original APIs. Yes: you can take this exact code, compile it
* against the Android 2.0 SDK, and it will against everything down to
* Android 1.0.
*/
public class NotificationService extends Service {
static final String ACTION_FOREGROUND = "com.example.android.apis.FOREGROUND";
static final String ACTION_BACKGROUND = "com.example.android.apis.BACKGROUND";
private static final Class<?>[] mSetForegroundSignature = new Class[] {
boolean.class};
private static final Class<?>[] mStartForegroundSignature = new Class[] {
int.class, Notification.class};
private static final Class<?>[] mStopForegroundSignature = new Class[] {
boolean.class};
// protected NotificationManager mNM;
private Method mSetForeground;
private Method mStartForeground;
private Method mStopForeground;
private Object[] mSetForegroundArgs = new Object[1];
private Object[] mStartForegroundArgs = new Object[2];
private Object[] mStopForegroundArgs = new Object[1];
void invokeMethod(Method method, Object[] args) {
try {
method.invoke(this, args);
} catch (InvocationTargetException e) {
// Should not happen.
Log.w("ApiDemos", "Unable to invoke method", e);
} catch (IllegalAccessException e) {
// Should not happen.
Log.w("ApiDemos", "Unable to invoke method", e);
}
}
/**
* This is a wrapper around the new startForeground method, using the older
* APIs if it is not available.
*/
void startForegroundCompat(int id, Notification notification) {
// If we have the new startForeground API, then use it.
if (mStartForeground != null) {
mStartForegroundArgs[0] = Integer.valueOf(id);
mStartForegroundArgs[1] = notification;
invokeMethod(mStartForeground, mStartForegroundArgs);
return;
}
// Fall back on the old API.
mSetForegroundArgs[0] = Boolean.TRUE;
invokeMethod(mSetForeground, mSetForegroundArgs);
// mNM.notify(id, notification);
}
/**
* This is a wrapper around the new stopForeground method, using the older
* APIs if it is not available.
*/
void stopForegroundCompat(int id) {
// If we have the new stopForeground API, then use it.
if (mStopForeground != null) {
mStopForegroundArgs[0] = Boolean.TRUE;
invokeMethod(mStopForeground, mStopForegroundArgs);
return;
}
// Fall back on the old API. Note to cancel BEFORE changing the
// foreground state, since we could be killed at that point.
// mNM.cancel(id);
mSetForegroundArgs[0] = Boolean.FALSE;
invokeMethod(mSetForeground, mSetForegroundArgs);
}
#Override
public void onCreate() {
// mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
try {
mStartForeground = getClass().getMethod("startForeground",
mStartForegroundSignature);
mStopForeground = getClass().getMethod("stopForeground",
mStopForegroundSignature);
return;
} catch (NoSuchMethodException e) {
// Running on an older platform.
mStartForeground = mStopForeground = null;
}
try {
mSetForeground = getClass().getMethod("setForeground",
mSetForegroundSignature);
} catch (NoSuchMethodException e) {
throw new IllegalStateException(
"OS doesn't have Service.startForeground OR Service.setForeground!");
}
}
#Override
public void onDestroy() {
// Make sure our notification is gone.
stopForegroundCompat(1);
}
// This is the old onStart method that will be called on the pre-2.0
// platform. On 2.0 or later we override onStartCommand() so this
// method will not be called.
#Override
public void onStart(Intent intent, int startId) {
handleCommand(intent);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
handleCommand(intent);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
#Override
public void onRebind(Intent intent) {
super.onRebind(intent);
handleCommand(intent);
}
void handleCommand(Intent intent) {
if (intent == null)
return;
if (ACTION_FOREGROUND.equals(intent.getAction())) {
DBHelper db = new DBHelper(this);
String lastTime = db.getLastVisitTime();
if(!lastTime.equals("-1")) {
new Notifications(this).InviteUser();
}
String target = db.getTargetValue();
if(target.equals("")) {
new Notifications(this).TargetlessNotification();
}
db.close();
/*
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getString(R.string.app_name);
CharSequence description = getString(R.string.recall_user);
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.icon, text, System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this notification PendingIntent
contentIntent = PendingIntent.getActivity(this, 1, new Intent(this, YKEYarinaSaklaActivity.class), 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(this, text, description, contentIntent);
// Set properties of notification
notification.flags = Notification.FLAG_INSISTENT | Notification.FLAG_AUTO_CANCEL;
notification.defaults |= Notification.DEFAULT_ALL;
startForegroundCompat(1, notification);
*/
} else if (ACTION_BACKGROUND.equals(intent.getAction())) {
stopForegroundCompat(1);
}
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
P.S.: I don't know if it's relevant or not but i'm starting this service onDestroy of my app, so it'll send notification to user on a specific time with AlarmManager. (So it should not be killed to avoid AlarmManager deleting my notification.)
I've tried to simplfy my service as possible as i can, but the situation is still the same... Then i realize that somehow, usage of memory decrease by itself... So, if i have no option, i could except that.
public class NotificationService2 extends Service{
private String target, lastTime, notifCheck, notifCheck2;
#Override
public void onStart(Intent intent, int startId) {
Bundle extras = intent.getExtras();
if(extras != null) {
this.lastTime = extras.getString("lastTime");
this.target = extras.getString("target");
this.notifCheck = extras.getString("notifCheck");
this.notifCheck2 = extras.getString("notifCheck2");
}
handleCommand(intent);
super.onStart(intent, startId);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Bundle extras = intent.getExtras();
if(extras != null) {
this.lastTime = extras.getString("lastTime");
this.target = extras.getString("target");
this.notifCheck = extras.getString("notifCheck");
this.notifCheck2 = extras.getString("notifCheck2");
}
handleCommand(intent);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
handleCommand(intent);
return null;
}
#Override
public void onRebind(Intent intent) {
super.onRebind(intent);
handleCommand(intent);
}
void handleCommand(Intent intent) {
if (intent == null)
return;
String lastTime = this.lastTime;
String notifCheck = this.notifCheck;
String target = this.target;
String notifCheck2 = this.notifCheck2;
if(lastTime != null && notifCheck != null) {
if(!lastTime.equals("-1") && !notifCheck.equals("1"))
new Notifications(this).InviteUser();
} else this.stopSelf();
if(target != null && notifCheck2 != null) {
if(target.equals("") && !notifCheck2.equals("1"))
new Notifications(this).TargetlessNotification();
} else this.stopSelf();
}
}
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);
}