eVY1 shieldでシリアル通信で歌詞指定&MML再生(その2)

 Android, PIC/arduino  eVY1 shieldでシリアル通信で歌詞指定&MML再生(その2) はコメントを受け付けていません。
11月 192013
 

MML再生をパワーアップさせてみました。

androidの会横浜支部で発表・実演してきました。

■発表資料です

■使用の前に
・arduinoとeVY1shieldソフトウェアシリアルを使用します。
eYV1shieldのJP2を2-3に変更してください。
・(ハードウェアの)シリアル通信でコマンド入力します。本サンプルでは9600bpsで改行コードは[LFのみ]です
arduinoコンソールから演奏できます。

・MMLの書き方、歌詞の書き方、サンプルデータは資料をご覧ください。

・さらにBluetoothモジュール(手持ちのRBT-001)をつけて、Android(BluetoothchatをSPP化したもの)から演奏できることは確認済みです。(iOSは開発できないので未確認)

 

■スケッチ(コード汚くてすみません・・・)

#include <avr/pgmspace.h>
#include <SoftwareSerial.h>

#define CHANNEL 0 //(0:eVocaloid 1-15:MIDI(9:drum) )
#define BUF_SIZE 512

typedef struct {
     //midi event
  int type;
  int channel;
  int note;
  int velocity;
  int length;
  int param1;
  int time;
} midi_message_t;

 //func
void send_midi_message(midi_message_t midi_message);

/*
 * 注意:
 * SoftwareSerial使用の為eYV1shieldのJP2を2-3に変更する事
 */

int led = 13;
//SW-Serial: eVY1 shield
SoftwareSerial swSerial(2, 3);

void setup() {
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);
  //  Set MIDI baud rate:
  swSerial.begin(31250);
  // HW-Serial: console
  Serial.begin(9600);
  delay(1000);

}

char recv_buf[BUF_SIZE]; // 受信用バッファ
char *p_buf=recv_buf;//受信バッファポインタ(初期値recv_buf先頭アドレス)

void loop(){
  //シリアル受信処理
  while(Serial.available()>0){
    *p_buf = Serial.read();//1byte読み込みバッファに書き込み

    if(*p_buf=='\n' /*|| *p_buf=='\''*/){
      *p_buf='\0';// \0:null(文字列終端)で改行を上書き

      receive_data(recv_buf);//受信データ処理

      p_buf=recv_buf;//受信バッファポインタ初期値化
    }else{
      p_buf++;
    }
  }
}

//serialコマンドデータ受信
void receive_data(char *recvdata) {
  if(strlen(recvdata)!=0){
     if(*recvdata=='#'){
       //歌詞設定
       lylic_send(recvdata);
       Serial.println("OK");
     }else{
       //mml再生
       int ret=play_mml2(recvdata);
       if(ret==0){
         Serial.println("OK");
       }else{
         Serial.print("ERR:");
         Serial.println(ret);
       }
     }
  }
}

//lylic send
void lylic_send(char *str){

  //Phonetic symbols hedaer f04379090050は固定
  swSerial.write((byte)0xF0);
  swSerial.write((byte)0x43);
  swSerial.write((byte)0x79);
  swSerial.write((byte)0x09);
  swSerial.write((byte)0x00);
  swSerial.write((byte)0x50);
  swSerial.write((byte)0x10);//10:replace 11:append

   Serial.print("lylic:[");
  for(int i=1;i<strlen(str)+1;i++){
     swSerial.write(*(str+i));
     Serial.print(*(str+i));
  }
   Serial.println("]");

  //footer
  swSerial.write((byte)0x00);//end of list
  swSerial.write((byte)0xF7);

}

//----------------------------
#define MAX_CHANNEL  15
#define RESOLUTION 480
#define MML_CHAR  "CDEFGABR#+-1234567890.&O><LV@TI "
#define MML_CMD_SOUND  "CDEFGABR" //音符・休符
#define MML_CMD_ETC  "O><LV@T&I " //その他制御コマンド TODO: PNQ,は非優先で実装
//#define MML_CMD  MML_CMD_SOUND+MML_CMD_ETC; //全コマンド
#define MML_OPT_SOUND  "#+-" //CMDの後ろに付くオプション
#define MML_OPT_LENGTH "." //CMDの後ろに付くオプション TODO:&は別途実装
#define MML_NUM "1234567890" //数字

