Question

I’m developing an Air mobile application for Android which uses Air Native Extensions (ANE). From the ANE I call a function that creates a Pending Intent which is passed to a Notification. Next, the native function starts a service and passes it the Pending Intent and the Notification. Here’s the code for that part:

public class StartServiceFunction implements FREFunction {

    @Override
    public FREObject call(FREContext context, FREObject[] args) {
        Log.d("MyApplicationStartServiceFunction", "call(StartService)");
        
        Context applicationContext = context.getActivity().getApplicationContext();
        
        try {
                String appPackageName = args[0].getAsString();
                String applicationID = args[1].getAsString();
                String notificationMessage = args[2].getAsString();
                String statusServiceURL = args[3].getAsString();
                String triggerResponse = args[4].getAsString();
                
                Intent appLaunchIntent = applicationContext.getPackageManager().getLaunchIntentForPackage(appPackageName);      
                PendingIntent pendingLaunchIntent = PendingIntent.getActivity(applicationContext, 0, appLaunchIntent, 0);
                
                int icon = context.getResourceId("drawable.notification_icon");
                
                Notification serviceNotification = new Notification.Builder(applicationContext)
                                                            .setContentTitle(applicationID)
                                                            .setContentText(notificationMessage)
                                                            .setSmallIcon(icon)
                                                            .setContentIntent(pendingLaunchIntent)
                                                            .build();
                Log.d("MyApplicationStartServiceFunction", "call(StartService): notification Created");

                Intent serviceIntent = new Intent(applicationContext, MyApplicationService.class);
                serviceIntent.putExtra("serviceNotification", serviceNotification);
                serviceIntent.putExtra("statusServiceURL", statusServiceURL);
                serviceIntent.putExtra("triggerResponse", triggerResponse);
                serviceIntent.putExtra("pendingLaunchIntent", pendingLaunchIntent);
                
                applicationContext.startService(serviceIntent);  
                Log.d("MyApplicationStartServiceFunction", "call(StartService): startService issued");
                
                return FREObject.newObject(true);
        } catch (Exception e) {
            Log.d("MyApplicationStartServiceFunction", "Exception: " + e.getMessage());
            e.printStackTrace();
        }
        
        try {
            return FREObject.newObject(false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return null;
    }
}

That part of the code works like a charm.

Next, when the onStartCommand() method is invoked within the service, I retrieve the Pending Intent and the Notification from the Intent passed as an argument to the mentioned method. The point of having the notification is to set the service as a foreground process (startForeground()), which it does, and also let the user reenter the application when he hits the notification. The point of passing the pending intent is that when certain conditions are met, the service can activate it (Pending Intent) and make the application as the main visible process. Here is the code I use for that (please keep in mind that part of it is omitted since it’s not a problem and it’s working ok):

public class MyApplicationService extends Service implements TriggeredResponseListener {

        private Timer timer;
        private PendingIntent pendingLaunchIntent;
        private String statusServiceURL;
        private String triggerResponse;

        @Override
        public IBinder onBind(Intent arg0) {
                Log.d("MyApplicationService", "onBind()");
                return null;
        }
        
        @Override
        public void onCreate() {
                Log.d("MyApplicationService", "onCreate()");
                super.onCreate();
        }
        
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
                Log.d("MyApplicationService", "onStartCommand(): " + intent.toString());
                Notification serviceNotification = intent.getParcelableExtra("serviceNotification");            
                startForeground(1,serviceNotification);
                Log.d("MyApplicationService", "onStartCommand(): startForegroundService");
                
                pendingLaunchIntent = intent.getParcelableExtra("pendingLaunchIntent");
                statusServiceURL = intent.getStringExtra("statusServiceURL");
                triggerResponse = intent.getStringExtra("triggerResponse");
                Log.d("MyApplicationService", "onStartCommand(): getParcelableExtras");
                
                if(timer == null)
                {
                        timer = new Timer("Printer");
                        HttpServiceTask serviceTask = new HttpServiceTask(statusServiceURL, triggerResponse);
                        serviceTask.addEventListener(this);
                        timer.schedule(serviceTask, 0, 2000);
                        Log.d("MyApplicationService", "onStartCommand(): startTimer");
                }
                
                return START_REDELIVER_INTENT;
        }
        
        public void handleTriggeredResponseEvent(TriggeredResponseEvent eventObject) {
                Log.d("MyApplicationService", "handleEvent()");
                destroyTimer();
                HttpServiceTask serviceTask = eventObject.httpServiceTask;
                serviceTask.removeEventListener(this);
                Log.d("MyApplicationService", "handleEvent(): launching pendingLaunchIntent");
                try{
                        pendingLaunchIntent.send();
                } catch (Exception e) {
                        Log.d("MyApplicationService", "handleEvent(): " + e.getMessage());
                        e.printStackTrace();
                }                               
                this.stopSelf();
                Log.d("MyApplicationService", "handleEvent(): stopSelf");
        }
        
        private void destroyTimer() {
                if (timer != null){
                        timer.cancel();
                        timer = null;
                }
        }

        @Override
        public void onDestroy() {
                Log.d("MyApplicationService", "onDestroy():");
                destroyTimer();
                Log.d("MyApplicationService", "onDestroy(): destroyTimer():");
                super.onDestroy();
        }
}

That code ALMOST works fine. It works great on most of the situations but not on every one of them.

The method involving the actions related to the Pending Intent is handleTriggeredResponseEvent() so let’s focus on that one. As you can see, when this handler gets called, the Pending Intent is sent. Remember that it should do exactly the same thing as the Notification since it was made from the same Pending Intent.

If the Air mobile application hasn’t been closed from the Recent Applications List (hold down Home button), the Pending Intent will be activated successfully from the handleTriggeredResponseEvent() and the Air application will be the main application visible to the user once again.

The problem comes when you close the Air mobile application from the Recent Applications List. When the conditions are met and the method handleTriggeredResponseEvent() is invoked, everything is ran correctly, even the pendingLaunchIntent.send() runs without throwing any exceptions. The only difference is that the application is not launched. I can only see a black screen (Air runtime launched?) but not the Air mobile application running.

On another side, when I closed the Air application from the Recent Applications List and I activate the service notification on the notifications bar, the application gets launched correctly, even though it uses the same Pending Intent as the handleTriggeredResponseEvent() method.

I checked all the Log statements and the code is running without any errors and on the same way in all scenarios.

How can this be? Shouldn’t the Pending Intent sent from the handleTriggeredResponseEvent() method have the same effect as the one sent when the notification of the service is activated?

Clearly I am missing something. Could it be something within the Application Manifest XML? Next is the declaration of the service within the manifest:

<application android:debuggable="true">
              <service android:enabled="true" android:exported="true" android:name="com.mycompany.myextension.services.MyService">    
                        <intent-filter>
                                 <action android:name="air.com.mycompany.myextension.DO_CUSTOM_ACTION"/>
                        </intent-filter>
                </service>
</application>

Any help will be deeply appreciated. Thanks in advance.

EDIT: errors on LogCat

I could capture the LogCat errors and here they are:

01-22 15:21:27.625: I/ActivityManager(381): Killing 25792:air.QOE/u0a122: remove task 
01-22 15:21:27.664: W/ActivityManager(381): Exception when starting activity air.QOE/.AppEntry 
01-22 15:21:27.664: W/ActivityManager(381): android.os.DeadObjectException 
01-22 15:21:27.664: W/ActivityManager(381): at android.os.BinderProxy.transact(Native Method) 
01-22 15:21:27.664: W/ActivityManager(381): at android.app.ApplicationThreadProxy.scheduleLaunchActivity(ApplicationThreadNative.java:743) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityStack.realStartActivityLocked(ActivityStack.java:821) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityStack.startSpecificActivityLocked(ActivityStack.java:949) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityStack.resumeTopActivityLocked(ActivityStack.java:2321) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityStack.resumeTopActivityLocked(ActivityStack.java:1862) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityStack.resumeTopActivityLocked(ActivityStack.java:1837) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityStack.completePauseLocked(ActivityStack.java:1411) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityStack.completePauseLocked(ActivityStack.java:1356) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityStack.activityPaused(ActivityStack.java:1266) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityManagerService.activityPaused(ActivityManagerService.java:4609) 
01-22 15:21:27.664: W/ActivityManager(381): at android.app.ActivityManagerNative.onTransact(ActivityManagerNative.java:401) 
01-22 15:21:27.664: W/ActivityManager(381): at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:1729) 
01-22 15:21:27.664: W/ActivityManager(381): at android.os.Binder.execTransact(Binder.java:367) 
01-22 15:21:27.664: W/ActivityManager(381): at dalvik.system.NativeStart.run(Native Method) 
01-22 15:21:27.726: I/ActivityManager(381): Process air.QOE (pid 25792) has died and restarted (pid 25900).

At the begging it seemed that the DeadObjectException was telling me that there weren't any activities to handle the intent, but later, I added the following code:

PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
Log.d("EasyControlService", "launchApplication(): Activities ready for intent: " + activities.size());
if (activities.size() == 0){
    Log.d("EasyControlService", "launchApplication(): there are no activities ready for intent");
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}

which means that the activity is still present.

Was it helpful?

Solution

Sebastian, from what I can see this probably is happening because you are running the service in the same process. You don’t even need a Pending intent to launch the application, you could just do it with a regular intent. The only thing you have to do is to make the service ran on a separate process. You do that adding the android:process=”:name_of_the_process” on the <service> tag on the manifest.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top