2011年8月12日金曜日

Xperia X10(SO-01B)とAndroid 2.3

昨年の夏、使用していたP701iDの液晶がついに壊れ、それならばと思い切って乗り換えたXperia X10(SO-01B)。 使い始めて1年経ったこともあり、少し纏めてみようと思う。

Androidとは

Xperia X10のOS、Androidは、Googleが開発・公開しているOSである。 Androidでは、各メーカが独自にカスタマイズしたものを、 キャリア向けにカスタマイズし販売、ユーザが購入する、というのが主流である。 Windowsならば、Microsoftがリリースし、各メーカがカスタマイズしたものを「メーカ製PC」 として販売するのが主流。

WindowsとAndroidの大きく異なる点は、間に介入する存在が多い点と、更新の方法が異なる点。 このため、Android開発元のGoogleがOSを更新してもすぐには反映されないどころか、 まったく反映されないことも多々ある。

Androidの更新を保証

そこで、18ヶ月間のupdateを保証するプログラムというものが発表されたことがあった。

GoogleとOEM各社、Android端末のアップデート保証プログラムを発表
http://japanese.engadget.com/2011/05/10/google-oem-android/

その結果か、Xperia X10のグローバルモデルにAndroid 2.3の更新が登場。 散々無いと言われ続けていたが、Sony Ericssonは出来る子だったようだ。

Android 2.3 for Xperia™ X10 is here
http://www.sonyericsson.com/update/?lc=en&cc=gb&pid=xperiax10

まさかの事態

しかし、これに水を差すとんでもない奴が居た。キャリアである。

Xperia(SO-01B)のAndroid 2.3アップデート見送りの理由が明らかに
http://gigazine.net/news/20110622_xperia_so_01b_not_gingerbread_reason/

これによると、更新を見送る理由は、

  • Android 2.3モデルとして今年発表した「Xperia arc(SO-01C)」や「Xperia acro(SO-02C)」のようなパフォーマンスを発揮できない
  • バージョンアップの際にユーザーデータを保持できず、必ず本体が初期化されてしまう
  • データのバックアップアプリが無くなる
  • カメラ撮影時の笑顔認識機能「スマイルシャッター」が無くなる
  • 現行のAndroid 2.1版で提供している日本語フォント「モトヤフォント」が適用できなくなる
とのこと。

パフォーマンスを発揮できない? そりゃハードの性能が違うんだから当たり前だろう。 本体内蔵データが消える? Android Marketのアプリは仕方ないとしても、ユーザデータを本体に保存しているとか、 何のためにGoogleのOSを使っているのか。 そして最大の「わけがわからないよ」はフォント。 フォントを理由としてサポートする側がupdate見送る携帯端末用OSというのは聞いたことがない。

こうして更新が見送られるほんの少し前、キャリアであるドコモ側はこんな事を言っていたらしい。

「我々の使命は最新OSをお届けすることに加え、日本独自の機能を入れていくこと」、ドコモの山田社長がAndroidのバージョンアップについて言及 http://gigazine.net/news/20110516_docomo_android_version_up/

まったく、わけがわからないよ。

アップデート見送りの問題点

当然ながら、(一部の)SO-01Bユーザからは失望の声が上がり、グローバルモデルのファームウェアと OSをインストールする方も出てきている。だが、それはあくまで非公認のやり方であり、リスクは端末更新で データ消失するより遥かに大きい。 また、「2.3待ってんのなんてオタだけだよw そんなんめんどいし儲からないからキャリアがやらなくて当然じゃんw」 という意見も見受けられる。

だが、アップデートされるのは何も見た目の機能だけではない。セキュリティホールに対する更新も含めての更新なのである。つい最近も、Android 2.2以前にSSLにおける重大な脆弱性が判明したばかりだ。

JVN#43105011 Android における SSL 証明書の表示に関する脆弱性
http://jvn.jp/jp/JVN43105011/

PC向けのWindows等と違い、Androidはユーザ側がパッチを適用することはできない。root権限すら持てない。 既存の携帯電話、いわゆる「ガラケー」と違い、Androidはオープンでグローバルなものであり、 脆弱性もオープンかつグローバル。これまでの常識は通用しない。 Androidの更新を提供しないメーカ・キャリアは、OS更新を提供しないことに対するリスクを、 全てユーザ側に押し付けているということになる。