//音色(MIDIノートナンバー)
#define NOTE_C  0
#define NOTE_D	2
#define NOTE_E	4
#define NOTE_F	5
#define NOTE_G	7
#define NOTE_A	9
#define NOTE_B	11

//イベントタイプ
#define TYPE_ETC 0
#define TYPE_NOTE  1
#define TYPE_REST  2
#define TYPE_INSTRUMENT  3
#define TYPE_TEMPO 4

int play_mml2(char *mmlstr){
  int pos=0;
  int mml_L=4;//Length(1-64?)デフォルト4(4分音符)
  int mml_O=4;//Octave(0-9)デフォルト4
  int mml_T=120;//Tempo(1-255)デフォルト120
  int mml_V=15;//volume(0-15)デフォルト15?
  int mml_AT=0;//音色(GM:0-127)デフォルト0
  char command_buf[16];

  midi_message_t midimessage;

   Serial.println("mml:");
   Serial.print(mmlstr);
   Serial.println("");

while (pos < strlen(mmlstr)) {
  *(mmlstr+pos)=toupper(*(mmlstr+pos));//小文字→大文字
  sprintf(command_buf,"%c",*(mmlstr+pos));

  // ①コマンド文字チェック
  if (strstr((char *)MML_CMD_SOUND,command_buf) == NULL
     && strstr((char *)MML_CMD_ETC,command_buf) == NULL) {
    // コマンド以外文字
    return -1;
  }

  // ②タイプ判別確定
  if(strstr((char *)MML_CMD_SOUND,command_buf) != NULL){ // 音符・休符
    midimessage.type = TYPE_NOTE;
    midimessage.channel=CHANNEL;	//暫定channel=0固定
    midimessage.velocity=(127*mml_V/15);	//ボリューム設定

    Serial.print("*");
    switch (*(mmlstr+pos)) {
      case 'C':
        midimessage.note = NOTE_C + ((mml_O+1) * 12);
        break;
      case 'D':
        midimessage.note = NOTE_D + ((mml_O+1) * 12);
        break;
      case 'E':
        midimessage.note = NOTE_E + ((mml_O+1) * 12);
        break;
      case 'F':
        midimessage.note = NOTE_F + ((mml_O+1) * 12);
        break;
      case 'G':
        midimessage.note = NOTE_G + ((mml_O+1) * 12);
        break;
      case 'A':
        midimessage.note = NOTE_A + ((mml_O+1) * 12);
        break;
      case 'B':
        midimessage.note = NOTE_B + ((mml_O+1) * 12);
        break;
      case 'R':
        midimessage.type = TYPE_REST;
        break;
      default:
        //error
        return -2;
    }

    // 音長仮決定(明示的指定、付点などで後で変更される可能性有り)
    midimessage.length = (RESOLUTION * 4) / mml_L;// 音長=1小節の時間/n分音符
    pos++;
  } else if(strstr((char *)MML_CMD_ETC,command_buf) != NULL){ // その他

    Serial.print("*");
    switch (*(mmlstr+pos)) {
      case 'O': // オクターブ(MIDIobj無し)
      {
        midimessage.type = TYPE_ETC;
        pos++;
	int tmpOct = atoi(mmlstr+pos);
	if (tmpOct < 1 || tmpOct > 9) {
	  // 範囲外
	  return -3;

        }
        mml_O = tmpOct;
        char buf[16];
        pos += strlen(itoa(tmpOct,buf,10));// 桁数分

        Serial.print("*");//一桁固定
      }
      break;
    case '>': // オクターブ(MIDIobj無し)
      midimessage.type = TYPE_ETC;
      if (mml_O < 9)
        mml_O++;
      pos++;
      break;
    case '<': // オクターブ(MIDIobj無し)
      midimessage.type = TYPE_ETC;
      if (mml_O > 0)
        mml_O--;
      pos++;
      break;
    case 'L': // 音長(MIDIobj無し)
    {
      midimessage.type = TYPE_ETC;
      pos++;
      int tmpLen = atoi(mmlstr+pos);

      if (tmpLen < 1 || tmpLen > 128) {
      // 範囲外
        return -4;
      }
      mml_L = tmpLen;
      char buf[16];
      pos += strlen(itoa(tmpLen,buf,10));// 桁数分
      for(int i=0;i<strlen(itoa(tmpLen,buf,10));i++){
          Serial.print("*");
      }
    }
    break;
    case 'V': // 音量(MIDIobj無し)
    {
      midimessage.type = TYPE_ETC;
      pos++;
      int tmpVol = atoi(mmlstr+pos);

      if (tmpVol < 0 || tmpVol > 15) {
        return -5;
      }
      mml_V = tmpVol;

      char buf[16];
      pos += strlen(itoa(tmpVol,buf,10));// 桁数分
      for(int i=0;i<strlen(itoa(tmpVol,buf,10));i++){
          Serial.print("*");
      }
    }
    break;
    case '@': // Instrument(楽器)変更
	midimessage.type = TYPE_INSTRUMENT;
	// 数値は後処理で読み込み
	pos++;
	break;
    case 'T': // TEMPO
      midimessage.type = TYPE_TEMPO;
      // 数値は後処理で読み込み
      pos++;
      break;
    case '&': // タイ・スラー
      //TODO: 実装未定
      midimessage.type = TYPE_ETC;
      pos++;
      break;
    case ' ': // 半角スペース
      //なにもしないで読み飛ばし
      midimessage.type = TYPE_ETC;
      pos++;
      break;
    default:
      return -6;

    }

  }

  // ③タイプごとの後処理
  /*
   * 音符時 オプションで半音、音長指定、付点がある
   *  Cの例:C C+ C+4 C+. C+4.
   *
   *  休符時、音長指定、付点がある
   *  例:R R4 R4.
   */

  if(strlen(mmlstr)>pos){

    *(mmlstr+pos)=toupper(*(mmlstr+pos));//小文字→大文字
    sprintf(command_buf,"%c",*(mmlstr+pos));

    // オプション(#+-)文字チェック(音符の後のみ)
    if (midimessage.type == TYPE_NOTE
        && strstr((char *)MML_OPT_SOUND,command_buf) != NULL) {
        Serial.print("*");
        // コマンドオプション文字
        switch (*(mmlstr+pos)) {
          case '#': // シャープ
          case '+': // シャープ
            if (midimessage.note < 127)
              midimessage.note++;
            break;
          case '-': // フラット
            if (midimessage.note > 0)
              midimessage.note--;
            break;
          default:
            return -7;
        }
        pos++;
      }

      // 音長(数値)処理(音符・休符の後のみ)
      sprintf(command_buf,"%c",*(mmlstr+pos));
      if ((midimessage.type == TYPE_NOTE || midimessage.type == TYPE_REST)
          && strstr((char *)MML_NUM,command_buf) != NULL) {

        int tmpLength = atoi(mmlstr+pos);

        if (tmpLength < 0 || tmpLength > RESOLUTION) {
          // 範囲外
          return -8;
        }
        if(tmpLength!=0){
          midimessage.length = (RESOLUTION * 4) / tmpLength;// 音長=1小節の時間/n分音符
        }
        char buf[16];
        pos += strlen(itoa(tmpLength,buf,10));// 桁数分
        for(int i=0;i<strlen(itoa(tmpLength,buf,10));i++){
            Serial.print("*");
        }
      }

      // 付点(.)処理(音符・休符の後のみ)
      sprintf(command_buf,"%c",*(mmlstr+pos));
      if ((midimessage.type == TYPE_NOTE || midimessage.type == TYPE_REST)
          && strstr((char *)MML_OPT_LENGTH,command_buf) != NULL) {
        midimessage.length *= 1.5;
        pos++;
        Serial.print("*");//一桁固定
      }

      // テンポ指定
      if(midimessage.type == TYPE_TEMPO
          && strstr((char *)MML_NUM,command_buf) != NULL) {

        midimessage.param1 = atoi(mmlstr+pos);

        if (midimessage.param1 < 1 || midimessage.param1 > 255) {
          // 範囲外
          return -9;
        }
        char buf[16];
        pos += strlen(itoa(midimessage.param1,buf,10));// 桁数分

        mml_T=midimessage.param1;
       for(int i=0;i<strlen(itoa(midimessage.param1,buf,10));i++){
          Serial.print("*");
      }
     }

      //音色指定
      if (midimessage.type == TYPE_INSTRUMENT ){
        midimessage.param1 = atoi(mmlstr+pos);

        if (midimessage.param1 < 0 || midimessage.param1 > 127) {
          // 範囲外
          return -10;
        }
        char buf[16];
        pos += strlen(itoa(midimessage.param1,buf,10));// 桁数分
              for(int i=0;i<strlen(itoa(midimessage.param1,buf,10));i++){
          Serial.print("*");
      }

      }
    }

    //イベント送信
    if(midimessage.type==TYPE_NOTE || midimessage.type==TYPE_INSTRUMENT){
      send_midi_message(midimessage);
    }

    //rest(何もしない)
    if(midimessage.type==TYPE_NOTE || midimessage.type==TYPE_REST){
      delay((60000/mml_T)*midimessage.length/480);    //次の音時間までwait
   }

    //note_off処理
    if(midimessage.type==TYPE_NOTE){
      midimessage.velocity=0;//Note Off
      send_midi_message(midimessage);

    }
}
  //loop end
  Serial.println("");
  return 0;
}

