[iOS] ネットワークを最適化するための調査 (準備)

最近、iosの3GやLTEのネットワーク周りはどのように制御されていて、実際高速化やバッテリーの最適化をアプリごとでできないか、調査してみようと思ったので、ちょっと前知識と調査に必要そうなものをまとめてみた。

# 3G通信の通信制御方式には複数ある
## Fast Dormancy と Network Controlled Fast Dormancy

それぞれ、

Fast Dormancy: cell_dch, cell_fach, Idle
Network Controlled Fast Dormancy: cell_dch, cell_fach, cell_pch, Idle

の状態がある。簡単に言うと、
cell_dch: 上りも下りも高速通信状態
cell_fach: cell_dchよりも遅く、上りはランダム通信。下りは常時通信。
cell_pch: ネットワークの接続を維持していて、上りは通信していなく、下りのみ一定間隔で基地局から信号を受信する状態。
idle: アイドル状態。

![fast_dormancy.png](https://qiita-image-store.s3.amazonaws.com/0/62386/668a2de8-fd98-e1ea-6c82-693fe5d3524c.png)


Network Controlled Fast Dormancyはcell_pchという中間領域を作ることで、ネットワーク側とのシグナルの量を削減している。
idleからcell_dchへの遷移へ必要なシグナルが30シグナルに対して、cell_pchからcell_dchへの遷移へ必要なシグナルが12シグナルになる。

cell_dchからcell_pchへの遷移はネットワーク側で制御されているため、端末側ではidleからcell_dch、cell_pchからcell_dch、cell_fachからcell_dchへの遷移を最適化するか、あとはcell_fachを極力以降させずに通信時cell_dch状態を保ち続けるように最適化することを考えていくのが良さそうだ。

また例え、Network Controlled Fast Dormancyを実装したOSであっても、ネットワーク側がそれに対応していないと結局Fast Dormancyとして扱われるんじゃないかと思うので、日本だけじゃなくて海外のネットワークを考慮するためには、
この2つの通信制御方式の壁をちゃんと認識していた方が、ネットワークのローカライズとかにも便利なのかも。

# LTE通信の通信制御方式と3G通信との違い

※ これは別途記述する。

# iosで通信系を調査するための準備
## Reachability
appleが昔からsample codeとして提供しているReachabilityだが、このPrintReachabilityFlagsメソッドで表示しているflag statusが利用できそう。

```lang:Reachability.m
static void PrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment)
{
//#if kShouldPrintReachabilityFlags

    NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n",
          (flags & kSCNetworkReachabilityFlagsIsWWAN)				? 'W' : '-',
          (flags & kSCNetworkReachabilityFlagsReachable)            ? 'R' : '-',

          (flags & kSCNetworkReachabilityFlagsTransientConnection)  ? 't' : '-',
          (flags & kSCNetworkReachabilityFlagsConnectionRequired)   ? 'c' : '-',
          (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)  ? 'C' : '-',
          (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
          (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)   ? 'D' : '-',
          (flags & kSCNetworkReachabilityFlagsIsLocalAddress)       ? 'l' : '-',
          (flags & kSCNetworkReachabilityFlagsIsDirect)             ? 'd' : '-',
          comment
          );
//#endif
}
```

例えば、iosエミュレータなら
```NSLogの出力
2015-08-19 21:35:17.550 NetworkCheck2[4634:4375998] Reachability Flag Status: -- t------ currentFlag
2015-08-19 21:35:18.553 NetworkCheck2[4634:4375998] Reachability Flag Status: -- tcC-D-- currentFlag
```
で通信状態が変化する。

# CoreTelephony.framework
## シグナル情報の取得
ios7から新たに利用できるようになったframeworkである。
このフレームワークを利用することによって、制御シグナルを確認することができるようだ。

```lang:NetworkObserver.m

int CTGetSignalStrength();

@implementation NetworkObserver

- (void)monitoringSignalStrength {

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_async(queue, ^{
           while (true) {
                printf("signal strength: %d\n", CTGetSignalStrength());
                sleep(1);
           }
        });
}
```

例えば、cell_pchからcell_dchへのシグナル数は12とわかっているから、そこから状態の逆算ができるかもしれない。

## ネットワークの変更観測とネットワークの種類

ネットワーク自体の変更が観測できるようにobserverを仕込む。

```lang:NetworkObserver.m

typedef enum : NSInteger{
    NONotReachable,
    NONetworkStatusWifi,
    NCNetworkStatus2G,
    NONetworkStatus3G,
    NONetworkStatusLTE
}NONetworkStatus;


@property (weak) id myObserver;

---------

@implementation NetworkObserver

- (void)addObserverForNetworkChange{


    CTTelephonyNetworkInfo *telephonyInfo = [CTTelephonyNetworkInfo new];
    NSLog(@"Current Radio Access Technology: %@", telephonyInfo.currentRadioAccessTechnology);
 
   self.myObserver = [NSNotificationCenter.defaultCenter addObserverForName:CTRadioAccessTechnologyDidChangeNotification
                                                    object:telephonyInfo
                                                     queue:nil
                                                usingBlock:^(NSNotification *note){
        CTTelephonyNetworkInfo currentTelephonyInfo = (CTTelephonyNetworkInfo *)note;
        NSLog(@"New Radio Access Technology: %@ \n Network Kind: %d", telephonyInfo.currentRadioAccessTechnology, [self currentNetwork:currentTelephonyInfo ]);
    }];
}

- (void)removeObserverWithTelephonyNetworkInfo{
   [[NSNotificationCenter defaultCenter] removeObserver:self.myObserver];
}

- (NCNetworkStatus)currentNetwork:(CTTelephonyNetworkInfo*)networkInfo{
    
    Reachability *reach = [Reachability new];
    
    if (reach.currentReachabilityStatus == NotReachable) {
        NSLog(@"No network access");
        return NONotReachable;
    }else if (reach.currentReachabilityStatus == ReachableViaWiFi) {
        NSLog(@"Wifi Connection");
        return NONetworkStatusWifi;
    }

    if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyGPRS] ||
        [networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyEdge] ||
        [networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMA1x]
        ) {
        //NSLog(@"2G");
        return NCNetworkStatus2G;
    }else if ([networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyWCDMA] ||
              [networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSDPA] ||
              [networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSUPA] ||
              [networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyeHRPD] ||
              [networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORev0] ||
              [networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevA] ||
              [networkInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevB]
              ) {
        //NSLog(@"3G");
        return NONetworkStatus3G;
    }
    
    //NSLog(@"LTE");
    return NONetworkStatusLTE;
    
}

```

ちょっと雑だけど、こんな感じかな。

だいたい準備はできたので、次回以降これらを利用してネットワークを制御状態を確認していきたいと思う。