それでは不味いと一応思ったらしく、「ドコモ あんしんスキャン」なるウィルス対策ソフトウェアの無償提供を開始した。

スマートフォン向けウイルス対策サービス「ドコモ あんしんスキャン」を無料で提供開始
http://www.nttdocomo.co.jp/info/news_release/2011/06/22_01.html

しかし、こんなモノは気休めでしか無い。根本的に解決しなければならないものを、 小手先の工作で誤魔化しているようにしか見えない。OSの更新によるセキュリティホールへのパッチと ウィルス対策は両方必須、Windowsではよく言われていることである。古いAndroidを使い続けるのは、 ウィルス対策ソフトがあるからとMicrosoftによる更新サポートが切れたWindows 9xや2000を使い続けるようなものだ。

それに、"Android 2.3モデルとして今年発表した「Xperia arc(SO-01C)」や「Xperia acro(SO-02C)」 のようなパフォーマンスを発揮できない"などと言ったのだから、ウィルス対策ソフトを導入することによる 負荷増・パフォーマンスの低下も考慮していただきたい。

また、脆弱性情報に対して、Android 2.2以前を用いて製品を販売している国内企業からの反応が ほとんど無いというのも問題がある。("JVN#43105011"でググって出てくるのは1300件。そのうち殆どがtwitterやblog) 例えそのセキュリティホールが販売している端末に対して問題なかったとしても、「問題なかった」 という情報を公開しなければ、不安が募るだけだ。

何を言いたかったか

簡単に言うと、言い訳言ってないでさっさと公式で更新提供するか、 日本独自モデルなんて止めるか、更新打ち切った端末で2年縛りは止めるかしてね!ということだと思う。

いろいろ文句や愚痴を書いたが、AndroidやXperiaが嫌いな訳ではない。 サポートする筈のキャリア(ドコモ)が足を引っ張っている現状に怒っているのである。 update以外にも、Windowsのメーカ製PCを思い起こされる量、かつアンインストール不可なアプリケーションを 大量にプレインストールといった、Androidのメリットである自由さを損ねるようなやり方など、 キャリアのやり方に対する不満点は多く存在する。

ドコモスマートフォン for Businessには、「セキュリティも安心」と書いてある。 しかし、現状のような対応では、私は安心してドコモやその他国内キャリアのスマートフォンを利用することはできない。 情報が流出したり、壊されてからサポートされるのでは遅すぎるのである。

2011年8月9日火曜日

FreeBSD 9.0-BETA1が登場したらしい

我が家のサーバとして活躍中のFreeBSD。
その最新バージョン9のBETA1版が登場したらしいので、早速ダウンロードしてインストールしてみた。

CDから起動

CDを入れて起動。なにやらBoot Loaderの雰囲気も8.0から変わっている。

インストールするか、Shellに落ちるか、LiveCDとして起動するかを選択できるらしい。 今回はinstallを選択する。

パーティショニング

パーティショニングには、Guided、Manual、Shellの3つのメニューがある。 Guidedはディスク全体または選択したパーティションを自動で割り当てる。Manualは自分でパーティションサイズやファイルシステムを選び割り当てる。

試しにfreebsd-zfsにしてみたが…

やはりインストーラを用いたManual設定ではZFSはうまくいかないようである。 Shellから直接叩くしか無いのだろうか…?

仕方がないので/をUFSとしファイルを展開する。

設定

展開終了後、引き続きシステムの設定を行う。設定項目は以下の通り。 基本的にFreeBSD 8と同じように見えるが、細かい変更がある部分もある。 ユーザアカウント設定は、useraddコマンドを実行しただけのようだったり、 それならOpenBSDっぽく全部CUIでいいじゃないかと言いたくなるところもあるが、 おそらくそのうち改善されるのであろう。

  • rootパスワード
  • NIC選択
  • IPv4/DHCP設定
  • IPv6/アドレス割り当て設定
  • DNS設定
  • タイムゾーン設定
  • サービス設定(sshd、moused、ntpd、powerd)
  • ユーザアカウント設定
設定が終了したらexitしてrebootする。

HDDから起動

CDをejectしてインストールしたHDDから起動。 ZFS filesystem versionは5、ZFS storage pool versionは28となっている。