void send_midi_message(midi_message_t midi_message) {

  switch(midi_message.type){
    case TYPE_NOTE:
      //NOTE-ON:0x9n
      swSerial.write((byte)(0x90|midi_message.channel));
      swSerial.write((byte)midi_message.note);
      swSerial.write((byte)midi_message.velocity);
      break;

    case TYPE_INSTRUMENT:
      //ProgramChange:0xCn
      swSerial.write((byte)(0xc0|midi_message.channel));
      swSerial.write((byte)midi_message.param1);
      break;
    default:
      break;
  }

}

すぐにできる!Android用艦これプレイヤーを作ろう!

 Android  すぐにできる!Android用艦これプレイヤーを作ろう! はコメントを受け付けていません。
8月 282013
 

タイトルは釣りですが、ただのWebViewサンプルレベルなので。
本当のタイトルは「Webviewでflash動かすメモ」です。ごめんなさい。

初めに。
flashplayerはadobeのサイトから4.0用のapkを落として入れておく(4.3でも使えました)
ただGalaxyS4は、なぜかFlashPlayerが途中で止まってしまい、動きませんでしたのでダメぽいです。

手持ちでの動作確認済み:
GalaxyNote(4.1), GalaxyNexus(4.3), Nexus7(4.3), XperiaZ(4.1)

あとflashplayerの仕様なのか、タップするとダブルタップになるようで、補給とかチェックボックスがON→OFFってなります。
ロングタップすると良いです。

■前準備

File>New Project>Android – Android Application project
1
ICS以降を指定しました。ICS未満は絶滅した方がいい。

あとは全部デフォルトのまま進む。
ビルドしてサンプルが動き、Hello,worldが表示されることを確認。

■AndroidManifest
・通信が必要なのでandroid.permission.INTERNETをつける。
・MainActivityにandroid:screenOrientation=”sensorLandscape”つける(横向き固定にしたい場合)
・画面回転などで再描画かかると、WebViewがリロードされてしまうので
android:configChanges=”keyboardHidden|orientation|screenSize”を書いておく。

横画面固定にして念のためkeyboardHidden|orientation書いて安心してたら、画面ロック⇒復帰で再描画かかってハマったよ。screenSize書いたら大丈夫になった。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.akagiplus"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="14" />
<uses-permission android:name="android.permission.INTERNET"/>

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.akagiplus.MainActivity"
android:label="@string/app_name"
android:screenOrientation="sensorLandscape"
android:configChanges="keyboardHidden|orientation|screenSize" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

■レイアウト
/res/layout/activity_main.xmlをひらく
Hello,worldのTextBox消して、全面WebViewにする。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <WebView
android:id="@+id/webView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true" />
</RelativeLayout>

WebViewの余白が邪魔なので、android:paddingXXXXX4行は消したほうがいいです。

■MainActivity

/src配下のMainActivity.javaを開く。

●onCreate()で
・WebViewを取得、各種設定をする
・画面広くして表示したいページ(艦これのURL)を指定する。
・javascriptとflashplayer(別途インストール必要)を有効にする。
・UAをPCのchromeと同じに偽装(AndroidのUAだと艦これが動かないから)

onCreateの処理書けば、もうDMMのログインページが出るはず。
ログインしたらちゃんと遊べます。
device-2013-08-28-005410

public class MainActivity extends Activity {
	private WebView mWebView;
	private Activity mActivity;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		mActivity=this;

		// タイトルバーを消す(画面広くしたい)
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		// ステータスバーを消す(画面広くしたい)
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

		setContentView(R.layout.activity_main);

	    String url ="http://www.dmm.com/netgame/social/-/gadgets/=/app_id=854854/"; // 艦これのURL

	    //WebViewの各種設定
	    mWebView = (WebView) findViewById(R.id.webView1);
	    mWebView.setVerticalScrollbarOverlay(true);
	    mWebView.getSettings().setLoadWithOverviewMode(true);
	    mWebView.getSettings().setUseWideViewPort(true);

	    mWebView.getSettings().setPluginState(PluginState.ON); //flash
	    mWebView.getSettings().setJavaScriptEnabled(true); //js
	    mWebView.getSettings().setBuiltInZoomControls(true); //+-のズーム表示
	    mWebView.getSettings().setUserAgentString("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36");

	    mWebView.setWebViewClient(new WebViewClient());
	    mWebView.loadUrl(url);

	}

	@Override
	protected void onResume() {
       super.onResume();
       mWebView.onResume();
	}

	@Override
	protected void onPause() {
        mWebView.onPause();
	    super.onPause();
	}

	@Override
	protected void onDestroy(){
		mWebView.stopLoading();
		mWebView.setWebViewClient(null);
		mWebView.setWebChromeClient(null);
		mWebView.removeAllViews();
		mWebView.destroy();
		mWebView = null;
		super.onDestroy();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}

●onResume()とonPause()
webviewに止まってね、動いてねと指示だししないと、電源キー押して画面消しても動き続けます。
電車内で「か・ん・こ・れ!」など鳴り響いたらたまりませんね?

●onDestroy()
しっかりWebViewを殺しておきます。

●その他
onCreateOptionsMenuは使ってないので消していいです。

艦コレは通信エラー起こると「リロードしてください」となるので、メニューにリロードを作ってみるのもいいでしょう。

あとバックキー塞いどかないと、間違って押してえらいことになりますので、塞いでおきましょう。

適当な場所に追記します。

    @Override
    public boolean onKeyDown( int keyCode, KeyEvent event ) {
        if ( event.getAction() == KeyEvent.ACTION_DOWN
                && keyCode == KeyEvent.KEYCODE_BACK ) {

        	//終了ダイアログ
        	AlertDialog.Builder alertDialog=new AlertDialog.Builder(this);

            // ダイアログの設定
            alertDialog.setTitle("確認");      //タイトル設定
            alertDialog.setMessage("終了するのです?");  //内容(メッセージ)設定

            alertDialog.setPositiveButton("応", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    // OKボタン押下時の処理
                	mActivity.finish();
                }
            });
            alertDialog.setNegativeButton("否", new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                	//なにもしない
                }
            });

            alertDialog.show();

            return true;
        }
        return super.onKeyDown( keyCode, event );
    }

device-2013-08-28-010402

