Android M (6.0)の新セキュリティモデルの注意点

TechBooster様の「Androidビルディングデザイン」に、弊社代表中西がAndroid Mの新しいセキュリティモデル(パーミッションモデル)の記事を寄稿しました。2015年8月25日現在、紙の書籍は完売しておりますが電子書籍版はまだ頒布されています。

電子版では、Android M Developer Preview 3、SDKバージョン23、Support Libraryバージョン23についても反映した内容に更新されていますが、その中では触れていない気をつけないといけない項目について、ここに記載します。

targetSdkVersionを23以上に上げたアプリは22以下に戻せない

Android Studioでデバッグ実行で試すとわかるのですが、targetSdkVersionを23にしたアプリをインストールし、それを22以下に変更して再度インストールしようとすると、以下の様な警告が表示されます。

Failure [INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE]

書いてあるとおり、パーミッションモデルのダウングレードを求めるようなアプリのインストールが許可されていません。この時、Android Studioは以下のようなダイアログも表示します。

スクリーンショット 2015-08-25 10.36.08

ここに書いてあるのは、今インストールされているアプリをアンインストールして、それからインストールするかと聞いてきています。つまり、インストール済みのアプリの保存しているデータや設定などを維持して、targetSdkVersionを22以下に落としたアプリのインストール(アップデート)を行うことはできません。

これ、おそらくはGoogle Play Storeでも対策がなされるのではないでしょうか。

一度targetSdkVersionを23以上に上げて新しいセキュリティモデルの対象となったアプリは、targetSdkVersionを上げたことによって発生したバグがあっても、22以下に戻したアプリをアップロードすることはできなくするのではないかと。

既存アプリを運用されている場合、targetSdkVersionを23に変更するのは慎重に行うようにしましょう。

targetSdkVersion 22以下のアプリを23にした場合、パーミッションが許可状態になる

これはユーザにとっての罠ですが、Android M端末にtargetSdkVersionが22以下のアプリをインストールし、それがバージョンアップでtargetSdkVersionが23になったとしても、パーミッションが許可状態になります(Context#checkSelfPermissionがtrueを返します)。

新規にtargetSdkVersionが23のアプリをインストールした場合、最初はContext#checkSelfPermissionはfalseとなりますので、そこからActivity#requestPermissionsによって権限を取得していくという流れになります。

しかし、前述の場合は、targetSdkVersionが22のアプリをインストールした時点で、権限利用を許可したという状態が端末に記録され、それはtargetSdkVersionを23以上に上げた場合にも維持されます。

この挙動は、iOSなどで新しいOSバージョンで権限チェックが強化された場合の挙動とは異なります。iOSでは、たとえばマイク利用にユーザによる権限確認が必要となった場合、アプリの新規インストールとアップデートのいずれであってもダイアログで確認するという挙動になります。

この挙動のまま正式版となるかはわかりませんが、ユーザは気をつけておくとよいでしょう。

Mac OSX Yosemite(10.10)でAndroid Studio起動に利用するJDKの変更

Android Studioは、利用するJDKを環境変数STUDIO_JDKに設定することで切り替えることができます。このことはAndroid Tools Project Site内のMac OSX JDK Selectionで説明されています。

ただし、上記の方法では、Android Studioをいちいちコマンドラインから立ち上げないといけないため面倒です。そのため、ランチャー向けにSTUDIO_JDKの環境変数を設定しておく方が便利です。

Marvericks(10.9)では、/etc/launchd.conf に環境変数を記述しておくと、ランチャー向けにその環境変数が設定されました。しかし、Yosemiteからは/etc/launchd.confの設定は無視されます。

Yosemite以降では、/Library/LaunchAgents以下にplistファイルを作成し、その中で環境変数を設定する必要があります。適当なファイル名で以下の様なplistファイルを作成してください(たとえば com.example.android.studio.plist)。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>com.example.android.studio.plist</string>
    <key>ProgramArguments</key>
    <array>
      <string>/bin/launchctl</string>
      <string>setenv</string>
      <string>STUDIO_JDK</string>
      <string>/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
  </dict>
</plist>

「com.example.android.studio.plist」は、将来的にもAndroid Studioなどが公式に作成するファイルと重ならないようにつけているだけで、ご自身の好みで名前は適当に変えてください(たとえば自分の持っているドメイン名をリバースドメインで使用するなど)。

使用するJDKのバージョンなどについても、ご自身が使用したいものを指定してください。必ずインストールされているJDKを指定してください。ここでは、執筆時点でのJava8最新のJDKを指定しています。

ファイルを作成したらOSを再起動すれば設定が反映されます。ランチャーからのAndroid Studio起動時に、STUDIO_JDKに設定したJDKが使用されるようになります。

OS再起動したくない場合、以下のようにすると追加したファイルの設定が追加反映されます。

% sudo launchctl load /Library/LaunchAgents/com.example.android.studio.plist

Android Wear: アプリプロジェクトの新規作成から実機起動まで

追記 (2014/06/27 11:38 PST)

Android 0.8.1 が公開され、そちらで新規作成したプロジェクトにはバグが含まれないことを確認しました。

ただし、Android 0.8.1での新規プロジェクト作成で作られるMyActivityクラスは、Wearアプリ用に用意されたInsetActivityクラスではなく、普通にActivityクラスを親クラスとして参照するようです。

(Android SDK Managerでの、Android Support RepositoryとGoogle Repositoryのrevisionが古い問題は未解決です。)

アプリビルド環境について

Android Wearアプリのビルドには、Android Studio 0.8.0 が必要です。ただし、単純にそれをインストールしてただけではビルドが通りません。

回避策は見つかっているので、前回の記事を参照してください。

Android Studio 0.8.0が生成するプロジェクトの問題

Android Studio 0.8.0 では、Wearアプリのプロジェクトを新規作成できますが、前述の環境設定が終わっていてもビルドに成功しません。

作成されるプロジェクトの初期状態のMyActivityが誤っているようです。新規作成ウィザードが完了すると、以下のようなMyActivityクラスが生成されます。

public class MyActivity  extends WatchActivity {

    private TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
        stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
            @Override
            public void onLayoutInflated(WatchViewStub stub) {
                mTextView = (TextView) stub.findViewById(R.id.text);
                Log.d(TAG, "TextView: " + mTextView.getText() + " view=" + mTextView);
            }
        });
    }
}