BETA1ということもあり、kernel panicしたりstack trace吐いたりとまだいろいろと不具合は残っているようだが、 RELEASEに期待したい。

www.FreeBSD.org
http://www.freebsd.org/

2011年7月31日日曜日

Rubyのio/consoleを試す

Rubyでコンソールアプリケーションを作成する際に、 標準のIOではうまくいかない問題がある。 例えば...

  • リアルタイム入力
  • パスワード入力時等に文字のエコーを停止
  • 端末のサイズを取得
等がある。これはcursesなどを用いれば解決できるが、パスワード入力のみで必要な場合等 cursesでは大げさすぎると感じる場合もある。

そこで、次のruby(1.9.3?)あたりに標準で付属されるらしい、 io/consoleというライブラリを使ってみる。

導入

今回は、rubyのリポジトリから最新開発版を入手し、ビルドする。RVMを用いてもよい。

miku@hatsune% svn co http://svn.ruby-lang.org/repos/ruby/trunk ruby19-svn
miku@hatsune% cd ruby19-svn
miku@hatsune% autoconf
miku@hatsune% ./configure --prefix=/usr/ruby19-svn --program-suffix=19-svn
miku@hatsune% make
miku@hatsune% sudo make install

lib/ruby/1.9.1/x86_64-linux/io/console.so がio/console用のライブラリらしい。

使ってみる

リファレンスなんて無いので、ソース(ext/io/console/console.c) を読みながらとりあえず叩いてみる。 IOクラスに追加されるメソッドは、以下のとおり...らしい。

  • IO.console #=> IO for console device
  • IO#raw
  • IO#raw!
  • IO#getch
  • IO#echo=
  • IO#echo?
  • IO#noecho
  • IO#winsize
  • IO#winsize=
  • IO#iflush
  • IO#oflush
  • IO#ioflush

実際にirbで叩いてみると分かるが、getchでは入力した瞬間にその入力した文字が返される。 また、winsizeではその瞬間のコンソールサイズが配列として返される。 その他のメソッドも、だいたい名前のままな動作をする模様。

irb(main):012:0> IO.console
=> #

irb(main):011:0> IO.console.getch
=> "m"

irb(main):013:0> IO.console.noecho{|io| p io.getch}
"a"
=> "a"

irb(main):014:0> IO.console.winsize
=> [55, 139]

改行の問題

io/consoleで入力を受け付けながら出力すると、 "\n"で正しく改行ができなくなることがある模様。 例えば以下のように書いた場合、LFのみが行われCRが行われず、 行は変わるものの開始位置がおかしくなる。

Thread.start(){
  loop {IO.console.getch}
}

IO.console.noecho{|io|
  loop{
   io.printf("%d\n", rand(1024))
  sleep 0.1
  }
}

これは、"\n"を"\r\n"に変えてやれば解決する。 今後修正されるかもしれないが、一応。

2011年6月30日木曜日

ruby/dlでlibaoを使う

Cなんて一行も書きたくないけれど、Cのダイナミックリンクライブラリをrubyから使いたい。 そんな人のためのライブラリ(?)がruby/dlである。 (Cは一行も書かなくてもよいものの、流石にCの知識がある程度無いと使えないが…)

利点と欠点は以下のとおり。

・利点

  • Cのダイナミックリンクライブラリをコンパイル作業無しで利用できる
  • すべてrubyで書ける
・欠点
  • 環境依存部分の吸収を自前でやらなければならない場合がある
  • 一部無理矢理な定義をしなければならない部分がある(入れ子の構造体、enum型など)

今回は、ruby1.9のdlライブラリを用いて、 マルチプラットフォームなサウンド出力ライブラリであるlibaoを叩き、 オーディオファイルを再生できるようにしてみる。

ヘッダファイルを読む

ruby/dlでlibaoを叩くには、まずlibaoにどのような関数や構造体などがあるのか調べる必要がある。 libaoでは、ほとんどが$PREFIX/include/ao/ao.hで宣言されているので、それを読む。 ao/ao.hで定義されていてruby/dlから叩くのに必要な部分は、以下のとおりである。

/* --- Constants ---*/
#define AO_TYPE_LIVE 1
#define AO_TYPE_FILE 2

