2012年8月21日火曜日

ProGuardによるライブラリとしてのJARの難読化(2)

というわけで昨日の続き。
実際にProGuard4.8を使ってライブラリJARを難読化してみます。


 これはProGuardの起動直後の画面。
左側に各設定項目が並んでいます。
右下の「Next」を押すと、各設定項目間を進んでいきます。正直いらん。




 「Input/Output」の設定画面。
ここではInputとして難読化対象のJARなどを指定し、
どこにOutputするかを設定します。
また、前提となるライブラリとかもココで指定します。

ココで重要なのは
  • 実際に使用しているAndroidのバージョンに応じたandroid.jarをライブラリとして指定する
ことです。デフォルトではjavaの共通ライブラリが指定されているのですが、それは消しましょう。
android.jarに含まれています。
android.jarはAndroidSDKの「platforms」の中にあるよ。
他にも自作JARで使用しているライブラリがあるならココで指定しておけば取り込まれます。

一応注意だけど、「Input」として指定するのと、「ライブラリ」として指定するのは意味が違います。
「Input」したクラスは全て処理されて「Output」に吐き出されます。
「ライブラリ」として指定したクラスは解析に利用されるだけで処理対象にはなりません。


とりあえずこれだけ設定すれば、処理を行う事は出来るのですが、
デフォルトのままだと全部Shrinkされて何も無いJARが出力されてしまうので、
いくつか設定が実際必要。


 というわけでShrinkingの設定画面です。
Shrinkingとは、不要なクラスを削除してライブラリを小さくしてくれる処理なのですが、
今回のようにライブラリ「だけ」でShrinkすると、実際に使ってるクラスひとつもねーじゃん!となり、
全部のクラスが排除されます。慈悲は無い。
(本来はアプリケーションのエントリポイントからクラスの参照ツリーが形成されて、残すクラスが決定される)

というわけで、「Keep」の設定が必要になるわけです。
この画面の上側の設定項目は標準状態のままで問題ないので、
下の方にある「Keep additional classes and class members」を設定します。

ここでは二つ指定しています。
消しについては気にしないで下さい。
それぞれ
「Class example.android.library.**」「Class example.android.util.io.**」と書いてあることにします。
書式については該当のエディットボックスにカーソル持って行くとバルーンヘルプが出るのでそれを読んで下さい。

要は、ここで指定したクラスはShrink対象から除外されて、キープされる訳です。
なので、ライブラリの公開クラスを指定しておけばうまいことしてくれるようになります。

クラス指定は単純な名前指定だけでは無く、見ての通りのワイルドカード指定と、
さらにクラスの属性によるフィルタリングなどにも対応しています。
たとえば、名前自体はワイルドカードで広く指定して、その中でも「public」のクラスだけをkeepする等。
まぁ細かいことはどうでも良い場合は名前だけで良いです。



次にObfuscationの設定です。
基本的にはShrinkと同様、難読化したくないクラスをここでKeep設定しておきます。
普通はShrinkと同じ設定になるんじゃないでしょうか。
加えて、ライブラリ内で定義したクラスを公開メソッドの引数に使ってたりする場合は、それもKeepしないと外から分からなくなるので設定します。
上記画面ではちょうどそんな感じのことをしています。

注意点をまとめておくと
  • 外部で使うクラスはShrink/Obfuscation共にKeep
  • Keepしてるクラスのメソッドの引数、返値の型のクラスもKeep (ShrinkはされないのでObfuscationだけ)
  • AIDLがらみのクラスもKeepしておく。
  • 外部で使うクラスはinner classとして宣言しない。難読化まわりでハマるので。listenerとかでもtop levelクラスとして独立で定義しておく。
