├── bin ├── classes.dex ├── resources.ap_ ├── FelicaEdit.apk └── jp │ └── co │ └── yumemi │ ├── nfc │ ├── NFCUtil.class │ ├── NfcTag.class │ ├── FelicaTag.class │ ├── NullNfcTag.class │ ├── TagFactory.class │ ├── NfcException.class │ ├── ReadResponse.class │ ├── FelicaTag$IDm.class │ ├── FelicaTag$PMm.class │ ├── PollingResponse.class │ ├── FelicaTag$SystemCode.class │ ├── FelicaTag$ServiceCode.class │ ├── FelicaTag$CommandPacket.class │ └── FelicaTag$CommandResponse.class │ ├── rd │ ├── misc │ │ ├── Util.class │ │ └── SimpleAlert.class │ └── felicaedit │ │ ├── R.class │ │ ├── R$id.class │ │ ├── R$xml.class │ │ ├── R$array.class │ │ ├── R$attr.class │ │ ├── EditBlock.class │ │ ├── FelicaEdit.class │ │ ├── R$drawable.class │ │ ├── R$layout.class │ │ ├── R$string.class │ │ ├── SystemList.class │ │ └── ServiceList.class │ └── LICENSE ├── res ├── drawable-hdpi │ └── icon.png ├── drawable-ldpi │ └── icon.png ├── drawable-mdpi │ └── icon.png ├── xml │ └── nfc_filter.xml ├── layout │ ├── service.xml │ ├── edit_block_byte.xml │ ├── edit_block.xml │ └── main.xml └── values │ └── strings.xml ├── .classpath ├── README.jp.txt ├── default.properties ├── README.txt ├── README.en.txt ├── src └── jp │ └── co │ └── yumemi │ ├── nfc │ ├── NFCUtil.java │ ├── TagFactory.java │ ├── NfcException.java │ ├── NfcTag.java │ ├── PollingResponse.java │ ├── ReadResponse.java │ └── FelicaTag.java │ ├── LICENSE │ └── rd │ ├── misc │ ├── SimpleAlert.java │ └── Util.java │ └── felicaedit │ ├── SystemList.java │ ├── FelicaEdit.java │ ├── EditBlock.java │ └── ServiceList.java ├── LICENSE ├── .project ├── proguard.cfg ├── AndroidManifest.xml └── gen └── jp └── co └── yumemi └── rd └── felicaedit └── R.java /bin/classes.dex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/classes.dex -------------------------------------------------------------------------------- /bin/resources.ap_: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/resources.ap_ -------------------------------------------------------------------------------- /bin/FelicaEdit.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/FelicaEdit.apk -------------------------------------------------------------------------------- /res/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/res/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /res/drawable-ldpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/res/drawable-ldpi/icon.png -------------------------------------------------------------------------------- /res/drawable-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/res/drawable-mdpi/icon.png -------------------------------------------------------------------------------- /bin/jp/co/yumemi/nfc/NFCUtil.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/nfc/NFCUtil.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/nfc/NfcTag.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/nfc/NfcTag.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/nfc/FelicaTag.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/nfc/FelicaTag.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/nfc/NullNfcTag.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/nfc/NullNfcTag.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/nfc/TagFactory.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/nfc/TagFactory.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/rd/misc/Util.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/rd/misc/Util.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/nfc/NfcException.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/nfc/NfcException.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/nfc/ReadResponse.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/nfc/ReadResponse.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/rd/felicaedit/R.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/rd/felicaedit/R.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/nfc/FelicaTag$IDm.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/nfc/FelicaTag$IDm.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/nfc/FelicaTag$PMm.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/nfc/FelicaTag$PMm.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/nfc/PollingResponse.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/nfc/PollingResponse.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/rd/felicaedit/R$id.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/rd/felicaedit/R$id.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/rd/felicaedit/R$xml.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/rd/felicaedit/R$xml.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/rd/misc/SimpleAlert.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/rd/misc/SimpleAlert.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/rd/felicaedit/R$array.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/rd/felicaedit/R$array.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/rd/felicaedit/R$attr.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/rd/felicaedit/R$attr.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/nfc/FelicaTag$SystemCode.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/nfc/FelicaTag$SystemCode.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/rd/felicaedit/EditBlock.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/rd/felicaedit/EditBlock.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/rd/felicaedit/FelicaEdit.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/rd/felicaedit/FelicaEdit.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/rd/felicaedit/R$drawable.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/rd/felicaedit/R$drawable.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/rd/felicaedit/R$layout.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/rd/felicaedit/R$layout.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/rd/felicaedit/R$string.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/rd/felicaedit/R$string.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/rd/felicaedit/SystemList.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/rd/felicaedit/SystemList.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/nfc/FelicaTag$ServiceCode.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/nfc/FelicaTag$ServiceCode.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/rd/felicaedit/ServiceList.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/rd/felicaedit/ServiceList.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/nfc/FelicaTag$CommandPacket.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/nfc/FelicaTag$CommandPacket.class -------------------------------------------------------------------------------- /bin/jp/co/yumemi/nfc/FelicaTag$CommandResponse.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mokemokechicken/Android_NFC_FelicaEdit/HEAD/bin/jp/co/yumemi/nfc/FelicaTag$CommandResponse.class -------------------------------------------------------------------------------- /res/xml/nfc_filter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | android.nfc.tech.NfcF 4 | 5 | 6 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.jp.txt: -------------------------------------------------------------------------------- 1 | ■ これは何? 2 | AndroidのNFC機能を使って、FeliCaのデータを読み書きするアプリケーションです。 3 | 4 | ■ 注意事項 5 | 基本的に全て無保証ですので、自己責任においてご利用下さい。 6 | 特に下記の点にご注意下さい。 7 | 8 | * 本アプリケーションは、@hide 扱いのJava Classを使用しているため、NexusSのAndroid2.3での動作は確認していますが、他のデバイス、他のOSバージョンで動く保証はありません。 9 | * 本アプリケーションは、実際にFeliCaの非暗号化&書き込み可能領域のデータを更新することができますが、正常に更新するという保証もありませんし、更新した結果問題が発生した場合も責任は負えません。 10 | 11 | -------------------------------------------------------------------------------- /res/layout/service.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /default.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system use, 7 | # "build.properties", and override values to adapt the script to your 8 | # project structure. 9 | 10 | # Project target. 11 | target=android-10 12 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | * WHAT IS THIS? 2 | It is an application which can read and write FeliCa data using Android NFC function. 3 | 4 | * BE CAREFUL! 5 | * This application uses Java Class marked @hide, and it can run on NexusS with AndroidOS2.3. But, there is no guarantee with other devices or other OS versions. 6 | * This application can write actually data into non-encrypted and writable memory of FeliCa. But, there is no guarantee of normal writing and the result of writing. 7 | 8 | -------------------------------------------------------------------------------- /README.en.txt: -------------------------------------------------------------------------------- 1 | * WHAT IS THIS? 2 | It is an application which can read and write FeliCa data using Android NFC function. 3 | 4 | * BE CAREFUL! 5 | * This application uses Java Class marked @hide, and it can run on NexusS with AndroidOS2.3. But, there is no guarantee with other devices or other OS versions. 6 | * This application can write actually data into non-encrypted and writable memory of FeliCa. But, there is no guarantee of normal writing and the result of writing. 7 | 8 | -------------------------------------------------------------------------------- /res/layout/edit_block_byte.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /res/layout/edit_block.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/jp/co/yumemi/nfc/NFCUtil.java: -------------------------------------------------------------------------------- 1 | package jp.co.yumemi.nfc; 2 | 3 | import android.nfc.Tag; 4 | import android.nfc.tech.TagTechnology; 5 | 6 | public class NFCUtil { 7 | static public boolean hasTech(Tag tag, String klassName) { 8 | for (String tech : tag.getTechList()) { 9 | if (tech.equals(klassName)) { 10 | return true; 11 | } 12 | } 13 | return false; 14 | } 15 | 16 | static public boolean hasTech(Tag tag, Class tech) { 17 | return hasTech(tag, tech.getCanonicalName()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2011 YUMEMI Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /bin/jp/co/yumemi/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2011 YUMEMI Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /src/jp/co/yumemi/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2011 YUMEMI Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /src/jp/co/yumemi/nfc/TagFactory.java: -------------------------------------------------------------------------------- 1 | package jp.co.yumemi.nfc; 2 | 3 | import android.content.Intent; 4 | import android.nfc.NfcAdapter; 5 | import android.nfc.Tag; 6 | import android.nfc.tech.MifareClassic; 7 | import android.nfc.tech.NfcF; 8 | import android.os.Parcelable; 9 | 10 | public class TagFactory { 11 | 12 | public static NfcTag create(Intent intent) { 13 | return create(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)); 14 | } 15 | 16 | public static NfcTag create(Parcelable tag) { 17 | if (tag != null && tag instanceof Tag) { 18 | Tag t = (Tag)tag; 19 | if (NFCUtil.hasTech(t, NfcF.class)) { 20 | return new FelicaTag(t); 21 | } else { 22 | return new NfcTag(t); 23 | } 24 | } 25 | return new NullNfcTag(null); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/jp/co/yumemi/nfc/NfcException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * Unless required by applicable law or agreed to in writing, software 7 | * distributed under the License is distributed on an "AS IS" BASIS, 8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | * See the License for the specific language governing permissions and 10 | * limitations under the License. 11 | */ 12 | 13 | package jp.co.yumemi.nfc; 14 | 15 | public class NfcException extends Exception{ 16 | private static final long serialVersionUID = 1L; 17 | public NfcException(Exception e) { 18 | super(e); 19 | } 20 | public NfcException(String s) { 21 | super(s); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | FeliCaをかざしてみてください 4 | FelicaEdit 5 | システムコード検索 6 | 終了 7 | サービス一覧 8 | Tap to Edit Data 9 | 10 | 0 11 | 1 12 | 2 13 | 3 14 | 4 15 | 5 16 | 6 17 | 7 18 | 8 19 | 9 20 | A 21 | B 22 | C 23 | D 24 | E 25 | F 26 | 27 | Save 28 | 29 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | FelicaEdit 4 | 5 | 6 | 7 | 8 | 9 | com.android.ide.eclipse.adt.ResourceManagerBuilder 10 | 11 | 12 | 13 | 14 | com.android.ide.eclipse.adt.PreCompilerBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.jdt.core.javabuilder 20 | 21 | 22 | 23 | 24 | com.android.ide.eclipse.adt.ApkBuilder 25 | 26 | 27 | 28 | 29 | 30 | com.android.ide.eclipse.adt.AndroidNature 31 | org.eclipse.jdt.core.javanature 32 | 33 | 34 | -------------------------------------------------------------------------------- /proguard.cfg: -------------------------------------------------------------------------------- 1 | -optimizationpasses 5 2 | -dontusemixedcaseclassnames 3 | -dontskipnonpubliclibraryclasses 4 | -dontpreverify 5 | -verbose 6 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* 7 | 8 | -keep public class * extends android.app.Activity 9 | -keep public class * extends android.app.Application 10 | -keep public class * extends android.app.Service 11 | -keep public class * extends android.content.BroadcastReceiver 12 | -keep public class * extends android.content.ContentProvider 13 | -keep public class * extends android.app.backup.BackupAgentHelper 14 | -keep public class * extends android.preference.Preference 15 | -keep public class com.android.vending.licensing.ILicensingService 16 | 17 | -keepclasseswithmembernames class * { 18 | native ; 19 | } 20 | 21 | -keepclasseswithmembernames class * { 22 | public (android.content.Context, android.util.AttributeSet); 23 | } 24 | 25 | -keepclasseswithmembernames class * { 26 | public (android.content.Context, android.util.AttributeSet, int); 27 | } 28 | 29 | -keepclassmembers enum * { 30 | public static **[] values(); 31 | public static ** valueOf(java.lang.String); 32 | } 33 | 34 | -keep class * implements android.os.Parcelable { 35 | public static final android.os.Parcelable$Creator *; 36 | } 37 | -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /gen/jp/co/yumemi/rd/felicaedit/R.java: -------------------------------------------------------------------------------- 1 | /* AUTO-GENERATED FILE. DO NOT MODIFY. 2 | * 3 | * This class was automatically generated by the 4 | * aapt tool from the resource data it found. It 5 | * should not be modified by hand. 6 | */ 7 | 8 | package jp.co.yumemi.rd.felicaedit; 9 | 10 | public final class R { 11 | public static final class array { 12 | public static final int hex=0x7f060000; 13 | } 14 | public static final class attr { 15 | } 16 | public static final class drawable { 17 | public static final int icon=0x7f020000; 18 | } 19 | public static final class id { 20 | public static final int btn_save=0x7f070000; 21 | public static final int data_list_view=0x7f070001; 22 | public static final int scrollView1=0x7f070004; 23 | public static final int service_list=0x7f070006; 24 | public static final int sp1=0x7f070002; 25 | public static final int sp2=0x7f070003; 26 | public static final int textView1=0x7f070005; 27 | } 28 | public static final class layout { 29 | public static final int edit_block=0x7f030000; 30 | public static final int edit_block_byte=0x7f030001; 31 | public static final int main=0x7f030002; 32 | public static final int service=0x7f030003; 33 | } 34 | public static final class string { 35 | public static final int EditBlockTitle=0x7f050005; 36 | public static final int app_name=0x7f050001; 37 | public static final int dump=0x7f050002; 38 | public static final int end=0x7f050003; 39 | public static final int hello=0x7f050000; 40 | public static final int save=0x7f050006; 41 | public static final int service_list_title=0x7f050004; 42 | } 43 | public static final class xml { 44 | public static final int nfc_filter=0x7f040000; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/jp/co/yumemi/rd/misc/SimpleAlert.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * Unless required by applicable law or agreed to in writing, software 7 | * distributed under the License is distributed on an "AS IS" BASIS, 8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | * See the License for the specific language governing permissions and 10 | * limitations under the License. 11 | */ 12 | 13 | package jp.co.yumemi.rd.misc; 14 | 15 | import android.app.Activity; 16 | import android.app.AlertDialog; 17 | import android.content.DialogInterface; 18 | import android.content.DialogInterface.OnClickListener; 19 | 20 | /** 21 | * 簡易メッセージダイアログ用クラス。 22 | * @author morishita_2 23 | * 24 | */ 25 | public class SimpleAlert implements OnClickListener { 26 | private final Activity parent; 27 | 28 | private AlertDialog.Builder builder; 29 | public AlertDialog.Builder getBuilder() { 30 | return builder; 31 | } 32 | 33 | private boolean finishParent; 34 | public SimpleAlert(Activity activity) { 35 | this.parent = activity; 36 | } 37 | 38 | /** 39 | * ダイアログにメッセージとOKボタンを表示します。 40 | * @param message 表示するMessage 41 | * @param finishParent if true ならば、閉じたときに parent.finish() を呼び出します。 42 | */ 43 | public void show(String message, boolean finishParent) { 44 | if (builder == null) { 45 | initBuilder(); 46 | } 47 | this.finishParent = finishParent; 48 | builder.setMessage(message); 49 | builder.create().show(); 50 | } 51 | 52 | public AlertDialog.Builder initBuilder() { 53 | builder = new AlertDialog.Builder(parent); 54 | builder.setPositiveButton("OK", this); 55 | return builder; 56 | } 57 | 58 | @Override 59 | public void onClick(DialogInterface dialog, int which) { 60 | if (finishParent) { 61 | this.parent.finish(); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/jp/co/yumemi/nfc/NfcTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * Unless required by applicable law or agreed to in writing, software 7 | * distributed under the License is distributed on an "AS IS" BASIS, 8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | * See the License for the specific language governing permissions and 10 | * limitations under the License. 11 | */ 12 | 13 | /* 14 | * 本packageは、 15 | * kazzz さんの nfc-felica(http://d.hatena.ne.jp/Kazzz/20110130/p1) からソースを色々複製して利用しています。 16 | * 整理しようと思ったのですが、現状あまり整理できてないです。。 17 | * 最終的には、MIFARE系,FeliCa系,NFC(NDEF)系を、上手く処理できるように整理できると良いと思っています。 18 | */ 19 | 20 | 21 | /* 22 | * Changes 23 | * * 2011/2/5: k_morishita 24 | * ** net.kazzz.felica.lib.FeliCaLib.executeRaw() を複製して修正。 25 | */ 26 | 27 | 28 | package jp.co.yumemi.nfc; 29 | 30 | import jp.co.yumemi.rd.misc.Util; 31 | import android.content.Intent; 32 | import android.nfc.NfcAdapter; 33 | import android.nfc.Tag; 34 | import android.os.Parcelable; 35 | 36 | public class NfcTag { 37 | static private final String TAG = "NfcTag"; 38 | 39 | protected final Tag tag; 40 | public Tag getTag() { 41 | return tag; 42 | } 43 | 44 | public static final String TYPE_NULL = "NULL"; 45 | public static final String TYPE_OTHER = "OTHER"; 46 | public static final String TYPE_FELICA = "FeliCa"; 47 | 48 | public NfcTag(Tag tag) { 49 | this.tag = tag; 50 | } 51 | 52 | public byte[] getId() { 53 | return tag.getId(); 54 | } 55 | 56 | public void putTagService(Intent intent) { 57 | intent.putExtra(NfcAdapter.EXTRA_TAG, tag); 58 | } 59 | 60 | public String getType() { 61 | return TYPE_OTHER; 62 | } 63 | 64 | public String getTechListAsString() { 65 | return Util.getHexString(tag.getTechList()); 66 | } 67 | } 68 | 69 | class NullNfcTag extends NfcTag { 70 | public NullNfcTag(Tag tag) { 71 | super(tag); 72 | } 73 | 74 | public String getType() { 75 | return TYPE_NULL; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/jp/co/yumemi/nfc/PollingResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * Unless required by applicable law or agreed to in writing, software 7 | * distributed under the License is distributed on an "AS IS" BASIS, 8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | * See the License for the specific language governing permissions and 10 | * limitations under the License. 11 | */ 12 | 13 | /* 14 | * Changes 15 | * * 2010/2/5: k_morishita 16 | * ** net.kazzz.felica.command.PollingResponse から複製して修正。 17 | */ 18 | 19 | package jp.co.yumemi.nfc; 20 | 21 | import java.util.Arrays; 22 | 23 | import jp.co.yumemi.nfc.FelicaTag.CommandResponse; 24 | import jp.co.yumemi.nfc.FelicaTag.PMm; 25 | import jp.co.yumemi.rd.misc.Util; 26 | 27 | /** 28 | * Pollingコマンドのレスポンスを抽象化したクラスを提供します 29 | * 30 | * @author Kazzz 31 | * @date 2011/01/22 32 | * @since Android API Level 9 33 | * 34 | */ 35 | 36 | public class PollingResponse extends CommandResponse { 37 | final PMm pmm; 38 | final byte[] requestData; 39 | /** 40 | * コンストラクタ 41 | * 42 | * @param data コマンド実行結果で戻ったバイト列をセット 43 | */ 44 | public PollingResponse(CommandResponse response) { 45 | super(response); 46 | this.pmm = new PMm(Arrays.copyOfRange(this.data, 0, 8)); 47 | this.requestData = Arrays.copyOfRange(this.data, 8, data.length); 48 | } 49 | /** 50 | * PMmを取得します 51 | * 52 | * @return PMm pmmが戻ります 53 | */ 54 | public PMm getPMm() { 55 | return this.pmm; 56 | } 57 | /* (non-Javadoc) 58 | * @see java.lang.Object#toString() 59 | */ 60 | @Override 61 | public String toString() { 62 | StringBuilder sb = new StringBuilder(); 63 | sb.append("FeliCa レスポンス パケット \n"); 64 | sb.append(" コマンド名 :" + Util.getHexString(this.responseCode) + "\n"); 65 | sb.append(" データ長 : " + Util.getHexString(this.length) + "\n"); 66 | sb.append(" コマンドコード : " + Util.getHexString(this.responseCode) + "\n"); 67 | if ( this.idm != null ) 68 | sb.append(" " + this.idm.toString() + "\n"); 69 | if ( this.pmm != null ) 70 | sb.append(" " + this.pmm.toString() + "\n"); 71 | sb.append(" データ: " + Util.getHexString(this.data) + "\n"); 72 | return sb.toString(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/jp/co/yumemi/rd/felicaedit/SystemList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * Unless required by applicable law or agreed to in writing, software 7 | * distributed under the License is distributed on an "AS IS" BASIS, 8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | * See the License for the specific language governing permissions and 10 | * limitations under the License. 11 | */ 12 | 13 | package jp.co.yumemi.rd.felicaedit; 14 | 15 | import jp.co.yumemi.nfc.FelicaTag; 16 | import jp.co.yumemi.nfc.NfcException; 17 | import jp.co.yumemi.nfc.TagFactory; 18 | import jp.co.yumemi.nfc.FelicaTag.SystemCode; 19 | import android.app.ListActivity; 20 | import android.content.Intent; 21 | import android.os.Bundle; 22 | import android.view.View; 23 | import android.widget.ArrayAdapter; 24 | import android.widget.ListView; 25 | 26 | /** 27 | * 1つのFeliCaに含まれるシステムコードの一覧を表示する Activity 28 | * @author morishita_2 29 | * 30 | */ 31 | public class SystemList extends ListActivity { 32 | private SystemCode[] systemCodeList; 33 | private FelicaTag felica; 34 | 35 | @Override 36 | protected void onCreate(Bundle savedInstanceState) { 37 | super.onCreate(savedInstanceState); 38 | 39 | Intent intent = getIntent(); 40 | felica = (FelicaTag)TagFactory.create(intent); 41 | systemCodeList = null; 42 | try { 43 | systemCodeList = felica.getSystemCodeList(); 44 | ArrayAdapter adapter = new ArrayAdapter(this, 45 | android.R.layout.simple_list_item_1, systemCodeList); 46 | // アダプタを設定 47 | setListAdapter(adapter); 48 | } catch (NfcException e) { 49 | ArrayAdapter adapter = new ArrayAdapter(this, 50 | android.R.layout.simple_list_item_1, new String[]{"読込みに失敗しました"}); 51 | // アダプタを設定 52 | setListAdapter(adapter); 53 | } 54 | } 55 | 56 | @Override 57 | protected void onListItemClick(ListView l, View v, int position, long id) { 58 | super.onListItemClick(l, v, position, id); 59 | if (systemCodeList == null) return; 60 | SystemCode sc = systemCodeList[position]; 61 | Intent intent = new Intent(SystemList.this, ServiceList.class); 62 | felica.putTagService(intent); 63 | // intent.putExtra(NfcTag.ANDROID_NFC_EXTRA_TAG, felica.getTagService()); 64 | intent.putExtra(SystemCode.class.getCanonicalName(), sc.getBytes()); 65 | startActivity(intent); 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/jp/co/yumemi/rd/felicaedit/FelicaEdit.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * Unless required by applicable law or agreed to in writing, software 7 | * distributed under the License is distributed on an "AS IS" BASIS, 8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | * See the License for the specific language governing permissions and 10 | * limitations under the License. 11 | */ 12 | 13 | package jp.co.yumemi.rd.felicaedit; 14 | 15 | import jp.co.yumemi.nfc.NfcTag; 16 | import jp.co.yumemi.nfc.TagFactory; 17 | import jp.co.yumemi.rd.misc.Util; 18 | import android.app.Activity; 19 | import android.content.Intent; 20 | import android.nfc.NfcAdapter; 21 | import android.os.Bundle; 22 | import android.util.Log; 23 | import android.widget.TextView; 24 | import android.widget.Toast; 25 | 26 | /** 27 | * 起動時に呼び出される Activity。 28 | * 結局あまり仕事していないので不要かもしれない。 29 | * 30 | * 何度か起動しているうちに、ちゃんとTagを読まなくなることがあります(><). 31 | * その場合は、アプリを一旦終了してから、かざしてみてください。 32 | * @author k_morishita 33 | */ 34 | public class FelicaEdit extends Activity { 35 | static private String TAG = "FelicaEdit"; 36 | private NfcTag nfcTag; 37 | 38 | /** Called when the activity is first created. */ 39 | @Override 40 | public void onCreate(Bundle savedInstanceState) { 41 | Log.d(TAG, "onCreate"); 42 | super.onCreate(savedInstanceState); 43 | setContentView(R.layout.main); 44 | 45 | Intent intent = this.getIntent(); 46 | String action = intent.getAction(); 47 | 48 | if (!NfcAdapter.getDefaultAdapter().isEnabled()) { 49 | setText("NFCが使えません"); 50 | } else { 51 | if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) { 52 | this.nfcTag = TagFactory.create(intent); 53 | String txt = action + "\n" + nfcTag.getTechListAsString(); 54 | Toast.makeText(this, txt, 15).show(); 55 | scan(); 56 | } 57 | } 58 | } 59 | 60 | private void setText(String text) { 61 | TextView tv = (TextView)findViewById(R.id.textView1); 62 | tv.setText(text); 63 | } 64 | 65 | private void scan() { 66 | if (nfcTag == null || !nfcTag.getType().equals(NfcTag.TYPE_FELICA)) { 67 | StringBuffer sb = new StringBuffer(); 68 | sb.append("FeliCaカードではないようです\n"); 69 | sb.append("TagType: " + nfcTag.getType()+"\n"); 70 | sb.append(Util.getHexString(nfcTag.getId())+"\n"); 71 | if (nfcTag != null) { 72 | sb.append(nfcTag.getTechListAsString()+"\n"); 73 | } 74 | setText(sb.toString()); 75 | return; 76 | } 77 | Intent intent = new Intent(FelicaEdit.this, SystemList.class); 78 | nfcTag.putTagService(intent); 79 | startActivity(intent); 80 | finish(); 81 | } 82 | 83 | } -------------------------------------------------------------------------------- /src/jp/co/yumemi/nfc/ReadResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * Unless required by applicable law or agreed to in writing, software 7 | * distributed under the License is distributed on an "AS IS" BASIS, 8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | * See the License for the specific language governing permissions and 10 | * limitations under the License. 11 | */ 12 | 13 | /* 14 | * Changes 15 | * * 2010/2/5: k_morishita 16 | * ** net.kazzz.felica.command.ReadResponse から複製して修正。 17 | */ 18 | 19 | package jp.co.yumemi.nfc; 20 | 21 | import java.util.Arrays; 22 | 23 | import jp.co.yumemi.nfc.FelicaTag.CommandResponse; 24 | import jp.co.yumemi.rd.misc.Util; 25 | 26 | /** 27 | * Read コマンドのレスポンスを抽象化したクラスを提供します 28 | * 29 | * @author Kazzz 30 | * @date 2011/01/22 31 | * @since Android API Level 9 32 | * 33 | */ 34 | 35 | public class ReadResponse extends CommandResponse { 36 | final int statusFlag1; 37 | final int statusFlag2; 38 | final int blockCount; 39 | final byte[] blockData; 40 | /** 41 | * コンストラクタ 42 | * 43 | * @param data コマンド実行結果で戻ったバイト列をセット 44 | */ 45 | public ReadResponse(CommandResponse response) { 46 | super(response); 47 | this.statusFlag1 = this.data[0]; 48 | this.statusFlag2 = this.data[1]; 49 | if ( this.getStatusFlag1() == 0 ) { 50 | this.blockCount = this.data[2]; 51 | this.blockData = Arrays.copyOfRange(this.data, 3, data.length); 52 | } else { 53 | this.blockCount = 0; 54 | this.blockData = null; 55 | } 56 | } 57 | 58 | /** 59 | * statusFlag1を取得します 60 | * @return int statusFlag1が戻ります 61 | */ 62 | public int getStatusFlag1() { 63 | return this.statusFlag1; 64 | } 65 | 66 | /** 67 | * statusFlag2を取得します 68 | * @return int statusFlag2が戻ります 69 | */ 70 | public int getStatusFlag2() { 71 | return this.statusFlag2; 72 | } 73 | 74 | /** 75 | * blockDataを取得します 76 | * @return byte[] blockDataが戻ります 77 | */ 78 | public byte[] getBlockData() { 79 | return this.blockData; 80 | } 81 | 82 | /** 83 | * blockCountを取得します 84 | * @return int blockCountが戻ります 85 | */ 86 | public int getBlockCount() { 87 | return this.blockCount; 88 | } 89 | 90 | /* (non-Javadoc) 91 | * @see java.lang.Object#toString() 92 | */ 93 | @Override 94 | public String toString() { 95 | StringBuilder sb = new StringBuilder(); 96 | sb.append("FeliCa レスポンス パケット \n"); 97 | sb.append(" コマンドコード : " + Util.getHexString(this.responseCode) + "\n"); 98 | sb.append(" データ長 : " + Util.getHexString(this.length) + "\n"); 99 | if ( this.idm != null ) 100 | sb.append(" " + this.idm.toString() + "\n"); 101 | sb.append(" ステータスフラグ1 : " + Util.getHexString((byte)(this.statusFlag1 & 0xff)) + "\n"); 102 | sb.append(" ステータスフラグ2 : " + Util.getHexString((byte)(this.statusFlag2 & 0xff)) + "\n"); 103 | if ( this.blockData != null ) 104 | sb.append(" ブロックデータ: " + Util.getHexString(this.blockData) + "\n"); 105 | return sb.toString(); 106 | } 107 | } 108 | 109 | -------------------------------------------------------------------------------- /src/jp/co/yumemi/rd/felicaedit/EditBlock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * Unless required by applicable law or agreed to in writing, software 7 | * distributed under the License is distributed on an "AS IS" BASIS, 8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | * See the License for the specific language governing permissions and 10 | * limitations under the License. 11 | */ 12 | 13 | package jp.co.yumemi.rd.felicaedit; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | import jp.co.yumemi.nfc.FelicaTag; 19 | import jp.co.yumemi.nfc.NfcException; 20 | import jp.co.yumemi.nfc.TagFactory; 21 | import jp.co.yumemi.nfc.FelicaTag.ServiceCode; 22 | import jp.co.yumemi.nfc.FelicaTag.SystemCode; 23 | import jp.co.yumemi.rd.misc.SimpleAlert; 24 | import android.app.Activity; 25 | import android.app.AlertDialog; 26 | import android.app.Dialog; 27 | import android.content.DialogInterface; 28 | import android.content.Intent; 29 | import android.os.Bundle; 30 | import android.view.LayoutInflater; 31 | import android.view.View; 32 | import android.widget.AdapterView; 33 | import android.widget.ArrayAdapter; 34 | import android.widget.ListView; 35 | import android.widget.Spinner; 36 | import android.widget.AdapterView.OnItemClickListener; 37 | 38 | /** 39 | * 特定のサービスコード・ブロックを編集するための Activity 40 | * @author k_morishita 41 | */ 42 | public class EditBlock extends Activity implements DialogInterface.OnClickListener, View.OnClickListener, OnItemClickListener { 43 | private static final String TAG = "EditBlock"; 44 | private static final String BYTE_DATA = "data"; 45 | public static final String BLOCK_INDEX = "BLOCK_INDEX"; 46 | private static final int BYTE_DIALOG = 1; 47 | 48 | private FelicaTag felica; // FeliCa オブジェクト 49 | private SystemCode systemCode; // システムコード 50 | private ServiceCode serviceCode; // サービスコード 51 | private int blockIndex; // 編集対象BlockのIndex 52 | private byte[] blockData; // 対象BlockのData 53 | 54 | @Override 55 | protected void onCreate(Bundle savedInstanceState) { 56 | super.onCreate(savedInstanceState); 57 | 58 | Intent intent = getIntent(); 59 | this.felica = (FelicaTag)TagFactory.create(intent); 60 | this.systemCode = new SystemCode(intent.getByteArrayExtra(SystemCode.class.getCanonicalName())); 61 | this.serviceCode = new ServiceCode(intent.getByteArrayExtra(ServiceCode.class.getCanonicalName())); 62 | this.blockIndex = intent.getIntExtra(BLOCK_INDEX, -1); 63 | setContentView(R.layout.edit_block); 64 | findViewById(R.id.btn_save).setOnClickListener(this); 65 | ((ListView)findViewById(R.id.data_list_view)).setOnItemClickListener(this); 66 | try { 67 | felica.polling(systemCode); 68 | blockData = felica.readWithoutEncryption(serviceCode, blockIndex); 69 | updateList(); 70 | } catch (NfcException e) { 71 | new SimpleAlert(this).show("データを読み込めませんでした", true); 72 | } 73 | } 74 | 75 | /** 76 | * 画面を更新する 77 | */ 78 | private void updateList() { 79 | if (blockData == null) return; 80 | List byteList = new ArrayList(); 81 | for (int i=0; i adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, byteList); 85 | // アダプタを設定 86 | ListView lv = (ListView)findViewById(R.id.data_list_view); 87 | lv.setAdapter(adapter); 88 | } 89 | 90 | /** 91 | * 編集用に選択された位置を保持 92 | */ 93 | private int bytePosition; 94 | 95 | /** 96 | * BlockDataの中のどのByteを編集するか、という画面で、1つの項目がTapされた。 97 | */ 98 | @Override 99 | public void onItemClick(AdapterView l, View v, int position, long id) { 100 | if (blockData == null) return; 101 | byte data = blockData[position]; 102 | Bundle bundle = new Bundle(); 103 | bundle.putByte(BYTE_DATA, data); 104 | bytePosition = position; 105 | showDialog(BYTE_DIALOG, bundle); 106 | } 107 | 108 | /** 109 | * Save ボタンが押された。書込みを行い、呼び出し元Activityに戻ります。 110 | */ 111 | @Override 112 | public void onClick(View v) { 113 | SimpleAlert sa = new SimpleAlert(this); 114 | if (blockData != null) { 115 | try { 116 | if (felica.writeWithoutEncryption(serviceCode, blockIndex, blockData) == 0) { 117 | 118 | sa.show("保存しました", true); 119 | } else { 120 | sa.show("保存失敗しました", true); 121 | } 122 | } catch (NfcException e) { 123 | sa.show("保存に失敗しました\n"+e.getMessage(), true); 124 | } 125 | } 126 | } 127 | 128 | // 129 | // 個々以降は、 ByteEdit Dialog 用の部分です。まとまりが悪い。。 130 | // 131 | /** 132 | * 最初に編集用ダイアログ(Selectが2つ並んでいるダイアログ)が呼び出されたときに、実行されます。 133 | */ 134 | @Override 135 | protected Dialog onCreateDialog(int id, Bundle args) { 136 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 137 | LayoutInflater factory = LayoutInflater.from(this); 138 | final View inputView = factory.inflate(R.layout.edit_block_byte, null); 139 | builder.setView(inputView) 140 | .setTitle("入力してください") 141 | .setPositiveButton("OK", this) 142 | .setNegativeButton("CANCEL", this); 143 | return builder.create(); 144 | } 145 | 146 | private Spinner sp1; 147 | private Spinner sp2; 148 | /** 149 | * 編集用ダイアログが呼び出される度に実行されます。 150 | */ 151 | @Override 152 | protected void onPrepareDialog(int id, Dialog dialog, Bundle args) { 153 | super.onPrepareDialog(id, dialog, args); 154 | sp1 = (Spinner)dialog.findViewById(R.id.sp1); 155 | sp2 = (Spinner)dialog.findViewById(R.id.sp2); 156 | byte data = args.getByte(BYTE_DATA); 157 | sp1.setSelection((int)((data >>> 4) & 0x0F)); // 元データをデフォルト値として設定 158 | sp2.setSelection((int)(data & 0x0F)); // 元データをデフォルト値として設定 159 | } 160 | 161 | /** 162 | * ByteEdit Dialog で OK か NG が押された。 163 | */ 164 | @Override 165 | public void onClick(DialogInterface dialog, int which) { 166 | switch(which) { 167 | case DialogInterface.BUTTON_POSITIVE: 168 | byte data = (byte)((sp1.getSelectedItemPosition() << 4) + sp2.getSelectedItemPosition()); 169 | blockData[bytePosition] = data; 170 | updateList(); 171 | break; 172 | 173 | case DialogInterface.BUTTON_NEGATIVE: 174 | break; 175 | } 176 | sp1 = sp2 = null; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/jp/co/yumemi/rd/felicaedit/ServiceList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * Unless required by applicable law or agreed to in writing, software 7 | * distributed under the License is distributed on an "AS IS" BASIS, 8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | * See the License for the specific language governing permissions and 10 | * limitations under the License. 11 | */ 12 | 13 | 14 | package jp.co.yumemi.rd.felicaedit; 15 | 16 | import java.util.ArrayList; 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | import jp.co.yumemi.nfc.FelicaTag; 22 | import jp.co.yumemi.nfc.NfcException; 23 | import jp.co.yumemi.nfc.TagFactory; 24 | import jp.co.yumemi.nfc.FelicaTag.ServiceCode; 25 | import jp.co.yumemi.nfc.FelicaTag.SystemCode; 26 | import jp.co.yumemi.rd.misc.SimpleAlert; 27 | import jp.co.yumemi.rd.misc.Util; 28 | import android.app.Activity; 29 | import android.content.Intent; 30 | import android.os.Bundle; 31 | import android.view.View; 32 | import android.widget.ExpandableListAdapter; 33 | import android.widget.ExpandableListView; 34 | import android.widget.SimpleExpandableListAdapter; 35 | import android.widget.TextView; 36 | 37 | /** 38 | * 1つのシステムコードに含まれるサービスの一覧を表示するActivity. 39 | * @author morishita_2 40 | */ 41 | public class ServiceList extends Activity implements ExpandableListView.OnChildClickListener{ 42 | private List serviceCodeList; 43 | private FelicaTag felica; 44 | private SystemCode systemCode; 45 | 46 | List>> childData; 47 | 48 | @Override 49 | protected void onCreate(Bundle savedInstanceState) { 50 | super.onCreate(savedInstanceState); 51 | setContentView(R.layout.service); 52 | setTitle(R.string.service_list_title); 53 | Intent intent = getIntent(); 54 | this.felica = (FelicaTag)TagFactory.create(intent); 55 | this.systemCode = new SystemCode(intent.getByteArrayExtra(SystemCode.class.getCanonicalName())); 56 | // ExpandableListView はなかなかややっこしい: see http://d.hatena.ne.jp/rudi/20100720/1279632918 57 | ExpandableListView listView = (ExpandableListView)findViewById(R.id.service_list); 58 | TextView tv = new TextView(this); // HeaderView用 59 | listView.addHeaderView(tv); 60 | try { 61 | felica.polling(systemCode); 62 | tv.setText(String.format("%s\nIDm: %s", systemCode.toString(), felica.getIdm().simpleToString())); 63 | this.serviceCodeList = felica.getServiceCodeList(); 64 | List> groupData = new ArrayList>(); // 親ノードリスト 65 | childData = new ArrayList>>(); // 子ノードリスト 66 | if (serviceCodeList.isEmpty()) { 67 | new SimpleAlert(this).show("このシステム領域からサービスコードが検出できませんでした", true); 68 | return; 69 | } 70 | for (ServiceCode sc : serviceCodeList) { 71 | MapgroupMap = new HashMap(); 72 | List> children = new ArrayList>(); // 対応する子要素を作り、追加 73 | String groupLabel = sc.toString(); 74 | if (!sc.encryptNeeded()) { 75 | for (int addr=0; ;addr++) { 76 | Map m = createBlockDataObject(sc, addr); 77 | if (m == null) { 78 | break; 79 | } 80 | children.add(m); 81 | } 82 | groupLabel = String.format("%s(%d)", groupLabel, children.size()); 83 | } 84 | groupMap.put("service_code", groupLabel); 85 | groupData.add(groupMap); // 親リストに追加 86 | childData.add(children); // 子リストに追加 87 | } 88 | ExpandableListAdapter adapter = new SimpleExpandableListAdapter( 89 | getApplicationContext(), 90 | groupData, 91 | android.R.layout.simple_expandable_list_item_1, 92 | new String[]{"service_code"}, 93 | new int[]{android.R.id.text1}, 94 | childData, 95 | android.R.layout.simple_expandable_list_item_2, 96 | new String[] {"addr", "data"}, 97 | new int[]{android.R.id.text1, android.R.id.text2} 98 | ); 99 | listView.setAdapter(adapter); 100 | listView.setOnChildClickListener(this); 101 | } catch (NfcException e) { 102 | new SimpleAlert(this).show("読込みに失敗しました", true); 103 | } 104 | } 105 | 106 | private Map createBlockDataObject(ServiceCode sc, int addr) throws NfcException { 107 | byte[] blockdata = felica.readWithoutEncryption(sc, addr); // read FeliCa Block Data 108 | if (blockdata == null) { 109 | return null; 110 | } 111 | // データを作成・追加 112 | Map curChildMap = new HashMap(); 113 | curChildMap.put("addr", String.format("Block %02d", addr)); 114 | curChildMap.put("data", String.format("%s", Util.getHexString(blockdata))); 115 | return curChildMap; 116 | } 117 | 118 | private int editGroupPosition; // 親(ServiceCode) の位置 119 | private int editChildPosition; // 親の中の子供の位置(Block No) 120 | /** 121 | * 書込み可能なブロックがTapされた場合、編集用 Activityを呼びます。 122 | */ 123 | @Override 124 | public boolean onChildClick(ExpandableListView parent, View v, 125 | int groupPosition, int childPosition, long id) { 126 | ServiceCode serviceCode = serviceCodeList.get(groupPosition); 127 | if (serviceCode.isWritable() && !serviceCode.encryptNeeded()) { 128 | Intent intent = new Intent(ServiceList.this, EditBlock.class); 129 | felica.putTagService(intent); 130 | intent.putExtra(SystemCode.class.getCanonicalName(), systemCode.getBytes()); 131 | intent.putExtra(ServiceCode.class.getCanonicalName(), serviceCode.getBytes()); 132 | intent.putExtra(EditBlock.BLOCK_INDEX, childPosition); 133 | this.editGroupPosition = groupPosition; 134 | this.editChildPosition = childPosition; 135 | startActivityForResult(intent, 0); 136 | } else { 137 | (new SimpleAlert(this)).show("このブロックはReadOnlyなので書き込めません", false); 138 | } 139 | return false; 140 | } 141 | 142 | /** 143 | * startActivityForResult で呼び出した Activityが終了したらここに戻ります。 144 | * 再度カードの内容を読み取り、表示内容を更新します。 145 | */ 146 | @Override 147 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 148 | super.onActivityResult(requestCode, resultCode, data); 149 | List> children = childData.get(editGroupPosition); 150 | try { 151 | Map b = createBlockDataObject(serviceCodeList.get(editGroupPosition), editChildPosition); 152 | children.set(editChildPosition, b); 153 | ((ExpandableListView)findViewById(R.id.service_list)).invalidateViews(); 154 | } catch (NfcException e) { 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/jp/co/yumemi/rd/misc/Util.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * Unless required by applicable law or agreed to in writing, software 7 | * distributed under the License is distributed on an "AS IS" BASIS, 8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | * See the License for the specific language governing permissions and 10 | * limitations under the License. 11 | */ 12 | 13 | /* 14 | * Changes 15 | * * 2010/2/5: k_morishita 16 | * ** net.kazzz.felica.lib.Util から複製して修正。 17 | */ 18 | 19 | package jp.co.yumemi.rd.misc; 20 | 21 | import java.lang.reflect.Field; 22 | import java.lang.reflect.InvocationTargetException; 23 | import java.lang.reflect.Method; 24 | import java.util.Arrays; 25 | 26 | import android.content.Intent; 27 | import android.nfc.NdefMessage; 28 | import android.nfc.NfcAdapter; 29 | import android.os.Bundle; 30 | import android.os.Parcelable; 31 | import android.util.Log; 32 | 33 | public class Util { 34 | public static String getHexString(byte[] byteArray, int... split) { 35 | StringBuilder builder = new StringBuilder(); 36 | byte[] target = null; 37 | if (split.length <= 1) { 38 | target = byteArray; 39 | } else if (split.length < 2) { 40 | target = Arrays.copyOfRange(byteArray, 0, 0 + split[0]); 41 | } else { 42 | target = Arrays.copyOfRange(byteArray, split[0], split[0] 43 | + split[1]); 44 | } 45 | int index = 0; 46 | for (byte b : target) { 47 | if (index > 0 && index % 4 == 0) { 48 | builder.append(" "); 49 | } 50 | builder.append(String.format("%02X", b).toUpperCase()); 51 | index++; 52 | } 53 | return builder.toString(); 54 | } 55 | 56 | public static String getHexString(byte data) { 57 | return String.format("%02X", data); 58 | } 59 | 60 | public static String getHexString(Object[] objList) { 61 | if (objList == null) { 62 | return null; 63 | } 64 | StringBuffer sb = new StringBuffer(); 65 | for (Object obj : objList) { 66 | sb.append(obj.toString()+","); 67 | } 68 | return sb.toString(); 69 | } 70 | 71 | /** 72 | * intent が ACTION_TAG_DISCOVERED なら適当に文字列化して返す。 73 | * 74 | * @param intent 75 | * @return 文字列化したIntent内の情報 76 | */ 77 | public static String tagIntent2String(Intent intent) { 78 | String action = intent.getAction(); 79 | StringBuffer sb = new StringBuffer(); 80 | if (NfcAdapter.ACTION_TAG_DISCOVERED.equalsIgnoreCase(action)) { 81 | Parcelable[] rawMsgs = intent 82 | .getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES); 83 | NdefMessage[] msgs; 84 | if (rawMsgs != null) { 85 | msgs = new NdefMessage[rawMsgs.length]; 86 | for (int i = 0; i < rawMsgs.length; i++) { 87 | msgs[i] = (NdefMessage) rawMsgs[i]; 88 | } 89 | sb.append("This is NDEF! ok!"); // TODO: implemet this 90 | } else { 91 | // Unknown tag type: Dump 92 | Bundle map = intent.getExtras(); 93 | for (String key : map.keySet()) { 94 | sb.append("KEY: " + key + "\n"); 95 | Object obj = map.get(key); 96 | String className = obj.getClass().getCanonicalName(); 97 | if (className.equals("byte[]")) { 98 | byte id[] = map.getByteArray(key); 99 | sb.append(Util.getHexString(id) + "\n\n"); 100 | } else { 101 | Util.dumpObject(sb, obj, className); 102 | } 103 | } 104 | } 105 | } else { 106 | Log.d("TagIntent2String", "Unknown intent" + intent); 107 | } 108 | return sb.toString(); 109 | } 110 | 111 | /** 112 | * オブジェクトのDumpを StringBuffer に出力します。 113 | * @param sb 出力する StringBuffer 114 | * @param obj Dumpする対象のObject 115 | */ 116 | public static void dumpObject(StringBuffer sb, Object obj) { 117 | dumpObject(sb, obj, obj.getClass().getCanonicalName()); 118 | } 119 | 120 | /** 121 | * @see #dumpObject(StringBuffer, Object) 122 | */ 123 | public static void dumpObject(StringBuffer sb, Object obj, String className) { 124 | try { 125 | Class tag = Class.forName(className); 126 | // dump fields 127 | Field fields[] = tag.getDeclaredFields(); 128 | for (Field f : fields) { 129 | sb.append("Field: " + f.getName() + "\n"); 130 | String cname = f.getType().getCanonicalName(); 131 | sb.append("Type: " + cname + "\n"); 132 | sb.append("Value: "); 133 | try { 134 | if (cname.equals("java.lang.String")) { 135 | sb.append(f.get(obj).toString()); 136 | } else if (cname.equals("byte[]")) { 137 | sb.append(Util.getHexString((byte[]) f.get(obj))); 138 | } else if (cname.equals("java.lang.String[]")) { 139 | sb.append("\n"); 140 | for (String v : (String[]) f.get(obj)) { 141 | sb.append(v + "\n"); 142 | } 143 | } else if (cname.equals("int")) { 144 | sb.append(f.getInt(obj)); 145 | } 146 | } catch (IllegalAccessException e) { 147 | sb.append("Exception:" + e.getMessage()); 148 | } 149 | sb.append("\n------------\n"); 150 | } 151 | // dump getter 152 | for (Method m : tag.getDeclaredMethods()) { 153 | sb.append("Method: " + m.getName() + "\n"); 154 | String retType = m.getReturnType().getCanonicalName(); 155 | sb.append("ReturnType: " + retType + "\n"); 156 | int numP = m.getParameterTypes().length; 157 | sb.append("NumParams: " + numP + "\n"); 158 | sb.append("Value: "); 159 | try { 160 | if (numP == 0) { 161 | if (retType.equals("int") 162 | || retType.equals("java.lang.String")) { 163 | sb.append(m.invoke(obj)); 164 | } else if (retType.equals("byte[]")) { 165 | sb.append(Util.getHexString((byte[]) m.invoke(obj))); 166 | } else if (retType.equals("java.lang.String[]")) { 167 | for (String v : (String[]) m.invoke(obj)) { 168 | sb.append(v + "\n"); 169 | } 170 | } 171 | } 172 | } catch (IllegalAccessException e) { 173 | sb.append(e.getMessage()); 174 | } catch (InvocationTargetException e) { 175 | sb.append(e.getMessage()); 176 | } 177 | sb.append("\n------------\n"); 178 | } 179 | } catch (ClassNotFoundException cnfe) { 180 | sb.append("ClassNotFoundException: " + className); 181 | } 182 | } 183 | 184 | /** 185 | * byte配列を2進数文字列で戻します 186 | * 187 | * @param byteArray byte配列をセット 188 | * @return 文字列が戻ります 189 | */ 190 | public static String getBinString(byte[] byteArray, int... split) { 191 | StringBuilder builder = new StringBuilder(); 192 | byte[] target = null; 193 | if ( split.length <= 1 ) { 194 | target = byteArray; 195 | } else if ( split.length < 2 ) { 196 | target = Arrays.copyOfRange(byteArray, 0, 0 + split[0]); 197 | } else { 198 | target = Arrays.copyOfRange(byteArray, split[0], split[0] + split[1]); 199 | } 200 | 201 | for (byte b : target) { 202 | builder.append(String.format("%8s" 203 | , Integer.toBinaryString(b & 0xFF)).replaceAll(" ", "0")); 204 | } 205 | return builder.toString(); 206 | } 207 | 208 | } 209 | -------------------------------------------------------------------------------- /src/jp/co/yumemi/nfc/FelicaTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); 3 | * you may not use this file except in compliance with the License. 4 | * You may obtain a copy of the License at 5 | * http://www.apache.org/licenses/LICENSE-2.0 6 | * Unless required by applicable law or agreed to in writing, software 7 | * distributed under the License is distributed on an "AS IS" BASIS, 8 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | * See the License for the specific language governing permissions and 10 | * limitations under the License. 11 | */ 12 | 13 | /* 14 | * Changes 15 | * * 2011/2/5: k_morishita 16 | * ** net.kazzz.felica.lib.FeliCaLib.java を複製して修正。 17 | */ 18 | 19 | package jp.co.yumemi.nfc; 20 | 21 | import java.io.IOException; 22 | import java.nio.ByteBuffer; 23 | import java.util.ArrayList; 24 | import java.util.Arrays; 25 | import java.util.List; 26 | 27 | import android.nfc.Tag; 28 | import android.nfc.tech.NfcF; 29 | import android.util.Log; 30 | 31 | import jp.co.yumemi.rd.misc.Util; 32 | 33 | public class FelicaTag extends NfcTag { 34 | private static String TAG = "FelicaTag"; 35 | 36 | // polling 37 | public static final byte COMMAND_POLLING = 0x00; 38 | public static final byte RESPONSE_POLLING = 0x01; 39 | 40 | // request service 41 | public static final byte COMMAND_REQUEST_SERVICE = 0x02; 42 | public static final byte RESPONSE_REQUEST_SERVICE = 0x03; 43 | 44 | // request RESPONSE 45 | public static final byte COMMAND_REQUEST_RESPONSE = 0x04; 46 | public static final byte RESPONSE_REQUEST_RESPONSE = 0x05; 47 | 48 | // read without encryption 49 | public static final byte COMMAND_READ_WO_ENCRYPTION = 0x06; 50 | public static final byte RESPONSE_READ_WO_ENCRYPTION = 0x07; 51 | 52 | // write without encryption 53 | public static final byte COMMAND_WRITE_WO_ENCRYPTION = 0x08; 54 | public static final byte RESPONSE_WRITE_WO_ENCRYPTION = 0x09; 55 | 56 | // search service code 57 | public static final byte COMMAND_SEARCH_SERVICECODE = 0x0a; 58 | public static final byte RESPONSE_SEARCH_SERVICECODE = 0x0b; 59 | 60 | // request system code 61 | public static final byte COMMAND_REQUEST_SYSTEMCODE = 0x0c; 62 | public static final byte RESPONSE_REQUEST_SYSTEMCODE = 0x0d; 63 | 64 | // authentication 1 65 | public static final byte COMMAND_AUTHENTICATION1 = 0x10; 66 | public static final byte RESPONSE_AUTHENTICATION1 = 0x11; 67 | 68 | // authentication 2 69 | public static final byte COMMAND_AUTHENTICATION2 = 0x12; 70 | public static final byte RESPONSE_AUTHENTICATION2 = 0x13; 71 | 72 | // read 73 | public static final byte COMMAND_READ = 0x14; 74 | public static final byte RESPONSE_READ = 0x15; 75 | 76 | // write 77 | public static final byte COMMAND_WRITE = 0x16; 78 | public static final byte RESPONSE_WRITE = 0x17; 79 | 80 | protected IDm idm; 81 | protected final NfcF felicaTag; 82 | 83 | public IDm getIdm() { 84 | return idm; 85 | } 86 | 87 | public FelicaTag(Tag tag) { 88 | super(tag); 89 | felicaTag = NfcF.get(tag); 90 | this.idm = new IDm(getId()); 91 | } 92 | 93 | protected CommandResponse execute(CommandPacket commandPacket) 94 | throws NfcException { 95 | byte[] result; 96 | 97 | if (this.felicaTag == null) { 98 | throw new NfcException("felicaTag is null!"); 99 | } 100 | 101 | try { 102 | if (!felicaTag.isConnected()) { 103 | felicaTag.connect(); 104 | } 105 | result = felicaTag.transceive(commandPacket.getBytes()); 106 | } catch (IOException e) { 107 | throw new NfcException(e); 108 | } 109 | return new CommandResponse(result); 110 | } 111 | 112 | /** 113 | * Pollingを行います。正常に終了した場合、取得したIDmを自身オブジェクト内に保存します。 114 | * 115 | * @param systemCode 116 | * Pollingを行う対象の systemCode 117 | * @return そのシステム領域の IDm を返します。 118 | * @throws NfcException 119 | */ 120 | public IDm polling(int systemCode) throws NfcException { 121 | CommandPacket polling = new CommandPacket(COMMAND_POLLING, new byte[] { 122 | (byte) (systemCode >> 8) // システムコード 123 | , (byte) (systemCode & 0xff), (byte) 0x01 //  システムコードリクエスト 124 | , (byte) 0x00 }); // タイムスロット}; 125 | return doPolling(polling); 126 | } 127 | 128 | /** 129 | * @see #polling(int) 130 | */ 131 | public IDm polling(SystemCode systemCode) throws NfcException { 132 | byte bytes[] = systemCode.getBytes(); 133 | CommandPacket polling = new CommandPacket(COMMAND_POLLING, new byte[] { 134 | (byte) bytes[0] // システムコード 135 | , (byte) bytes[1], (byte) 0x01 //  システムコードリクエスト 136 | , (byte) 0x00 }); // タイムスロット}; 137 | return doPolling(polling); 138 | } 139 | 140 | private IDm doPolling(CommandPacket polling) throws NfcException { 141 | CommandResponse r = execute(polling); 142 | PollingResponse pr = new jp.co.yumemi.nfc.PollingResponse(r); 143 | this.idm = pr.getIDm(); 144 | return idm; 145 | } 146 | 147 | /** 148 | * @see #getSystemCodeList(IDm) 149 | */ 150 | public SystemCode[] getSystemCodeList() throws NfcException { 151 | return getSystemCodeList(this.getIdm()); 152 | } 153 | 154 | /** 155 | * SystemCodeの一覧を取得します。 156 | * 157 | * @return 検出された SystemCodeの一覧を返します。 158 | * @throws NfcException 159 | */ 160 | public SystemCode[] getSystemCodeList(IDm idm) throws NfcException { 161 | // request systemCode 162 | CommandPacket reqSystemCode = new CommandPacket( 163 | COMMAND_REQUEST_SYSTEMCODE, idm); 164 | CommandResponse r = execute(reqSystemCode); 165 | byte[] retBytes = r.getBytes(); 166 | int num = (int) retBytes[10]; 167 | Log.d(TAG, "Num SystemCode: " + num); 168 | SystemCode retCodeList[] = new SystemCode[num]; 169 | for (int i = 0; i < num; i++) { 170 | retCodeList[i] = new SystemCode(Arrays.copyOfRange(retBytes, 171 | 11 + i * 2, 13 + i * 2)); 172 | } 173 | return retCodeList; 174 | } 175 | 176 | /** 177 | * 前回 Polling したシステム領域のサービスの一覧を取得します。 178 | * 必ずしもそのシステム領域をPollingしなくても取得できるのかもしれません。 179 | * 180 | * このServiceCode取得コマンドの仕様がよくわからなかったので、手探りの結果良さそうな方法を実装してあります。 181 | * 戻ってきたデータが2バイト長なら ServiceCode と判定していますが、正しい判定法なのかは定かではないです。 182 | * 183 | * @return 検出された ServiceCode の List 184 | * @throws NfcException 185 | */ 186 | public List getServiceCodeList() throws NfcException { 187 | int index = 1; // 0番目は root area らしいので、1番目から始めます。 188 | List serviceCodeList = new ArrayList(); 189 | while (true) { 190 | byte[] bytes = doSearchServiceCode(idm, index); // 1件1件 通信して聞き出します。 191 | if (bytes.length != 2 && bytes.length != 4) 192 | break; // 2 or 4 バイトじゃない場合は、とりあえず終了しておきます。正しい判定ではないかもしれません。 193 | if (bytes.length == 2) { // 2バイトは ServiceCode として扱っています。 194 | if (bytes[0] == (byte) 0xff && bytes[1] == (byte) 0xff) 195 | break; // FFFF が終了コードのようです 196 | serviceCodeList.add(new ServiceCode(bytes)); 197 | } 198 | index++; 199 | } 200 | return serviceCodeList; 201 | } 202 | 203 | /** 204 | * COMMAND_SEARCH_SERVICECODE を実行します。 参考: 205 | * http://wiki.osdev.info/index.php?PaSoRi%2FRC-S320#content_1_25 206 | * 207 | * @param idm 208 | * 問い合わせるシステム領域のIDm 209 | * @param index 210 | * ?番目か 211 | * @return Response部分 212 | * @throws NfcException 213 | */ 214 | public byte[] doSearchServiceCode(IDm idm, int index) throws NfcException { 215 | CommandPacket reqServiceCode = new CommandPacket( 216 | COMMAND_SEARCH_SERVICECODE, idm, new byte[] { 217 | (byte) (index & 0xff), (byte) (index >> 8) }); 218 | CommandResponse r = execute(reqServiceCode); 219 | byte[] bytes = r.getBytes(); 220 | if (bytes[1] != (byte) 0x0b) { // 正常応答かどうか 221 | throw new NfcException("ResponseCode is not 0x0b"); 222 | } 223 | return Arrays.copyOfRange(bytes, 10, bytes.length); 224 | } 225 | 226 | /** 227 | * @see #readWithoutEncryption(ServiceCode, byte) 228 | */ 229 | public byte[] readWithoutEncryption(int serviceCode, byte addr) 230 | throws NfcException { 231 | return readWithoutEncryption(new ServiceCode(serviceCode), addr); 232 | } 233 | 234 | /** 235 | * 認証不要なサービスコードのデータを読み取ります。 本来は、複数のブロックを同時に読めます。 JIS_X_6319_4 236 | * を見ると複数ブロックにアクセスする方法がわかります。 237 | * 238 | * @param serviceCode 239 | * データを読み取るサービスコード 240 | * @param addr 241 | * 何ブロック目を読むか。 0~N 242 | * @return 読み取ったブロックのByte列を返します。読み取りステータスが正常でなければnullを返します。 243 | * @throws NfcException 244 | */ 245 | public byte[] readWithoutEncryption(ServiceCode serviceCode, int addr) 246 | throws NfcException { 247 | byte[] bytes = serviceCode.getBytes(); 248 | CommandPacket readWoEncrypt = new CommandPacket( 249 | COMMAND_READ_WO_ENCRYPTION, idm, new byte[] { (byte) 0x01 // サービス数 250 | , (byte) bytes[0], (byte) bytes[1], (byte) 0x01 // 同時読み込みブロック数 251 | , (byte) 0x80, (byte) addr }); // ブロックリスト 252 | 253 | CommandResponse r = execute(readWoEncrypt); 254 | ReadResponse rr = new ReadResponse(r); 255 | if (rr.getStatusFlag1() == 0) { 256 | return rr.getBlockData(); 257 | } else { 258 | return null; // error 259 | } 260 | } 261 | 262 | /** 263 | * 本来は複数ブロックやサービスに同時に書き込めますが、この実装は1ブロックだけです。 JIS_X_6319_4 264 | * を見ると複数ブロックに書き込む方法がわかります。 265 | * 266 | * @param serviceCode 267 | * 書込むサービスコード 268 | * @param addr 269 | * 何ブロック目に書き込むか。 0~N 270 | * @param buff 271 | * 書きこむブロックデータ. 16バイトである必要があります。 272 | * @return 0: 正常終了, -1: 異常終了 273 | * @throws NfcException 274 | */ 275 | public int writeWithoutEncryption(ServiceCode serviceCode, int addr, 276 | byte[] buff) throws NfcException { 277 | if (buff == null || buff.length != 16) { 278 | return -1; 279 | } 280 | 281 | byte[] bytes = serviceCode.getBytes(); 282 | ByteBuffer b = ByteBuffer.allocate(6 + buff.length); 283 | b.put(new byte[] { (byte) 0x01 // Number of Service 284 | , (byte) bytes[0] // サービスコード (little endian) 285 | , (byte) bytes[1], (byte) 1 // 同時書き込みブロック数 286 | , (byte) 0x80, (byte) addr // ブロックリスト 287 | }); 288 | b.put(buff); // 書き出すデータ 289 | 290 | CommandPacket writeWoEncrypt = new CommandPacket( 291 | COMMAND_WRITE_WO_ENCRYPTION, idm, b.array()); 292 | CommandResponse r = execute(writeWoEncrypt); 293 | byte[] retBytes = r.getBytes(); 294 | if (retBytes != null && retBytes.length > 10 295 | && retBytes[10] == (byte) 0) { 296 | return 0; // normal 297 | } else { 298 | return -1; // error 299 | } 300 | } 301 | 302 | /** 303 | * @see #writeWithoutEncryption(ServiceCode, byte, byte[]) 304 | */ 305 | public int writeWithoutEncryption(int serviceCode, byte addr, byte[] buff) 306 | throws NfcException { 307 | return writeWithoutEncryption(new ServiceCode(serviceCode), addr, buff); 308 | } 309 | 310 | // //////////////////////////////////////////////////////////////////// 311 | // 以下、データ構造的Class 312 | // //////////////////////////////////////////////////////////////////// 313 | /** 314 | * FeliCa コマンドパケットクラスを提供します 315 | * 316 | * @author Kazzz 317 | * @date 2011/01/20 318 | * @since Android API Level 9 319 | */ 320 | public static class CommandPacket { 321 | protected final byte length; // 全体のデータ長 322 | protected final byte commandCode;// コマンドコード 323 | protected final IDm idm; // FeliCa IDm 324 | protected final byte[] data; // コマンドデータ 325 | 326 | /** 327 | * コンストラクタ 328 | * 329 | * @param response 330 | * 他のレスポンスをセット 331 | */ 332 | public CommandPacket(CommandPacket command) { 333 | this(command.getBytes()); 334 | } 335 | 336 | /** 337 | * コンストラクタ 338 | * 339 | * @param data 340 | * コマンドパケット全体を含むバイト列をセット 341 | * @throws FeliCaException 342 | */ 343 | public CommandPacket(final byte[] data) { 344 | this(data[0], Arrays.copyOfRange(data, 1, data.length)); 345 | } 346 | 347 | /** 348 | * コンストラクタ 349 | * 350 | * @param commandCode 351 | * コマンドコードをセット 352 | * @param data 353 | * コマンドデータをセット (IDmを含みます) 354 | * @throws FeliCaException 355 | */ 356 | public CommandPacket(byte commandCode, final byte... data) { 357 | this.commandCode = commandCode; 358 | if (data.length >= 8) { 359 | this.idm = new IDm(Arrays.copyOfRange(data, 0, 8)); 360 | this.data = Arrays.copyOfRange(data, 8, data.length); 361 | } else { 362 | this.idm = null; 363 | this.data = Arrays.copyOfRange(data, 0, data.length); 364 | } 365 | this.length = (byte) (data.length + 2); 366 | } 367 | 368 | /** 369 | * コンストラクタ 370 | * 371 | * @param commandCode 372 | * コマンドコードをセット 373 | * @param idm 374 | * システム製造ID(IDm)をセット 375 | * @param data 376 | * コマンドデータをセット 377 | * @throws FeliCaException 378 | */ 379 | public CommandPacket(byte commandCode, IDm idm, final byte... data) { 380 | this.commandCode = commandCode; 381 | this.idm = idm; 382 | this.data = data; 383 | this.length = (byte) (idm.getBytes().length + data.length + 2); 384 | } 385 | 386 | /** 387 | * コンストラクタ 388 | * 389 | * @param commandCode 390 | * コマンドコードをセット 391 | * @param idm 392 | * システム製造ID(IDm)がセットされたバイト配列をセット 393 | * @param data 394 | * コマンドデータをセット 395 | * @throws FeliCaException 396 | */ 397 | public CommandPacket(byte commandCode, byte[] idm, final byte... data) { 398 | this.commandCode = commandCode; 399 | this.idm = new IDm(idm); 400 | this.data = data; 401 | this.length = (byte) (idm.length + data.length + 2); 402 | } 403 | 404 | /* 405 | * (non-Javadoc) 406 | * 407 | * @see net.felica.IFeliCaCommand#getIDm() 408 | */ 409 | public IDm getIDm() { 410 | return this.idm; 411 | } 412 | 413 | /** 414 | * バイト列表現を戻します 415 | * 416 | * @return byte[] このデータのバイト列表現を戻します 417 | */ 418 | public byte[] getBytes() { 419 | ByteBuffer buff = ByteBuffer.allocate(this.length); 420 | if (this.idm != null) { 421 | buff.put(this.length).put(this.commandCode).put( 422 | this.idm.getBytes()).put(this.data); 423 | } else { 424 | buff.put(this.length).put(this.commandCode).put(this.data); 425 | } 426 | return buff.array(); 427 | } 428 | 429 | /* 430 | * (non-Javadoc) 431 | * 432 | * @see java.lang.Object#toString() 433 | */ 434 | @Override 435 | public String toString() { 436 | StringBuilder sb = new StringBuilder(); 437 | sb.append("FeliCa コマンドパケット \n"); 438 | sb.append(" コマンド名:" + Util.getHexString(this.commandCode) + "\n"); 439 | sb.append(" データ長: " + Util.getHexString(this.length) + "\n"); 440 | sb.append(" コマンドコード : " + Util.getHexString(this.commandCode) 441 | + "\n"); 442 | if (this.idm != null) 443 | sb.append(" " + this.idm.toString() + "\n"); 444 | sb.append(" データ: " + Util.getHexString(this.data) + "\n"); 445 | return sb.toString(); 446 | } 447 | 448 | } 449 | 450 | /** 451 | * FeliCa コマンドレスポンスクラスを提供します 452 | * 453 | * @author Kazz 454 | * @since Android API Level 9 455 | */ 456 | public static class CommandResponse { 457 | protected final byte[] rawData; 458 | protected final byte length; // 全体のデータ長 (FeliCaには無い) 459 | protected final byte responseCode;// コマンドレスポンスコード) 460 | protected final IDm idm; // FeliCa IDm 461 | protected final byte[] data; // コマンドデータ 462 | 463 | /** 464 | * コンストラクタ 465 | * 466 | * @param response 467 | * 他のレスポンスをセット 468 | */ 469 | public CommandResponse(CommandResponse response) { 470 | this(response.getBytes()); 471 | } 472 | 473 | /** 474 | * コンストラクタ 475 | * 476 | * @param data 477 | * コマンド実行結果で戻ったバイト列をセット 478 | */ 479 | public CommandResponse(byte[] data) { 480 | this.rawData = data; 481 | this.length = data[0]; 482 | this.responseCode = data[1]; 483 | this.idm = new IDm(Arrays.copyOfRange(data, 2, 10)); 484 | this.data = Arrays.copyOfRange(data, 10, data.length); 485 | } 486 | 487 | /* 488 | * (non-Javadoc) 489 | */ 490 | public IDm getIDm() { 491 | return this.idm; 492 | } 493 | 494 | /** 495 | * バイト列表現を戻します 496 | * 497 | * @return byte[] このデータのバイト列表現を戻します 498 | */ 499 | public byte[] getBytes() { 500 | return this.rawData; 501 | } 502 | 503 | /* 504 | * (non-Javadoc) 505 | * 506 | * @see java.lang.Object#toString() 507 | */ 508 | @Override 509 | public String toString() { 510 | StringBuilder sb = new StringBuilder(); 511 | sb.append(" \n\n"); 512 | sb.append("FeliCa レスポンスパケット \n"); 513 | sb.append(" コマンド名:" + Util.getHexString(this.responseCode) + "\n"); 514 | sb.append(" データ長: " + Util.getHexString(this.length) + "\n"); 515 | sb.append(" レスポンスコード: " + Util.getHexString(this.responseCode) 516 | + "\n"); 517 | sb.append(" " + this.idm.toString() + "\n"); 518 | sb.append(" データ: " + Util.getHexString(this.data) + "\n"); 519 | return sb.toString(); 520 | } 521 | } 522 | 523 | /** 524 | * 525 | * FeliCa IDmクラスを提供します 526 | * 527 | * @author Kazzz 528 | * @date 2011/01/20 529 | * @since Android API Level 9 530 | */ 531 | public static class IDm { 532 | final byte[] manufactureCode; 533 | final byte[] cardIdentification; 534 | 535 | /** 536 | * コンストラクタ 537 | * 538 | * @param bytes 539 | * IDmの格納されているバイト列をセットします 540 | */ 541 | public IDm(byte[] bytes) { 542 | this.manufactureCode = new byte[] { bytes[0], bytes[1] }; 543 | this.cardIdentification = new byte[] { bytes[2], bytes[3], 544 | bytes[4], bytes[5], bytes[6], bytes[7] }; 545 | } 546 | 547 | /* 548 | * (non-Javadoc) 549 | * 550 | * @see net.felica.IFeliCaByteData#getBytes() 551 | */ 552 | public byte[] getBytes() { 553 | ByteBuffer buff = ByteBuffer.allocate(this.manufactureCode.length 554 | + this.cardIdentification.length); 555 | buff.put(this.manufactureCode).put(this.cardIdentification); 556 | return buff.array(); 557 | } 558 | 559 | /* 560 | * (non-Javadoc) 561 | * 562 | * @see java.lang.Object#toString() 563 | */ 564 | @Override 565 | public String toString() { 566 | StringBuilder sb = new StringBuilder(); 567 | sb.append("IDm (8byte) : " + Util.getHexString(this.getBytes()) 568 | + "\n"); 569 | sb.append(" 製造者コード: " + Util.getHexString(this.manufactureCode) 570 | + "\n"); 571 | sb.append(" カード識別番号:\n"); 572 | sb.append(" 製造器:" 573 | + Util.getHexString(this.cardIdentification, 0, 2) + "\n"); 574 | sb.append(" 日付:" 575 | + Util.getHexString(this.cardIdentification, 2, 2) + "\n"); 576 | sb.append(" シリアル:" 577 | + Util.getHexString(this.cardIdentification, 4, 2) + "\n"); 578 | return sb.toString(); 579 | } 580 | 581 | public String simpleToString() { 582 | return Util.getHexString(getBytes()); 583 | } 584 | 585 | } 586 | 587 | /** 588 | * 589 | * FeliCa PMmクラスを提供します 590 | * 591 | * @author Kazzz 592 | * @date 2011/01/20 593 | * @since Android API Level 9 594 | */ 595 | public static class PMm { 596 | final byte[] icCode; // ROM種別, IC種別 597 | final byte[] maximumResponseTime; // 最大応答時間 598 | 599 | /** 600 | * コンストラクタ 601 | * 602 | * @param bytes 603 | * バイト列をセット 604 | */ 605 | public PMm(byte[] bytes) { 606 | this.icCode = new byte[] { bytes[0], bytes[1] }; 607 | this.maximumResponseTime = new byte[] { bytes[2], bytes[3], 608 | bytes[4], bytes[5], bytes[6], bytes[7] }; 609 | } 610 | 611 | /* 612 | * (non-Javadoc) 613 | * 614 | * @see net.felica.IFeliCaByteData#getBytes() 615 | */ 616 | public byte[] getBytes() { 617 | ByteBuffer buff = ByteBuffer.allocate(this.icCode.length 618 | + this.maximumResponseTime.length); 619 | buff.put(this.icCode).put(this.maximumResponseTime); 620 | return buff.array(); 621 | } 622 | 623 | /* 624 | * (non-Javadoc) 625 | * 626 | * @see java.lang.Object#toString() 627 | */ 628 | @Override 629 | public String toString() { 630 | StringBuilder sb = new StringBuilder(); 631 | sb.append("PMm(製造パラメータ)\n"); 632 | sb 633 | .append(" ICコード(2byte): " + Util.getHexString(this.icCode) 634 | + "\n"); 635 | sb.append(" ROM種別: " + Util.getHexString(this.icCode, 0, 1) 636 | + "\n"); 637 | sb.append(" IC 種別: " + Util.getHexString(this.icCode, 1, 1) 638 | + "\n"); 639 | sb.append("\n"); 640 | sb.append(" 最大応答時間パラメタ(6byte)\n"); 641 | sb.append(" B3(request service):" 642 | + Util.getBinString(this.maximumResponseTime, 0, 1) + "\n"); 643 | sb.append(" B4(request response):" 644 | + Util.getBinString(this.maximumResponseTime, 1, 1) + "\n"); 645 | sb.append(" B5(authenticate):" 646 | + Util.getBinString(this.maximumResponseTime, 2, 1) + "\n"); 647 | sb.append(" B6(read):" 648 | + Util.getBinString(this.maximumResponseTime, 3, 1) + "\n"); 649 | sb.append(" B7(write):" 650 | + Util.getBinString(this.maximumResponseTime, 4, 1) + "\n"); 651 | sb.append(" B8():" 652 | + Util.getBinString(this.maximumResponseTime, 5, 1) + "\n"); 653 | return sb.toString(); 654 | } 655 | } 656 | 657 | /** 658 | * FeliCa SystemCodeクラスを提供します 659 | * 660 | * @author Kazzz 661 | * @date 2011/01/20 662 | * @since Android API Level 9 663 | */ 664 | public static class SystemCode { 665 | final byte[] systemCode; 666 | 667 | /** 668 | * コンストラクタ 669 | * 670 | * @param bytes 671 | * バイト列をセット 672 | */ 673 | public SystemCode(byte[] bytes) { 674 | this.systemCode = bytes; 675 | } 676 | 677 | /* 678 | * (non-Javadoc) 679 | */ 680 | public byte[] getBytes() { 681 | return this.systemCode; 682 | } 683 | 684 | /* 685 | * (non-Javadoc) 686 | * 687 | * @see java.lang.Object#toString() 688 | */ 689 | @Override 690 | public String toString() { 691 | StringBuilder sb = new StringBuilder(); 692 | sb.append("システムコード : " + Util.getHexString(this.systemCode)); 693 | return sb.toString(); 694 | } 695 | 696 | public String simpleToString() { 697 | return Util.getHexString(systemCode); 698 | } 699 | } 700 | 701 | /** 702 | * FeliCa ServiceCodeクラスを提供します 703 | * 704 | * @author Kazzz 705 | * @date 2011/01/20 706 | * @since Android API Level 9 707 | */ 708 | public static class ServiceCode { 709 | final byte[] serviceCode; 710 | final byte[] serviceCodeLE; // little endian 711 | 712 | /** 713 | * コンストラクタ 714 | * 715 | * @param bytes 716 | * バイト列をセット 717 | */ 718 | public ServiceCode(byte[] bytes) { 719 | this.serviceCode = bytes; 720 | if (bytes.length == 2) { 721 | this.serviceCodeLE = new byte[] { bytes[1], bytes[0] }; 722 | } else { 723 | this.serviceCodeLE = null; 724 | } 725 | } 726 | 727 | public ServiceCode(int serviceCode) { 728 | this(new byte[] { (byte) (serviceCode & 0xff), 729 | (byte) (serviceCode >> 8) }); 730 | } 731 | 732 | /* 733 | * サービスコードをバイト列として返します。 734 | * 735 | * @return サービスコードのバイト列表現 736 | */ 737 | public byte[] getBytes() { 738 | return this.serviceCode; 739 | } 740 | 741 | /** 742 | * このサービスコードは、認証が必要か? 743 | * 744 | * @return 必要ならTrue 745 | * @author morishita_2 746 | */ 747 | public boolean encryptNeeded() { 748 | boolean ret = false; 749 | if (serviceCodeLE != null) { 750 | ret = (serviceCodeLE[1] & 0x1) == 0; 751 | } 752 | return ret; 753 | } 754 | 755 | /** 756 | * このサービスコードは書込み可能か? 757 | * 758 | * @return 書込み可能ならTrue 759 | * @author morishita_2 760 | */ 761 | public boolean isWritable() { 762 | boolean ret = false; 763 | if (serviceCodeLE != null) { 764 | int accessInfo = serviceCodeLE[1] & 0x3F; // 下位6bitがアクセス情報 765 | ret = (accessInfo & 0x2) == 0 || accessInfo == 0x13 766 | || accessInfo == 0x12; 767 | } 768 | return ret; 769 | } 770 | 771 | /** 772 | * サービスコードのアクセス権の意味は、JIS_X_6319_4 を参照しました。 773 | * 774 | * @author morishita_2 775 | */ 776 | @Override 777 | public String toString() { 778 | StringBuilder sb = new StringBuilder(); 779 | sb.append(Util.getHexString(serviceCodeLE)); 780 | if (serviceCodeLE != null) { 781 | int accessInfo = serviceCodeLE[1] & 0x3F; // 下位6bitがアクセス情報 782 | switch (accessInfo) { 783 | case 0x09: 784 | sb.append(" 固定長RW"); 785 | break; // RW: ReadWrite 786 | case 0x0b: 787 | sb.append(" 固定長RO"); 788 | break; // RO: ReadOnly 789 | case 0x0d: 790 | sb.append(" 循環RW"); 791 | break; 792 | case 0x0f: 793 | sb.append(" 循環RO"); 794 | break; 795 | case 0x11: 796 | sb.append(" 加減算直接"); 797 | break; 798 | case 0x13: 799 | sb.append(" 加減算戻入"); 800 | break; 801 | case 0x15: 802 | sb.append(" 加減算減算"); 803 | break; 804 | case 0x17: 805 | sb.append(" 加減算RO"); 806 | break; 807 | // 808 | case 0x08: 809 | sb.append(" 固定長RW(Locked)"); 810 | break; // RW: ReadWrite 811 | case 0x0a: 812 | sb.append(" 固定長RO(Locked)"); 813 | break; // RO: ReadOnly 814 | case 0x0c: 815 | sb.append(" 循環RW(Locked)"); 816 | break; 817 | case 0x0e: 818 | sb.append(" 循環RO(Locked)"); 819 | break; 820 | case 0x10: 821 | sb.append(" 加減算直接(Locked)"); 822 | break; 823 | case 0x12: 824 | sb.append(" 加減算戻入(Locked)"); 825 | break; 826 | case 0x14: 827 | sb.append(" 加減算減算(Locked)"); 828 | break; 829 | case 0x16: 830 | sb.append(" 加減算RO(Locked)"); 831 | break; 832 | } 833 | 834 | } 835 | // sb.append("\n"); 836 | return sb.toString(); 837 | } 838 | } 839 | 840 | /** 841 | * カードの種別を返します。 842 | */ 843 | @Override 844 | public String getType() { 845 | return TYPE_FELICA; 846 | } 847 | } 848 | --------------------------------------------------------------------------------