2010年10月24日

[android] iモードとSPモードのWAP PUSH信号(PDU)をデコードしてみた

WAP_PUSH信号を受信した時、WapPushOverSmsクラスが
10-23 13:10:27.779: DEBUG/WAP PUSH(1015): Rx: 000605b0af02800202066a00850903646f636f6d6f2e6e652e6a703f50493d30360001
のようなログを出力します。Rx: 以降に出力されているのが受信したWAP PUSHのPDU(Protocol Data Unit)です。このログを使って、iモードとSPモードのメール受信時のWAP PUSH信号(PDU)をデコードしてみます。

WiFi接続時のiモードメール受信時のPDU
さきほどのログがWiFi接続時のiモードメール受信時のログです。このPDUをデコードしてみます
// WSP header
0x00 - transaction ID
0x06 - pdu type(0x06: push)
0x05 - header length: 0x05
0xb0 - content-type: application/vnd.wap.slc
0xaf - X-Wap-Application-Id
0x02 - length of the Multi-octet-integer
0x80 0x02 - x-wap-docomo:imode.mail.ua

// push data
0x02 - WBXML Version 1.2
0x06 - "-//WAPFORUM//DTD SL 1.0//EN" (Service Loading 1.0)
0x6a - Charset UTF-8
0x00 - String Table Length ( = 0 )
0x85 - <sl>
0x09 - href
0x03 - start of string
0x64 6f 63 6f 6d 6f 2e 6e 65 2e 6a 70 3f 50 49 3d 30 36 - "docomo.ne.jp?PI=06"
0x00 - end of string
0x01 - </sl>
WAP PUSHのSL(service loading)データのhrefに"docomo.ne.jp?PI=06"が設定されるようです。次にSPモードメール着信のケースでデコードしてみます

SPモードメールのメール受信時のPDU
SPモードメールを受信した時のログです。
10-23 23:29:27.823: DEBUG/WAP PUSH(1016): Rx: 0006080302030aaf02905c030d6a008507036d656c6f6e63616b6540******************2e6a700005c3072010102314293001
10-23 23:29:28.116: DEBUG/WAP PUSH(1016): dispatchWapPdu_MailPush Start : appId = 36956
10-23 23:29:28.232: DEBUG/WAP PUSH(1016): call startService : Intent { act=android.provider.Telephony.WAP_PUSH_RECEIVED typ=application/vnd.wap.emn+wbxml cmp=jp.co.nttdocomo.carriermail/.SMSService (has extras) }
SPモードメールのPDUをデコードしてみます(不明点が多くデコードできていない箇所が残っています・・・)。
// WSP header
0x00 - transaction ID
0x06 - pdu type(0x06: push)
0x08 - header length: 0x08
0x03 - length of content-type
0x02 - length of the Multi-octet-integer
0x03 0x0a - application/vnd.wap.emn+wbxml
0xaf - X-Wap-Application-Id
0x02 - length of the Multi-octet-integer
0x90 0x5c - x-oma-docomo:xmd.mail.ua

// push data
0x03 - WBXML Version 1.3
0x0d - "-//WAPFORUM//DTD CHANNEL 1.2//EN" (Channel 1.2)
0x6a - Charset UTF-8
0x00 - String Table Length ( = 0 )
0x85 - <channel>
0x07 - href
0x03 - start of string
** ** ** ** ** ** ** ** ** 40 64 6f 63 6f 6d 6f 2e 6e 65 2e 6a 70: *********@docomo.ne.jp(SPモードメールのメールアドレスのようです)
0x00 - end of string
0x05 - 不明
0xc3 - 不明
0x07 - 不明
0x20 - 不明
0x10 - 不明
0x10 - 不明
0x23 - 不明
0x14 - 不明
0x29 - 不明
0x30 - 不明
0x01 - </channel>
残念ながら、SPモードメールのメール受信時のWAP_PUSHは前回記事のコードでは受信できません。

content-type: application/vnd.wap.emn+wbxml 且つ X-Wap-Application-Id:x-oma-docomo:xmd.mail.uaの WAP PUSHメッセージは明示的にjp.co.nttdocomo.carriermail/.SMSServiceサービスのインテントを呼び出すよう WapPushOverSmsがカスタマイズされているからと思われます。