ばっちりガード!

 

■今後の課題

flashplayerのインストールチェックして、なければ
Uri uri = Uri.parse(“http://helpx.adobe.com/jp/flash-player/kb/228683.html#main_Android_Flash_Player______”);
でブラウザ起動してあげると親切ですね。

あと艦これのページのソースgetして、flash部分だけ全画面表示するように書き換えると良いと思うけど、時間かかりそうだったので諦めました。

AR+パノラマでねねさんが部屋にやって来た

 Android  AR+パノラマでねねさんが部屋にやって来た はコメントを受け付けていません。
7月 192013
 

部屋にねねさんがいる!的なお遊び

Nintendo 3DS「ラブプラスTOOLS」でARマーカーを撮影。ねねさん登場。

表示された3DS画面をandroidの某パノラマカメラアプリで動画撮影後、部屋をパノラマ撮影…
という手の込んだ実験でした。

■関連リンク

・パノラマカメラアプリ(GooglePlay)
https://play.google.com/store/apps/details?id=jp.co.nttdocomo.android.panophoto

・ラブプラスTOOLS
http://www.konami.jp/products/newloveplus/tools.html

 

Notificationに「アクセス制限が変更されました」と表示されるのは何なのか?

 Android  Notificationに「アクセス制限が変更されました」と表示されるのは何なのか? はコメントを受け付けていません。
1月 192013
 

Notificationに「アクセス制限が変更されました」と表示されるのは何なのか?
Xperia SXで通知エリアに何度も表示されたり消えたりしたとの事。

「アクセス制限」という言葉は何でしょう。
一般のユーザ視点で考えると、PCでいうセキュリティソフトの通信制御のようにも感じます。それが勝手に変更されるとは?不安を感じさせるメッセージです。

結論から言うと、業界用語でいう「アクセス」は無線通信を指します。
「制限」はどちらかというと「規制」の意味です。

つまり端末が「無線通信に対する規制」を受けたという事です。
専門用語をそのまま訳してユーザに通知してしまってるようです(–;)

通信事業者側の都合で一時的に規制がかかっただけだと思うので、ユーザ側の対応としてはあまり心配せずに待っていれば良いと思います。

折角のレア状態発生なので、無線側のログを確認します。

adb logcat -b radio

ログを眺めているとまんま日本語で出ている所がありました。


V/RILJ ( 779): [UNSL]< UNSOL_RESTRICTED_STATE_CHANGED {18}

D/GSM ( 779): [GsmSST] setNotification: put notification アクセス制限が変更されました / 音声サービスがブロックされています。

D/GSM ( 779): [GsmSST] onRestrictedStateChanged: X rs Restricted State CS: normal call PS:true

端末がネットワーク側から規制状態の変更通知{18}を受信し、表示しているようです。
そして、CS:normal、PS:trueという規制状態を認識したみたい。

専門用語
CS:回線交換呼(音声、テレビ電話、データ通信64kなど)
PS:パケット呼

「Restricted State CS: normal call PS:true」や{18}はなんでしょう?

androidのソースコードのril.hを見ます。


/* No restriction at all including voice/SMS/USSD/SS/AV64 and packet data. */
#define RIL_RESTRICTED_STATE_NONE 0x00
/* Block emergency call due to restriction. But allow all normal voice/SMS/USSD/SS/AV64. */
#define RIL_RESTRICTED_STATE_CS_EMERGENCY 0x01
/* Block all normal voice/SMS/USSD/SS/AV64 due to restriction. Only Emergency call allowed. */
#define RIL_RESTRICTED_STATE_CS_NORMAL 0x02
/* Block all voice/SMS/USSD/SS/AV64 including emergency call due to restriction.*/
#define RIL_RESTRICTED_STATE_CS_ALL 0x04
/* Block packet data access due to restriction. */
#define RIL_RESTRICTED_STATE_PS_ALL 0x10

PS規制は1種類の規制しかなく、CS規制は3種類あるのですね。

{18}は16進数0x12=RIL_RESTRICTED_STATE_CS_NORMAL | RIL_RESTRICTED_STATE_PS_ALL
で、CS-noral規制とPS全規制がかかっていると。
#CS_NORMALとCS_ALLの違いがぱっと見分かりませんが

ログの「Restricted State CS: normal call PS:true」とも一致しますね。

色々教えてくれた@MichiTsuchidaに感謝!

追記:
事象の出ていた本日1時過ぎについて、神奈川県の工事情報が出ていました。
神奈川県:1月19日(土曜) 午前1時~午前6時のうち 5分~10分 音声およびパケット通信がご利用いただけなくなります。
今回の原因はこれですね。

docomo工事のお知らせ
http://www.nttdocomo.co.jp/info/construction/

10月 082012
 

@youtrn_redo先生の記事を元に色々実験。
http://greety.sakura.ne.jp/redo/2012/07/jb.html

ざっくり要約すると。android4.1から、kcmファイルをリソースに持たせたアプリ入れると、OS側のキーボードレイアウト一覧に出してくれるよ!って事らしい。
(kcmだけでklはだめなのね)

でまあ、完成したものをマーケットに上げました。

日本語106/109キーボードレイアウト
https://play.google.com/store/apps/details?id=net.init0.android.keyboard109

動作確認済機種:
・Nexus7(JB)
・GalaxyNexus(JB)

接続確認済キーボード:
[Bluetooth]ポケモンキーボード(Nintendo Wireless Keyboard)
[USB]その辺にあったUSBキーボード(^^;
※Appleキーボードも動作報告を頂いています

備考:
・テンキー追加してないです(手持ちに無いので、いりますかね?)
・ポケモンキーボード用に「家マーク」キーに追加でHOMEボタン割り当ててます
(他のキーボードで変な動作しないことを祈る)

以下、記録。

english_usなkeymap書き換えて日本語配列キーボードに対応させました。

元ファイル
/frameworks/base/packages/InputDevices/res/raw
keyboard_layout_english_us.kcm

変更点(①~⑪は比較して「?」となったポイント)

・key 2
shift: ‘@’ → ‘”‘

・key 6
shift: ‘^’ → ‘&’

・key 7
shift: ‘&’ → ‘\”

・key 8
shift: ‘*’ → ‘(‘

・key 9
shift: ‘(‘ → ‘)’

・key 0
shift: ‘)’ → none

・key MINUS
shift: ‘_’ → ‘=’

①EQUALSが余分
②’^’がない
⇒英語キーボードのEQUALSキーが日本語キーボードの’^’の為
key EQUALS
label: ‘=’ → ‘^’
base: ‘=’ → ‘^’
shift: ‘+’ → ‘~’
に変更

③\がない(一見BACKSLASHがそのまま使えそうだが、キーコードが違うので)
map key 124 YEN

key YEN {
label: ‘\u00a5’
base: ‘\u00a5’
shift: ‘|’
}
を追加

2列目
④’@’が無い
map key 26 AT
key AT {
label: ‘@’
base: ‘@’
shift: ‘`’
}
を追加

⑤RIGHT_BRACKETが余分にある
⇒⑧でつかうので放置
⑥BACKSLASHが余分にある
⇒⑩でつかうので放置

3列目

key SEMICOLON
shift: ‘:’ → ‘+’

⑦’:’がない
⇒⑨で解決

⑧’}’がない
⇒⑤RIGHT_BRACKETがそのまま使えるので放置。
⇒キーコードが違うので、
map key 43 RIGHT_BRACKET
を追加

⑨APOSTROPHEが余分
⇒コロンの所なので書き換え
key APOSTROPHE {
label: ‘\” → ‘:’
base: ‘\” → ‘:’
shift: ‘”‘ → ‘*’
}

4列目
⑩’\'(shiht:’_’)がない。
⇒BACKSLASHを修正
key BACKSLASH {
shift: ‘|’ → ‘_’
}
⇒キーコードが違うので、
map key 89 BACKSLASH
を追加


⇒キーコードが違うので、
map key 27 LEFT_BRACKET
を追加