最後の項目で大ハマリしたのがこの記事を書くきっかけな訳だが。
実際の所、inner classは例えば「example.android.library.hoge$huga」の様に、
親クラス名に「$」付けてインナークラス名を付ければProGuardとしては参照出来るんだけど、
JARをインポートしたアプリ側からの参照がうまく出来ない。
難読化対象からは確実に外れているにもかかわらず、だ。
この辺はソース参照とclassファイル参照の違いだと思うんだけど、面倒なので独立定義にした方が良いです。
どうせアプリに公開しなきゃいけないインターフェースなので、明示的にした方がいいでしょうし。

なお、調査のためには「Print mapping」を有効にすると、どう難読化されたかを吐き出してくれます。

あとは「Optimization」とかあるけど、これは標準で良いんじゃ無いかな。
マニュアルの方にはループが過剰最適化される場合とか載ってるので、問題が有る場合とかに外した方が良いことがあるかも?

 一通りできたら「Process」ページの右下に「Process!」ってボタンがあるので、押せば設定通りに処理してくれます。
このときにエラーがあったら教えてくれるので、もしもなんか出てきたらよく読んで対処。
ちょくちょくあるのはKeep設定したクラスで記述されてるクラスがKeepされてないよ?というNoteかな。
言われたクラスもKeepしないと、ただしくKeepされません。


あとは実際にアプリの方でインポートして動作確認できれば完了。

2012年8月20日月曜日

ProGuardによるライブラリとしてのJARの難読化(1)

ProGuardすごいですね!それほどでもない。
と思ってたけどよく見たらGUIがあるじゃん、ということでやる気回復したので、
ちょっと試してみることにした。

経緯としては
  • 自社製コンポーネントをライブラリとして社外提供するお仕事発生
  • Javaでライブラリ提供とか、ソースコード丸見えも同然じゃないか…
  • EclipseでProGuard使ってたんだから、アレで難読化出来ないかな? -> 無理でした。
  • StackOverflow見たけどやっぱり「JARの難読化は出来ねえよ!」って書いてある
  • アイエエエ…
で、一度はあきらめてた。
成せばなるんだからぁ…ということで、他の難読化ツール使えないかと調べ直し。

世の中にどんな難読化ツールがあるかというのは以下のページにまとめてありました。

  • http://d.hatena.ne.jp/second_sky/20051220/1135052529
  • http://web.archive.org/web/20100430134447/http://cafebabe.jp/item/7
上のは「難読化(obfuscation)」ツールだけをまとめたリンク。
下のはクラスファイルの暗号化とか最適化とかのツールもまとめてあります。感謝。

で、お金掛かる奴はややこしいので今回はパスということで、フリーで使えそうなのは
この二つかな。
ProGuardはおなじみのアレ。EclipseでAndroid開発やってたら最近ははじめから使える奴。
二つ目はよくわからないけどabsolute free!とか書いてあるのでフリーですね。
結局使わなかったのでこれは置いておきます。

で、ProGuardを良く見直すと、当然ながら単体でも利用可能な訳ですね。
というわけで落としてきて解凍すると「proguardgui.jar」などというファイルが。
これを
java -jar proguard.gui.jar
とやって起動してみたら見事にGUIが立ち上がりました。GUIヤッター!
コマンドラインでもまぁEclipseで使ってるような設定ファイル書けば使えるんだろうけど、書式調べるのがめんどくさいのよね…。
というわけでおとなしくProGuardを使ってライブラリJARを難読化してみることにしました。
これで冒頭に戻ってきました。

 具体的な使い方は次回に詳しく書くとしましょう。
-> ProGuardによるライブラリとしてのJARの難読化(2)

2012年7月11日水曜日

iPod LibraryからのAVAudioPlayerによる再生

かなりハマって調べたのでここにまとめておく。

まず、iPhone/iPodでの音楽ファイルは、アプリケーションのリソースとして持ち込むか、iPodのライブラリにある物が扱える。
が、iPodのライブラリ内の曲は基本的に「AVPlayer」か「MediaPlayer」でしか再生出来ない。
しかし、これらのコンポーネントは非常に自由度が低いので、AVAudioPlayerなどで再生したくなったわけだ。

