caw
caw

Reputation: 31497

Enable communication between two Android apps via custom permissions

One of my Android applications needs some features that are "dangerous" when it comes to permissions. They require some permissions such as "Internet access" which are critical in combination with private data.

This is why I want to create a separate "Add-on", i.e. a second app that provides these permission-critical features. So if users want them, they can install the add-on, but the main app will still work without those permissions.

Using a sharedUserId would obviously be the easiest solution, but adding this afterwards, when lots of users use the app already, could cause serious problems. Wouldn't this mean that the app can't access its own data any longer?

So I have to choose another approach. ContentProviders are something that I try to avoid, because they're too complex for this simple need in my opinion.

I thought custom permissions could solve the issue. Can they? I've added the following permission declaration to both the main app as well as the add-on as a child to the manifest tag in AndroidManifest.xml:

<permission
    android:name="com.my.package.ADDON"
    android:label="@string/permission_title"
    android:description="@string/permission_description"
    android:permissionGroup="android.permission-group.PERSONAL_INFO"
    android:protectionLevel="signature" />

Furthermore, both manifest files have got this part now:

<uses-permission android:name="com.my.package.ADDON"></uses-permission>

The add-on app includes an IntentService that has the following attribute now:

android:permission="com.my.package.ADDON"

Shouldn't this do the job so that I can call the add-on's IntentService from my main app via this code?

Intent addonIntent = new Intent();
addonIntent.setClassName("com.my.package", "com.my.package.MyService");
startService(addonIntent);

Unfortunately, this call always fails with the following exception:

E/AndroidRuntime(16721): java.lang.SecurityException: Not allowed to start service Intent { cmp=com.mypackage.addon/.MyService } without permission com.mypackage.permission.ADDON

What did I do wrong? Thank you very much in advance!

Addition #1 - Add-on manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      android:versionCode="1"
      android:versionName="1.0"
      package="com.mypackage.addon">
    <uses-sdk android:minSdkVersion="8" />
    <permission
        android:name="com.mypackage.permission.ADDON"
        android:label="@string/permission_title"
        android:description="@string/permission_description"
        android:permissionGroup="android.permission-group.PERSONAL_INFO"
        android:protectionLevel="signature" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:permission="com.mypackage.permission.ADDON"
    android:exported="true">
    <service
        android:enabled="true"
        android:name=".MyService" />
</application>
</manifest>

Addition #2 - main app manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      android:versionCode="1"
      android:versionName="1.0"
      package="com.mypackage.mainapp">
    <uses-sdk android:minSdkVersion="8" />
    <permission
        android:name="com.mypackage.permission.ADDON"
        android:label="@string/permission_title"
        android:description="@string/permission_description"
        android:permissionGroup="android.permission-group.PERSONAL_INFO"
        android:protectionLevel="signature" />
    <uses-permission android:name="com.mypackage.permission.ADDON"></uses-permission>
<application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:name="MyApp">
    <activity android:name=".MainActivity" android:launchMode="singleTask" android:label="@string/app_name">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>
</manifest>

Upvotes: 4

Views: 2696

Answers (1)

CommonsWare
CommonsWare

Reputation: 1007658

Wouldn't this mean that the app can't access its own data any longer?

Correct.

I've added the following permission

I would dump the permission-group, as that should not be necessary.

Furthermore, both manifest files have got this part now

Only the one calling your IntentService might need that.

Shouldn't this do the job so that I can call the add-on's IntentService from my main app via this code?

Not if that IntentService is not exported. Make sure that your IntentService either has an <intent-filter> or android:exported="true". I would recommend going the <intent-filter> route, so you can declare and use a custom action string, so you get away from hard-coding package and class names in the client app.

Here is a directory with two sample projects using this basic approach, though in my case the communications are based on a secured ContentProvider rather than a secured IntentService. The concept is the same, though, and so with these minor tweaks, I would expect what you are doing to work just fine.

Upvotes: 6

Related Questions