上記には以下の問題があります。

  • 親クラスとして指定しているWatchActivityというクラスが存在しない
  • TAGが定義されていない (おそらく WatchActivityに定義されている前提)

Wear用のサポートライブラリに含まれるJavaDocから、android.support.wearable.activity.InsetActivity がWearアプリの基本的なベースActivityクラスだとわかりますので、WatchActivity を InsetActivityに置き換えます。

そして、InsetActivityでabstractで定義されている onReadyForContent()をオーバーライドして実装します。

TAGが定義されてないのでLog.d()も取り払いましょう。

public class MyActivity  extends InsetActivity {

    private TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);
        final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
        stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
            @Override
            public void onLayoutInflated(WatchViewStub stub) {
                mTextView = (TextView) stub.findViewById(R.id.text);
            }
        });
    }

    @Override
    public void onReadyForContent() {
    }
}

以上で、Wearアプリのビルドが通るようになります。

しかし、これを実際にWearにインストールして実行すると IllegalStateExceptionが発生して落ちます。

エラーメッセージによると、setContentView()以降はonReadyForContent()で実行しないといけないようです。ということで、以下のように修正します。

public class MyActivity  extends InsetActivity {

    private TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public void onReadyForContent() {
        setContentView(R.layout.activity_my);
        final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
        stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
            @Override
            public void onLayoutInflated(WatchViewStub stub) {
                mTextView = (TextView) stub.findViewById(R.id.text);
            }
        });
    }
}

これでアプリの実行が成功するようになります。

Let’s enjoy Android Wear app development!

Android Studio 0.8.0 で Wear プロジェクトのビルド失敗について

問題

新規作成したWearプロジェクトのビルド時に、依存ライブラリが見つからないというエラーが出て失敗します。

見つからないのは以下の2ライブラリです。

  • com.google.android.gms:play-services-wearable:+
  • com.google.android.support:wearable:+

原因

Wear プロジェクトの依存ライブラリがまだ配布されていません (PST 2014年6月26日 18時現在)

Android SDK Manager でSDKを最新に更新し、Android 4.4W のコンポーネントを全部インストールし、サポートライブラリとGogoleのレポジトリも最新に更新した状態でも、sdk/extras 以下に必要なライブラリファイルが配信されていないために発生しています。

解決策

待ちましょう。Android SDK Managerで配信されるコンポーネントが更新されないと解決しないため。

Google I/O 2014でGoogleの人にこの件は伝達し、さらにadt-devというAndroid開発環境に関するGoogle Groupsにも報告を送りました。

adt-dev への報告はモデレータさんがOKしないと出ないと思われるので、念のため issue tracker にも以下で登録しました。

Can’t build Android Wear application project generated by Android Studio 0.8.0

(緩和策について追記: 2014/06/27 00:16 PST)
緩和策は見つかっています。以下のURLからダウンロードしたファイルを所定のディレクトリに置くことです。