まず、iPodライブラリからMPMediaItemとして取得した音楽ファイルを、AVAudioPlayerで開こうとしても容赦なくエラーでぬるぽとなる。(実際にはinitに対してnilを返してくる)
これを無理矢理開くには、一旦iPodライブラリからファイルをエクスポートし、ローカルのリソースとして扱わなければならない。
このあたりの経緯は以下のwebpageにて解説されている。感謝。

iPodライブラリからのファイル書き出し その1
http://objective-audio.jp/2010/08/ipod.html
iPodライブラリからのファイル書き出し その2
http://objective-audio.jp/2010/08/ipod-1.html
ついでに参考:http://unlimapps.com/?p=41

簡単にまとめておくと、"AVAssetExportSession"を使ってiPodLibraryからファイルをexportする事になる。
しかし、素直にpresetNameを"AVAssetExportPresetAppleM4A"にした場合、mp3がexportできない。
なので、AVAssetTrackを取り出し、そこからAudioStreamBasicDescriptionを取得して、その中のファイルタイプを利用する。
presetNameはAVAssetExportPresetPassthroughを使用する。


書き出すあたりまでは上記でサンプルソースまであるので難なく行ける・・・と思ったがそうはいかない。
二つ目の記事で書かれているように、このコードはiOS5.1以降ではうまく動かない。
(記事にはiOS4.2以降では動かないかも?とある)

というわけで探しまくった結果、以下の記事を発見。


get error code -11843 while exporting mp3 file in ipod library since iOS 5.1
http://stackoverflow.com/questions/9653241/get-error-code-11843-while-exporting-mp3-file-in-ipod-library-since-ios-5-1


まさに、5.0までは動いていたのに5.1ではダメになった。ナンデ?という話。
この人は自力でworkaroundを見つけ出してくれていた。ワザマエ!


つまり、AVAssetExportSessionに設定するoutputURLの拡張子として、mp3の場合は"mov"にしなければ失敗する、ということ。

おそらくAVAssetExportSessionの内部実装でエラーのチェックが強化されたのだろう。
outputFileTypeとして"com.apple.quicktime-movie"を設定している以上、拡張子はmovで無ければならないという拡張子原理主義者がAppleに入社したとかそんな感じではないだろうか。めんどくせぇ。爆発四散するべき。

というわけで、上記の情報を元にファイルを書き出し、拡張子をmovからmp3に変えたファイルをAVAudioPlayerに食わせたところ、無事に再生できました。
なおiOSは5.1.1です。

2012年2月7日火曜日

TBi11Mで解像度別drawableが適用されない

まぁこの機種に限ったことでは無いのかもしれないけど。
TBi11MはモトローラのAndroidタブレットの事です。

で、こいつはどうもmdpiな機種の様なので、hdpiな機種とはレイアウトを分けて対応することに。
ビットマップはViewに直接張る奴と、プログラムでSurfaceViewに直接描く奴があるので、
前者は drawable-hdpi に入れて自動スケーリングを期待。
後者については drawable-nodpi に放り込んでみました。

結果、前者は良いのだが後者がダメ。
追いかけてみると、リソースから取得したビットマップが見事にスケーリングされている。
(元は128x128が85x85に)

プログラム描画処理の方は128x128前提で描いているので当然ひどいことに。
まぁ、Density見て補正すりゃいいんですが、それはここでは問題ではない。

問題は、「何故drawable-nodpiに入れたビットマップがスケーリングされてるの」ということ。
どう調べてもこの動作には納得がいかない…。

機種固有の問題なのか、Android3系の問題なのか、実は元々そうなのか。

なお、hdpiとnodpiに分けないで、全てのビットマップをnodpiに放り込むと問題無くなる。
この場合はスケーリングされない訳で。ますますわけわからん。
解像度の混在が出来ないの?

結局の所はとりあえず全部nodpiで行ってみることにしました。