#define AO_ENODRIVER   1
#define AO_ENOTFILE    2
#define AO_ENOTLIVE    3
#define AO_EBADOPTION  4
#define AO_EOPENDEVICE 5
#define AO_EOPENFILE   6
#define AO_EFILEEXISTS 7
#define AO_EBADFORMAT  8

#define AO_EFAIL       100

#define AO_FMT_LITTLE 1
#define AO_FMT_BIG    2
#define AO_FMT_NATIVE 4


/* --- Structures --- */
typedef struct ao_info {
int  type; /* live output or file output? */
char *name; /* full name of driver */
char *short_name; /* short name of driver */
        char *author; /* driver author */
char *comment; /* driver comment */
int  preferred_byte_format;
int  priority;
char **options;
int  option_count;
} ao_info;

typedef struct ao_functions ao_functions;
typedef struct ao_device ao_device;

typedef struct ao_sample_format {
int  bits; /* bits per sample */
int  rate; /* samples per second (in a single channel) */
int  channels; /* number of audio channels */
int  byte_format; /* Byte ordering in sample, see constants below */
        char *matrix; /* input channel location/ordering */
} ao_sample_format;

typedef struct ao_option {
char *key;
char *value;
struct ao_option *next;
} ao_option;


/* --- Functions --- */
/* library setup/teardown */
void ao_initialize(void);
void ao_shutdown(void);

/* device setup/playback/teardown */
int   ao_append_global_option(const char *key,
                              const char *value);
int          ao_append_option(ao_option **options,
                              const char *key,
                              const char *value);
void          ao_free_options(ao_option *options);
ao_device*       ao_open_live(int driver_id,
                              ao_sample_format *format,
                              ao_option *option);
ao_device*       ao_open_file(int driver_id,
                              const char *filename,
                              int overwrite,
                              ao_sample_format *format,
                              ao_option *option);

int                   ao_play(ao_device *device,
                              char *output_samples,
                              uint_32 num_bytes);
int                  ao_close(ao_device *device);

/* driver information */
int              ao_driver_id(const char *short_name);
int      ao_default_driver_id(void);
ao_info       *ao_driver_info(int driver_id);
ao_info **ao_driver_info_list(int *driver_count);
char       *ao_file_extension(int driver_id);

/* miscellaneous */
int          ao_is_big_endian(void);

さっと眺めると、定数・構造体を定義した後、 関数を定義してあるシンプルな物であることが分かる。

rubyのAOモジュールを作成する

ao/ao.hの情報から、ruby/dlを用いてrubyからlibaoを叩くためのモジュール、 AOを作成する。 基本的な使用例は リファレンスにサッと書いてあるので、それを参考にする。

