Androidのモバイルネットワークを制御する

おととしスマートフォンGalaxy S)に機種変更してから、毎月の支払いが8千円近くになった。
諸般の事情でお金の出を見直していて、この金額は無いわーと思い、契約内容を見直した。

契約内容は、以下の2つに変更

このプランへ変更することで、毎月の支払いが1,500円くらいになる。
ここへ、外でネットを使うための公衆無線LANの費用が315円かかる。

ただ、これだけだと公衆無線LANに繋がらないと、外でメールを受けることが出来ない。
なので、必要なときだけパケット通信を有効にするウィジェットを作ってみた。

Androidのモバイルネットワークの制御のしかた

モバイルネットワークの制御には、ConnectivityManagerを利用する。
ただ、モバイルネットワークの制御を行うメソッドは公開されていないので、リフレクションを利用して

setMobileDataEnabled(boolean enable)

を実行する。

同様に、接続状態を取得するには

setMobileDataEnabled()

を実行する。

いま作成しているアプリでは、以下のようなクラスを作成してネットワークの接続状態を制御している。

public class MobileNetwork {

  private final ConnectivityManager connectivityManager;

  public MobileNetwork(ConnectivityManager connectivityManager) {
    this.connectivityManager = connectivityManager;
  }

  public void connect() {
    if (isConnected()) {
      return;
    }

    setMobileDataEnabled(true);
  }

  public void disconnect() {
    if (!isConnected()) {
      return;
    }

    setMobileDataEnabled(false);
  }

  public boolean isConnected() {
    return isMobileDataEnabled();
  }

  void setMobileDataEnabled(boolean enabled) {
      try {
      Object connectivityManagerStub = getConnectivityManagerStub();
      Class<?> connectivityManagerStubClass = getConnectivityManagerStubClass(connectivityManagerStub);

      Method setMobileDataEnabledMethod = connectivityManagerStubClass.getDeclaredMethod("setMobileDataEnabled", Boolean.TYPE);
      setMobileDataEnabledMethod.setAccessible(true);
      setMobileDataEnabledMethod.invoke(connectivityManagerStub, enabled);
    } catch (Exception e) {
      throw new MobileNetworkException("モバイルネットワークの切り替えでエラーが発生しました。enabled:" + enabled, e);
    }
  }

  boolean isMobileDataEnabled() {
      try {
      Object connectivityManagerStub = getConnectivityManagerStub();
      Class<?> connectivityManagerStubClass = getConnectivityManagerStubClass(connectivityManagerStub);

      Method getMobileDataEnabledMethod = connectivityManagerStubClass.getDeclaredMethod("getMobileDataEnabled");
      getMobileDataEnabledMethod.setAccessible(true);
      Object result = getMobileDataEnabledMethod.invoke(connectivityManagerStub);
      return Boolean.TRUE.equals(result);
    } catch (Exception e) {
      throw new MobileNetworkException("モバイルネットワークの状態確認でエラーが発生しました。", e);
    }
  }

  Object getConnectivityManagerStub() throws ClassNotFoundException, SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
    Class<?> connectivityManagerClass = Class.forName(connectivityManager.getClass().getName());
    Field serviceField = connectivityManagerClass.getDeclaredField("mService");
    serviceField.setAccessible(true);
    return serviceField.get(connectivityManager);
  }

  Class<?> getConnectivityManagerStubClass(Object connectivityManagerStub) throws ClassNotFoundException {
    return Class.forName(connectivityManagerStub.getClass().getName());
  }

  public static class MobileNetworkException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    public MobileNetworkException(String detailMessage, Throwable throwable) {
      super(detailMessage, throwable);
    }

  }

}