2011年12月30日金曜日

Chain Of ResponsibilityパターンによるFizzBuzz実装

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
出落ち感満載ですが、実装してみました。
単純にGoFデザインパターンの一つであるChain Of Responsibilityパターンを使ってみたかっただけです。
今まで実際に使った事が無かったので・・・。

Chain Of Responsibilityパターンって?


ggrks。
Wikipedia:Chain of Responsibility パターン
簡単に言うと責任の鎖。「ちょっと責任者に代わって。君じゃ埒があかん。」とこういう事ですね。


実装


ChainOfResponsibilityクラスのインナークラスとして全て書いています。
Fizz, Buzz, FizzBuzz, Integersクラスは抽象クラスであるHandlerを継承しています。
ChainOfResponsibility#fizzBuzz()の中でsetNextしてお互いを連結しています。

Handler#fizzBuzz(int)で実行。inCharge(int)で自分がこの値を担当しているかどうかをbooleanで返します。担当であればpring(int)を実行して終了。担当じゃなかったら自分の次の人のHandler#fizzBuzz(int)を呼び出します。

例えば65という値が入れられた場合はFizzBuzz->Fizz->Buzzまで行ってprintされます。

なんとなくFizzBuzzクラスやIntegersクラスのinCharge(int)はFizzクラスとBuzzクラスを使って判定させてます。
連結の仕方によって計算量が変わる気がしますがなんだか面倒なので考えるのをやめました。
もっと短く出来ると思いますが面倒なので(ry

public class ChainOfResponsibility {
    static public abstract class Handler{
        private Handler next = null;
       
        public Handler setNext(Handler next){
            this.next = next;
            return next;
        }
        public final void fizzBuzz(int value){
            if(inCharge(value)){
                print(value);
            }else if(next != null){
                next.fizzBuzz(value);
            }else{
                throw new RuntimeException("Oops!");
            }
        }
        protected abstract boolean inCharge(int value);
        protected abstract void print(int value);
    }
    
    static class Integers extends Handler{
        Fizz fizz = new Fizz();
        Buzz buzz = new Buzz();
        @Override
        protected boolean inCharge(int value) {
            return !fizz.inCharge(value) && !buzz.inCharge(value);
        }
        @Override
        protected void print(int value) {
            System.out.println(value);
        }
    }
    static class Fizz extends Handler{
        @Override
        protected boolean inCharge(int value) {
            return value%3 == 0;
        }
        @Override
        protected void print(int value) {
            System.out.println("Fizz");
        }
    }
    static class Buzz extends Handler{
        @Override
        protected boolean inCharge(int value) {
            return value%5 == 0;
        }
        @Override
        protected void print(int value) {
            System.out.println("Buzz");
        }
    }
    
    static class FizzBuzz extends Handler{
        Fizz fizz = new Fizz();
        Buzz buzz = new Buzz();
        @Override
        protected boolean inCharge(int value) {
            return fizz.inCharge(value) && buzz.inCharge(value); 
        }
        @Override
        protected void print(int value) {
            System.out.println("FizzBuzz");
        }
    }
    
    public void fizzBuzz(){
        Handler integer = new Integers();
        Handler fizz = new Fizz();
        Handler buzz = new Buzz();
        Handler fizzBuzz = new FizzBuzz();
        
        fizzBuzz.setNext(fizz).setNext(buzz).setNext(integer);
        for(int i = 1; i <= 100; i++){
            fizzBuzz.fizzBuzz(i);
        }
    }
    
    public static void main(String[] args) {
        new ChainOfResponsibility().fizzBuzz();
    }
}

まとめ


このパターンはいろいろ使えそうだなぁ。
デザインパターンによるFizzBuzz実装をシリーズ化できるかなー。どうかな。

2011年12月26日月曜日

Androidアプリケーションのパーミッション改竄を検知するスニペット

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

はじめに



AppNetBlockerというINTERNETパーミッションを消しちゃうアプリが出ました。
そもそもアプリ改竄自体いいのかどうか。まぁ怪しいパーミッション消したいとかいうのも判るけども。
AppNetBlockerに限らず実際にはINTERNETパーミッションどころじゃなくこんなアプリ↓まで既にAndroid Marketでは販売されています。

https://market.android.com/details?id=com.gmail.exathink.appshield

一部でapk改竄は著作権の侵害に当たるといった声もありますねー。しらんけど。
僕はよくわからないんでとりあえずなんとも言えないですけど、

パーミッション改竄されたかどうかは検知しとくかー

という感じです。


実装


どっかのUtilクラスにでもコピペして下さい。

    private static List<String> missingPermissions(Context context, String[] expectations) {
        return missingPermissions(context, new ArrayList<String>(Arrays.asList(expectations)));
    }
    private static List<String> missingPermissions(Context context, List<String> expectations){
        if (context == null || expectations == null) {
            return null;
        }
        try {
            PackageManager pm = context.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(getPackageName(), PackageManager.GET_PERMISSIONS);
            String[] info = pi.requestedPermissions;
            if (info != null) {
                for (String i : info) {
                    expectations.remove(i);
                }
            }
            return expectations;
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

使い方


missingPermissionsにアプリケーションが本来持っているべきパーミッションのリストを渡すと、消えてる奴が返ってきます。
改竄されてなければ空リストが返るはずです。nullの時は多分何かエラーが発生していると思います。知らん。

import android.Manifest.permission;
//〜〜略〜〜
List<String> missing = missingPermissions(this, 
    new String[]{permission.INTERNET, permission.WRITE_EXTERNAL_STORAGE}
);
if(mission != null && !missing.isEmpty()){
    //なんか改竄されてる!
}

こういう風に使えるかもしれないですね



おわりに


Twitterを引き続き注視していきまーす。


2011年12月18日日曜日

え、まさか難読化の為にわざわざproguard.cfgをいじってるんですか?

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

photo by http://www.flickr.com/photos/jonnygoldstein/

Android Advent Calendar 2011 12/18(日)担当の@sys1yagiです。

いきなりですがproguard.cfgをいじるのってクソめんどくせーわけですよ。何が悲しくてあんな意味不明なオプションの文法を毎回ググりながらsitと言いながら書かないといけないのか。ていうか複数人でやってたらproguard.cfgをいじるタイミングがさー色々面倒でもう最後にぱっと追加するかーとか皆思っちゃってて最後にカオスになったりして\(^o^)/とかさ、もっと簡単な方法はねーのかよ!?例えばアノテーションでさー、クラスのとこに書いたらさー、難読化されないとかさ。

あるでー


はい、ProguardがそもそもAnnotation用のライブラリを提供しています。これを使えば難読化の対象外にしたいクラスに所定のアノテーションを書くだけで難読化から逃れられます。普通にProguardが提供している機能なので、全くトリッキーな事なしでいけます。ちょっと副作用がありますが


導入の準備


ProguardはオープンソースでSoruceForgeで公開されています。
アノテーションによる難読化の回避はこのSourceForgeで配布されているアーカイブに含まれているライブラリを利用します。ダウンロードページからproguard4.6.zipをダウンロードして下さい(多分4.6じゃなくてもいけると思いますが)。
http://sourceforge.net/projects/proguard/files/



導入


proguard4.6.zipを展開し、proguard4.6/examples/annotations/lib/に入っているannotations.jarannotations.proをAndroidプロジェクトにコピーして下さい。annotations.jarはビルドバスを通しておきます。今回はproject/libsに置きました。


するとアノテーションで@Keepとか書けるようになります。


次にプロジェクト直下あたりのproguard.cfgの先頭辺りに以下の二行を記述します
-dontshrink
-include libs/annotations.pro
annotations.proはproguard.cfgと拡張子は異なりますが中身は大体同じです難読化についてのオプションが記述されています。-dontshrinkは書かないと何故かアノテーションが消えるので書きます。すかさず-keepattributes **を思いついたあなたはやっぱり凄い!ですがこのオプション書いてもダメでした。四の五の言わずに書いて下さいませね。

これだけでおk


実行


あとはProguardにかけるだけ。そういやAntで直接Proguardやれば-dontshrinkはいらないかもしれません。ここではEclipseのAndroid Toolsで"Export Signed Application Package ..."とかやっています。
難読化実施後のクラス構成


見事@KeepをつけたDonotObfuscationクラスが難読化されていません。
メソッドはどうでしょう。javapしてみます。


@KeepNameのついたnotObfuscation()は難読化されていませんね。obfuscation()の方は難読化されている事がわかります。
Keep以外にも色々なアノテーションがあります。色々使えそうですね。


副作用


良い事ばかりじゃもちろんありません。ちょっと副作用がありますんで注意して下さい。


不要クラスが残る


-dontshrinkは利用していないクラスなどの削除を行なってバイナリサイズを小さくしてくれる処理を禁止します。つまりProguardにかけた対象となるクラスが難読化後全て残ります。物によってはapkサイズがでかくなってしまいます。ただ、ほぼ気にしなくてもいいレベルだと思います。精々数KBの違いでしょう。これなら他のリソースの容量削った方が効率的だと思います。


何を難読化したくないかが一覧できない


proguard.cfgにKeepオプションなどを書く場合は全部書かれているので一覧性がありますが、アノテーションの場合はソースに個別に書くのでどのクラスが難読化したくないかについて分かりにくくなります。まーKeepとかでソース検索すりゃいいしどーでもいい気もしますがー


そういえば


annotations.proの中身を見てみるとこんな感じです。

-keep @proguard.annotation.Keep class *

-keepclassmembers class * {
    @proguard.annotation.Keep *;
}

つまりふつーにProguardのKeepの条件に「指定のアノテーションがある場合難読化しねーぜ」、と設定してるだけです、つまり別にProguardの提供しているannotations.jarを使わなくても自分で適当にアノテーションを定義して、annotation.proみたいなのを書いてincludeするかProguard.cfgに書くだけでいけるわけです。つまりそういう事です。


結論


皆楽になるといいですねー。
メリークリスマスー
明日は@patorash@Nkznさんです。わーい。

2011年12月16日金曜日

Honeycomb以降ではMotionEventのACTION_DOWNでevent.get(1)すると死ぬ

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

まさかこんな実装をしているわけがない、と思います。僕くらいです。はい。おおonTouchメソッドよ死んでしまうとは情けない。

こうすると死ぬ


常識的に考えて以下のコードは気持ち悪いですよね。でもAndroid3.0未満、つまりGingerbread以前では問題なく動作します。しかしHoneycomb以降ではjava.lang.IllegalArgumentExceptionが発生してしまいます。エラーメッセージはpointerIndex out of rangeです。なんででしょうね?!

@Override
public boolean onTouchEvent(MotionEvent event) {
 switch(event.getAction() & MotionEvent.ACTION_MASK){
 case MotionEvent.ACTION_DOWN:
  int x = event.getX(1);
  break;
 }
}


ソースを読む


MotionEventのソースを見ると・・・。げぇ全然違うぜぇえええ。

Android2.3.3_r1
public final float getX(
 int return mDataSamples[mLastDataSampleIndex
  + pointerIndex * NUM_SAMPLE_DATA + SAMPLE_X] + mXOffset;
}

Android4.0.1
public final float getX(int pointerIndex) {
        return nativeGetAxisValue(mNativePtr, AXIS_X, pointerIndex, HISTORY_CURRENT);
    }


どう変わったか


上記の4.0.1のソースではもはやnativeメソッド。さすがに実装まで追うのはしんどいんで省略しますが、2.3.3_r1では配列アクセスをしている事がわかります。この配列はMotionEventのコンストラクタで初期化されます。配列サイズはコンストラクタに渡したpointerCount, sampleCountと定数NUM_SAMPLE_DATAを乗じた数となります。

private MotionEvent(int pointerCount, int sampleCount) {
 mPointerIdentifiers = new int[pointerCount];
 mDataSamples = new float[pointerCount * sampleCount * NUM_SAMPLE_DATA];
 mEventTimeNanoSamples = new long[sampleCount];
}

現タッチ数は内部でmNumPointersで管理しています。2.3.3_r1のgetX(int)では利用していません。つまりgetX(int)でアクセスするインデックスに影響を与えません。MotionEventはイベントを受け取るViewで使い回しているので(一応MotionEventのhasCode()で確認した)、コンストラクタで確保された配列をオーバーしない限りは死なない事になります。

操作 実タッチ数 Gingerbread以前 Honeycomb以降
event.getX(0) 2
event.getX(2) 2 ×
event.getX(100000) 2 × ×


結論


今までギリギリ動いていたけどHoneycomb、ICSで爆死、そんな事があるかもしれませんね。怖いわー。