Sync adapter service exported but unprotected

Viewed 900

Fellow Developers!

I have a sync adapter in my app and a corresponding sync service. I have declared everything, including the sync service, according to Google example code. The greater picture looks something like this:

<service
    android:name="com.myapp.SyncService"
    android:exported="true"
    android:process=":sync">

    <intent-filter>
        <action
            android:name="android.content.SyncAdapter"/>
    </intent-filter>

    <meta-data
        android:name="android.content.SyncAdapter"
        android:resource="@xml/syncadapter" />

</service>

While it makes sense to set the android:exported attribute to true on the service (enabling the Android system to reach it), I'm a bit puzzled on how to tie it down in terms of access rights. I don't want anyone else but my app and the Android system to have access to the service.

Maybe a bit naively I have created my own permission for this:

<permission
    android:name="com.myapp.permission.SYNC_ADAPTER"
    android:protectionLevel="signatureOrSystem" />

But reading up a bit on the protectionLevel makes me wonder even more. Google says:

Please avoid using this option [...] "signatureOrSystem" permission is used for certain special situations where multiple vendors have applications built into a system image and need to share specific features explicitly because they are being built together.

The described scenario is far from my use case. The question then remains:

How do I secure my sync service so that the Android system, but no third party apps, can access it?

Any clarification would be greatly appreciated!

5 Answers

You can safely set exported=false for both services

  • one service registering to the SyncAdapter
  • one service registering to the AccountAuthenticator intents

The system can still call the SyncAdapter Service via intents.

  • The reason why this works is mentioned in the official exported guide

If false, the activity can be launched ... [by] privileged system components.

I.e. you don't need to struggle with exported=true.

  • tested on Android 10 (Emulator with targetVersion: 30)
  • tested on Android 13 (with targetVersion: 31)

Old answer

The following is (for me) irrelevant as the above works.

I interpreted the replies in this thread this way:

  • Requesting this permission (see example) in an exported service (see below) should only allow apps signed with your signature - and the system - to call this service.

  • If you use exported=false then the system cannot call your service, i.e. Synchronization won't start if your app is closed(?)

    <permission
        android:name="com.myapp.USE_SYNC_AND_AUTHENTICATOR"
        android:protectionLevel="signature" />

    <application>
        <!-- Export sync and auth to allow the Android System to call them. -->
        <!-- Protect it using a signature permission.  -->
        <service
            android:name="com.myapp.SyncService"
            android:exported="true"
            android:process=":sync"
            android:permission="com.myapp.USE_SYNC_AND_AUTHENTICATOR">
            <intent-filter>
                <action android:name="android.content.SyncAdapter" />
            </intent-filter>
            <meta-data
                    android:name="android.content.SyncAdapter"
                    android:resource="@xml/sync_adapter" />
        </service>
        <service>..authenticator..</service>
    </application>
Related