別Package別Process別APKのServiceにAIDLからBindするサンプル (Android L以降対応版)

# Android 5.0 Lollipopでの変更点
AIDLを使ってAndroid Serviceにbind/unbindする方法は初期のAndroidから使えますが，Android 5.0 Lollipopから必要条件が追加されていて，以前からあるサンプルコードではIllegalArgumentException発生で正常に動作しなくなっています．

↓ Android L以上でIllegalArgumentException発生させる部分のCode

```java:ContextImple.validateServiceIntent()
private void validateServiceIntent(Intent service) {
    if (service.getComponent() == null && service.getPackage() == null) {
        if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
            IllegalArgumentException ex = new IllegalArgumentException(
                    "Service Intent must be explicit: " + service);
            throw ex;
        } else {
            Log.w(TAG, "Implicit intents with startService are not safe: " + service
                    + " " + Debug.getCallers(2, 3));
        }
    }
}
```
http://androidxref.com/5.0.0_r2/xref/frameworks/base/core/java/android/app/ContextImpl.java#validateServiceIntent

のように，service.getComponent()かservice.getPackage()のどちらか一方が非NULLでなければExplicit Intentとは判定されなくなっているようです．

これを踏まえて，AIDLからServiceにBindするサンプルを書いてみます．

# Server側
AIDLから接続される側のAPK．

## Dir構成 (Gradle Build世代)

```text:
AidlServer/
  - app/
    - src/
      - main/
        - aidl/
          - test/aidl/server/IAidlServerService.aidl
        - java/
          - test/aidl/server/AidlServerService.java
        - res/
          - (省略)
        - AndroidManifest.xml
```
GitHubにSample CodeをUpしました．
https://github.com/fezrestia/sample-bind-aidl-service/tree/master/AidlServer

## Android Manifest
公開するServiceの定義．Package + Intent FilterでExplicit Intentにする．

```xml:AndroidManifest.xml
<manifest
    package="test.aidl.server"
    xmlns:android="http://schemas.android.com/apk/res/android"
>
    <application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
    >
        <service
            android:name=".AidlServerService"
            android:exported="true"
        >
            <intent-filter>
                <action android:name="test.aidl.server.ACTION_BIND" />
            </intent-filter>
        </service>
    </application>
</manifest>
```

## Sample Service
『HELLO WORLD !』という文字列を返すだけのAPIをもつサンプル．

```java:AidlServerService.java
package test.aidl.server;

// import省略

public class AidlServerService extends Service {

    private IAidlServerService.Stub mStub = new IAidlServerService.Stub() {
        @Override
        public String getMessage() throws RemoteException {
            return "HELLO WORLD !";
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return mStub;
    }

    // その他@Override省略
}
```

## AIDL
同じものをClient側にも持たせる．Build時の名前解決にも必要．

```java:IAidlServerService.aidl
package test.aidl.server;

interface IAidlServerService {
    String getMessage();
}
```

ちなみに，.aidlのファイルはBuild時にParseされ，Stubという子クラスを持ったCodeが自動生成されてBuildに使われます．

# Client側
AIDLに接続する側のAPK.

## Dir構成 (Gradle Build世代)

```text:
AidlClient/
  - app/
    - src/
      - main/
        - aidl/
          - test/aidl/server/IAidlServerService.aidl (Server側と同じpackage/file)
        - java/
          - test/aidl/client/AidlClientActivity.java
        - res/
          - (省略)
        - AndroidManifest.xml
```
GitHubにSample CodeをUpしました．
https://github.com/fezrestia/sample-bind-aidl-service/tree/master/AidlClient

## Android Manifest
Client側のManifestはServiceにbindするActivityのみ．Activityである必要は無いです．

```xml:AndroidManifest.xml
<manifest
    package="test.aidl.client"
    xmlns:android="http://schemas.android.com/apk/res/android"
>
    <application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
    >
        <activity
            android:name=".AidlClientActivity"
        >
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
</manifest>
```

## Sample Activity
onResume/onPauseでbind/unbindService，ButtonのonClick()でAPI Callする設定．

```java:AidlClientActivity.java
package test.aidl.client;

// AIDLのimport
import test.aidl.server.IAidlServerService;

// その他import省略

public class AidlClientActivity extends Activity {

    // onCreateなどでButton View取得 + setOnClickListener()などしておく．

    @Override
    public void onResume() {
        super.onResume();

        // Bind.
        //
        // NOTICE:
        //     Android 5.0 Lolipop以降でExplicit Intent判定に必須のため，setPackage()でpackage名を明示．
        //
        Intent service = new Intent("test.aidl.server.ACTION_BIND");
        service.setPackage("test.aidl.server"); // Android 5.0 Lolipop以降で必須の設定
        bindService(service, mAidlConnection, BIND_AUTO_CREATE);
    }

    @Override
    public void onPause() {
        // Unbind.
        if (mService != null) {
            unbindService(mAidlConnection);
        }

        super.onPause();
    }

    private IAidlServerService mService = null;
    private AidlConnection mAidlConnection = new AidlConnection();

    // Service接続状態のCallback.
    private class AidlConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = IAidlServerService.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mService = null;
        }
    }

    // Button ClickでAPI Callするサンプル．
    private class TriggerOnClickListener implements View.OnClickListener {
        @Override
        public void onClick(View view) {
            try {
                ret = mService.getMessage();
            } catch (RemoteException e) {
                e.printStackTrace();
            }

            Log.e("TraceLog", "#### Received : " + ret);
        }
    }
}
```

## AIDL
Server側のAIDLをそのままコピーしてClientのProjectにもたせておく．

```java:IAidlServerService.aidl
package test.aidl.server;

interface IAidlServerService {
    String getMessage();
}
```

こちらもServer側と同じくCodeの自動生成 + Build時名前解決に使われる．

# まとめ
AndroidはOS Updateでpublic APIの暗黙仕様がコロコロ変わって過去資産がいきなり使えなくなったりして震えますね．

Code一式をGitHubとかに上げたら更新します．
→追記しました．

///---