WAPの技術資料はOMA(OpenMobileAlliance)に掲載されています。
WAPのネーミング規則はOMNA(Open Mobile Naming Authority)にて定義されています。
続きを読む
posted by meloncake at 04:42| Comment(1) | TrackBack(0) | Android

2010年10月23日

[android] SMSでのWAP PUSHを受信するには

android端末でSMSでのWAP PUSHを受信するには、WAP_PUSH_RECEIVEDのブロードキャスト通知を受信します。

IntentFilterの設定
端末がSMSのWAP_PUSHを受信すると"android.provider.Telephony.WAP_PUSH_RECEIVED"アクションのインテント通知がブロードキャストされます。BroadcastReceiverのインテントフィルターにWAP_PUSH_RECEIVEDアクションを登録して、WAP_PUSHの受信通知を受け取ります。このブロードキャスト処理はcom.android.internal.telephony.WapPushOverSmsクラスのdispatchWapPdu_defaultメソッドから発行されているようです。

  
    
    
  


Permissionの設定
WAP_PUSH_RECEIVEDを受信するにはRECEIVE_WAP_PUSH"のパーミッションが必要です。


BroadcastReceiverの処理
dispatchWapPdu_defaultから発行されたWAP_PUSH_RECEIVEDインテントには以下のExtraデータが格納されています
キー名内容備考
transactionIdPush信号のトランザクションID
pduTypePDUタイプ(Push信号なら0x06)詳細は"WAP-230-WSP-20010705-a"のTable 34(PDU Type Assignments)
headerWSP(Wireless Session Protocol)のヘッダ詳細は"WAP-230-WSP-20010705-a" の8.2.4章(Push and Confirmed Push Facilities)
dataPUSH信号の本体WBXML(Wireless Binary XML)形式

public class WapPushReceiver extends BroadcastReceiver {
  private static final String WAP_PUSH_RECEIVED_ACTION = "android.provider.Telephony.WAP_PUSH_RECEIVED";
  private static final String TAG = "WAP_PUSH";

  @Override
  public void onReceive(Context context, Intent intent) {
    if (WAP_PUSH_RECEIVED_ACTION.equals(intent.getAction())) {
      Bundle extras = intent.getExtras();
      if (extras != null) {
        int transactionId = extras.getInt("transactionId");
        int pduType = extras.getInt("pduType");
        byte[] header = extras.getByteArray("header");
        byte[] data = extras.getByteArray("data");
        
        Log.d(TAG, "contentType: " + ((intent.getType() != null) ? intent.getType() : ""));
        Log.d(TAG, "transactionId: " + Integer.toString(transactionId));
        Log.d(TAG, "pduType: " + Integer.toString(pduType));

        if (header != null) {
          for (int i = 0; i < header.length; i++) {
            Log.d(TAG, String.format("header[%03d]: 0x%02x (%s)", i, header[i], (char) header[i]));
          }
        } else {
          Log.d(TAG, "header is null");
        }

        if (data != null) {
          for (int i = 0; i < data.length; i++) {
            Log.d(TAG, String.format("data[%03d]: 0x%02x (%s)", i, data[i], (char) data[i]));
          }
        } else {
          Log.d(TAG, "data is null");
        }
      }
    }
  }
}

