楽天モバイルで自社回線かパートナー(KDDI)回線のどちらに接続しているか判定する

# 前置き
修正しても好ましくない履歴が残ってしまうために再投稿してます。(2020年5月の記事になります。)
筆者はデジモノガジェットが好きなので、楽天モバイルのRakuten UN-LIMITを契約しています。
ご存知の方も多いと思いますが、楽天モバイルは後発のキャリアですので自社の回線のエリアが狭く、パートナー(KDDI)回線でエリアを補強しています。
自社回線は容量無制限でパートナー(KDDI)回線は5GBという制限があるので、リアルタイムでどちらの回線に接続しているか把握できれば便利だと思い調査しました。

# 構成
| 名前           | バージョン          |
| ------------- | ---------------- |
| macOS         | Catalina 10.15.4   |
| AndroidStudio | 3.6.3            |
| Kotlin        | 1.3.72           |
| AVD(API)      | 使用しません |
| 実機      | SH-RM11 |

# 判定ロジック
世間では電波の周波数(バンド)を取得して判断していることが多いと思います。

| バンド | 事業者            |
| ---: | ---------------- |
|    ３ | 楽天モバイル自社回線 |
|   18 | パートナー(KDDI)回線 |

この記事では接続中の基地局の情報を取得して確認してみます。

# テスト方法
楽天モバイル公式対応の端末(SH-RM11)で自社回線エリアとパートナー(KDDI)回線エリアの境界付近で検証します。
自宅が境界付近で自社回線に接続されたりパートナー(KDDI)回線に接続されたりしていまして、実験に適しています。

# ソースコードの説明

```kotlin:MainActivity.kt
package com.devnokiyo.bandscope

import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.telephony.*
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat

class MainActivity : AppCompatActivity() {
    private lateinit var telephonyManager: TelephonyManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Manifest.permission.ACCESS_COARSE_LOCATIONのパーミッションを取得して良いか確認します。
        // 〜〜 省略 〜〜

        // TelephonyManagerを取得します。
        telephonyManager = getSystemService(TELEPHONY_SERVICE) as TelephonyManager
    }

    public override fun onPause() {
        super.onPause()
        // リスナーを解除します。
        telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
    }

    public override fun onResume() {
        super.onResume()
        // セルの情報が変わったり、電波状態が変更になったときのリスナーを登録します。
        telephonyManager.listen(
            phoneStateListener,
            PhoneStateListener.LISTEN_CELL_INFO or PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
        )
    }

    public override fun onDestroy() {
        super.onDestroy()
    }

    /**
     * PhoneStateListenerを定義します。
     */
    private val phoneStateListener = object : PhoneStateListener() {
        /**
         * 接続中の基地局の情報が変更された時のイベントです。
         */
        override fun onCellInfoChanged(cellInfo: MutableList<CellInfo>?) {
            super.onCellInfoChanged(cellInfo)

            // SIMカードを抜いたときはnullになっていました。
            cellInfo ?: return
            showCellInfo(cellInfo)
        }

        /**
         * 電波強度が変化した時のイベントです。
         */
        override fun onSignalStrengthsChanged(signalStrength: SignalStrength?) {
            super.onSignalStrengthsChanged(signalStrength)
            // Manifest.permission.ACCESS_COARSE_LOCATIONのパーミッションを取得しているか判定します。
            // 〜〜 省略 〜〜

            showCellInfo(telephonyManager.allCellInfo)
        }

        private fun showCellInfo(cellInfos: List<CellInfo>) {
            // LTE(4G)の基地局のうち信号があるものを抽出します。
            val cellInfoLtes = cellInfos.filter { q -> q is CellInfoLte && q.isRegistered }
                .map { q -> q as CellInfoLte }

            // LTE(4G)の基地局情報をログに出力します。
            cellInfoLtes.forEach { q ->
                Log.d("TestApp", "Cell ID                 is ${q.cellIdentity.ci}")
                Log.d(
                    "TestApp",
                    "Mobile Network Operator is ${q.cellIdentity.mobileNetworkOperator}"
                )
                Log.d("TestApp", "Earfcn                  is ${q.cellIdentity.earfcn}")
                Log.d("TestApp", "Operator Alpha Short    is ${q.cellIdentity.operatorAlphaShort}")
            }
        }
    }
}
```

# ログの出力内容
実機をUSBデバックしてサンプルアプリを実行してみます。
Cell IDは位置が割り出せると聞きますので、一部マスクさせて頂きますが、自社回線に接続されたりパートナー(KDDI)回線に接続されたりしていることが確認できました。

```
2020-05-09 00:30:20.823 10205-10205/com.devnokiyo.bandscope D/TestApp: Cell ID                 is 37*****1
2020-05-09 00:30:20.823 10205-10205/com.devnokiyo.bandscope D/TestApp: Mobile Network Operator is 44050
2020-05-09 00:30:20.823 10205-10205/com.devnokiyo.bandscope D/TestApp: Earfcn                  is 5900
2020-05-09 00:30:20.823 10205-10205/com.devnokiyo.bandscope D/TestApp: Operator Alpha Short    is KDDI

2020-05-09 00:32:17.033 10205-10205/com.devnokiyo.bandscope D/TestApp: Cell ID                 is 36*****3
2020-05-09 00:32:17.033 10205-10205/com.devnokiyo.bandscope D/TestApp: Mobile Network Operator is 44050
2020-05-09 00:32:17.033 10205-10205/com.devnokiyo.bandscope D/TestApp: Earfcn                  is 5900
2020-05-09 00:32:17.034 10205-10205/com.devnokiyo.bandscope D/TestApp: Operator Alpha Short    is KDDI

2020-05-09 00:32:44.043 10205-10205/com.devnokiyo.bandscope D/TestApp: Cell ID                 is 34*****6
2020-05-09 00:32:44.043 10205-10205/com.devnokiyo.bandscope D/TestApp: Mobile Network Operator is 44050
2020-05-09 00:32:44.043 10205-10205/com.devnokiyo.bandscope D/TestApp: Earfcn                  is 5900
2020-05-09 00:32:44.043 10205-10205/com.devnokiyo.bandscope D/TestApp: Operator Alpha Short    is KDDI


2020-05-09 00:35:43.779 12740-12740/com.devnokiyo.bandscope D/TestApp: Cell ID                 is 88*****4
2020-05-09 00:35:43.779 12740-12740/com.devnokiyo.bandscope D/TestApp: Mobile Network Operator is 44011
2020-05-09 00:35:43.779 12740-12740/com.devnokiyo.bandscope D/TestApp: Earfcn                  is 1500
2020-05-09 00:35:43.779 12740-12740/com.devnokiyo.bandscope D/TestApp: Operator Alpha Short    is Rakuten

2020-05-09 00:40:27.566 14476-14476/com.devnokiyo.bandscope D/TestApp: Cell ID                 is 88*****6
2020-05-09 00:40:27.567 14476-14476/com.devnokiyo.bandscope D/TestApp: Mobile Network Operator is 44011
2020-05-09 00:40:27.567 14476-14476/com.devnokiyo.bandscope D/TestApp: Earfcn                  is 1500
2020-05-09 00:40:27.567 14476-14476/com.devnokiyo.bandscope D/TestApp: Operator Alpha Short    is Rakuten
```

# 終わりに
本当はServiceで通知バーにリアルタイム表示するアプリを作成する予定でしたが、新しく買ったOPPOのスマホは通知バーにうまく表示できないようです。すっかり開発意欲が萎えてしまいました:sob::sob::sob: