AWSの請求金額を取得するAndroidアプリ

#AWSの請求金額をさくっと確認したい
AWSを使いすぎないように請求アラームを設定していますが、何かと気になるので請求金額を定期的に確認しています。ただ、その度にコンソールにログインするのは面倒くさかったので、「AWS SDK for Android」を利用して請求金額をさくっと確認できるAndroidアプリを作成します。

#Amazon Cognitoで認証基盤を作成する
AndroidアプリからAWSのリソースにアクセスするために、Cognitoで認証基盤を作成します。

##Identity Poolを作成する
AWSコンソールのモバイルサービス＞Cognitoより「Manage Federated Identities」を選択してIdentity Poolを新規作成します。

![WS000006.JPG](https://qiita-image-store.s3.amazonaws.com/0/83813/fd7e232a-9e27-4025-0029-39c12104a6ed.jpeg)
「Identity pool name」に任意の名前を入力して、認証されていないユーザからアクセスできるように「Enable access to unauthenticated identities」にチェックを入れます。

![WS000007.JPG](https://qiita-image-store.s3.amazonaws.com/0/83813/6f114ef3-ebd4-8a85-28fb-f66942013abf.jpeg)
ロール名に任意の名前を入力して、IAMロールを新規作成します。

![WS000008.JPG](https://qiita-image-store.s3.amazonaws.com/0/83813/124ae1d6-70ab-db81-2e7d-08a97a46b08c.jpeg)
サンプルコードに「Identity Pool ID」が表示されます。後でアプリ作成時に利用するのでメモしておきます。

##Cognito用のIAMロールにポリシーをアタッチする
上記で作成したIAMロールからCloudwatchのメトリクスを参照できるように、権限を付与します。

![WS000009.JPG](https://qiita-image-store.s3.amazonaws.com/0/83813/ddd9cbef-8577-42ed-83f7-18a76ccddb12.jpeg)

IAMよりロールを選択して、CloudWatchFullAccessをアタッチします。（適宜、最小権限となるポリシーに置き換えて下さい）
今回は、未認証のアプリからアクセスするので、Unauth用のロールに適切なポリシーをアタッチします。

#Androidアプリを作成する
上記で作成したCognito認証基盤を利用して実際にアプリを作成します。

##「AWS SDK for Android」のライブラリを取得する
「build.gradle」のdependenciesに以下を追記します。

```groovy:build.gradle
dependencies {
    compile 'com.amazonaws:aws-android-sdk-cloudwatch:2.+'
}
```
## uses-permissionを設定する
AndroidアプリからAWSにネットワークアクセスできるように「AndroidManifest.xml」に以下を追記します。

```xml:AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
```

##AmazonCloudWatchClientオブジェクトを生成するユーティリティクラスを実装する
CloudWatchにアクセスするためのクライアントオブジェクトを生成するクラスを作ります。
以下のソースは「AWS SDK for Android 」のコードサンプル（[https://aws.amazon.com/jp/sdkforandroid/](https://aws.amazon.com/jp/sdkforandroid/)）を参考にしています。
なお、「COGNITO_POOL_ID」定数には、先ほどメモしておいた「Identity Pool ID」を設定してください。
また、東京リージョンではない場合は、「Regions.AP_NORTHEAST_1」を修正してください。

```java:Util.java
import android.content.Context;
import com.amazonaws.auth.CognitoCachingCredentialsProvider;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.cloudwatch.AmazonCloudWatchClient;

public class Util {
    public static final String COGNITO_POOL_ID = "your Identity Pool ID";

    private static AmazonCloudWatchClient cloudWatchClient;
    private static CognitoCachingCredentialsProvider sCredProvider;

    private static CognitoCachingCredentialsProvider getCredProvider(Context context) {
        if (sCredProvider == null) {
            sCredProvider = new CognitoCachingCredentialsProvider(
                    context.getApplicationContext(),
                    COGNITO_POOL_ID,
                    Regions.AP_NORTHEAST_1);
        }
        return sCredProvider;
    }

    public static AmazonCloudWatchClient getCloudWatchClient(Context context){
        if(cloudWatchClient == null){
            cloudWatchClient = new AmazonCloudWatchClient(getCredProvider(context.getApplicationContext()));
        }
        return cloudWatchClient;
    }
}
```

##CloudWatch APIを用いて請求金額を取得するTaskクラスを実装する
「AmazonCloudWatchClient」からCloudWatch APIを利用して今月の請求金額を取得するTaskクラスを作ります。
Androidはメインスレッドでhttpリクエストを行ってはいけないようなので、別スレッドで実行できるようにCallableインタフェースを実装しています。

```java:BillingInfoTask.java
import com.amazonaws.services.cloudwatch.AmazonCloudWatchClient;
import com.amazonaws.services.cloudwatch.model.Datapoint;
import com.amazonaws.services.cloudwatch.model.Dimension;
import com.amazonaws.services.cloudwatch.model.GetMetricStatisticsRequest;
import com.amazonaws.services.cloudwatch.model.GetMetricStatisticsResult;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;

public class BillingInfoTask implements Callable<String> {

    // AmazonCloudWatcのクライアント
    private AmazonCloudWatchClient _cloudWatchClient;

    public BillingInfoTask(AmazonCloudWatchClient cloudWatchClient) {
        _cloudWatchClient = cloudWatchClient;
    }

    @Override
    public String call() throws Exception {
        // リクエストオブジェクト作成
        GetMetricStatisticsRequest getMetricStatisticsRequest = new GetMetricStatisticsRequest();
        Date date = new Date();
        getMetricStatisticsRequest.setStartTime(new Date(date.getTime() - (24 * 3600 * 1 * 1000)));
        getMetricStatisticsRequest.setEndTime(new Date());
        getMetricStatisticsRequest.setMetricName("EstimatedCharges");
        getMetricStatisticsRequest.setNamespace("AWS/Billing");
        getMetricStatisticsRequest.setPeriod(86400);

        Collection<String> statistics = new ArrayList<String>();
        statistics.add("Maximum");
        getMetricStatisticsRequest.setStatistics(statistics);

        // ディメンションオブジェクト作成
        Collection<Dimension> dimensions = new ArrayList<Dimension>();
        Dimension currenctDimension = new Dimension();
        currenctDimension.setName("Currency");
        currenctDimension.setValue("USD");
        dimensions.add(currenctDimension);

        getMetricStatisticsRequest.setDimensions(dimensions);

        // 請求メトリクス取得
        GetMetricStatisticsResult getMetricStatisticsResult = _cloudWatchClient
                .getMetricStatistics(getMetricStatisticsRequest);
        List<Datapoint> dataPoints = getMetricStatisticsResult
                .getDatapoints();

        return dataPoints.get(0).getMaximum().toString();
    }
}
```

##メインアクティビティを実装する
アプリ起動時、画面に請求金額を表示するアクティビティクラスを作成します。
一応、更新ボタンも作成しています。

```java:MainActivity.java
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class MainActivity extends AppCompatActivity {

    // 更新ボタン
    private Button updateBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
    }

    // 初期処理
    private void init() {
        // 更新ボタン登録
        Button updateBtn = (Button) findViewById(R.id.update);
        updateBtn.setOnClickListener(clicked);

        // 請求金額表示
        dispBillingCost();
    }

    // 更新ボタン押下処理
    private View.OnClickListener clicked = new View.OnClickListener() {
        public void onClick(View v) {
            dispBillingCost();
        }
    };

    // 請求金額出力処理
    private void dispBillingCost() {
        ExecutorService billingInfoService = Executors.newCachedThreadPool();
        Future<String> billingInfoFuture = billingInfoService.submit(
                new BillingInfoTask(Util.getCloudWatchClient(MainActivity.this)));

        TextView billingTextView = (TextView) findViewById(R.id.billingCost);
        try {
            billingTextView.setText(billingInfoFuture.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
```

![WS000011.JPG](https://qiita-image-store.s3.amazonaws.com/0/83813/39785ed4-4f20-5c61-a51b-c2ea32ee64e4.jpeg)

以上で、AWSの請求金額がさくっと確認できる（だけの）アプリが出来上がりました。