Monday, November 29, 2010

Finally a good start of Monday :-). Resolved a weird problem of Android Framework which was disturbing me through out the weekend. One of my team mate (Sandeep) did exceptional R&D and helped me to come to this conclusion.

[Problem]

NoSuchMethodError exception while I was trying to access ISnsRequester.aidl of SNSProvider.

In Android 2.2 (Froyo) some of the method definitions of ISnsRequester.aidl file has been changed. A new input argument targetSubID has been added to commentPost() and commentRetrieve() methods. But in our Android source code repository by mistake Telephony framework was still using older version of ISnsRequester.aidl (i.e. of Android 2.1). This was the main cause of this problem.

For all framework classes/packages Android build system (when you try to make a binary) makes entry into Android\frameworks\base\api\current.xml

In fact current.xml acts as Look up for any method or class reference. Thus definition of commentPost() and commentRetrieve() methods were conflicting between current.xml and latest AIDL in SNSProvider. When my code was trying to call commentPost() and commentRetrieve() methods, being the look up for Framework, current.xml is being searched for the method definition instead of latest AIDL in SNSProvider. This was causing force close with NoSuchMethodError exception.

[Conclusion]

If any AIDL is part of Android Frmaework be careful about any duplicated resource or method definition conflicts and verify the current.xml after build is over.

It’s definitely SSL certificate error, but it took me a while to figure out the actual cause and the resolution. So thought to share the finding. As we all do :-), I did Google and got this site helpful- http://www.java-samples.com/showtutorial.php?tutorialid=210 it explains a generic solution to this problem.

Check whether you can find the MD5 (noted in the above step) in the displayed list of registered certificates. If you find the match, this indicates, Google Authentication SSL certificate has been successfully added to JDK Keystore.

Now, try again that sample application. It should be able to do the authentication successfully. Hope this helps.

As we all know Android Service concept is good for tasks running in the background. Based on scope of accessibility services are of 2 types- Local and Remote. As the name indicates, Local Service can be accessed only within the process or application which it is part of. On the other hand, Remote Services can be accessed from different process/application and thus it posses inter-application communication use cases e.g. you are running one service to provide Geo Location for Cell ID. So, any application can read the Cell-ID and access your service to get Geo Co-ordinates.

As the usability increases so as the building blocks for creating Remote Service. The first challenge is to do inter-process communication so that one application running on one process can access services running on another process. To achieve this IPC (Inter Process Communication) Android has provided a concept of AIDL Interface which acts as the communication interface between 2 processes.

Following section explains steps to create a Remote Service and an Application to call that service-

Steps to create a Remote Service

1.Create an AIDL Interface. Define methods which you are planning to expose to client applications. AIDL interface syntax is similar to Java. It supports most of the Java primitive data types and does not need explicit import statements. Though for custom classes you need to add specific import statements.

a)Create the interface file, PrasRemote.aidl under pras.service.aidl package. I have defined one method sayHello(String name) which I’ll access from client application and pass some name and it will return “Hello [Name]”

packagepras.service.aidl;

interface PrasRemote {

String sayHello(String name);

}

b)If there is no syntax error, it will generate a Java file under gen folder pras.service.aidl.PrasRemote.java

2.Define the implementation of the methods defined in Step 1.

package pras.service;

import android.os.RemoteException;

import pras.service.aidl.PrasRemote;

/**

*ImplementationofAIDLinterfacemethod

*

*@authorprasanta

*/

publicclass MethodImpl extends PrasRemote.Stub {

public String sayHello(String name) throws RemoteException {

return"Hello "+ name;

}

}

3.Define the Service.

package pras.service;

import android.app.Service;

import android.content.Intent;

import android.os.IBinder;

import android.util.Log;

/**

*TheactualServiceclasswhichwillreceiveremotecallsfromClients

*

*@authorprasanta

*/

publicclass PrasService extends Service {

String TAG = "PrasService";

@Override

public IBinder onBind(Intent intent) {

Log.i(TAG, "onBind_________________________");

//Return Instance of MethodImpl.java which implements AIDL Interface

returnnew MethodImpl();

}

}

4.Add entries into Manifest for the service defined in Step 3

[serviceandroid:name=".PrasService"]

[intent-filter]

[actionandroid:name="com.my.service.REMOTE_SERVICE"/]

[/intent-filter]

[/service]

An intent action having value com.my.service.REMOTE_SERVICE need to be used to call this Service from Client Application

Steps to Call the Remote Service-

1.Create a new Android project.

2.Copy the AIDL file i.e. PrasRemote.aidl file into this new Application Project. Please make sure you are keeping the .aidl file in the same package name as you define in RemoteService application i.e. pras.service.aidl Otherwise it might causejava.lang.SecurityException : Binder invocation to an incorrect interface…

3.Create a Listener class which implements ServiceConnection Interface and handle call backs when connected to Remote Service. ServiceConnection interface has 2 methods- onServiceConnected(ComponentName cmp, IBinder binder) which will get invoked when client is connected to RemoteService andonServiceDisconnected(ComponentName cmp) if unexpectedly it gets disconnected.

onServiceConnected will provide a IBinder instance which will be casted to get instance of the Interface i.e. PrasRemote. Now you can call the method i.e.sayHello() using this instance.

PrasRemote remoteIntf = PrasRemote.Stub.asInterface(binder);

try{

String remoteMsg = remoteIntf.sayHello("Prasanta");

}catch(RemoteException ex){}

4.Define an UI to Bind and Unbind RemoteService. Execute following when user click on “Bind_Remote_Service” button-

RemoteServiceCon serCon = new RemoteServiceCon(this);

Intent intent = new Intent("com.my.service.REMOTE_SERVICE");

bindService(intent, serCon, Context.BIND_AUTO_CREATE);

To unbind-

unbindService(serCon);

Kindly note:bindService() and unbindService() are methods of Context.java