弊社代表取締役の中西良明が、国家資格「情報処理安全確保支援士」を取得いたしました。登録番号は004004となります。前述のウェブサイト内の「登録者公開情報」からご確認いただけます。
セキュリティ業界やスマートフォンアプリの開発の経験などをもとに、情報セキュリティ向上の業務はこれまでと同様に行ってまいります。お困りのことがございましたらご相談ください。サーバサイドからアプリ、組み込みまで広範にサポートいたします。
弊社代表取締役の中西良明が、国家資格「情報処理安全確保支援士」を取得いたしました。登録番号は004004となります。前述のウェブサイト内の「登録者公開情報」からご確認いただけます。
セキュリティ業界やスマートフォンアプリの開発の経験などをもとに、情報セキュリティ向上の業務はこれまでと同様に行ってまいります。お困りのことがございましたらご相談ください。サーバサイドからアプリ、組み込みまで広範にサポートいたします。
今年はGoogle I/Oに合わせるのではなく、3月にAndroid N Previewが公開されました。また、4月13日(米国時間)にPreview 2へのアップデートも公開されました。
以前、「DroidKaigi 2016:「 Andoridの省電力について考える」に対する補足」にて、DroidKaigi 2016での省電力関連の発表資料の紹介とその補足をいたしましたが、その時点で最新だったAndroid 6.0系よりもさらにDozeが強化されています。
Googleが公開しているドキュメントによると、Dozeに段階が設けられました。Android 6.0で導入されたDoze(深いDoze)とは別に、端末が静止1していなくてもバッテリ駆動かつスクリーンオフの状態が一定時間続くと、制限の緩いDoze(浅いDoze)に入ります。2
「浅いDoze」では、アプリのネットワーク通信不可となり、ジョブ(詳しくはJobSchedulerを参照)と同期がメンテナンスウィンドウもしくはDozeの解除まで遅延されます。
では、実際にどのくらいの時間経つと「浅いDoze」に入るのでしょうか。Preview時点ではソースコードがまだ開示されていませんので、実際の挙動から調査をしました。
Nexus 5XにAndroid N Preview 2を入れた状態では、以下のような挙動を確認しています。
5分
5分(1回目)、10分(2回目)、15分(3回目)……と、Linear Incremental Backoffで時間が増加(上限値は未確認)
3/2〜4の3日間、渋谷にて開催されている「try! Swift」のセッションで紹介されたSwiftの「Type Erasure(型消し)」についての理解を深めるため、内容を順を追って記述していきます。
以下のようなコードをJavaでは書くことが可能です。
interface BaseInterface<T> {
void hoge(T action);
}
class Foo implements BaseInterface<String> {
@Override
public void hoge(String action) {
System.out.println("action = " + action);
}
}
class Bar implements BaseInterface<Integer> {
@Override
public void hoge(Integer action) {
System.out.println("action = " + action);
}
}
......
void fuga(BaseInterface<?> param) {
// something
}
.......
Foo foo = new Foo();
Bar bar = new Bar();
fuga(foo);
fuga(bar);
では、同様のコードをSwiftでも書いてみましょう。
protocol BaseProtocol {
typealias T
func hoge(action: T)
}
class Foo: BaseProtocol {
func hoge(action: String) {
print("action = \(action)\n")
}
}
class Bar: BaseProtocol {
func hoge(action: Int) {
print("action = \(acton)\n")
}
}
// この関数定義でコンパイルエラー
func fuga(param: BaseProtocol) {
// something
}
let foo = Foo()
let bar = Bar()
fuga(foo) // fuga()の宣言失敗しているのでそもそも呼べない
fuga(bar) // 同上
上記のコードは、コメントに示したところでコンパイルエラーとなります。
これはSwiftの言語仕様の問題です。SwiftのprotocolはJavaのinterfaceと違い、クラス実装時の制約であり型ではないためです。
その制約を一部解消する方法を紹介したセッションが「平常心で型を消し去る」でした。しかし、前述のJavaのようなことは(少なくとも現時点のSwiftでは)できないことも説明されていました。
ラッパークラスを作成することで「型を消す=(型制約の有用性を維持しつつ、同じprotocolで別の型のオブジェクトを仲間として取り扱う)」というアプローチが説明された。それが以下のようなものである。
class AnyBaz<T>: BaseProtocol {
let _hoge: T -> Void // T型を受け取る関数、戻り値なし(Voidを返す)
// 初期化の引数で渡されたオブジェクトの型をUとして、
// Uの中のT型とこのクラスの宣言で指定したT型が同一
// とする制約をつける
required init<U: BaseProtocol where U.T == T>(_ baz: U) {
_hoge = baz.hoge
}
func hoge(action: T) {
_hoge(action)
}
}
let foo = AnyBaz(Foo()) // fooはAnyBaz<String>型
let bar = AnyBaz(Bar()) // barはAnyBaz<Int>型
ただし、この”Type Erasure”のテクニックを用いても、Javaでできた以下のようなことはできません。
// この関数定義でコンパイルエラー
func fuga(param: AnyBaz) {
// something
}
fuga(foo) // fuga()の宣言失敗しているのでそもそも呼べない
fuga(bar) // 同上
たとえ以下のような継承関係があるクラスであったとしても、
class X {
// something
}
class Y: X {
// something
}
class Z: X {
// something
}
func fuga(param: AnyBaz<X>) {
// something
}
fuga()の呼び出しに渡せる引数は、AnyBaz<X>のインスタンスでなければならず、AnyBaz<Y>やAnyBaz<Z>のインスタンスは渡せません。このこと自体はJavaも同様で、以下の場合list2はコンパイルエラーを起こします。ジェネリクスの制限としては妥当だと考えられます。
class X {
}
class Y extends X {
}
List<X> list1 = new ArrayList<X>();
List<X> list2 = new ArrayList<Y>();
しかし、”Type Erasure”では解決できないことがあるという事実は変わらないままです。
Type Erasureによって問題を解決できるシーンは当然あると思います。
しかし他の言語からの移植時などに、その言語でのやり方すべてをそのまま持ってこようとするのは良いアプローチではなく、そのための方法として利用するものではないとの理解をしました。
簡単に持ってこられるものは持ってくるが、そうじゃないところはアプローチを変えるべきでしょう。おそらくis-a関係(継承など)ではなくhas-a関係(移譲)を用いて検討し直した方がよい場合が多いのではないかと。
Android 5.0から最新のWebViewはGoogle Playで更新されるようになりました。これにより「AndroidシステムのWebView」というアプリを更新することで、Chrome(Chromium)をベースとした、バグが修正されたWebViewを利用することができます。バグが修正されるということは、更新しておくとセキュリティ面でも安心です。
WebViewを利用しているアプリは多いので、セキュリティ問題が含まれたままの古いWebViewを利用していると、なんらかの攻撃を受けるリスクはかなり高くなります。
また、そのようなマイナスを減らすというだけではなく、更新することで新しいWeb技術を利用できるというプラスもあります。
ということで、Android 5.0以上の端末において「AndroidシステムのWebView」は是非とも更新すべきアプリの一つです。
(いつからかまでは特定できていませんが)2016年2月9日のバージョン48.0.2564.106までの「AndroidシステムのWebView」にはかなり重大なバグが潜んでいました。
このリンク先のissueがそれを示したものなのですが、簡単にまとめると「WebViewを利用したアプリがランダムに落ちる」というものです。
OpenGL関連のネイティブライブラリ内でクラッシュするようで、アプリ側で例外をキャッチして回復処理を行うということもできません。またランダムというのもくせものでした。弊社での確認でもそうだったのですが、この問題はAndroid 6.0および6.0.1上でのみ発生しているようでした。
ただ、一応アプリ開発する上での回避方法はありまして、以下のようにすることで回避できていました。
WebView webView; // この変数にWebViewインスタンスをセット
....
webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
これがどういうことをやっているかというと、「指定したWebViewの描画にハードウェア支援(GPUによる描画支援)を使用しない」という設定です。つまり回避のためにこの設定を行うと、WebViewの表示が遅くなりメモリ使用量も増えるというデメリットがあります。
Android 6.0以降が搭載された端末は非常に限られていたことや、パフォーマンス問題を引き起こすことを嫌うことなどから、回避策を入れていないアプリも多いと考えられます。
Alex Mineer氏によると、49.0.2623.63がベータ版としてリリースされ、問題なければ正式にリリースされるとのことです。
issueのコメントによると、49.0.2623.55には問題のworkaround(回避策)が入っていますので、正式にリリース予定のバージョンでは問題は改善している見込みです。
弊社でWebViewのテスター登録を行ってベータ版をインストールしてみたところ、以前は落ちることがあったものが安定することを確認しました。
日本国内でも大手キャリアのAndroid端末に対するAndroid 6.0へのアップデートが始まります。たとえば、 NTTドコモによるAndoid 6.0へのアップデートのお知らせが出ています。
旧機種へのアップデートが行われるということは、今年の春夏モデル端末にはAndroid 6.0が搭載されてくるでしょう。
特にアップデートによってAndroid 6.0になるものについては、古いWebViewが搭載されたままとなっている可能性が高いでしょう。試験にかかる時間などを考えると、2月下旬に公開されたものを組み込んだ状態でアップデートがリリースされるとは考えにくいためです。
上記の3つがAND条件で成立する場合、ユーザを「AndroidシステムのWebView」の更新に誘導してもよいかもしれません。
「AndroidシステムのWebView」を更新しましょう。アプリがクラッシュするのを少しでも減らせるのはよいことです。
弊社代表の中西がDroidKaigi 2016にて「Androidの省電力について考える」というタイトルでセッションを担当いたしました。
以下にその際に用いた資料を示します。
また、上記の資料では説明しきれなかったDozeの細かい話について少しだけ補足をします。
Dozeに入るまでの状態の変化については、同イベントでのSmartium株式会社の江川さんによるセッション資料の以下のページが参考になります。
あくまでも簡略的な図であり、正確な状態遷移について知りたい方はコードを追いかけてみてください。
資料には状態を遷移する時間が記されていますが、その情報はDeviceIdleControllerクラスのupdateConstants()メソッドの定義を見るとよいでしょう。状態遷移についてはstepIdleStateLocked()を確認するとわかります。
stepIdleStateLocked()を見ると、状態はACTIVE -> INACTIVE -> IDLE_PENDING -> SENSING -> LOCATINGというように段階を追って遷移していくことが示されています。
LOCATING後すぐにIDLE_MAINTENANCEに遷移し、一定時間後にIDLEへと遷移し、IDLEからもまた時間が経つとIDLE_MAINTENANCEへと遷移するというようなコードとなっています。
DozeServiceクラスを読むことで、どういう条件でDozeに入るのか、またDozeが継続されるのかがわかります。
資料に簡単に記載しましたが、DozeServiceはAPI Level 17で導入されたDaydreamという機能をベースに実現されています。言いかえると「DozeServiceはDreamServceを継承したサービス」です。
Daydreamはスクリーンセーバーを実現する機能であり、画面がOFFになるタイミングで働き始めます。そのタイミングから各種の条件が成立するかの監視を開始し、成立すると先に挙げた状態遷移が行われるというわけです。
資料にも簡単に記載しましたが、面白いところとしてはコードの中でpulseと表現されている事象(たとえば端末が動かされる)が発生することがIDLE状態から抜けるトリガとなるのですが、近接センサ(proximity sensor)で端末が何かの物体と近接している場合には抜け出すトリガは捨てるという処理があります。コードを細かく見ていきコメントなども確認するとわかるのですが、これは端末がポケットやかばんに入っている場合にはDozeで省電力モードのままとするための処理です。
ディープスリープは端末として最低限の電力消費まで機能を落としている状態であるとするなら、弊社で調べた限りではDoze自体がディープスリープとは言えないのではないかと考えております。
いわゆるACPU(アプリが動作するプロセッサ)は動作しシステムとしてはネットワーク通信もしている状態です。除外設定されたアプリは普通に動いています。そうでなければ高優先度のGCMを受け取って即時に対象のアプリに渡すということもできません。
Dozeによる省電力に対して非協力的なアプリが多いと、省電力効果は限定的になるだろうと考えられます。
そのため、Androidの将来バージョンにおいて更なる強制的な制限が行われることを避けるためにも、アプリ開発者ができるだけ省電力に対して協力的になることは非常に大事なことだと考えております。
注意:本記事については、弊社でもどうするのがベストプラクティスなのか掴みかねております。その点を留意してお読みください。
Andorid M (6.0)のPreview 2から、ActivityクラスにshouldShowRequestPermissionRationaleというメソッドが追加されています。rationaleとは「理論的根拠」とかそんな感じの意味があるので、このメソッドは「パーミッション要求の根拠を表示すべきかどうか」を判定するためのものであることが名前からわかります。
実際、Previewのドキュメントでもそういう風に使うように記載されています。
しかし、このメソッドの挙動が少し不思議です。
上記のような挙動になります。
ここで不思議なのは1番目です。初回のパーミッション要求の前にも、そのパーミッションをどういう根拠で求めるのかはユーザに提示した方が良いのではないかと思われますが、このAPIに従った場合、表示しないというパスに流れます。
また、PreviewのAPI Overviewに示されたコードも不思議です。
if (checkSelfPermission(Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (shouldShowRequestPermissionRationale(
Manifest.permission.READ_CONTACTS)) {
// Explain to the user why we need to read the contacts
}
requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant
return;
}
このコードを参考にして実装すると、shouldShowRequestPermissionRationale()の結果がtrueであったときに説明を表示します。たとえばダイアログで表示するなどが考えられますが、その場合はダイアログ表示している間にrequestPermissions()がすでに呼び出され、外部アプリ(PackageInstallerアプリ)による権限確認ダイアログの表示に遷移してしまうことでしょう。
以上から、このサンプルコードをベースに書くのはベストプラクティスでは無いだろうと考えています。
ということで、現時点ではどうするのが良いのかについてはまだ模索中です。二つの問題点とうまく折り合いをつけたベストプラクティスがGoogleから提示されることを期待しています。
TechBooster様の「Androidビルディングデザイン」に、弊社代表中西がAndroid Mの新しいセキュリティモデル(パーミッションモデル)の記事を寄稿しました。2015年8月25日現在、紙の書籍は完売しておりますが電子書籍版はまだ頒布されています。
電子版では、Android M Developer Preview 3、SDKバージョン23、Support Libraryバージョン23についても反映した内容に更新されていますが、その中では触れていない気をつけないといけない項目について、ここに記載します。
Android Studioでデバッグ実行で試すとわかるのですが、targetSdkVersionを23にしたアプリをインストールし、それを22以下に変更して再度インストールしようとすると、以下の様な警告が表示されます。
Failure [INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE]
書いてあるとおり、パーミッションモデルのダウングレードを求めるようなアプリのインストールが許可されていません。この時、Android Studioは以下のようなダイアログも表示します。
ここに書いてあるのは、今インストールされているアプリをアンインストールして、それからインストールするかと聞いてきています。つまり、インストール済みのアプリの保存しているデータや設定などを維持して、targetSdkVersionを22以下に落としたアプリのインストール(アップデート)を行うことはできません。
これ、おそらくはGoogle Play Storeでも対策がなされるのではないでしょうか。
一度targetSdkVersionを23以上に上げて新しいセキュリティモデルの対象となったアプリは、targetSdkVersionを上げたことによって発生したバグがあっても、22以下に戻したアプリをアップロードすることはできなくするのではないかと。
既存アプリを運用されている場合、targetSdkVersionを23に変更するのは慎重に行うようにしましょう。
これはユーザにとっての罠ですが、Android M端末にtargetSdkVersionが22以下のアプリをインストールし、それがバージョンアップでtargetSdkVersionが23になったとしても、パーミッションが許可状態になります(Context#checkSelfPermissionがtrueを返します)。
新規にtargetSdkVersionが23のアプリをインストールした場合、最初はContext#checkSelfPermissionはfalseとなりますので、そこからActivity#requestPermissionsによって権限を取得していくという流れになります。
しかし、前述の場合は、targetSdkVersionが22のアプリをインストールした時点で、権限利用を許可したという状態が端末に記録され、それはtargetSdkVersionを23以上に上げた場合にも維持されます。
この挙動は、iOSなどで新しいOSバージョンで権限チェックが強化された場合の挙動とは異なります。iOSでは、たとえばマイク利用にユーザによる権限確認が必要となった場合、アプリの新規インストールとアップデートのいずれであってもダイアログで確認するという挙動になります。
この挙動のまま正式版となるかはわかりませんが、ユーザは気をつけておくとよいでしょう。
[amazonjs asin=”4897979838″ locale=”JP” title=”Android開発者のためのSwift入門”]
中西が、Tech Boosterの日高さんと一緒に書いた上記の本が5/27に発売されます。早ければ今週末くらいには書店やアマゾンから入手可能かもしれません。1
ネイティブでのAndroidアプリ開発とiOSアプリ開発の両方やっていると、それぞれのプラットフォームに違いはあっても、似ている部分も多々あることがわかります。そして、一方の経験がもう一方の開発に生きることもよくあります。
たとえばどちらのプラットフォームでも、「メインスレッド(UIスレッド)では重い処理を行ってはいけない」という原則があります。
しかし、ネイティブのiOSアプリ開発ではこれまで、Objective-Cという開発言語を使用することが必須でした。前書きにも記載したのですが、個人的にはObjective-Cは非常に好きな言語の一つです。しかし、以下のような特徴があってハードルは高めだということも理解しています。
そのようなこともあって、Androidアプリ開発の経験があってiOSアプリ開発に興味もあるけれども手を出しかねているという話もよく聞きました。
マニア的にいろいろなプログラミング言語を学習してきた経験からも、ある言語での経験は新しい言語の習得に役立つことを知っています。Haskellなどのような関数型言語のようにパラダイムが大きく違う場合は、その理解に時間がかかったりもしますが、そういう場合でなければ、プログラミング言語は結構類似しているので、一つ知っていると他の言語の学習はしやすいものです。
そういう背景があって、本書の企画が生まれました。
筆者たちが、ゲーム開発などの特定の領域を除いて、共通開発環境で複数のプラットフォーム向けに一つのアプリを開発するということのメリットがほとんどないと考えているためです。
バグ改修や将来的なメンテ、機能追加などを考えると、ネイティブ開発をすることが最終的には手間がかからないと考えているためです。特に、OSが提供する最新の面白い機能を利用しようとするなら、ネイティブ開発は必須です。
完全に一対一で対応するものがあるわけではないのですが、できるだけAndroid経験者がどういうものか理解しやすくなるように、「Androidの○○のようなもの」という説明をするようにしています。
また、Android経験者がiOSアプリ開発を行う際の壁の一つであるUIレイアウト作成については、ページ数を割いて解説をしております。Auto LayoutやSize Classesについても実際にハンズオン形式で確認いただくようになっています。
Swift言語を駆使すると非常にマニアックなことも出来るのですが、実際の現場ではそこまでマニアックな記述方法が必要になることはほとんどありません。
それを踏まえて、以下の様な方針で言語仕様を解説しています。
言語仕様の詳細については、本書以外に、他の書籍やウェブサイトなどを参照してより深められることが望ましいと考えております。
たくさんのことが書かれていません。一例を挙げると、インスタンスオブジェクトが参照カウンタ(Reference Counting)で生存管理されていることについてはあまり説明していません。
これは、ARC(Automatic Reference Counting)によってほぼ意識しないで記述できるためです。Androidアプリ開発者がiOSアプリを最初に学習する時点でそこまで説明するのは不要だろうと判断をしています。
ARC以降のiOSアプリ開発では、インスタンスオブジェクトの破棄(release)を意識しないといけない場面が少ないためです。
また、利用したUI部品のAPIを網羅的に説明するなどはしていません。
Yahoo! JapanさんのYahoo! Open Local Platform(YOLP)の提供する地図SDKと、検索API(Web API)について説明しています。
標準の地図やGoogleさんのSDKやAPIを利用せず、上記を利用したのは以下の理由によります。
デザイナーさん向けに、AndroidとiOSを両方理解して、ちゃんとそれぞれに適したデザインを作る本とかどなたか出して欲しいですね。そしてベストセラーになってほしいです。
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
これまでに弊社blogにて、Android 4.3からの隠し機能AppOpsでユーザによるパーミッション制御ができるようになっていたということを書いておりました。
Android 5.0でもAppOpsの仕組みは一部で生き残っていますが、あまり拡張されませんでした。別の機能としてRestrictionsManager というものが追加されているようです。
API Referenceから引用します。
Apps can also send permission requests to a local or remote device administrator to override default app-specific restrictions or any other operation that needs explicit authorization from the administrator.
もしかしてpermission機構とは別の仕組みで権限管理が!と期待しましたが、直交する概念だったようです。ただし、Universal Data Controls というAndroid 5.0から導入される仕組みとどう関係するかは要確認です。
サンプルアプリがgithub上で公開されていますが、アプリごとに定義したrestrictionを管理する仕組みでしょうか。具体的な使用方法がまだイメージ出来ていません。
より正確な情報は調査の上で追加していく予定ですが、まずは一報だけ。