https://dl-ssl.google.com/android/repository/google_m2repository_r09.zip

(上記の説明だけで対応出来る人にしか緩和策の実行はおすすめしません。)

これは上記のissueへのコメントに示されたxmlファイルから推察されたURLであり、実際にそのファイルは存在するようです。

ただし、これは正式な方法ではありません。もしかすると同じファイル名で差し替えのファイルが提供され、その後に公式にAndroid SDK Managerからダウンロードが提供されるかもしれません。その場合、SDK Managerからダウンロードできるファイルが改善版であるにも関わらず、同じリビジョンであるためにダウンロードが行われないということが起こる可能性もあります。

リスクを勘案し、元に戻せるようにするなどの対策をした上で使用することをおすすめします。

おまけ: Android Wearについて

Google I/O 2014にて参加者に先行して端末が配布されました。

実際に半日使用してみたところ、スマートフォンを取り出して確認するという行動がかなり減り、いろいろと快適になった感じがします。

ハングアウト(Google+のチャット機能)やTwitterへの返信を、日本語の音声入力で腕時計から簡単に返すというようなことも出来て非常に便利です。

Wearにインストールするアプリ次第でさらにいろいろなことも出来るので、今後が楽しみです。

Android Studio 0.5.0への移行Tips

Android Studio 0.5.0がCanary Channel (いわゆる人柱向け)で公開されましたが、0.4.x時とはbuild.gradleのファイルの書き方などが違うので、移行(マイグレーション)の作業が必要です。

特に0.4.2からのアップデートの場合、公式サイトの情報だけでは足りない(気づきにくい)部分があるので、簡易に手順をまとめておきます。

gradle plugin を0.8(0.7)から0.9へ変更

当然ですが、まずはgradle pluginのバージョンを変更します。以下は例です。

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.9.+'
    }
}

SDKのアップデートでbuild toolsの19.0.3が出ているのでそれをインストールして、こちらも最新版を参照するようにしましょう(前のバージョンでもしばらく大丈夫じゃないかとは思いますが)。

android {
    compileSdkVersion 19 // お好みで
    buildToolsVersion "19.0.3"

    // ... 以下他の設定
}

公式サイトに移行ガイドが提示されていますが、ポイントは2つです。

  • instrumentTestXXX という記述を androidTestXXX に書き換える
  • ライブラリプロジェクトの記述を書き換える

src/instrumentTest というフォルダを使用している場合は、そのフォルダ名をandroidTestに変更してください。build.gradleで、instrumentTest.setRoot()で別のフォルダを指定している場合、androidTest.setRoot()に書き換えます。

//instrumentTest.setRoot('tests')
androidTest.setRoot('tests')

上記、以前にEclipseからbuild.gradle ファイルを生成した場合には含まれているはずです。

また、instrumentTestCompile も同様にandroidTestCompileに書き換えます。

ライブラリプロジェクトは変更の必要があればガイドに従ってください。

gradle-wrapper.properties の変更

公式サイトで配布されているAndroid Studio 0.4.2の場合、gradleプラグインの0.7を使用しているはずです。プラグインの0.7ではgradleの1.10は非対応だったので1.9を使っているでしょう。

ラッパーを通じてgradleを使っている場合、ラッパーが取得するgradleのバージョンを1.10に上げる必要があります。

プロジェクトの gradle/wrapper/gradle-wrappper.properties ファイルの以下の部分を書き換えます。

distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-all.zip

以上です。

Android Studio (IntelliJ IDEA) の Language Injection

Android Studio (およびそのベースとなっているIntelliJ IDEA)にはLanguage Injection というおもしろ機能があります。I/Oのセッションでも簡単に説明していた正規表現チェックの例を使ってみます。

まず、以下は(Javaのリファレンスにも載っているような)非常に簡単な正規表現チェックコードの例です。aが0回以上とbという文字列が含まれるかどうかチェックしています。

正規表現コード例
正規表現コード例

ここで正規表現(“a*b”)にカーソルを合わせて、Alt+Returnでポップアップメニューを表示し、Inject Language を選択……

Inject Language
Inject Language

さらにどんな言語の機能を差し込むのか選択が出るので、Regular Expression(正規表現)を選択します。

Inject RegExp
Inject RegExp

これで準備完了です。再度 Alt+Returnでポップアップメニューを呼び出すと、正規表現チェックのメニューが増えているかと思いますので、それを選択肢ます。

Check RegExp
Check RegExp

すると、正規表現をチェックするダイアログのようなものが表示されます。

正規表現チェッカ登場
正規表現チェッカ登場

