2013年1月20日日曜日

Android 端末では Bluetooth HFP のメモリダイヤル機能が動作しない

Bluetooth HFP (Hands-free Profile) には、仕様上「メモリダイヤル」と呼ばれる機能が用意されています。これは "1 番目" にダイヤルしようとした場合、 1 番目に登録されている電話番号が自動的に呼び出される、いわゆる「短縮ダイヤル」とか「スピードダイヤル」とかいう機能なのですが、これは現状 Android 端末ではサポートされていないようです。

その辺の情報があまりにも少なかったので、ちょこっとメモしておきます。
# まあ、この機能を使う人も少ない、ってことなのかもしれませんが…

Bluetooth HFP はヘッドセットから電話を受け付けたり、あるいはかけたりするためのプロファイルで、例えば端末をポケットに入れたまま、ヘッドセットのボタンを押すなりして電話を受け付けたりすることができます。メモリダイヤルは、具体的にはボイスダイヤル機能で "1 番目にダイヤルしろ" といった音声を認識するような操作で発動し、登録済みの番号へと自動でかけてくれる便利な奴なのです。

この機能は一般のガラケーではほぼサポートされていて、スマートフォンでも iPhone であれば、明示的に「メモリダイヤル番号の登録」といった形ではサポートされていないものの、「連絡先」の「よく使う項目」に登録しておけば、実は一番上が "1 番目"、次の項目が "2 番目"... という形で解釈されて、ちゃんと Bluetooth HFP のメモリダイヤル機能を使うことが出来るように実装されています。

ところが、 Android では何番目を指定しようとも、必ず最後にかけた番号にリダイヤルしてしまうのです。

何でリダイヤルするんだろうなぁ、と不思議に思っていましたが、 Android 本体のソースコードを見れば何のことはない、確かにリダイヤルする仕様になっていました。

Bluetooth HFP ではヘッドセットからのコマンドは全て AT コマンドで送られるようになっており、これはメモリダイヤルであれば "ATD>1" といったコマンドが送られてきたとき、 1 番目の登録先に電話するような簡単な仕様です。その部分のパーサが Phone.app の com.android.phone.BluetoothHandsfree というクラスに実装されていて、 問題の箇所 を見てみると…

         parser.register('D', new AtCommandHandler() {
             @Override
             public AtCommandResult handleBasicCommand(String args) {
                 if (args.length() > 0) {
                     if (args.charAt(0) == '>') {
                         // Yuck - memory dialling requested.
                         // Just dial last number for now
                         if (args.startsWith(">9999")) {   // for PTS test
                             return new AtCommandResult(AtCommandResult.ERROR);
                         }
                         return redial();
                     } else {
                         // Send terminateScoUsingVirtualVoiceCall
                         terminateScoUsingVirtualVoiceCall();
                         // Remove trailing ';'
                         if (args.charAt(args.length() - 1) == ';') {
                             args = args.substring(0, args.length() - 1);
                         }
 
                         args = PhoneNumberUtils.convertPreDial(args);
 
                         Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,
                                 Uri.fromParts(Constants.SCHEME_TEL, args, null));
                         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                         mContext.startActivity(intent);
 
                         expectCallStart();
                         return new AtCommandResult(AtCommandResult.UNSOLICITED);  // send nothing
                     }
                 }
                 return new AtCommandResult(AtCommandResult.ERROR);
             }
         });

とまあごらんの有様。 "ATD" のあとに ">" が来た場合、 redial() を呼び出しているのが分かります。 "Yuck - memory dialling requested. Just dial last number for now" (げっ、メモリダイアルが要求されやがった。とりあえず最後の番号にかけよっと…) とまでコメントが付いており、まるで対応するつもりが無いことが分かります。

一応ここで例示したのは Android 4.0.3 (Ice Cream Sandwich) のソースなのですが、このメモリダイヤル部分のコードは Android 1.0 の頃からさっぱり変化無く、現状最新の Android 4.2 Jellybean でも基本的に同じでした。何だか寂しいので自分でコミットしてでも対応させたいんですが、これ単なるバグフィックスじゃなくて、場合によっては ContentProvider を追加したり Broadcast Intent を追加したりって話になるし、新米にはちょっと荷が重そうだよなぁ…。

そういうわけで、 Android 端末ではメモリダイヤル機能が動作しません。ただ例外もありまして、 Samsung の Galaxy Nexus SII とかでは、独自の電話帳アプリに「スピードダイヤル」機能が搭載されており、これに登録するとちゃんと HFP のメモリダイヤルが動作するようなベンダ独自のパッチが適用されていました。変なところで偉い子な端末ですね。

ま、そんなわけで、 Android にメモリダイヤル機能は基本的に搭載されていないから期待するな、というお話でした。

ちなみに Bluetooth HFP 1.5 の仕様書はこちら にありまして、 14 ページ目を
見ると、 "Place a call using memory dialing" (メモリダイヤルを使用した電話の発信) は AG (Audio Gateway, 携帯端末側) での実装が M (Must, 必須) となっているので、ある意味 Android 端末は Bluetooth HFP 非対応、非準拠とも言えるのかなぁと思ったり。
# ATD> コマンドをとりあえず解釈すれば OK なのかもしれませんが

0 件のコメント:

コメントを投稿