Android: how to share code between projects signed with the same certificate - android

In Android documentation concerning code signing we can read: "By signing multiple applications with the same certificate and using signature-based permissions checks, your applications can share code and data in a secure manner."
How exactly such code sharing can be done? Is it possible to release main application and multiple exchangeable plugins then discover them at runtime? What does source code looks like and what are advantages over "standard" intents calls from/to different APK packages?

Use Context.createPackageContext() to instantiate a package for another .apk you are interested in. If it is signed with the same cert as yours, AND you are both using the same shared user ID, then you can use the flag to load its code into your process, allowing you to get the ClassLoader from the context and instantiate whatever classes you want.
Otherwise, if they are not signed the same and explicitly using the same shared used ID, you can only load its resources.
Please note that you can not change the shared user ID for an application (to something else or moving between having and not having a shared user ID) once that application is on market.

Say you want to call a public function of the dynamically loaded class. Use the following code snippet:
Context friendContext = this.createPackageContext("packageName", Context.CONTEXT_INCLUDE_CODE);
Class friendClass = friendContext.getClassLoader().loadClass("packageName.className");
Class noparams[] = {}; //say the function (functionName) required no inputs
friendClass.getMethod("functionName", noparams).invoke(friendClass.newInstance(), null);

Related

Android - Apk Inside APK

Let's say I have 2 apps for my school:
student.apk: Student logs in, check grades, check classes time, does a lot of stuff.
teacher.apk: Teacher logs in, lauches grades, attendence list, etc
Is it possible to create just 1 apk, with a login screen, detect whether it is a student or a teacher and than lauch the correct apk?
Basically what I'm asking is whether I can put these 2 apks (student and teacher) inside one code (login.apk).
The apps are totally different, and it would be very difficult to merge than into one, that's why I would like to launch them inside a simple "login.apk"
Not sure if that's possible. But I believe the better approach would be to use Dynamic Delivery to achieve this. Bundling the Teacher and Student as Dynamic Feature Modules and load them dynamically during runtime based on the login status.
You can find more info here :
https://developer.android.com/studio/projects/dynamic-delivery
And a tutorial here :
https://medium.com/mindorks/dynamic-feature-modules-the-future-4bee124c0f1
I would suggest playing around with intents: the concept is simple, put the login part in one of the two apps (in this example, it will be the student app).
when attempting to login, depending on the response from the server, if it is a student account, then it will simply login, otherwise, if it is a teacher account, you will verify if the teacher app is installed. if it is installed you will simply launch it and pass the needed parameters in an intent, otherwise, open the play store and install the needed app. Here is the code for that:
try {
//launch the app if it exists
Intent intent = new Intent("teacher app signature here");
intent.putExtra("some_parameter_name", "parameter value");
} catch (Exception e) {
// here is the case where the app is not installed
Uri marketUri = Uri.parse("market://details?id=teacher app signature here");
Intent marketIntent = new Intent(Intent.ACTION_VIEW, marketUri);
startActivity(marketIntent);
}
on teacher app, you will need to intercept this intent and its parameters:
String param= (String) getIntent().getSerializableExtra("some_parameter_name");
you can then save the needed variables in the shared preferences to make sure the user stays connected.
Yes it should be possible. I haven't tested it though. Make a third app (login.apk) which contains both student.apk and teacher.apk. As soon as the user logged in within login.apk, the correct sub apk can be exported/saved
from the project to the device internal/external storage (read and write permission required for login.apk), then just launch the exported apk from login.apk by opening it -> Package installer will install it (user interaction required).

Publish a Android private app for multiple clients