ではここに正規表現とマッチしない文字列を入れてみます。

正規表現がマッチしなければ赤
正規表現がマッチしなければ赤

マッチしないので赤表示となります。次にマッチする文字列を入れます。

正規表現がマッチすれば緑
正規表現がマッチすれば緑

マッチするので緑に変わりました。

ということで、プログラム開発中に、テストを動かすまでも無く簡易に正規表現が意図通りに動作するものとなっているかを確認することができます。 (( ちゃんとテストコードも書くべきなのは当然です。 ))

もうここではこのLanguage Injectionを使わないぞと思ったら、取り除くこともできます。

不要になったらun-inject
不要になったらun-inject

ちょっとした機能ではありますが、これも開発効率アップに貢献してくれる機能の一つです。

Android Studio Tips

Google I/O 2013において、Androidの新しい開発環境である Android Studio が発表されました。これまでのEclipseを用いた環境からガラリと一新し、JetBrainsIntellIJ IDEA Community Editionをベースにしたものとなりました。

弊社ではすでにアプリ開発においてIntelliJ IDEA(の有償版であるUltimate Edition)を活用しており、非常に強力なコード開発補助機能に助けられています。Androidアプリ開発用に無償で公開されたAndroid Studioにおいても、その強力な支援機能は健在です。

Android Studio は現在バージョン0.1のEarly Access Preview版として公開されています。まだ正式版では無いためバグなども潜在しているものと思われます。しかし、正式版が公開された後のどこかでおそらくEclipse用のプラグインの提供も終了し、最新の開発環境はAndroid Studioに絞られるのではないかと予想されますので、出来るだけ今のうちからどういったものか知っておくことが望ましいでしょう。 ((しばらくはAndroid StudioとADTは並行して提供されるという噂あり))

インストール手順などについては、TechBoosterHow to Install “Android Studio” for Mac OSなど多数のサイトで紹介されていますので、本記事ではインストール手順などについては参照することにとどめ、Android Studioを使う上でのいくつかのTipsを、IntelliJ IDEA利用の経験から示したいと思います。

コード補完 (Java編)

IDEA系のIDEの強さの一つはなんといっても強力なコード補完です。たとえば、Androidアプリを書く際によく使用する例として getSystemService() を入力する場合の補完例を以下に示します。

completion8

“gSySer” という入力で、getSystemService() が補完候補として提示されています。メソッドに含まれる単語の頭一文字を入力して補完候補を出すCamelCase補完という機能を持っているIDEは多いですが、Android Studioの補完はそれよりも柔軟に対応してくれます。この例では、”System”を飛ばして “gSer” と入力した場合でも getSystemService()が補完候補として提示されます。

ただし、補完候補の提示には若干のクセがありますので、出てほしいものが出てきてくれない場合があります。たとえば、アプリケーションを新規作成した状態で、SharedPreferences の変数を宣言しようとした場合、以下のような状態なります。

completion3

“SharedPreferences” のすべてを入力しているにも関わらず、補完候補として現れてくれません。Android Studioの補完はSmart Completionという少し頭のいい補完をしてくれるのですが、たまに間違った推論が行われてしまう場合があるようです。

completion4

CamelCase入力でも同様です。

このような推論ズレが発生している場合、Ctrl+スペースを入力することで、Basic Completionによる補完候補の提示が行われますので、補完候補が出てくれないなという時には Ctrl-スペースで補完方法を切り替えてみましょう。ちなみに、キーバインドをEmacsライクに変更している場合は、Ctrl-スペースではなく Alt+/ となります。

completion7

一度補完が働き、コードの中でその型が使用されていれば、次回以降はSmart Completionでも適切に候補として提示されます。

completion6

completion5

コード入力支援 (Java編)

Android Studio はコード補完以外の入力支援も強力です。たとえば、以下はメソッド呼び出しで戻り値がある場合に、その戻り値を格納するローカル変数の作成の支援の例です。支援が可能な箇所(や、エラーがある箇所)では、Alt+Return を入力すると、適切な支援作業がポップアップで提示してくれます。

introduce_local_var1

変数の宣言と代入を”Introduce local variable” のポップアップから選択すると……

introduce_local_var2

このように変数宣言が追加されます。変数名の候補も、メソッド名などからの類推で自動的に提示されたりもします。

また、メソッドの引数が抜けている場合、その位置にカーソルを移動して Ctrl-P で何を引数として設定すれば良いかをポップアップで教えてくれます。

method_parameters1

上記の例では第二引数に@MagicConstant というアノテーションがついています。このアノテーションにより、第二引数を入力する時にCtrl-スペースで補完候補を表示すると、このメソッドの第二引数として利用してよい定数に絞りこまれた候補が一覧されます。

