fxosコードリーディングミートアップ#16 vibration apiも読んでみた
TRANSCRIPT
自己紹介
• 藪下正美
• 株式会社グローバルサイバーグループでFirefox OS担当やってます
• Firefox OSコミュニティでイベントとかやってます
• Codezineに記事乗りました!
– http://codezine.jp/article/detail/8540
今日のおはなし
• Vibration APIとは
• お決まりのwebidlから
• navigator探索
• GeckoのHALインターフェイス
• GonkのVibrate
• GonkのRunnableオブジェクト
• ハードウェアの操作
• まとめ
Vibration APIとは
• 端末をぶるぶるするAPI
• ぶるぶるする時間 (ミリ秒)を指定するとその時間だけぶるぶるする
• ぶるぶるする時間 (ミリ秒) ぶるぶる止める時間 (ミリ秒) の配列でパターンを表現
お決まりのwebidlから
• vibratorはnavigatorに属するAPIなのでNavgator.webidlを見ます
• 鳴動時間を渡すインターフェイスと時間の配列を渡すとパターンで鳴らしてくれるインターフェイスがあった
• implementsとかの指定もないので見るべきソースはnavigatorのものとわかった
// http://www.w3.org/TR/vibration/#vibration-interfacepartial interface Navigator {
// We don't support sequences in unions yet//boolean vibrate ((unsigned long or sequence<unsigned long>) pattern);boolean vibrate(unsigned long duration);boolean vibrate(sequence<unsigned long> pattern);
};
navigator探索(1)
• navigatorの実装を見てみましょう
boolNavigator::Vibrate(uint32_t aDuration){
nsAutoTArray<uint32_t, 1> pattern;pattern.AppendElement(aDuration);return Vibrate(pattern);
}
navigator探索(2)
boolNavigator::Vibrate(const nsTArray<uint32_t>& aPattern){
if (!mWindow) {return false;
}
nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();if (!doc) {
return false;}
if (doc->Hidden()) {// Hidden documents cannot start or stop a vibration.return false;
}
navigator探索(3)
nsTArray<uint32_t> pattern(aPattern);
if (pattern.Length() > sMaxVibrateListLen) {pattern.SetLength(sMaxVibrateListLen);
}
for (size_t i = 0; i < pattern.Length(); ++i) {if (pattern[i] > sMaxVibrateMS) {
pattern[i] = sMaxVibrateMS;}
}
// The spec says we check sVibratorEnabled after we've done the sanity// checking on the pattern.if (!sVibratorEnabled) {
return true;}
navigator探索(4)
// Add a listener to cancel the vibration if the document becomes hidden,// and remove the old visibility listener, if there was one.if (!gVibrateWindowListener) {
// If gVibrateWindowListener is null, this is the first time we've vibrated,// and we need to register a listener to clear gVibrateWindowListener on// shutdown.ClearOnShutdown(&gVibrateWindowListener);
}else {
gVibrateWindowListener->RemoveListener();}gVibrateWindowListener = new VibrateWindowListener(mWindow, doc);
hal::Vibrate(pattern, mWindow);return true;
}
GeckoのHALインターフェイス(1)
• hal::Vibrateを見てみます
• これまた長いですが
voidVibrate(const nsTArray<uint32_t>& pattern, nsIDOMWindow* window){
Vibrate(pattern, WindowIdentifier(window));}
voidVibrate(const nsTArray<uint32_t>& pattern, const WindowIdentifier &id){
AssertMainThread();
GeckoのHALインターフェイス(2)
// Only active windows may start vibrations. If |id| hasn't gone// through the IPC layer -- that is, if our caller is the outside// world, not hal_proxy -- check whether the window is active. If// |id| has gone through IPC, don't check the window's visibility;// only the window corresponding to the bottommost process has its// visibility state set correctly.if (!id.HasTraveledThroughIPC() && !WindowIsActive(id.GetWindow())) {
HAL_LOG("Vibrate: Window is inactive, dropping vibrate.");return;
}
if (!InSandbox()) {if (!gLastIDToVibrate) {
InitLastIDToVibrate();}*gLastIDToVibrate = id.AsArray();
}
GeckoのHALインターフェイス(3)
// Don't forward our ID if we are not in the sandbox, because hal_impl// doesn't need it, and we don't want it to be tempted to read it. The// empty identifier will assert if it's used.PROXY_IF_SANDBOXED(Vibrate(pattern, InSandbox() ? id : WindowIdentifier()));
}
GeckoのHALインターフェイス(4)
• ここもやってることを要約するとwindowがアクティブかどうかを判定して震わせるだけ
• ここでも最後のこれがキモで結局呼ばれるのはGonkのVibrate
• 次からGonkの世界に入っていきます
PROXY_IF_SANDBOXED(Vibrate(pattern, InSandbox() ? id : WindowIdentifier()));
GonkのVibrate(1)
• さっそくVibrateを見てみると
• おや短い
voidVibrate(const nsTArray<uint32_t> &pattern, const hal::WindowIdentifier &){
MOZ_ASSERT(NS_IsMainThread());if (VibratorRunnable::ShuttingDown()) {
return;}EnsureVibratorThreadInitialized();sVibratorRunnable->Vibrate(pattern);
}
GonkのVibrate(2)
• ざっくり見ていくと
–すでにぶるぶるしていたら止める
–バイブレータ用のスレッドを初期化
–バイブレータスレッドで動くVibrateメソッドの呼び出し
• 震わせてそうなのは sVibratorRunnable->Vibrate(pattern); なので追いかけると
GonkのRunnableオブジェクト(1)
NS_IMETHODIMPVibratorRunnable::Run(){MonitorAutoLock lock(mMonitor);(コメント省略)while (!sShuttingDown) {
if (mIndex < mPattern.Length()) {uint32_t duration = mPattern[mIndex];if (mIndex % 2 == 0) {
vibrator_on(duration);}mIndex++;mMonitor.Wait(PR_MillisecondsToInterval(duration));
}else {mMonitor.Wait();
}}sVibratorRunnable = nullptr;return NS_OK;
}
GonkのRunnableオブジェクト(2)
• こんな感じで鳴動パターンの制御をしている
• その中でも震わせていそうなのが
– vibrator_on(duration);
• このあたり
• ラストスパートで追いかけて
ハードウェアの操作(1)
• vibrator_onを見てみると
int vibrator_on(int timeout_ms){
/* constant on, up to maximum allowed time */return sendit(timeout_ms);
}
ハードウェアの操作(2)
• sendifも見てみると
static int sendit(int timeout_ms){
int nwr, ret, fd;char value[20];(エミュレータ用コード省略)fd = open(THE_DEVICE, O_RDWR);if(fd < 0)
return errno;
nwr = sprintf(value, "%d\n", timeout_ms);ret = write(fd, value, nwr);
close(fd);
return (ret == nwr) ? 0 : -1;}
ハードウェアの操作(3)
• エミュレータ向けのコードを無視するとやっているのはファイルのオープン、書き込み、クローズ
• ここを読むのはLinuxの知識が必要で、openしているのはキャラクタデバイスと呼ばれるファイルとデバイスがマップされたもの
• キャラクタデバイスへの文字列の書き込みがドライバへの入力となる
まとめ
• 上からざっと
–アプリから呼ばれるとnavigatorでGeckoのHALインターフェイスがたたかれる
– GeckoのHALインターフェイスはGonkのVibrateを叩く
– GonkのVibrateはスレッドの準備をしてVibratorRunnableを鳴動スレッドに渡す
– VibratorRunnableは鳴動パターンを制御しながらvibrator_onを叩く
– vibrator_onはキャラクタデバイスを叩いて鳴動を制御
• vibrateが一番素直な形でハードウェア制御が書かれている
• 他のハードウェア制御APIを追いかけるときにも参考になるはずなので一読してみるといいよ!