What we are dealing with
We have this app which we distribute to our clients in an offline fashion (i.e. not uploaded to Play store). The app flavour distributed to each client is almost identical with a bit of tweak here and there. All our clients share this app to their employees for usage. Basically this is an Enterprise App.
What's the problem
Recently one of our client started using a MDM (Mobility Device management) tool which blocks apps which are not downloaded from Google play. As obviously we got a request from our client to see if we can upload this app on Google play or not.
Important thing here is that we have over 100 clients and the package name of the app provided to each client is actually the same. So it's the same app with a bit of tweak here and there. If we go down the road of publishing the app to the play store, we might end up in a mayhem (we don't wanna upload 100 different apps to the play store - i.e. one for each client). We are doing some optimisation from our end so that multiple clients can use the same app (but we can't make all 100+ clients use the same app.).
What am I looking at ?
I started looking at Android For Work (AFW), Google private apps , Managed Google play and still digesting the stuffs. But to me it looked like just a secure way for enterprises to deploy/publish apps which can be downloaded only on specific devices and under a certain profile (which keeps things separate from user's personal apps and data in case they use the same phone for personal and work purpose).
What solution i am looking for ?
To privately deploy an app (host it with Google or privately host
but listed with Google play in both cases) and let my clients share
this app with their employee.
Each private app for each client should be on its own little
private island. I want to distribute the app with the same package
name to all my clients (From what I have read so far, this might not
be possible with Google play. But I am hoping somebody can point out
facts if I am missing something).
This is my solution:
Creating run-time dynamic app that get data and configs from back-end and render its views and data with its own Client Id.
You can create single app and upload to google play, but you should manage your clients by clientId that makes every app acts separated. This clientId is unique and generated per your clients. This solution have two sides. Android side and server side.
1 - Android side: Our app should have a baseUrl like this in Constants:
baseUrl = "http://yourCorporation.com/{clienId}/api/"
And then all the services of All clients use the same url. clientId is the key point. The difference of you client app is clientId. For generating url of api-call you should do something like this:
Constant.ClientId = scannedQRCode;
url = baseUrl.replace("{client_id}",Contant.ClientId) + apiUrl ;
You must create QR code per your clients that should scanned in app first run. It is good to send QR code after registration to his/her (client of your Clients) email. This QR code have clientId. Therefor every clients have their own services and really works as separated islands, even if you want to change server address, you can put all baseUrl in QR code but this is not suggested, because you have to create server per clients and this is headache.
You can even handle config and UI elements of you app with calling a config api that returns a customConfigDto as json like this:
public class CustomConfigDto {
String colorPrimary ;
String colroPrimaryDark ;
String colorAccent ;
int tabCounts;
//and more...
public String getColorPrimary() {
return colorPrimary;
}
public void setColorPrimary(String colorPrimary) {
this.colorPrimary = colorPrimary;
}
public String getColroPrimaryDark() {
return colroPrimaryDark;
}
public void setColroPrimaryDark(String colroPrimaryDark) {
this.colroPrimaryDark = colroPrimaryDark;
}
public String getColorAccent() {
return colorAccent;
}
public void setColorAccent(String colorAccent) {
this.colorAccent = colorAccent;
}
public int getTabCounts() {
return tabCounts;
}
public void setTabCounts(int tabCounts) {
this.tabCounts = tabCounts;
}
}
And render your views by this configurations. All of this works separated per app by their clientId.
I prefer QR code because it is very handy and classy and fit in your case, however you can enter this clientId with many other ways. This is one of best free and simple QR code generating service, and this is one of best QR-code scanner library for android.
2 - Server Side: You have to handle step1 in server-side and it is very easy. You can have entity calls Client that all other entities have it. Because you should keep all of your data in one place but separated by your clients. You can also map APIs like this in Spring:
#RequestMapping(value = "http://yourCorporation.com/{clienId}/api/customers", method = RequestMethod.GET)
Customers getCustomers(#PathVariable("clienId") Long clientId) {
return customerService.findCustomerByClientId(clientId);
}
Based on what you've said, this sounds more like you can solve this with configuration management than sending each client completely separate APKs.
Google has a private channel, but based on the documentation it seems much more oriented towards having a single membership list (i.e. once you're granted access you have access to the entire private channel) rather than highly customized access (i.e. certain people have access to certain items in the channel).
An alternative that I suggest: have all clients download the same APK. Give each of them a client-specific "activation code" for your app. When the app starts for the first time, it calls a web service and passes it its activation code; on the server side, you use the Activation Code to identify the client and then return data on the correct configuration to the client. Then you can distribute the same APK to everyone on your private channel and configure it remotely once it's installed.
A major advantage of this scheme is that you can have multiple configurations for an organization. Just give the client a choice of several activation codes, each of which will give them a certain configuration. For example, if you have an app that's used by both dock workers and janitors (and I'm just throwing out an example here), you could give the dock workers one activation code and janitors a second activation code and you can then easily give them different configurations.
Google Play now allows a developer to publish an app privately to up to 20 Managed Play organizations (or enterprises). To do so (instructions copied from the help center):
Sign in to the Google Play Console.
Go to Pricing & Distribution > User programs > Managed Google Play.
Check the Turn on advanced managed Google Play features box.
Check the Privately target this app to a list of organizations box.
Click Choose Organizations.
For each organization that you want to publish the app to, enter the Organization ID and a description (or name) and click Add. You can enter up to 20 organizations per app.
The good, long solution:
don't use the same package name for different apps. Create a multimodule project, set one module for the core, shared stuff, and add a module for each client where you can tweak what you need and configurate the package name dynamically based on build type. That way you can use the same package name for your CI server and everything else and have another package name when releasing the app.
The short workaround that may work:
Publish the app as a closed google play beta, and send invitations only to this client. That way he can distribute the app to his employees through play store and the other clients won't notice I can't assure it will work not knowing which MDM tool you are facing, but since beta channel apps don't require unknow origins permissions, you should be fine.
If you want the same package name, you'll have to do something like what EJoshuaS suggested: manage the different configurations inside of one app version. You won't be able to have more than one app with the same package deployed on Google Play.
If you're open to having different packages, you could just change the package name in the Android Manifest for each one and release as a different app. You would need to change the package everywhere you import the R file and you would need to make sure that all class references in your Manifest include the entire class path (<activity android:name="[full.package].MainActivity"> rather than <activity android:name=".MainActivity">). This gets pretty confusing and is terrible in terms of configuration management, so it's not really a great solution in general, but it might work for you.
I started looking at Android For Work (AFW), Google private apps , Managed Google play and still digesting the stuffs.
This would probably be a good fit for AFW.
But to me it looked like just a secure way for enterprises to deploy/publish apps which can be downloaded only on specific devices and under a certain profile
That's what an MDM does, yes, but there's more to it. With Android for Work you also have Managed Configurations which let you pass in a configuration for the app. This can be used to change backend urls, etc.
It for sure supports your second requirement, but I know too little to be certain about the first. While you can privately host and rollout an app on Google Play for Work, I don't know about distributing it privately to multiple clients.
The obvious benefit of using this Google API is that you don't have to build anything yourself. Also most MDMs support those Android for Work APIs, so that a domain admin can buy the app in bulk and distribute them to the employees. Have a look at the AppConfig Community which shows MDM Providers that incorporated those APIs and best practices.
Whatever you decide, you should definitely have a good look at Android for Work as what you are describing is exactly what it is intended for. The initial setup is a pain and there is way too little information about how it all works and plays together, but spending a few days trying to figure it out might be better than just building your own managed solution which you then will have to maintain too.

Use cases for Dynamic Permissions in Android

I am trying to understand what some of the uses cases are for dynamic permissions in Android. In other words, I am trying to understand why we have addPermission*() API methods and why just having static permissions is not sufficient. There doesn't seem to be much material out there explaining this, so I would appreciate some explanation.
Also, in order to understand what apps do with dynamic permissions, I downloaded some Android apps and started to reverse engineer them and look for the addPermission*() API methods in the source code. I've noticed that there are some apps that implement a wrapper class for PackageManager and I was wondering what the purpose is for doing this. Here is an example to the wrapper classes implemented by these apps, all they do is call the respective method of the PackageManager class:
public class PackageManagerWrapper
extends PackageManager
{
protected PackageManager mInner;
public PackageManagerWrapper()
{
this.mInner = null;
}
public PackageManagerWrapper(Context paramContext)
{
this.mInner = paramContext.getPackageManager();
}
#Inject
public PackageManagerWrapper(PackageManager paramPackageManager)
{
this.mInner = paramPackageManager;
}
public void addPackageToPreferred(String paramString)
{
this.mInner.addPackageToPreferred(paramString);
}
public boolean addPermission(PermissionInfo paramPermissionInfo)
{
return this.mInner.addPermission(paramPermissionInfo);
}
...
}
Thanks a lot!
Apps can define custom permissions in order to protect it's code / data but still let other apps (with the right permission) to use it. While it's easy to think of use cases - like a suite of apps need to share a data/functionality only between them, those can be achieved by using a <permission> tag in manifest. However, this API used only for creating permissions not yet used by any other app:
New permissions must be added before any .apks are installed that use those permissions. Permissions you add through this method are remembered across reboots of the device. If the given permission already exists, the info you supply here will be used to update it.
But still meant to be used by any app that had declared a permission-tree in it's manifest.
So, since it's creating permissions at runtime and therefore cannot be applied to own Activities/Services, we are left only with broadcasts. The only advantage of this API over declaring those permissions in Manifest I can think of is that the user don't need to update the application. So if you are having a suit of apps, with one 'main' app (similar to Google Play), and you want to be able to broadcast to new apps in this suit securely even if the user had not updated your app, you can still get updates over the net and add needed permissions to communicate with the new apps.
For your second question - it's cannot be deducted from you example. There might be several reasons, such as acting as Bridge, or in order to add custom functionality.

Dynamic screens in Android application

i am programming an android application which can be use by different business and/or users, each can have its own configuration.
each business will set his required configuration (screens, text, and so) in a back office site (data will be saved in database).
on Splash screen i will fetch a Json string from the server which
contain this business specific configuration.
from now on the application should work base on this json.
my questions:
is it recommended to build it this way?
is it good practice to have a workflow manager class that will take care of screens transitions.
can you please recommend a strategy you would go with.
here is a function that handle screen transitions
public static void startNext(String FromActivity, Context context)
{
try
{
Intent intent = new Intent(context, Class.forName(context.getPackageName() + getNextActivity(FromActivity)));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
catch (ClassNotFoundException ex)
{
Log.e("test", ex.getMessage());
}
}
the getNextActivity function return a class that should be open next based on the json string.
and example of call from activity 1 to move to next activity
FlowManager.startNext(this.getLocalClassName(),getApplicationContext());
When you have a customization like that it is best to make a build script which will download the latest code for a specific publisher from github/svn or something like that. It should also download publisher specific strings, launcher icons, name of the app and the end package package name, version, certificate to sign the application with, assets, rename all occurrences of default package... Make sure you exclude any unnecessary libraries that some publishers don't need and comment out the stuff related to that library. You can define start/end tags in code and then search for the start tag, replace it with /* and then replace the end tag with */ effectively commenting everything between them out. At the end the script runs gradle wrapper. First run clean and then build. It's also good to define the end output location of apk so it's easier to find.
The most easy way to build a build script like that is using bash scripts. They run on linux/mac or even both if you do it the right way. The whole think should run on a build server like Jenkins
After the build is done, it is good to upload the final *.apk to a server where publishers can download the latest apk and then upload it to Google Play Store.
Happy Coding!

Issues with application signature

I'm trying to create my own version of Gesture Builder. In eclipse I selected android project from existing code and I renamed the project and package name to new gesture. Then I added in android:fadeOffset = "1000" in create gesture xml(so that I can create gestures for letters like t and f) and in AndroidManifest.xml I set the version name to NewGestures and I set a different icon but when I try to run it I get this error message:
"Re-installation failed due to different application signatures. You must perform a full uninstall of the application. WARNING: This will remove the application data! Do you want to uninstall?"
From what I've seen online I need to match the signature used originally on Gesture Builder, but I've no idea how to do this on eclipse, shouldn't the signature have been handled properly when I created from existing code? Any help would be very much appreciated. I just need this app working so I can get a gestures library for a different application I'm working on for college.
This message concerns the application signature. This happens when you are trying to install an application on your device while an application of the same package name is already installed, but signed with a different certificate (see details here).
For example:
You have exported and installed your application using your Google Play keystore (so using your actual developer's certificate), and now you are running/debugging it from Eclipse, implicitely using the debug certificate (which is different)
You have runned/debugged your application from Eclipse at home on this device, and now your are running it/debugging it from Eclipse with another computer (which is using a different implicit debug certificate)
etc
Normally, below the error message, you have a button that allows uninstalling/reinstalling. If not, just uninstall your app manually and everything will be fine again.
versionName:
The version number shown to users. This attribute can be set as a raw
string or as a reference to a string resource. The string has no other
purpose than to be displayed to users.
package:
The package name serves as a unique identifier for the application.
The package name declared in your manifest.xml is what makes your application unique. Therefore, there can not be two application installed with the same package name at the same time. If you try this, your error occurs.

Categories

Resources