method_parameters2

Java8っぽいコード表示

Androidアプリ開発において、現時点では Java6相当(+α)の機能しか使用することができません。たとえば、ボタンクリック時のハンドラはリスナオブジェクトを生成して、そのオブジェクトを引数で渡すという方法で登録します。

callback_like_lambda1

おや?左側に (-) が表示されていて、そこをクリックすると折りたたみ表示が出来るようですね。押してみましょう。

callback_like_lambda

折りたたまれて表示されたコードは、Java8のlambda風に表示されます!

実際にlambdaのコードになっているわけではありませんが、直感的に理解しやすくなってコードの可読性は向上しますし、同ページ内に表示される情報量が増します。

レイアウトエディタ

Android Studioはレイアウトエディタも強力です。かなりの部分をGUIで記述するのも簡単でしょう。

layout_editor1

慣れている方はXMLを直接編集した方が早いかもしれません。その場合は、エディタの下部にあるタブでTextを選択します。

layout_editor2

ここでも補完機能は強力です。レイアウトXMLで非常によく使う属性の一つに android:id がありますが、これを入力してみましょう。

layout_editor3

上図は andid と入力しただけで、android:idが候補として提示されている例です。

以下はViewの必須属性である android:layout_width と andorid:layout_height が未設定の場合です。警告が出ています。

layout_editor4

右側に表示されているプレビュー画面上に、何か支援候補が表示されています。

layout_editor5

その中から”Automatically add all missing attributes” を選択してみます。

layout_editor6

自動的に必須項目が追加されます。楽ですね!

当然、レイアウトXMLに埋め込んだテキストを strings.xml に追い出すのもお手のものです。

layout_editor7

ハードコードしているものがあるよという警告が出ているので、Alt+Returnで修正方法の候補を表示し……

layout_editor8

その中から、”Extracting String Resource” を選択するとダイアログが表示されますので……

layout_editor9

ダイアログからその文字列を定義するstrings.xmlの置き場を選択すると、レイアウトXML内にそのまま埋め込まれた文字列を追い出せます。

ちなみに、カスタムビューを作成している場合、それもレイアウト作成時に入力の候補として表示されます。

layout_editor10

署名されたAPKの出力

アプリをGoogle Playを通じて公開する場合、必ず署名されたAPKを作成しなければなりません。Android Studioはこの部分の支援も優秀です。

generate_signed_apk1

メニューの Build -> Generate Signed APK… を選択すると以下のダイアログが表示されますので、出力したいアプリ(Module)を選んでNextを押して進みます。すると、Eclipseでもおなじみのように、鍵ストアおよび署名鍵の選択画面が表示されます。Android Studioでは一画面に収まっています。

generate_signed_apk2

このダイアログに “Remember Password”というチェックボックスがありますので、いちいち毎回パスワードを打つのが面倒だという方はチェックしておくと良いでしょう。パスワードを覚えせるのは不安だ?ご安心ください。それに対してはちゃんと対策が用意されています。

先ほど入力したパスワードを記憶させる場合、マスターパスワードを設定して、そのマスターパスワードを入力しなければ保存されたパスワードを参照されないようになっています。下記は、すでに設定済みのマスターパスワードを入力するダイアログです。

input_master_password

すでにマスターパスワードによる解除が行われているものとして、次の画面に Next で進むと、出力する署名したAPKファイルの出力先選択ダイアログが表示されます。

generate_signed_apk3

ProGuardによる難読化を行うかどうかもここで設定出来ます。

出力に成功するとその旨を示したダイアログが表示されます。

generate_signed_apk4

Macの場合は、出力されたフォルダをFinderで表示することもワンボタンです。

おまけ

残念ながら新規ファイル作成で作成できるAndroidコンポーネントに、Fragmentは含まれないようです。これはSupport Libraryを使うアプリとそうでないアプリとがあるということに起因しているんでしょうか。

new android componet1

何かの機能を呼び出すキーを忘れたら、Help -> Find Action… を使うと便利です。

find_action_help_menu1

たとえば「補完」を意味する completion で検索してみましょう。

find_action_help_menu2

私が Emacs キーバインドで使用しているため、Alt+/ がBasic Completionの呼び出し方だよと表示されています。Defaultのキーバインドを使用されている場合、ここには Ctrl+スペースが示されることになります。

以上、簡単なものだけですが、Android Studio Tipsをお送りしました。

Android Studio (およびそのベースとなっている IntelliJ IDEA)が便利なものであることを少しでもご理解いただけていると幸いです。