My goal is to have a main application and a selection of independent separate data packs, which are available on demand as APK and have their own entry in the Play Store each. It's completely up to the user which, and how many, of these they care to install. Think language packs, keyboard layouts, dictionaries, this sort of thing. The application would always check what data packs are installed and give the user the choice of which one to use.
I thought that the data APKs would not provide any activities but would contain a Service that allows access to a ContentProvider of some sort. But it's all new to me and there's a lot of terminology that confuses me: is this a receiver? A provider? Both of these seem to fit my purpose to some extent. Do I need to have this service (one per data pack) run in the background, waiting for broadcast calls, or can I just somehow inform Android it exists, so it would launch it when this particular query comes from my application?
It would be ideal if nothing would have to be run in the background, possibly even during the listing phase. Then one data file would be chosen and the corresponding APK would only run for as long as the data is being accessed. All the data packs are of the same sort (dictionaries) and need not provide any further functionality than to provide some static metadata and then give access to a raw file.
All of the information I could find relates to "extension APK"s but that's orthogonal to what I want.
I thought that the data APKs would not provide any activities but would contain a Service that allows access to a ContentProvider of some sort.
You do not need a Service to provide access to a ContentProvider.
is this a receiver? A provider?
I do not know what "this" is in that sentence. A ContentProvider uses a <provider> element in the manifest. A Service uses a <service> element in a manifest.
Do I need to have this service (one per data pack) run in the background, waiting for broadcast calls, or can I just somehow inform Android it exists, so it would launch it when this particular query comes from my application?
None of the above. Your main app finds out about the existence of these "data pack" APKs via PackageManager. For example, you might use a standard naming convention for the application ID/package names for those "data pack" APKs, then use getInstalledPackages() to find out which "data pack" APKs are installed.
From there, you could:
Use createPackageContext() to access assets and resources in the "data pack"
Generate a Uri pointing to the "data pack" APK's ContentProvider, then use a ContentResolver to access content published by the APK (e.g., openInputStream(), query())
Use other forms of IPC (broadcasts, started services, bound services) to interact with the "data pack"
All the data packs are of the same sort (dictionaries) and need not provide any further functionality than to provide some static metadata and then give access to a raw file.
I do not know why you would distribute this material in the form of APK files via the Play Store. Just download the material from your own Web server, in some convenient format (e.g., ZIP archive).
Related
Background: we are porting an enterprise system to have android clients. The architecture for windows and html is based around a core library that does the hard business logic but no user interaction at all, and we use programs or single page web apps to provide the user interface and simply call the core API library to actually do stuff.
The "core" is implemented as a shared library on windows and built into each app. If we mirror this and use a java library, we need to share files using external storage, which is a not permitted as data needs to be reasonably secure. (Nb data is binary data, not Sql database, in case that is relevant)
So we thought about using a bound service, and using intents, content provider etc, but it seems (from googling) we must then distribute the background service separately the user interface app, but this seems terrible experience for new users. However, a bound service seems ideal from all other angles.
We also cannot guarantee which apps a user might download, we will have at least 10 individual apps all doing logically different things, but referencing similar data.
In brief:
lots of individual apps all wanting access to same data
no control over which apps are downloaded
using external data is not permitted as data should be semi secure
using sqllite might not work as data is long binary chunks ( eg 3Mb plus ). (Ref: How to share data across a group of applications in Android )
some data files are big and do not want every app to download a private copy
some data changes dynamically, say every 15min
core business logic is big and complex, cannot be distributed in source form, lib/jar ok though.
the windows solutions all use network IO to an application server, but we want to avoid as much network traffic as possible by storing data locally.
How can we bundle a bound service in each and every user interface app we distribute? Or is there a different way to approach this whole design?
I think that there is a few number of options that you can explore:
1) I never have done this before though this seems possible as Android is package based.
First you need to use the same main package across all your apps though each app must be in a separated sub package, e.g. : main -> au.com.myapp.main and the app actually have it's first screen on app1 -> au.com.myapp.main.app1 .
Create on your main app a method(s) that will look for those extra packages (within your project), as it find something you create a trigger that will display a item on the menu. Each app should have the same main packages and main activity, as it will be responsible for enable the user have access to the others and all of them can share the same preferences, files folders and Database.
When installing the same packages should be overrides though those different ones should keep intact. You should have all the 'main' classes for each app, not the real main one declared on your manifest (that will be quite big depending on the amount of activities in all your apps) with those packages.
2) You can using Spongy Castle, create a shared zone (folder) where you create the DB and write your settings or files, encrypting everything with a key (strong one or using RSA) that might be made by the user or provided once for your company at the very first run. You must decide how to handle this.
3) You also can use the share id in all your apps and each app before run perform look up for all packages (it's possible to do) to know and if and what packages exist to check if there is a DB with data in that package.
4) Not really nice though create a background service that keep updated all tables in all apps (sharing id or using content provider), you can user AlarmManager to trigger it in intervals rather keep it on at all times and you have all apps.
My last project had a similar requirement though as the user had to login to do anything, I opted for the option 3 and each data pertinent exclusively to each app went the app DB.
I hope this helps.
Android Content Provider system allows other apps to access the data of an app.
Is there any equivalent, or similar approach in WinRT?
I read App contract and extensions and Share contract in MSDN, but it has to execute provider app (an app holds data source) firstly, and then select target app afterward.
In Android Content Provider however, it can query data provider's repository while using target app (data receiver) with no selection of choosing steps. small but big difference in usage.
I'm not sure what kind of data you want your app to share, but it seems to me that what you are looking for might be the file picker contracts. They, I quote:
Make files accessible from a file picker if your app has a unique and/or valuable view of those files or if users cannot easily access the files another way.
Consider the following scenario. A company releases many apps. And they want some data to be shared across all these apps. Any of these app can create or read these data, just like a common database. So company decided to create an android library which does this purpose. I searched for a few days and my analysis are given below.
SharedPreferences- not recommended and is deprecated. It does not serve the purpose too. All other apps need to know the package name of the app that created the data to create PackageContext. Here this is impractical as any app can create/update/read data and it is not possible to say who is who.
ContentProviders - This does not work for me. The reason being ContentProviders has to be present in each app. There can not be 2 content providers with same name in a device. In addition to that, ContentProviders are basically meant for one app creates data and other apps subscribe to it using Content_Uri.
Network connection - We do not want to do store data in any server.
External storage - This is the only option remaining. Should I go for this?
And interestingly the data has to be secured as well which is nowhere supported in any of the storage options.
Note: For iOS, we use keychain to implement the same functionality
Understanding the problem on Android
Ironically, due to the intense sandboxing on iOS, there's a straightforward way to make this happen there (App Groups) provided the apps that need to share data are all by the same developer. Likely because Android is more flexible on security, this actually ends up being a more difficult problem there. The Android team have so far not seen fit to provide a convenient and secure way to specifically share this kind of data because there's a low security workaround.
That said, there are plenty of ways to share data between applications without involving the cloud.
SharedPreferences
The original question states that SharedPreferences are deprecated. This isn't true, as far as I can tell, however the MODE_WORLD_READABLE and MODE_WORLD_WRITABLE contexts are deprecated which makes this approach subject to not working in the future. The mode has been deprecated for quite some time, though - since Android 4.2 (2012). There's no threat in the current Android docs to suggest they're actually phasing it out (sometimes deprecation just means "this isn't a great idea" not "this is going to be removed"). I suspect the lack of a more secure OS-level direct alternative for application data sharing at the settings level is probably the reason for preserving it in a state of deprecation for the last 5 years.
File Access
The simplest and most common way I'm aware of to implement data sharing between applications on Android is to simply request file access on the device and create a shared location on external storage for this data. (Don't be confused by the "external storage" designation - this is just how Android refers to shared data. It doesn't necessarily refer to an SD card.) You give the file a unique name, and you store it somewhere that your apps know where to look for it. Best way to get that path is something like:
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
The obvious problem with this is security. While not deprecated by the OS, it introduces the same problem that the Android docs list as the reason for deprecating MODE_WORLD_* - it's inherently insecure and opens up potential exploits in your application.
You're placing your information where everything has ready access to
it.
You're asking for permissions that your app may not otherwise need.
You're reading files that you can't verify the origin of.
If your application isn't handling any sensitive data, maybe this doesn't matter to you (it might to your users). If you're planning to read data from those files, you should ensure you're providing maximum validation for that data before parsing. Check the size of the file, validate the formatting, etc.
Creating your own service
You could always create a Service or an IntentService. (There are subtle differences between the two, but IntentService is a subclass of Service that runs in a Worker thread while Service interrupts the main thread. IntentService also implements Intent support which provides the most straightforward interapplication communication on Android).
This service has its own private storage, for which it has full read/write access, but nothing else does. This service then provides an interface to receive Intents from other apps, and to return results (as Intents) to those apps. This is an extremely friendly way to implement interapplication data while maximizing data privacy and security of that data. If outlying apps mostly need to request very basic information from a central application, this is your entry-level option.
Implementing a BroadcastReceiver
Along the same lines is the BroadcastReceiver class. Depending on what sort of data you're intending to share between applications, and how familiar those applications may be with your specific approach, this another possibility. Again, you'll be managing the shared data under one application's private storage. Communication is done by Intents, so it's similar to an IntentService - except that applications may communicate with a BroadcastReceiver by issuing systemwide events (that is, they don't need to be explicitly communicating with your app or service - they're shouting out the world for a piece of info, and expecting an answer.)
Creating a ContentProvider
The Original Post seems to misunderstand what a ContentProvider is and how it works. You have to think of this type of item like you would a cloud solution - even though it's local to your device. Every app doesn't need a ContentProvider - they all need to communicate with a ContentProvider, and that ContentProvider maintains, updates and returns data.
This is probably the most "Android-y" solution for this particular usecase and offers the greatest expandability. You implement an independent process that handles data storage and responds to other applications. It is, however, a more evolved solution - and as such may be more a more challenging endeavor. If you need a real database service, rather than a fairly simple request/response type service, ContentProvider seems to be the best choice.
Seems like what you need is shared user id.
It allows application sandbox to be shared across multiple android applications if they are all signed by the same signature.
But, watch out for gotchas!
Yes. Probably using same path in external storage for all applications is the best way. A common portion of code could be used to know whether database there exists or not and therefore open or create new one. For security I recommend always to use 'user' and 'password' when connecting to DB, but if you think it is not sufficient I advise you to see this: http://www.hwaci.com/sw/sqlite/see.html
Is there anyway I can do something like this
all data I/O functions are written in a library package
data can only be shared between apps with this library (optional)
every apps with this library can initiate the "DB" at first time, and later-installed apps can access the same "DB"
I thought ContentProvider is a perfect solution for me, but it seems that condition 3 is impossible.
any suggestion plz?
all data I/O functions are written in a library package
OK.
data can only be shared between apps with this library (optional)
Perfectly fine with the proper permissions for your provider (signature).
every apps with this library can initiate the "DB" at first time, and later-installed apps can access the same "DB"
I thought ContentProvider is a perfect solution for me, but it seems that condition 3 is impossible.
It's up to you to code the underlying structure of your data. Since you already assumed that the provider will belong in a dedicated library package, a possible solution is:
Implement your provider in package com.mysuite.library.
Publish this app in the Play Store.
Make client apps A, B and C.
Publish them in the Play Store.
Require your users to download this library package whenever apps A, B or C can't find com.mysuite.library installed.
However, if you don't want to provide a central package, I believe you will need to serve a provider in each of your own apps, with different authorities (to avoid CONFLICTING_PROVIDER error). Upon initializing each client, you first check if there is another provider in your namespace (com.mysuite.provider*), assuming you either know all possible authorities you are going to create and/or iterate among them when searching (com.mysuite.provider1, 2 etc.).
However, this proposition may create problems with custom backups (say, if only one of the clients is backed up), which will force the re-creation of data. It certainly has caveats and is definitely more complex (ugly, IMHO), but it can be made to work.
Personally, I'd stick with option 1 (library package). I don't see users complaining when downloading required library packages for apps.
It's just an architectural decision, really.
There are only four ways to maintain shared data:
You have a single APK that stores the data and makes it available to other apps. If this app is uninstalled the data is gone.
Every app maintains its own copy of the data but synchronizes changes to the others.
The data is stored on the SD card. This is generally a poor solution though several apps do it.
The data is stored on a server and is only accessible when the device has a network connection.
Once you've chosen where you want your data to reside, you can transfer it around by any number of mechanisms.
ContentProvider is a good choice if you are not storing the data on SD card or on a server, but you want to be able to transfer the data to apps that don't know it exists using content: URI's.
If you have no need to ever share any of this data with anyone outside your particular suite of apps, and your data is not structured as a database, you may prefer a simpler transport.
I'm having a rough time figuring out what would be the best way to do the following:
I'm building a library to be used by different apps. This library provides a user system,
through a service, that is started by a "manager app" (used to log-in, add users, etc.).
I need to provide a facility for sharing files across apps.
Imagine that we have an application called APP XPTO. This APP need to store
some files (it could be voice recordings, for this example). But It should not do so
in its private application space (provided by Android, by default). It should be on a space
that is accessible to the manager app (or the service if you will). Also, these
files need to be encrypted, since I'm providing a multi-user system through the service,
but I don't want to face privacy issues.
I thought of having the service manage this file system (FS) abstraction, by creating
a class for representing a file:
class MyFile { // make it parcelable
private String fullpath;
private byte[] data;
private User user;
}
And providing basic FS usage through intents.
APP XPTO could send an intent asking for a file, providing, in the intent, its
path and the username it "belongs" to. The service would look for the file,
decrypt it by using the user key (identified by the username), and create a new
MyFile object to put it in the intent and send it to APP XPTO.
This way the files can be stored in a "private app space", since they will be
served by the service. Right?
This seems a "little bit" odd, however. I know that Android isn't a multi-user
OS (although that is being addressed) and that this idea, alone, goes against at
least one of the Android's security mechanisms (the "private space" for each app),
but I need this to work, this or something similar. It also seems to me that
serving files through intents is somewhate "strange"...
Any observations, or ideas?
To share data among different applications on Android use ContentProvider
Instead of Service, you will implement a ContentProvider. The users of the data can call the provider through ContentResolver