動作ログ
WiFi接続時にiモードのメール受信通知がブロードキャストされた時のログです
10-23 13:10:28.263: DEBUG/WAP_PUSH(22039): contentType: application/vnd.wap.slc
10-23 13:10:28.263: DEBUG/WAP_PUSH(22039): transactionId: 0
10-23 13:10:28.263: DEBUG/WAP_PUSH(22039): pduType: 6
10-23 13:10:28.263: DEBUG/WAP_PUSH(22039): header is null
10-23 13:10:28.273: DEBUG/WAP_PUSH(22039): data[000]: 0x02 ( )
10-23 13:10:28.283: DEBUG/WAP_PUSH(22039): data[001]: 0x06 ( )
10-23 13:10:28.303: DEBUG/WAP_PUSH(22039): data[002]: 0x6a (j)
10-23 13:10:28.313: DEBUG/WAP_PUSH(22039): data[003]: 0x00 ( )
10-23 13:10:28.323: DEBUG/WAP_PUSH(22039): data[004]: 0x85 ( )
10-23 13:10:28.333: DEBUG/WAP_PUSH(22039): data[005]: 0x09 ( )
10-23 13:10:28.343: DEBUG/WAP_PUSH(22039): data[006]: 0x03 ( )
10-23 13:10:28.353: DEBUG/WAP_PUSH(22039): data[007]: 0x64 (d)
10-23 13:10:28.363: DEBUG/WAP_PUSH(22039): data[008]: 0x6f (o)
10-23 13:10:28.373: DEBUG/WAP_PUSH(22039): data[009]: 0x63 (c)
10-23 13:10:28.383: DEBUG/WAP_PUSH(22039): data[010]: 0x6f (o)
10-23 13:10:28.393: DEBUG/WAP_PUSH(22039): data[011]: 0x6d (m)
10-23 13:10:28.403: DEBUG/WAP_PUSH(22039): data[012]: 0x6f (o)
10-23 13:10:28.413: DEBUG/WAP_PUSH(22039): data[013]: 0x2e (.)
10-23 13:10:28.423: DEBUG/WAP_PUSH(22039): data[014]: 0x6e (n)
10-23 13:10:28.433: DEBUG/WAP_PUSH(22039): data[015]: 0x65 (e)
10-23 13:10:28.443: DEBUG/WAP_PUSH(22039): data[016]: 0x2e (.)
10-23 13:10:28.453: DEBUG/WAP_PUSH(22039): data[017]: 0x6a (j)
10-23 13:10:28.463: DEBUG/WAP_PUSH(22039): data[018]: 0x70 (p)
10-23 13:10:28.473: DEBUG/WAP_PUSH(22039): data[019]: 0x3f (?)
10-23 13:10:28.483: DEBUG/WAP_PUSH(22039): data[020]: 0x50 (P)
10-23 13:10:28.493: DEBUG/WAP_PUSH(22039): data[021]: 0x49 (I)
10-23 13:10:28.503: DEBUG/WAP_PUSH(22039): data[022]: 0x3d (=)
10-23 13:10:28.523: DEBUG/WAP_PUSH(22039): data[023]: 0x30 (0)
10-23 13:10:28.533: DEBUG/WAP_PUSH(22039): data[024]: 0x36 (6)
10-23 13:10:28.543: DEBUG/WAP_PUSH(22039): data[025]: 0x00 ( )
10-23 13:10:28.553: DEBUG/WAP_PUSH(22039): data[026]: 0x01 ( )

posted by meloncake at 23:59| Comment(0) | TrackBack(0) | Android

2010年10月12日

[android] SMSを受信するには

SMS(Short Message Service)を受信するにはSMS_RECEIVEDのブロードキャスト通知を受信します。

IntentFilterの設定
端末がSMSを受信すると"android.provider.Telephony.SMS_RECEIVED"アクションのインテント通知がブロードキャストされます。BroadcastReceiverのインテントフィルターにSMS_RECEIVEDアクションを登録して、SMSの受信通知を受け取ります。

  
    
  


Permissionの設定
SMS_RECEIVEDを受信するにはRECEIVE_SMSのパーミッション、SMSを読み取るにはREAD_SMSのパーミッションが必要です。



BroadcastReceiverの処理
SMS_RECEIVEDインテントのExtra(キー名:"pdus")にメッセージデータが格納されています。Object[]形式でメッセージが複数格納されていますので、for文等でメッセージを単体に分割します。メッセージ単体はPDU(protocol description unit)形式のbyte[]文字列ですのでSmsMessageクラスのcreateFromPdu(bute[] pdu)メソッドを呼び出し、byte[]形式のPDUデータをSmsMessageインスタンスへデコードします。

public class SMSReceiver extends BroadcastReceiver {
  private static final String SMS_RECEIVED_ACTION = "android.provider.Telephony.SMS_RECEIVED";
  private static final String TAG = "SMSReceiver";

