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で行ってみることにしました。

2011年12月13日火曜日

ProGuardを実行したときに出たエラーの対処

ProGuardを使ったときに「Conversion to Dalvik format failed with error 1」というエラーが出た場合の話。
以下のページにWorkaroundがあるので、バッチファイルの修正を行えば行けた。

http://wada811.blog.fc2.com/blog-entry-62.html

念のため、内容をコピペしておく。以下引用。

タイトルの通り、「Conversion to Dalvik format failed with error 1」というエラーが出る場合の対処法です。
このエラーが出たらやることは基本的に一つ。Eclipseのクリーンです。
今回はそれでも改善されずにapkファイルが作れなかった場合について書いておきます。

もしかして:Android SDK Tools r12 にバージョンアップした

当てはまる人はこの記事の内容で対処できるはずです。
この問題は既に報告されているようです。
Android SDK tools revision 12 has problem with Proguard - Android - An Open Handset Alliance Project
Android SDK Tools r12 に含まれるProGuardのバージョンが古いためバグを内包しているというもの。
解決法は主に二つ

proguard.batを書き換える
ProGuardの最新版をダウンロードしてきて置き換える

proguard.batを書き換える

[Android SDK]\tools\proguard\bin

を開きます。
proguard.bat をエディターで開きます。
内容をコピーします。(おそらく書き換え禁止になっているので)
エディターを新しく開き、コピーしたものを貼り付けます。

call %java_exe% -jar "%PROGUARD_HOME%"\lib\proguard.jar %*

を

call %java_exe% -jar "%PROGUARD_HOME%"\lib\proguard.jar %1 %2 %3 %4 %5 %6 %7 %8 %9

に書き換えます。
proguard.bat というファイル名で保存して元の proguard.bat と置き換えます。
コレでapkファイルを作れるようになったと思います。
一部の人はこれでもできない場合があるらしいです。
ProGuardの最新版をダウンロードしてきて置き換える

ProGuard Java Optimizer and Obfuscator - Browse /proguard at SourceForge.net にアクセスします。
Android SDK Tools に含まれる ProGuard4.4 より新しいものを選ぶ(現在なら 4.6 が最新なので 4.6 が好ましい)
ダウンロードしたファイルを解凍します。
bin フォルダと lib フォルダで Android SDK Tools の proguard フォルダの bin フォルダと lib フォルダを置き換えます。
コレでapkファイルを作れるようになったと思います。

これでも直らない場合はお手上げです。あとは頑張ってください。

「resources.ap_ does not exist」が出る

EclipseでAndroidアプリケーションを開発しているときに、不意にハマった現象。

もしかして:Eclipseのオプションで、Android > Build > Build output > Verbose にしていないか?
今回ハマった現象はこれで、ADT14のaaptが-vオプション有りで死ぬらしい。
https://code.google.com/p/android/issues/detail?id=20395 参照。

注:これを書いている時点でADT16がリリースされていたので、もう直ってるかも。

そうでなければ以下を参照。

stackoverflow.comの記事
http://stackoverflow.com/questions/4437023/resources-ap-does-not-exist-when-compile-my-android-project

他のマシンで動作するシンプルなサンプルでも、自分の環境では冒頭のエラーで実行出来ない症状。
いくつかのWorkaroundが存在する。

1. Project > Clean を実行する(いくつかの派生がある。Clean,Delete gen,restart eclipseとか)
2. 指摘された場所に「resources.ap_」という空ファイルを作成してClean/Rebuildする
3. 不正なリソースファイル(adbの認識出来ないPNGファイルとか)を削除する。ただしこの場合は別のエラーが出ているはず。
4. aaptをアップデートした際に、順序のない書式指定文字列をビルドエラーとして扱うようになった。
ちょっとややこしい。
This was my problem as well. Basically anywhere in your strings.xml where you have any combination of %s or %d in the same string, replace them with %1$s or %2$d where the number between the % and $ is the order in which they are placed in the string. For exmaple: "Blah %d something %s" should now be "Blah %1$d something %2$s" – GuyNoir Dec 26 '10 at 20:12
Oh, you did not mention that you upgraded to SDK v8 when you encountered the problem. Yes, the SDK is more strict now when it comes to ordering format strings. :) – Zarah Jan 9 at 18:06
It can't be, we can find this problem in android sdk examples, SearchableDictionary has this problem ._. – rubdottocom Feb 22 at 16:48
5. "gen"を消してからProject > Build All を実行して、R.javaを再生成する。
6. API-Levelが間違っている。正しいLevelを選択すればよい。
7. string.xmlに問題がある。最低限必要な分を残し、一回全部消してみる。
8. Admob adsを使っている場合に
1) I was using Admob ads, and I didn't have an attrs.xml file in my res/values folder
2) I deleted a line that says "import andriod.R" from my main activity, and all my resources connected again and this ultimately made the error go away.
3) The last thing I had wrong is I had a "lib" folder instead of a "libs" folder that held my Admob jar file.
Lastly, I cleaned the project after these changes.
9. (Alt-Shift-Q then X)でなんかでてくる?他にもConsoleのproblemログをきちんと確認する。(異常なリソースファイルとかはこれでわかるかも)
結構9patchで問題が出ている場合が多いっぽい。作り方の問題か?
10. バックスラッシュをstrings.xmlに含んでいる
11. アップデート時に問題が出た場合、プロジェクトのバックアップから復元し直してみることで治るかもしれない。
12. binおよびgenを消して、リフレッシュ(F5)してみる
13. eclipseのWorkspaceの".metadata"が壊れている。Workspaceを作り直す。ただしEclipseの環境は1から作り直しになる。

2011年12月12日月曜日

ProGuardを使う

というわけで、必要になったのでProGuardを使ってみた。

基本的な情報はWebでいろいろ拾える。参考にしたのは

  • http://y-anz-m.blogspot.com/2010/10/androidlvl-securing-android-lvl.html
  • http://d.hatena.ne.jp/bs-android/20101207/1291702021
  • http://d.hatena.ne.jp/hyoromo/20101120/1290216449
このあたり。

で、いざ実践してみてハマったところとか気がついたところをまとめておく。

まず、上記参考URLの情報では、"default.properties"にパスを追加するということが書かれているのだが、これではProGuardが走らなかった。
正解は"project.properties"の方に書く。
実際に新しいプロジェクトを作成して見るとそうなっているので、そのまま持ってくるようにすれば良い。

proguard.cfg自体はEclipse上でコピー&ペーストすればOK。

んで、どっかで外部ライブラリは難読化されないと書いてた気がするんだけど、
自分で作った物はちゃんと難読化されている様子。
今回だとLVL(Licensing Verification Library)を併用しているんだけど、
そっちのクラスが難読されている様子が、mapping.txtにはき出されていた。

そして難読化してはいけないもの。
JNI関係は難読化してはいけない、というのは上記参考URLにも書いてある。
これは具体的には"native"接頭辞を付けたメソッドを宣言しているクラスをkeepに指定すれば良い。
逆にこれを難読化すると、JNIのメソッドが一切呼ばれなくなる。エラーは吐かないのでわかってないとハマるかも。
LVLは特に書かなくても動いてるなー。あとでなんか問題でてくるかも?
もしくはデフォルトで書いてある

-keep public class com.android.vending.licensing.ILicensingService

という指定で行けてるような気がする。