Android Bug/Architectural Issue: How do I handle multiple versions of my own Content Provider?

I am writing a component that makes certain functionality available to any application running on an Android device (e.g. an advertising service, stock ticker cache, Snowball server, etc). cp1.gif This functionality is useful for application developers, but not interesting to actual users i.e. my component should be included as part of a new application, but not require the end user to explicitly install the additional component themselves. cp2.gif I expect that over time multiple applications installed on the phone will want to communicate with my component.  As each new application will have a different certificate, I want to communicate between applications using an Android Content Provider.  To save resources on the device (networking, caching, etc) only one instance of my component should be appointed to handle all queries. cp3.gif This works well as Android only registers the first Content Provider for a given URI and then ignores the rest (throwing an “WARN/PackageManager: Skipping provider name xxxx name already used” error each time a new one is installed). cp4.gif However if the registered Content Provider is uninstalled, it will immediately break all the other applications that rely on it, even though other instances of the component still exist. Questions:

  • Does anyone have any suggestions on how to better handle this situation?
  • If I could reregister Content Providers I could handle situations like this, and upgrade components when newer versions are installed.  Perhaps the Android OS could also handle this situation better, by tracking Content Provider naming collisions?
  • Should I be looking at other communication methods to solve this issue?
Note: This has been posted on the following discussion boards:
  • Android Developers (click)
  • Android-platform (click)
Edit 20090108 Just to clarify, the attribute “android:multiprocess” in the “provider” tag seems to have a behaviour that is not relevant to this problem, i.e. it prevents multiple instances of the same Content Provider from existing, rather than affecting multiple Content Providers trying to use the same namespace. Edit 20090109 Thanks everyone for the replies.  Just to clarify: I am writing an advertising server component at the moment, but other projects that I have in the pipeline essentially have the same requirement. »Dianne Hackborn - As far as naming issues, this is why you should use an authority in a namespace/domain you own, so that there will not be naming collisions.>Dianne Hackborn - …any client app that is relying on a component that is not part of the base platform needs to deal with the error cases if that component does not exist.>Peli - Hm, I think you end up with unnecessary code duplication if you include your component / content provider in every application.>Peli - I think it would be better if every application that wants to use your component shows a small dialog “You need to install the following component…” with a button that brings you to the download page of the Market for your component…
  • No user will download a 3rd party advertising server (or any non-core functionality) just to run a game.
  • Installation is already a convoluted process and should only take place once.  Additional APK installations require more downloading, application permission settings, button pressing, confusion, etc.
  • No client of mine will accept a solution that asks the user to jump through any additional hoops (e.g. even the Android Developer Challenge only permitted a single executable).
  • »Peli - At OpenIntents we try to resolve dependencies as late as possible - i.e. not at installation time, but the first time the user actually wants to use a particular feature (they may never use it in which case there is no need to install it).>Peli - I think in the long run Market should have better tools for resolving compulsory dependencies automatically, but for the moment I think this is a good compromise for developers and end users.>Dianne Hackborn - Oh wait if what is being talked about is having the same content provider in multiple applications, that is completely the wrong approach.  For a given authority there must be one and only one content provider implementation, living in a single specific .apk.>Muthu Ramadoss - AFAIK, a Content Provider is tied up to a unique Authority.  If you want to support multiple URI’s you do so accordingly by providing different URI paths using the same authority.Edit 20090109 (#2) »Peli - Indeed, for an advertising server, it makes more sense to include that in every application separately, for the reasons you mentioned. I could suggest the following…and all applications can access that one content provider. Would this sound like a suitable solution?>Mark Murphy - Since the content in question is going on /sdcard (right? right?!?), solve the problem by managing the space, not managing the components. Since you already require each application to have your own code anyway, just use a regular Java class to access the /sdcard data store and dump the ContentProvider interface.>Peli - Ad images and banners should be placed on the SD card, but private data for accounting (number of times an ad had been shown, click- through-rate, last ad shown, etc.) could be stored in a content provider, and it makes sense to share this content provider among several applications installed (e.g. so that the end user is not presented the same ad in every application they launch).Edit 20090115 - Solution The simplest solution (not using intents) is to instruct each user of my component to define a new content provider: content://myComponentNamespace.theirApplicaitonNamespace Android lets me search through all available content providers, which can be string matched to my own component namespace. context.getPackageManager().queryContentProviders(null, 0, 0); If I find more than one valid Content Provider I can then pass around Synchronisation information (validity, compatibility, version, status, etc) using any of the ContentProvider class methods (I used getType()). The only problem with this approach is that the application developer who imports my component (as a JAR) will have to define their own custom Content Provider.  This is done by extending the MyComponentContentProvider class (from my JAR) and defining a CONTENT_URI variable. package test; import com.whitemice.MyComponentContentProvider; public class TheirContentProvider extends MyComponentContentProvider { public static final Uri CONTENT_URI = Uri.parse( "content:// myComponentNamespace.theirApplicaitonNamespace"); } As well as adding the custom provider to the Android manifest. --provider android:name="test.TheirContentProvider" android:authorities=" myComponentNamespace.theirApplicaitonNamespace" As this is a work time project, I will have to check if I can release the synchronisation code as a working example.

    Edit 20090116 »Peli  - Probably you should not mis-use getType()… String out), but I will now use query() instead.  However, as my application is not compatible with any other software (i.e. my data exchange has no MIME type), I would be interested if anyone could provide a concrete example of where an inappropriate getType() response might cause problems.

    Also, for simplicity I am not using intents.

    »Dianne Hackborn - …Along those same lines, looking for your own special text in the authority to identify content providers you are going to work with is also kind-of screwy.  Typically how we do this is attach meta-data to the component that whoever is interested in identifying specifically tagged components looks for.  Look at the way input method service components are discovered in Cupcake as an example.

    Here is a small component discovery rewrite, now using meta-data to mark valid Content Providers:

    ####AndroidManifest.xml####

    —provider— android:name=”test.TheirContentProvider” android:authorities=”myComponentNamespace.theirApplicaitonNamespace” —meta-data android:name=”type” android:value=”myComponentNamespace”— —/provider—

    ####Java Code####

    List—ProviderInfo— pis = androidContext.getPackageManager().queryContentProviders(null, 0, PackageManager.GET_META_DATA);

    for(ProviderInfo pi:pis) { Bundle md = pi.metaData; if (md != null && md.getString(“type”) != null && md.getString(“type”).equals(“myComponentNamespace”)) {

        //Congratulations you have now found a valid Content Provider //Note: keep looking as you might find more

        } }

    The upside of this is that the application developer can now deploy my Content Provider using any URI namespace they choose.  However, I will still recommend the previous naming scheme to prevent damaging naming conflicts.

    »Dianne Hackborn - …Also, have you thought about security at all?  What kind of information is stored on the content provider?  Who can access it?

    My current application (an advertising server) treats this as an open/free to use API, with risk scenarios being managed mainly on the server side.

    My next application (component for peer-to-peer data sharing) will require a custom Android Permission for sending data.  This is similar to the “android.permission.INTERNET” permission whereby the user still gets a say as to whether the application can send data off the phone.

    I am not yet confident enough in the security capabilities of Android and/or my knowledge of them, to implement an application that handles confidential data.

    0 notes