diff

 diff --git a/src/com/android/fm/radio/FmSharedPreferences.java b/src/com/android/fm/radio/FmShared
 index 6843b53..8fb6338 100644
 --- a/src/com/android/fm/radio/FmSharedPreferences.java
 +++ b/src/com/android/fm/radio/FmSharedPreferences.java
 @@ -12,6 +12,8 @@ import java.util.HashMap;
  import java.util.List;
  import java.util.ListIterator;
  import java.util.Map;
 +import java.io.IOException;
 +
 
 public class FmSharedPreferences {
      public static final int REGIONAL_BAND_NORTH_AMERICA = 0;
 @@ -562,12 +564,20 @@ public class FmSharedPreferences {
                  mFrequencyBand_Stepsize = 50;
                  mFMConfiguration.setLowerLimit(76000);
                  mFMConfiguration.setUpperLimit(108000);
 +       //BCM4325_I2C_FM_RDS_SYSTEM BCM4325_FM_RDS_SYSTEM_FM
 +       try { Runtime.getRuntime().exec("hcitool cmd 0x3f 0x15 0x00 0x00 0x01"); }catch (IOExcepti
 +       //BCM4325_I2C_FM_CTRL BCM4325_I2C_FM_CTRL_BAND_JAPAN BCM4325_I2C_FM_CTRL_STEREO
 +       try { Runtime.getRuntime().exec("hcitool cmd 0x3f 0x15 0x01 0x00 0x05"); }catch (IOExcepti
                  break;
              }
              case FmReceiver.FM_JAPAN_STANDARD_BAND: {
                  mFrequencyBand_Stepsize = 100;
                  mFMConfiguration.setLowerLimit(76000);
                  mFMConfiguration.setUpperLimit(90000);
 +       //BCM4325_I2C_FM_RDS_SYSTEM BCM4325_FM_RDS_SYSTEM_FM
 +       try { Runtime.getRuntime().exec("hcitool cmd 0x3f 0x15 0x00 0x00 0x01"); }catch (IOExcepti
 +       //BCM4325_I2C_FM_CTRL BCM4325_I2C_FM_CTRL_BAND_JAPAN BCM4325_I2C_FM_CTRL_STEREO
 +       try { Runtime.getRuntime().exec("hcitool cmd 0x3f 0x15 0x01 0x00 0x05"); }catch (IOExcepti
                  break;
              }
              case FmReceiver.FM_USER_DEFINED_BAND: {
 @@ -579,6 +589,10 @@ public class FmSharedPreferences {
                  band = FmReceiver.FM_US_BAND;
                  mFMConfiguration.setLowerLimit(87500);
                  mFMConfiguration.setUpperLimit(107900);
 +       //BCM4325_I2C_FM_RDS_SYSTEM BCM4325_FM_RDS_SYSTEM_RDS
 +       try { Runtime.getRuntime().exec("hcitool cmd 0x3f 0x15 0x00 0x00 0x02"); }catch (IOExcepti
 +       //BCM4325_I2C_FM_CTRL BCM4325_I2C_FM_CTRL_BAND_US BCM4325_I2C_FM_CTRL_STEREO
 +       try { Runtime.getRuntime().exec("hcitool cmd 0x3f 0x15 0x01 0x00 0x04"); }catch (IOExcepti
                  mFrequencyBand_Stepsize = 200;
              }
          }
 @@ -679,7 +693,8 @@ public class FmSharedPreferences {
          switch (nCountryCode) {
              case REGIONAL_BAND_NORTH_AMERICA: {
                  // NORTH_AMERICA 87500 TO 108000 IN 200 KHZ STEPS
 -                mFMConfiguration.setRadioBand(FmReceiver.FM_US_BAND);
 +                //mFMConfiguration.setRadioBand(FmReceiver.FM_US_BAND);
 +       setRadioBand(FmReceiver.FM_US_BAND);
                  mFMConfiguration.setChSpacing(FmReceiver.FM_CHSPACE_200_KHZ);
                  mFMConfiguration.setEmphasis(FmReceiver.FM_DE_EMP75);
                  mFMConfiguration.setRdsStd(FmReceiver.FM_RDS_STD_RBDS);
 @@ -692,17 +707,18 @@ public class FmSharedPreferences {
              }
              case REGIONAL_BAND_EUROPE: {
                  // EUROPE/Default: 87500 TO 10800 IN 100 KHZ STEPS
 -                mFMConfiguration.setRadioBand(FmReceiver.FM_EU_BAND);
 +                //mFMConfiguration.setRadioBand(FmReceiver.FM_EU_BAND);
 +       setRadioBand(FmReceiver.FM_EU_BAND);
                  break;
              }
  
              case REGIONAL_BAND_JAPAN: {// - JAPAN 76000 TO 090000 IN 100 KHZ
                                         // STEPS
 -
                  mFMConfiguration.setChSpacing(FmReceiver.FM_CHSPACE_100_KHZ);
                  mFMConfiguration.setEmphasis(FmReceiver.FM_DE_EMP75);
                  mFMConfiguration.setLowerLimit(76000);
                  mFMConfiguration.setUpperLimit(90000);
 +                setRadioBand(FmReceiver.FM_JAPAN_STANDARD_BAND);
                  break;
              }
              case REGIONAL_BAND_JAPAN_WIDE: {// - JAPAN_WB 090000 TO 108000 IN 100
 @@ -711,6 +727,7 @@ public class FmSharedPreferences {
                  mFMConfiguration.setEmphasis(FmReceiver.FM_DE_EMP75);
                  mFMConfiguration.setLowerLimit(90000);
                  mFMConfiguration.setUpperLimit(108000);
 +                setRadioBand(FmReceiver.FM_JAPAN_WIDE_BAND);
                  break;
              }
 

hcitoolについて

サブコマンドの cmd だけ覚書として載せておく。
hcitoolのコードは、external/bluetooth/bluez/tools/ にある.
FMラジオモジュールへ読書の説明は、探した限りでは
frameworks/base/core/jni/android_hardware_fm.cpp にしかなかった

 Usage:
	cmd   [parameters]
 Example:
	cmd 0x03 0x0013 0x41 0x42 0x43 0x44
  1. ogfは、Opcode Group Fieldの略で、コマンドのグループを指す
  2. ocfは、Opcode Command Filedの略で、コマンドを指す

らしいが、コードを見ても 0x3f 0x15 が何を意味するのかはわからなかった
おそらく、FMモジュールにパラメータを渡す命令ではないだろうか。
HCI Commands(1/15 追記)


上で書いたhcitoolのマジックナンバーをdefine,enumどおりに書くと下記になる

 hcitool cmd 0x3f 0x15 BCM4325_I2C_FM_RDS_SYSTEM 0x00 BCM4325_FM_RDS_SYSTEM_FM
 hcitool cmd 0x3f 0x15 BCM4325_I2C_FM_CTRL 0x00 BCM4325_FM_CTRL_BAND_JAPAN

4番目の引数は、I2Cでおくるコマンド?を指していると思う
やっていることは読み書きなので、チップのレジスタを指しているのかもしれない
5番目が0x00なら書き込み、0x01なら読み込み
6番目が書き込む値で、読み込む場合は0x01で固定のようだ。


 int hci_w(int reg, int val)
 {
    int returnval = 0;

    char s1[100] = "hcitool cmd 0x3f 0x15 ";
    char stemp[10] = "";
    char starget[100] = "";
    char *pstarget = starget;

    sprintf(stemp, "0x%x ", reg);
    pstarget = strcat(s1, stemp);

    sprintf(stemp, "0x%x ", 0);
    pstarget = strcat(pstarget, stemp);

    sprintf(stemp, "0x%x ", val);
    pstarget = strcat(pstarget, stemp);
    returnval = system(pstarget);
    return returnval;
 }

 int hci_r(int reg)
 {
    int returnval = 0;

    char s1[100] = "hcitool cmd 0x3f 0x15 ";
    char stemp[10] = "";
    char starget[100] = "";
    char *pstarget = starget;

    sprintf(stemp, "0x%x ", reg);
    pstarget=strcat(s1, stemp);

    sprintf(stemp, "0x%x ", 1);
    pstarget=strcat(pstarget, stemp);

    sprintf(stemp, "0x%x ", 1);
    pstarget = strcat(pstarget, stemp);
    returnval = system(pstarget);
    returnval /= 0x100;
    LOGD("hci_r 0x%x \n", returnval);

    return returnval;
 }

hcitoolで周波数を変える

android_hardware_fm.cpp:setControlNativeにあるが、
hcitoolで周波数を変えることができる。
80MHz似合わせたいときは、

  1. 16000(=80000-64000) => 0x3E80
  2. BCM4325_I2C_FM_FREQ0 に 0x80 を、
  3. BCM4325_I2C_FM_FREQ1 に 0x3E を、
  4. BCM4325_I2C_FM_SEARCH_TUNE_MODE に BCM4325_FM_PRE_SET_MODE を書き込む
 hcitool cmd 0x3f 0x15 0x0a 0x00 0x80
 hcitool cmd 0x3f 0x15 0x0b 0x00 0x3e
 hcitool cmd 0x3f 0x15 0x09 0x00 0x01
 /*native interface */
 static jint android_hardware_fmradio_FmReceiverJNI_setFreqNative
    (JNIEnv * env, jobject thiz, jint fd, jint freq)
 {
    /* Adjust frequency to be an offset from 64MHz */
    freq -= BCM4325_FREQ_64MHZ;

    /* Write the FREQ0 register */
    hci_w(BCM4325_I2C_FM_FREQ0, freq & 0xFF);

    /* Write the FREQ1 register */
    hci_w(BCM4325_I2C_FM_FREQ1, freq >> 8);

    /* Write the TUNER_MODE register to PRESET to actually start tuning */
    if ( hci_w(BCM4325_I2C_FM_SEARCH_TUNE_MODE, BCM4325_FM_PRE_SET_MODE) < 0){
        LOGE("fail \n");
    }

    return 0;
 }

ラジオについて

常識かもしれないが、日本のラジオは欧米とは違う周波数を使っているらしい
以下Wikipediaのリンク

  1. 日本のラジオ事情
  2. FM_broadcasting
  3. RDS

モジュールの仕様では、76-108Mhzとあるが、
シームレスに周波数を変えられるわけではなく、欧米対応・日本対応で周波数のレンジをわけているようで、
その設定がアプリで対応していなかったのが、動かなかった原因のようだ

NexusOneのラジオモジュールについて

  1. NexusOneのハード一覧?はここを参照した
  2. Bluetoothについてはここを参考にさせていただいた.

NexusOneは、BCM4329というチップセットで、
WiFi・Bluethooth・FMRadioがまとめてまかなわれているらしい
調べた限り、BCM4329のドキュメントはPRODUCT BriefというPDFしか無かった
FMモジュールのインターフェースは、UART,I2Cらしい。

HCIをインターフェースにしているため、
おそらくfroyoでも、オーディオの設定さえうまくいけば、
インテントと hcitool だけで、ラジオを聞けるのではないだろうか。

コードを漁っても、Bluetoothを介しているためHCIにうもれており、
ドライバの詳細はわからなかった。
わかる人はわかるのか?

CyanogenMod6.1でFMラジオ

6.1がリリースされて一月経ったが、
誰も興味ないのか、FMラジオが聞けないままだ。
コードを見たらアプリ自体作りかけという感じだったが、
いじったら、とりあえず聞けるようになった。
暫定対応的なパッチを添付したかったが、できなかったので diff を1番下に載せた。

ただ、ラジオを聞けるようにするだけならアプリの再インストールの必要はなく、
adb shell で下記のコマンドを打つだけでいいはず。

 hcitool cmd 0x3f 0x15 0x00 0x00 0x01
 hcitool cmd 0x3f 0x15 0x01 0x00 0x01

HttpEntity中のInputStreamについて

DefaultHttpClient を使ってpostをするとき、
HttpPostでリクエストを作り,HttpPost.setEntityでリクエストパラメータを仕込む
大雑把には下記のようになる

 File file = new File(path);
 HttpPost request = new HttpPost();
 request.setEntity(new InputStreamEntity(new FileInputStream(file)));
 DefaultHttpClient client = new DefaultHttpClient();
 HttpResponse response = client.execute(request);

普通に考えると、
この時仕込んだInputStreamを使用後にcloseしなくてはいけないが、
DefaultHttpClientの使用例を調べた際、closeしている記述が見当たらなかった。
ひょっとしてexecute内でcloseしてくれているのかと思ったが,そのような説明もなかったので、
本当に必要か、コードを追って見てみた。

今振り返ると、実際にcloseせずに大量にFileInputStreamを作ってexecuteしてみるのが
手っ取り早いし確実な確認方法だったと思う。
結論を言えばやはりcloseは必要なようだ。

DefaultHttpClient.executeをたどると、

DefaultHttpClient.execute → AbstractHttpClient.execute →
DefaultRequestDirector.execute →
DefaultRequestDirector.tryExecute
HttpRequestExecutor.execute→
HttpRequestExecutor.doSendRequest→
HttpClientConnection.sendRequestEntity;

と、sendRequestEntityに行き着く。DefaultRequestDirector.executeが、
コネクションを開いて,sendし、receiveし、redirect等の処理をしている実態のようだった。
このDefaultRequestDirector.executeや他のメソッドを眺めてみたが、
リクエストのentityについてconsumeしている処理は見当たらなかった。

HttpClientConnectionは、

DefaultHttpClient→
SingleClientConnManager(いくつかあるようだが今回はこれを辿った)→
DefaultClientConnectionOperator→
DefaultClientConnection→
AbstractHttpClientConnection

とたどっていくと、AbstractHttpClientConnectionがその実態であることが分かる
AbstractHttpClientConnection.sendRequestEntityをさらに辿ると、

AbstractHttpClientConnection.sendRequestEntity→
EntitySerializer.serialie→
HttpEntity.writeTo

となって、ようやくInputStream.readをよんでいるメソッドに行き着いた

ここまでで InputStream の close をしているような箇所は出会わず、
HttpEntityの実態であるInputStreamEntityのwriteToではJavaDocにあるようにcloseはしていない。

というわけで、やはりInputStreamのcloseは必要で,上記例では、下記のようなものを書く必要がある

 File file = new File(path);
 HttpPost request = new HttpPost();
 request.setEntity(new InputStreamEntity(new FileInputStream(file)));
 DefaultHttpClient client = new DefaultHttpClient();
 HttpResponse response = client.execute(request);
 request.getEntity().getContent().close();

ちなみに、HttpEntity.consumeContent は depriciated であり,
InputStreamをとりだして、直接 close することが推奨されている
直接が推奨されているが、DefaultRequestDirector.execute をみると、
再利用するresponseをクリアする際には,EntityUtils.consumeを使っていることが分かる。
EntityUtilsには、Entity操作でよく利用する処理が入っているようで、
おそらくこのユーティリティの利用が望ましいのだろう。

と思ったら,HttpCore 4.1 APIJavaDoc には、EntitiyUtils.consume が載っているのだが,
Android Developers の JavaDoc には EntitiyUtils.consume が載っておらず、
Eclipseで確認した限りでは実装もされていないようだった。
結局Androidでは自前で書くことになりそうだ。

これだけではただの覚書になってしまうが、この件についてはここまでにする。