Search code examples
androidandroid-intentandroid-alarmscommonsware-cwac

Android service execution denied


I have a service implemented as a WakefulIntentService. It is started every time a proper intent is issued to the BroadcastReceiver responsible by starting it. The service starts in two situations: upon device boot and when a running instance of the service is about to finish and schedules a new execution by asking Android's conventional task scheduler, the AlarmManager, to issue a starter intent at a future time.

The question is, I have been advised not to use android:exported="true" in the service declared in the Manifest file for security reasons. However, omitting it causes the service execution to be denied in one of the test phones (a Samsung S3 running Android 4.1.2):

06-13 11:34:34.181: W/ActivityManager(2270): Permission denied: checkComponentPermission() owningUid=10155
06-13 11:34:34.181: W/ActivityManager(2270): Permission Denial: Accessing service ComponentInfo{com.mypackage.myapp/com.mypackage.myapp.MyService} from pid=10320, uid=2000 that is not exported from uid 10155

Adding android:exported="true" fixes the problem. Is there an alternative to avoid the execution denial without compromising the app's security?

The Manifest xml file:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mypackage.myapp"
    android:versionCode="3"
    android:versionName="1.0">

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <receiver android:name=".MyBroadcastReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <intent-filter>
                <action android:name="com.mypackage.myapp" />
            </intent-filter>
        </receiver>

        <service
            android:name="com.mypackage.myapp.MyService"
            android:exported="true">
        </service>

    </application>

</manifest>

The BroadcastReceiver:

package com.mypackage.myapp;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

import com.commonsware.cwac.wakeful.WakefulIntentService;

public class MyBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        WakefulIntentService.sendWakefulWork(context, MyService.class);
    }
}

The service containing the starter intent scheduling code in onDestroy():

public class MyService extends WakefulIntentService {

(...)

    @Override
    public void doWakefulWork(Intent intent) {
        (...)
    }

    @Override
    public void onDestroy() {

        PendingIntent pi = PendingIntent.getBroadcast(this, 0, new Intent("com.mypackage.myapp"), 0);

        AlarmManager am = (AlarmManager)(this.getSystemService(Context.ALARM_SERVICE));

        am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, ONE_MINUTE, pi);

        super.onDestroy();
    }
}

Solution

  • Actually it doesn't occur from boot but when I attempt to start it manually on adb shell: am startservice com.mypackage.myapp/.MyService.

    Then don't do that. Your users won't be doing that. Exporting a service, just so you can run an adb shell command, is not an especially wise move. Moreover, you can test sending the boot-time broadcast from adb shell, achieve the same end, and not have to export the service.

    I haven't been able to make the service start upon boot after removing the action string in my new Intent()

    Sorry, I meant your second action string. Your <receiver> should look like:

        <receiver android:name=".MyBroadcastReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>
    

    And the corresponding PendingIntent would be:

    PendingIntent pi = PendingIntent.getBroadcast(this, 0, new Intent(this, MyBroadcastReceiver.class), 0);