module AO
  # DL::Importerのインスタンスメソッドを
  # AOモジュールの特異メソッドとして追加する
  extend(DL::Importer)

  # libao.so(Windowsならlibao.dll)を
  # ライブラリパスから検索しロードする
  dlload('libao.so')


  # ao/ao.hで定義されていた定数を定義する
  # ao/ao.h
  AO_TYPE_LIVE   = 1
  AO_TYPE_FILE   = 2

  AO_ENODRIVER   = 1
  AO_ENOTFILE    = 2
  AO_ENOTLIVE    = 3
  AO_EBADOPTION  = 4
  AO_EOPENDEVICE = 5
  AO_EOPENFILE   = 6
  AO_EFILEEXISTS = 7
  AO_EBADFORMAT  = 8
  
  AO_EFAIL       = 100

  AO_FMT_LITTLE  = 1
  AO_FMT_BIG     = 2
  AO_FMT_NATIVE  = 4


  # ao/ao.hで定義されていた構造体を定義する
  AO_Info =
    struct(['int  type',
            'char *name',
            'char *short_name',
            'char *author',
            'char *comment',
            'int  preferrd_byte_format',
            'int  priority',
            'char **options',
            'int  option_count'])
  AO_Sample_Format =
    struct(['int  bits',
            'int  rate',
            'int  channels',
            'int  byte_format',
            'char *matrix'])
  AO_Option =
    struct(['char   *key',
            'char   *value',
            'struct ao_option *next'])


  # 空の構造体は、voidのaliasとして定義する
  typealias('ao_functions', 'void')
  typealias('ao_device', 'void')


  # 未定義の型uint_32を、
  # unsignd intのaliasとして定義する
  typealias('uint_32', 'unsigned int')


  # 関数を定義する
  # (仮引数は型のみ記入する)

  # /* --- Functions --- */
  # /* library setup/teardown */
  # void ao_initialize(void);
  extern('void ao_initialize(void)')

  # void ao_shutdown(void);
  extern('void ao_shutdown(void)')

  # /* device setup/playback/teardown */
  # int   ao_append_global_option(const char *key,
  #                               const char *value);
  extern('int ao_append_global_option(const char *, const char *)')

  # int          ao_append_option(ao_option **options,
  #                               const char *key,
  #                               const char *value);
  extern('int ao_append_option(ao_option **, const char *, const char *)')

  # void          ao_free_options(ao_option *options);
  extern('void ao_free_options(ao_option *)')

  # ao_device*       ao_open_live(int driver_id,
  #                               ao_sample_format *format,
  #                               ao_option *option);
  extern('ao_device * ao_open_live(int, ao_sample_format *, ao_option *)')

  # ao_device*       ao_open_file(int driver_id,
  #                               const char *filename,
  #                               int overwrite,
  #                               ao_sample_format *format,
  #                               ao_option *option);
  extern(' ao_device* ao_open_file(int, const char *, int, ao_sample_format *, ao_option *)')

  # int                   ao_play(ao_device *device,
  #                               char *output_samples,
  #                               uint_32 num_bytes);
  extern('int ao_play(ao_device *, char *, uint_32)')

  # int                  ao_close(ao_device *device);
  extern('int ao_close(ao_device *)')

  # /* driver information */
  # int              ao_driver_id(const char *short_name);
  extern('int ao_driver_id(const char *)')

  # int      ao_default_driver_id(void);
  extern('int ao_default_driver_id(void)')

  # ao_info       *ao_driver_info(int driver_id);
  extern('ao_info *ao_driver_info(int)')

  # ao_info **ao_driver_info_list(int *driver_count);
  extern('ao_info **ao_driver_info_list(int *)')

  # char       *ao_file_extension(int driver_id);
  # extern('char *ao_file_extension(int)')

  # /* miscellaneous */
  # int          ao_is_big_endian(void);
  extern('int ao_is_big_endian(void)')
end

比較してみると分かるが、ao/ao.hで定義されているものをruby/dlに合わせ 機械的に定義し直しているだけである。

実際に使用する

作成したAOモジュールを実際に使用してみる。この時、関数の戻り値等はC言語で使用する際と同様に 適切に処理しなければならない。またerrnoの値を取得するには、 DL::CFunc.last_errorを用いる。

puts 'initialize'
AO.ao_initialize

puts 'setup default driver id'
drv = AO.ao_default_driver_id
if drv < 0
  puts 'usable audio output device is not found.'
  exit 1
end

puts 'setup sample format structure'
format = AO::AO_Sample_Format.malloc
format.bits     = 16
format.rate     = 44100
format.channels = 2
format.byte_format = AO::AO_FMT_LITTLE
format.matrix   = nil

puts 'open live device'
dev = AO.ao_open_live(drv, format, nil)
unless dev
  printf("errno: %d\n", DL::CFunc.last_error)
  exit DL::CFunc.last_error
end

puts 'play'
ARGV.each{|argv|
  if File.file?(argv)
    File.open(argv){|f|
      while buf = f.read(4096)
        if AO.ao_play(dev, buf, buf.size) == 0
          puts 'ao_play() failure.'
          exit 1
        end
      end
    }
  end
}

puts 'close'
if AO.ao_close(dev) == 0
  puts 'ao_close() failure.'
end

puts 'shutdown'
AO.ao_shutdown

あとは、作成したスクリプトを、引数に16bit・44.1KHz・2ch・Little Endianな音声の RAWファイルへのパスを付けて実行すれば、それを再生してくれる筈である。 WAVファイルはヘッダがあるため先頭に少々ノイズが入るが、再生することはできる。

参考文献

・xiph.org - libao
http://www.xiph.org/ao/

・Ruby 1.9.2 Reference Manual - dl
http://rurema.clear-code.com/1.9.2/library/dl.html