  @Override
  public void onReceive(Context context, Intent intent) {
    if (SMS_RECEIVED_ACTION.equals(intent.getAction())) {
      Bundle extras = intent.getExtras();
      if (extras != null) {
        // pduのデコードとログ出力
        // サンプルのためBroadcastReceiverで処理(本来はServiceで)
        Object[] pdus = (Object[]) extras.get("pdus");
        for (Object pdu: pdus) {
          SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) pdu);
          Log.d(TAG, "from:" + smsMessage.getOriginatingAddress());
          Log.d(TAG, "time:" + Long.toString(smsMessage.getTimestampMillis()));
          Log.d(TAG, "body:" + smsMessage.getMessageBody().replaceAll("\n", "\t"));
        }
      }
    }
  }
}

※SmsMessageクラスはAPIレベル4(Donuts)を境に機能とパスが変更されています(Donuts以降、android.telephony.gsm.SmsMessageはdeprecated)

API Levelクラス備考
Cupcake(3)までandroid.telephony.gsm.SmsMessageGSMのみ
Donuts(4)以降android.telephony.SmsMessageGSMとCDMA

docomoの電源OFF・圏外時着信通知を受信した時のログです
10-12 01:33:18.840: DEBUG/SMSReceiver(5272): from:DoCoMo SMS
10-12 01:33:18.840: DEBUG/SMSReceiver(5272): time:1286814795000
10-12 01:33:18.840: DEBUG/SMSReceiver(5272): body:10/12 01:29     090********

posted by meloncake at 02:05| Comment(1) | TrackBack(0) | Android

2010年10月06日

[My docomo checker] 1.6.0を公開しました(料金プラン分析機能など)

◆FOMA通話料
・サマリーにFOMA通話料を表示するようにしました。
・タップすると当月末の通話料を予測表示します。

1.6.0_call_bill.png

◆料金プラン分析
・サマリーの料金プランの項目をタップすると当日に料金プラン変更した時の基本料金と通話料金がどのようになるか分析結果を表示します(予測ですので、プラン変更後の実請求額等を保証するものではありません)

1.6.0_plan_analyze.png

◆WiFi自動更新処理の改善
・WIFi接続限定時の自動更新処理に不具合があったため修正しました。

◆FAQへのリンク
・設定メニューにFAQへのリンクボタンを追加しました
1.6.0_faq.png

◆プロセス管理の改善
・自動更新の設定がOFFの時タスクができるだけ常駐しないようにしました。

◆その他
・軽微な不具合修正を行いました。
posted by meloncake at 02:43| Comment(1) | TrackBack(0) | my docomo checker

2010年10月05日

[android] WiFiのON/OFFを取得するには

ワイヤレス設定のWiFi ON/OFFを取得する方法です
※WiFiの接続状態ではなくWiFi設定のON/OFFです

IntentFilterの設定
WiFiのON/OFFが変更された時にWIFI_STATE_CHANGED_ACTIONのインテントが投げられるので、IntentFilterで受け取ります

  
    
  


Permissionの設定
WIFI_STATE_CHANGED_ACTIONのインテントを受け取るにはACCESS_WIFI_STATEのパーミッションが必要です


Receiverの処理
WifiManagerのgetWifiState()でWiFiのON/OFF状態を取得できます
public class WifiOnOffReceiver extends BroadcastReceiver {
  @Override
  public void onReceive(Context context, Intent intent) {
    if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) {
      WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);

      if (wm.getWifiState() == WifiManager.WIFI_STATE_ENABLED) {
        // WiFiがONになった時の処理
      } else if (wm.getWifiState() == WifiManager.WIFI_STATE_DISABLED) {
        // WiFiがOFFになった時の処理
      }
    }
  }
}

WifiState()の遷移
[WIFIをON ] → WIFI_STATE_ENABLING → WIFI_STATE_ENABLED
[WIFIをOFF] → WIFI_STATE_DISABLING → WIFI_STATE_DISABLED
[失 敗 時 ] → WIFI_STATE_UNKNOWN
posted by meloncake at 01:35| Comment(2) | TrackBack(0) | Android