AndroidでバックグラウンドでAWS IoTメッセージを待ち受ける

以前の投稿で、Androidにおいて、UDPメッセージを待ち受けるバックグラウンドサービスを作成しました。([AndroidでバックグラウンドでUDP受信を待ち受ける](https://qiita.com/poruruba/items/01731d425b6e658fc56f))
今回は、AWS IoTすなわちMQTTのメッセージを待ち受けるバックグラウンドサービスを作成します。
それだけだと面白くないので、独立でサービスを起動し、AIDLを使って別のAndroidアプリケーションからサービスを操作し、メッセージを送受信できるようにします。
以下に構成をまとめます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/60de546a-7aa6-47d7-02a9-e8d0128223e1.png)

バックグラウンドサービスを独立に立ち上げるので、同じAndroid内の複数のアプリケーションで共用できます。
また、AWS IoT CoreをMQTTブローカとして使うので、他のMQTTクライアントからも送受信できるとともに、他のAndroidスマホでバックグラウンドサービスを立ち上げれば、それとも送受信することができるようになります。

ソースコードもろもろは、GitHubに上げておきました。

poruruba/AwsIotService

https://github.com/poruruba/AwsIotService

# フォアグラウンドサービスを使ってバックグラウンドで立ち上げる。

以下の投稿で詳しく述べています。
　[AndroidでバックグラウンドでUDP受信を待ち受ける](https://qiita.com/poruruba/items/01731d425b6e658fc56f)

以降、ポイントだけ説明します。

android.app.Serviceを継承したクラスを実装します。
オーバライドするメソッドonCreate()で、通知のためのチャネルを定義します。通知は、フォアグラウンドサービスを立ち上げる際に必須です。

```java:AwsIotService.java
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, NOTIFICATION_TITLE, NotificationManager.IMPORTANCE_DEFAULT);
        notificationManager.createNotificationChannel(channel);
```

フォアグラウンドサービスの開始は、MainActivityからは以下のように呼び出します。

```java:MainActivity.java
                    SharedPreferences.Editor editor = pref.edit();
                    Intent intent = new Intent(this, AwsIotService.class);
                    EditText edit;
                    edit = (EditText) findViewById(R.id.edit_endpoint_prefix);
                    String endpointPrefix = edit.getText().toString();
                    edit = (EditText) findViewById(R.id.edit_client_id);
                    String clientId = edit.getText().toString();
                    edit = (EditText) findViewById(R.id.edit_topic_name);
                    String topicName = edit.getText().toString();
                    intent.putExtra("keyStorePath", keyStorePath);
                    intent.putExtra("endpointPrefix", endpointPrefix);
                    intent.putExtra("clientId", clientId);
                    intent.putExtra("topicName", topicName);
                    editor.putString("endpointPrefix", endpointPrefix);
                    editor.putString("clientId", clientId);
                    editor.putString("topicName", topicName);

                    startForegroundService(intent);
                    Toast.makeText(this, "待ち受けを開始しました。", Toast.LENGTH_LONG).show();
                    editor.apply();
```

なんかいろいろ引数を渡したり、次回呼び出したときに引数を覚えて置けるようにSharedPreferenceに保存したりしていますが、大事なのはstartForegroundService()です。

そうすると、Service側のオーバライドするメソッドonStartCommand()が呼び出されます。その中で、AWS IoT Coreをセットアップし、通知を生成してフォアグラウンドサービスを開始して、最後にSTART_NOT_STICKY を返して終わりです。

AWS IoT Coreのセットアップは後述しますが、フォアグラウンドサービスは以下のように開始します。通知を引数に渡す必要があります。

```java:AwsIotService.java
        Intent notifyIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);

        Notification notification = new Notification.Builder(getApplicationContext(), CHANNEL_ID)
                .setSmallIcon(android.R.drawable.btn_star)
                .setContentText(NOTIFICATION_CONTENT)
                .setContentIntent(pendingIntent)
                .setWhen(System.currentTimeMillis())
                .build();
        startForeground(NOTIFICATION_ID, notification);
```

PendingIntentを含めるのは任意ですが、フォアグラウンドサービス開始後に表示される通知をタップすると、MainActivityを起動させるためのものです。

そして、AndroidManifest.xmlに以下を追加します。

```xml:AndroidManifest.xml
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
・・・
        <service
            android:name=".AwsIotService"
            android:parentActivityName=".MainActivity"
            android:enabled="true"
            android:exported="true" >
            <intent-filter>
                <action android:name="AwsIotService" />
            </intent-filter>
        </service>
```

intent-filterとandroid:exported="true"は、後程説明するAIDLを使った別アプリからのバインド用に必要です。
以下のオーバライドメソッドも必要で、のちほど変更しますが、とりあえず、nullを返すようにしておきます。

```java:AwsIotService.java
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind called");
        return null;
    }
```

# 補足：フォアグラウンドサービスの生存期間

フォアグラウンドサービスの生存期間については、2つの視点で考えます。

1つ目が、サービスとフォアグラウンドサービスの2つの状態があること
2つ目が、フォアグラウンドサービス自体の生存期間です。

1つ目の話ですが、通常は、MainActivityからstartForegroundService を呼び出すと、サービスのonCreateが呼び出されたのちonStartCommandが呼び出されます。一方で、AndroidManifest.xmlでサービスを定義してあるので、MainActivityからのstartForegroundServiceの呼び出しにかかわらず、呼び出し側からバインドできる状態です。startForegroundServiceを呼び出さずにバインドされるとonCreate()のみが呼ばれた状態になります。今回の実装の場合ですと、onStartCommandでAWS IoT Coreへ接続しているため、バインドできるがpublishMessageの呼び出しに失敗する形になります。

2つ目の話ですが、フォアグラウンドサービスは、MainActivityからstopServiceを呼び出すことで停止できます。しかし、他のクライアントからバインドされている状態ではまだサービスが破棄されず、すべてのクライアントからアンバインドされてから破棄されます。

# AWS IoT Coreにポリシと証明書を作成

AWS IoT Coreを使えるように、まずは、AWSの管理コンソールから、AWS IoTの設定をしておく必要があります。

AWS管理コンソール
　https://ap-northeast-1.console.aws.amazon.com/iot/home?region=ap-northeast-1#/home

左側のナビゲーションメニューからセキュリティの下のポリシを選択します。
「ポリシを作成」ボタンを押下します。ポリシ名は適当に決めます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/8564198f-bb92-9a73-d5d4-beaaa75194b6.png)

以下のアクションを許可します。

・iot:Connect : *
・iot:Publish : arn:aws:iot:ap-northeast-1:*:topic/awsiot/*
・iot:Subscribe : arn:aws:iot:ap-northeast-1:*:topicfilter/awsiot/*
・iot:Receive : arn:aws:iot:ap-northeast-1:*:topic/awsiot/*

最後に「作成」ボタンを押下して作成完了です。
これで、awsiot/* という名前のトピックでPublishしたりSubscribeしたりできるようになります。

次に、上記ポリシを使える人であることを示すために、証明書を作成します。
左側のナビゲーションメニューからセキュリティの下の証明書を選択します。

「証明書を追加」ボタンを押下し、証明書を作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/3e1222f6-fb74-b58b-3369-3db114da5196.png)

「新しい証明書の自動生成（推奨）」を選択します。また、すぐ使うので、証明書のステータスは「アクティブ」にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/8b4a100b-2ba1-b57a-e20d-4a4b9f336e3b.png)

作成ボタンを押下すると、証明書が生成されるので、必要なファイルをダウンロードします。
デバイス証明書とプライベートキーファイルを使いますが、他のファイルもダウンロードしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/de11e88e-e015-75a1-9cec-23cdc1bf1376.png)

証明書一覧に、今回作成された証明書があるかと思います。それを選択します。
そして、「ポリシをアタッチ」ボタンを押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/7416a3cd-8e4c-1a92-4834-a9b8ff5dfa3a.png)

先ほど作成したポリシを選択して、「ポリシをアタッチ」ボタンを押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/a585edcc-5548-dd49-2b6c-cb4586c156e8.png)

これでポリシの許可が証明書の持ち主に割当たりました。

# AndroidからAWS IoT Coreに接続する

AWSがAndroid向けにAWS IoT Coreのライブラリを用意してくれているので簡単です。

appフォルダのbuild.gradleのdependenciesに以下を追記します。

```gradle:build.gradle
implementation 'com.amazonaws:aws-android-sdk-iot:2.62.0'
```

まずは、このライブラリを使ってAWS IoT Coreに接続する前に、先ほどAWS管理コンソールからダウンロードした証明書とプライベートキーファイルを読み込み、アプリ内に保持しておく必要があります。
保持するためのstatic関数は以下のようにしました。

```java:AwsIotService.java
    public static void saveCertificate(String keyStorePath, String cert, String priv) {
        if (AWSIotKeystoreHelper.isKeystorePresent(keyStorePath, AWSIOT_KEY_STORE_NAME))
            AWSIotKeystoreHelper.deleteKeystoreAlias(AWSIOT_CERT_ID, keyStorePath, AWSIOT_KEY_STORE_NAME, AWSIOT_DEFAULT_PASSWORD);
        AWSIotKeystoreHelper.saveCertificateAndPrivateKey(AWSIOT_CERT_ID, cert, priv, keyStorePath, AWSIOT_KEY_STORE_NAME, AWSIOT_DEFAULT_PASSWORD);
    }
```

肝心のファイル読み出しは、MainActivity.javaの方で行っています。
便利なインテント「ACTION_OPEN_DOCUMENT」を使いました。

```java:MainActivity.java
                Toast.makeText(this, "証明書ファイルを選択してください。", Toast.LENGTH_SHORT).show();
                Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
                intent.addCategory(Intent.CATEGORY_OPENABLE);
                intent.setType("application/x-x509-ca-cert");
                startActivityForResult(intent, REQEST_CODE_FILEOPEN_CRT);
```

上記を呼び出すとファイル選択画面が表示されます。まずは証明書ファイル(application/x-x509-ca-cert)を選択します。
選択されると、以下のonActivityResultが呼び出されます。
そして、ファイルを読み出したのち、さらに続けて、プライベートキーファイル(application/pgp-keys)を選択するように促します。
プライベートキーファイルも選択されたら、ファイルを読み出したのち、さきほどのstatic関数を呼び出しています。

```java:MainActivity.java
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
        super.onActivityResult(requestCode, resultCode, resultData);
        if( resultCode == RESULT_OK && requestCode == REQEST_CODE_FILEOPEN_CRT ){
            try {
                Uri fileUri = resultData.getData();
                InputStreamReader reader = new InputStreamReader(getContentResolver().openInputStream(fileUri));
                char[] buffer = new char[1024];
                StringWriter writer = new StringWriter();
                int size;
                while((size = reader.read(buffer)) != -1 ){
                    writer.write(buffer, 0, size);
                }
                crt_string = writer.toString();

                Toast.makeText(this, "秘密鍵ファイルを選択してください。", Toast.LENGTH_SHORT).show();
                Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
                intent.addCategory(Intent.CATEGORY_OPENABLE);
                intent.setType("application/pgp-keys");
                startActivityForResult(intent, REQEST_CODE_FILEOPEN_KEY);
            }catch(Exception ex){
                Toast.makeText(this, ex.getMessage(), Toast.LENGTH_LONG).show();
            }
        }else if( resultCode == RESULT_OK && requestCode == REQEST_CODE_FILEOPEN_KEY ){
            try {
                Uri fileUri = resultData.getData();
                InputStreamReader reader = new InputStreamReader(getContentResolver().openInputStream(fileUri));
                char[] buffer = new char[1024];
                StringWriter writer = new StringWriter();
                int size;
                while((size = reader.read(buffer)) != -1 ){
                    writer.write(buffer, 0, size);
                }
                key_string = writer.toString();
                EditText edit;
                AwsIotService.saveCertificate(keyStorePath, crt_string, key_string);
                Toast.makeText(this, "証明書が設定されました。", Toast.LENGTH_LONG).show();
            }catch(Exception ex){
                Toast.makeText(this, ex.getMessage(), Toast.LENGTH_LONG).show();
            }
        }
    }
```

あとは、Serviceの派生クラスのonStartCommandの中でAWS IoT CoreのMQTTブローカに接続するために以下を呼び出します。

```java:AwsIotService.java
        String endpointPrefix = intent.getStringExtra("endpointPrefix");
        String keyStorePath = intent.getStringExtra("keyStorePath");
        mTopicName = intent.getStringExtra("topicName");
        String clientId = intent.getStringExtra("clientId");
        if (endpointPrefix != null && keyStorePath != null && clientId != null && mTopicName != null ) {
            try {
                mqttManager = new AWSIotMqttManager(clientId, Region.getRegion(Regions.AP_NORTHEAST_1), endpointPrefix);
                KeyStore keyStore = AWSIotKeystoreHelper.getIotKeystore(AWSIOT_CERT_ID, keyStorePath, AWSIOT_KEY_STORE_NAME, AWSIOT_DEFAULT_PASSWORD);
                mqttManager.connect(keyStore, new AWSIotMqttClientStatusCallback() {
                    @Override
                    public void onStatusChanged(AWSIotMqttClientStatus status, Throwable throwable) {
                        Log.d(TAG, "AWSIotMqttClientStatus changed.(" + status.toString() + ")");
                        if (status.equals(AWSIotMqttClientStatus.Connected)) {
                            mqttManager.subscribeToTopic(mTopicName, AWSIotMqttQos.QOS1, new AWSIotMqttNewMessageCallback() {
                                @Override
                                public void onMessageArrived(String topic, byte[] data) {
                                    Log.d(TAG, "onMessageArrived");
                                    String message = new String(data);
                                    fireReceiveMessage(topic, message);
                                }
                            });
                            isSubscribed = true;
                        }
                    }
                });
            } catch (Exception ex) {
                Log.d(TAG, ex.getMessage());
            }
        }
```

AWS IoT Coreに対して、connectを呼び出して接続を行い、接続が完了したらonStatusChangedが呼び出されるので、トピック名を指定してSubscribeを呼び出しています。

endpointPrefixとkeyStorePathとtopicNameとclientIdは、MainActivity側で指定できるようにしておきました。
endpointPrefixというのがありますが、これは、AWS管理コンソールのAWS IoT Coreの設定を選択すると表示されるデバイスデータエンドポイントにあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/19f6d1ae-4500-8938-2d04-797650e37087.png)

```XXXXXXXX-ats.iot.ap-northeast-1.amazonaws.com```

となっており、上記のXXXXXXXX-atsとなっている部分のことです。

# AIDLを使った他のアプリからの呼び出し

AIDLを使うと、フォアグラウンドサービスとして立ち上げたサービスが公開する関数を別のアプリから呼び出したり、サービスから別のアプリのコールバック関数が呼び出されたりすることができます。

AIDLとは、呼び出し元、呼び出し側が、共通で認識しておくべきインタフェースを定義するものです。
Android Studioのプロジェクトから右クリックして、AIDLを生成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/b56b50b2-f2a6-c99f-0b0f-98125060c6f4.png)

今回は、IAwsIotServiceとIAwsIotServiecListenerを作成します

```aidl:IAwsIotService.aidl
package jp.or.myhome.sample.awsiotservice;

import jp.or.myhome.sample.awsiotservice.IAwsIotServiceListener;

interface IAwsIotService {
    void publishMessage(String topicName, String message);
    boolean isSubscribed();
    void addListener(IAwsIotServiceListener listener);
    void removeListener(IAwsIotServiceListener listener);
}
```

```aidl:IAwsIotServiceListener.aidl
package jp.or.myhome.sample.awsiotservice;

interface IAwsIotServiceListener {
    void onReceiveMessage(String topicName, String message);
}
```

IAwsIotService.publishMessageは、AWS IoT CoreにMQTTメッセージを送信したいときに呼び出します

IAwsIotService.addListenerは、AWS IoT CoreからMQTTメッセージを受信した場合にコールバック呼び出しするためのIAwsIotServiceListenerのインスタンスを登録するためのものです。

IAwsIotServiceListener.onReceiveMessage は、AWS IoT CoreからMQTTメッセージを受信した場合に呼び出されるコールバック関数です。

この状態で一度ビルドすると、IAwsIotServiceListenerとIAwsIotServiceのクラスが生成されimportできるようになります。

この関数が呼び出し側から呼ばれたときのサービス側の実装を追加見ていきます。

```java:AwsIotService.java
    RemoteCallbackList<IAwsIotServiceListener> listeners;

    final IAwsIotService.Stub binder = new IAwsIotService.Stub() {
        @Override
        public void publishMessage(String topicName, String message) throws RemoteException {
            if( mqttManager == null )
                throw new IllegalStateException("is not subscribed");

            mqttManager.publishString(message, topicName == null ? mTopicName : topicName, AWSIotMqttQos.QOS1);
        }

        @Override
        public boolean isSubscribed() throws RemoteException {
            return isSubscribed;
        }

        @Override
        public void addListener(IAwsIotServiceListener listener) throws RemoteException {
            listeners.register(listener);
        }

        @Override
        public void removeListener(IAwsIotServiceListener listener) throws RemoteException {
            listeners.unregister(listener);
        }
    };
```

IAwsIotService.Stubの中身を実装しています。
publishMessage()が、AWS IoT CoreのMQTTブローカにPublishするための関数で、AWS IoTのライブラリのAPIを呼び出しています。

ちなみに、Exceptionを返したい場合は、返せるExceptionに限りがあるようです。

https://stackoverflow.com/questions/3156389/android-remoteexceptions-and-services

呼び出し元からaddListenerにコールバック関数を指定して呼び出されると、コールバック関数のインスタンスをlistenersに登録しておいています。後程使います。

出来上がったこのバインダbinderは、オーバライドする関数onBind()で呼び出し元に渡すことができます。

```java:AwsIotService.java
    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind called");
        return binder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d(TAG, "onUnbind called");
        return true;
    }

    @Override
    public void onRebind(Intent intent) {
        Log.d(TAG, "onRebind called");
    }
```

登録されるコールバック関数は、以下の関数で使っています。登録されたすべてのコールバック関数の、onReceiveMessage()を呼び出しています。それにより、呼び出し元のコールバック関数が発火する形になります。

```java:AwsIotService.java
    private void fireReceiveMessage(String topic, String message) {
        Log.d(TAG, "fireReceiveMessage");

        int num = listeners.beginBroadcast();
        try {
            for (int i = 0; i < num; i++) {
                try {
                    IAwsIotServiceListener listener = listeners.getBroadcastItem(i);
                    listener.onReceiveMessage(topic, message);
                } catch (RemoteException ex) {
                    Log.d(TAG, ex.getMessage());
                }
            }
        } catch (Exception ex) {
            Log.d(TAG, ex.getMessage());
        } finally {
            listeners.finishBroadcast();
        }
    }
```

この関数は、SubscribeによりMQTTメッセージを受信したとき(AWSIotMqttNewMessageCallback)に呼び出しています。

# サービスの呼び出し

サービスの呼び出しは、バインダを使って呼び出します。
呼び出し元がサービスのインスタンスと同じアプリケーションの場合と、別のアプリケーションから呼び出す場合でちょっと異なりますが、今回は別のアプリケーションから呼び出してみます。

まず、同じAIDLを、今度は呼び出したいアプリケーションにコピーしておきます。
その後いったんビルドすると、それ用のクラスが生成されます。

次に、AndroidManifest.xmlに、接続するサービスのパッケージ名を指定します。Android 11以降はこれが必要だそうです。

```xml:AndroidManifest.xml
    <queries>
        <package android:name="jp.or.myhome.sample.awsiotservice" />
    </queries>
```

（参考）パッケージの公開を求める宣言をする
https://developer.android.com/training/package-visibility/declaring?hl=ja

そして、以下を呼び出すことで、相手のサービスのバインダを取得することができます。
REMOTE_ACTION_NAME は、サービス側のAndroidManifest.xmlにおいて、serviceのintent-filterのactionで指定した値と同じにします。

```java:MainActivity.java
public static final String REMOTE_ACTION_NAME = "AwsIotService";
                    Intent intent = new Intent(REMOTE_ACTION_NAME);
                    intent.setPackage(IAwsIotService.class.getPackage().getName());
                    bindService(intent, connection, Context.BIND_AUTO_CREATE);
```

bindServiceが成功すると、引数で指定したServiceConnectionのメソッドonServiceConnected()が呼び出され、引数でバインダを取得できます。

```java:MainActivity.java
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onBindingDied(ComponentName className){
            Log.d(TAG, "onBindingDied(" + className.getClassName() + ")");
        }

        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            Log.d(TAG, "onServiceConnected(" + className.getClassName() + ")");
            try {
                binder = IAwsIotService.Stub.asInterface(service);
                Log.d(TAG, "isSubscribed:" + binder.isSubscribed());
                binder.addListener(new IAwsIotServiceListener.Stub() {
                    @Override
                    public void onReceiveMessage(String topicName, String message) throws RemoteException {
                        Log.d(TAG, "onReceiveMessage: " + message);
                        try {
                            handler.sendTextMessage(message);
                        }catch(Exception ex){
                            Log.d(TAG, ex.getMessage());
                        }
                    }
                });
                mBound = true;
            }catch(Exception ex){
                Toast.makeText(getApplicationContext(), ex.toString(), Toast.LENGTH_LONG).show();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName className) {
            Log.d(TAG, "onServiceDisconnected(" + className.getClassName() + ")");
            unbindService(connection);
            mBound = false;
        }
    };
```

ついでに、binder.addListenerを呼び出して、コールバックを受け取れるようにしています。

# (参考)Cordovaプラグインとして組み込む

横道にそれますが、Cordovaでプラグインの実装が面倒な場合、別アプリケーションとしてフォアグラウンドサービスを立ち上げ、AIDLで呼び出すCordovaプラグインを作れば、プラグイン開発の手間が減りますね。

plugin.xmlを例えば以下のように定義すればよいです。

```xml:plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
    id="cordova-plugin-sampleaidl" version="0.0.1">
    <name>SampleAidl</name>
    <js-module src="www/plugin_sampleaidl.js" name="sampleaidl">
        <clobbers target="sampleaidl" />
    </js-module>
    <platform name="android">
        <config-file target="res/xml/config.xml" parent="/*">
            <feature name="SampleAidl" >
                <param name="android-package" value="jp.or.sample.SampleAidl.Main"/>
                <param name="onload" value="true" />
            </feature>
        </config-file>
        <config-file target="AndroidManifest.xml" parent="/*">
            <queries>
                <package android:name="jp.or.myhome.sample.awsiotservice" />
            </queries>
        </config-file>
        <source-file src="src/android/jp/or/sample/SampleAidl/Main.java" target-dir="src/jp/or/sample/SampleAidl" />
        <source-file src="src/android/aidl/jp/or/myhome/sample/awsiotservice/IAwsIotService.aidl" target-dir="src/jp/or/myhome/sample/awsiotservice/" />
        <source-file src="src/android/aidl/jp/or/myhome/sample/awsiotservice/IAwsIotServiceListener.aidl" target-dir="src/jp/or/myhome/sample/awsiotservice/" />
    </platform>
</plugin>
```

ポイントは2つ。
source-file として、AIDLファイルを指定すれば、勝手に取り込んでくれます。
また、config-fileでqueriesの指定を追加します。

それ以外は、以下の参考の通りです。
　[Cordovaアプリ開発の備忘録(プラグイン編)](https://qiita.com/poruruba/items/a454f1b90c5d8b0cb2f8)

以上