真面目なブログはこっち 👉 blog.s64.jp

Apktoolのインストール手順 for Mac

apkを展開したり再パッケージするツールとして有名なものに、ApktoolというCLIツールがある。これのMacにおけるインストール手順について。

検証環境

  • macOS High Sierra 10.13.6 (on Parallels)
  • Apktool 2.4.0
  • OpenJDK 1.8.0_202 (Amazon)

今回クリーンな環境で試してみてわかったが、Android SDKはインストールされていなくてもよい。

wrapperにPATHを通す

Apktool自体はjarとしてパッケージされています。直接実行するためのwrapperスクリプトが用意されているため、これを導入します。

pwd
# /Users/username
mkdir bin && cd bin && pwd
# /Users/username/bin
curl https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/osx/apktool -Lo apktool
chmod +x apktool
echo 'export PATH=$HOME/bin:$PATH' >> ~/.bash_profile

ここで新たなシェルを起動するなどし、~/.bash_profile を読み直す。

Apktool本体を配置する

さきほどのwrapper scriptと同じディレクトリに置く。

pwd
# /Users/username
cd bin && pwd
# /Users/username/bin
curl https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.4.0.jar -Lo apktool.jar
ls
# apktool      apktool.jar
which apktool
# /Users/username/bin/apktool
apktool
# Apktool v2.4.0 - a tool for reengineering Android apk files
# with smali v2.2.6 and baksmali v2.2.6

以上。

SDKMANでJDKを入れる on macOS

SDKMANを使ってJRE / JDKをインストールする手順。

検証環境

  • macOS High Sierra 10.13.6 (on Parallels)
  • SDKMAN 5.7.3

SDKMANをインストールする

curl -s "https://get.sdkman.io" | bash
# ...
# All done!
# 
# Please open a new terminal, or run the following in the existing one:
# 
#     source "/Users/username/.sdkman/bin/sdkman-init.sh"
# 
# Then issue the following command:
# 
#     sdk help
# 
# Enjoy!!!

新しいターミナルを開くなりし、SDKMANを初期化する。

sdk version
# 
# SDKMAN 5.7.3+337

利用するJavaバージョンを選びインストール

たとえばAndroid開発では標準でJDK 8系が用いられたり、JavaでGUI開発をするならJava FXの含まれたものにする必要がある。

sdk list java
# ================================================================================
# Available Java Versions
# ================================================================================
#      13.ea.19-open       10.0.2-zulu         1.0.0-rc-15-grl                    
#      12.0.1-sapmchn      10.0.2-open         1.0.0-rc-14-grl                    
#      12.0.1-zulu         9.0.7-zulu                                             
#      12.0.1-open         9.0.4-open                                             
#      12.0.1.j9-adpt      8.0.212-zulu                                           
#      12.0.1.hs-adpt      8.0.212-amzn                                           
#      12.0.1-librca       8.0.212.j9-adpt                                        
#      11.0.3-sapmchn      8.0.212.hs-adpt                                        
#      11.0.3-zulu         8.0.212-librca                                         
#      11.0.3-amzn         8.0.202-zulu                                           
#      11.0.3.j9-adpt      8.0.202-amzn                                           
#      11.0.3.hs-adpt      8.0.202-zulufx                                         
#      11.0.3-librca       7.0.222-zulu                                           
#      11.0.2-open         7.0.181-zulu                                           
#      11.0.2-zulufx       1.0.0-rc-16-grl                                        

# ================================================================================
# + - local version
# * - installed
# > - currently in use
# ================================================================================

今回は 8.0.202-amzn を使うことにした。

sdk install java 8.0.202-amzn
# 
# Downloading: java 8.0.202-amzn
# ...
# Done installing!
# 
# Setting java 8.0.202-amzn as default.

デフォルトで用いるJavaとして設定

必要に応じて。PATHが通るバイナリが切り替わる。

sdk default java 8.0.202-amzn
# Default java version set to 8.0.202-amzn
sdk current java
# Using java version 8.0.202-amzn
which java
# /Users/username/.sdkman/candidates/java/current/bin/java
java -version
# openjdk version "1.8.0_202"
# OpenJDK Runtime Environment Corretto-8.202.08.2 (build 1.8.0_202-b08)
# OpenJDK 64-Bit Server VM Corretto-8.202.08.2 (build 25.202-b08, mixed mode)

MavenにKotlin Multiplatform向けライブラリを公開する方法

先日 Kotlin Multiplatform 製モバイルアプリ向けの ライブラリを作って公開した ので、その時にちょっとつまづいた部分について。

検証環境

  • Gradle 4.10.3
  • Kotlin Gradle Plugin 1.3.31
    • org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.31
  • Android Gradle plugin 3.3.2
    • com.android.tools.build:gradle:3.3.2

前提

Kotlin Multiplatform ProjectはGradleを用いて(Kotlin Gradle Pluginを用いて)ビルドされ、それらの依存関係はMavenリポジトリから取得されます。
すなわちKotlin MPP向けにライブラリを公開する際はMavenリポジトリへ公開する必要があり、またGradleでの依存性解決が前提とされています。

Gradle Module Metadataを有効にする

settings.gradleに以下の1行を記述します:

enableFeaturePreview('GRADLE_METADATA')

Kotlin MPP向けのライブラリは、Gradle Module Metadataを用いて依存関係を解決します。具体的には こういったファイル を出力します。

これがあると何が嬉しいかというと、依存関係としてたったこの1行だけでiOSやAndroid向けのバイナリをよしなに読み込ませることができます:

kotlin {
    ...
    sourceSets {
        commonMain {
            dependencies {
                implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.31"
                implementation "com.example:your-published-library:1.0.0"
                // ^ これだけ
            }
        }
        iosMain {
            dependencies {
                implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.31"
                // なにか参照しなくても、commonで記述した内容でiOS向けバイナリが取ってこれる
            }
        }
        androidMain {
            dependencies {
                implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.31"
                // こっちも同様
            }
        }
    }
}

各プラットフォーム向けに複数バイナリを出力する

平易な表現にしようとして厳密さを失っていますが、これはiOSやAndroidなどのプラットフォーム特有の問題を解決するためのものです。

iOSの場合

iOS向けアプリ開発では、開発時にはSimulatorを用いる場合が多々あります。
流通しているiOSデバイス実機はARM64アーキテクチャなのに対し、このSimulatorはx64アーキテクチャで動きます。
そのため、ビルドするアーキテクチャとして2通り分(必要ならそれ以上) の記述が必要です:

kotlin {
    def ios64 = iosArm64('iosArm64')
    def iosSim = iosX64('iosX64')

    sourceSets {
        ...
        iosMain {
            ...
        }
        ios64.compilations.main.defaultSourceSet {
            dependsOn iosMain
        }
        iosSim.compilations.main.defaultSourceSet {
            dependsOn iosMain
        }
    }
}

Androidの場合

現行のGradle Module Metadataの都合上、各variant向けの出力を設定しておく必要があります:

kotlin {
    android {
        publishLibraryVariants('debug', 'release')
    }
   ...
}

maven-publishプラグインを設定する

Kotlin Gradle Pluginは親切なので、maven-publishプラグインが適切に有効化されていれば MavenPublication を自動的に設定してくれます。
よって必要な設定は以下です:

plugins {
    id 'maven-publish' // apply plugin: 'maven-publish'
}
...
group = 'com.example.your.artifact.group'
version = '0.0.1-SNAPSHOT-YOUR-VERSION'

publishする

ローカルリポジトリであれば以下のコマンドでOKです:

./gradlew publishToMavenLocal

実行すると、たとえばiOS, Android向けにここまでの内容を参考に設定していた場合は 以下の成果物が得られます:

  • com.example:your-published-library:1.0.0
  • com.example:your-published-library-metadata:1.0.0
  • com.example:your-published-library-android:1.0.0
  • com.example:your-published-library-android-debug:1.0.0
  • com.example:your-published-library-iosarm64:1.0.0
  • com.example:your-published-library-iosx64:1.0.0

先頭のサフィックスが無いパッケージは .pom と .module ファイルしか持ちません。ここを起点に各プラットフォーム向けの依存性を解決する役割のみを持ちます。
-metadataはcommonで記述された公開I/Fの定義を持ちます。
残るモジュールは、各プラットフォームやABI向けのバイナリです。

使われ方

公開したライブラリの参照方法は Gradle Module Metadataを有効にする のセクションの通りで、サフィックスの無いモジュールを依存関係に加えるのみです。
ただし、Gradle Module Metadataを有効にしていないアプリの場合、各モジュールの参照が必要になる模様です。

こうして開発したライブラリは Kotlin MPP, Kotlin/Native, Pure Androidプロジェクト(Kotlinが有効なもの)で共通して利用することができました。

拒否設定してるのに非通知着信めっちゃくるときの正体はUber Eatsかも

自分が起きている時間帯に 2分に1回以上の頻度で非通知着信が来ており、かつ非通知拒否設定(番号通知お願いサービス, 148)を設定しているにも関わらず端末まで着信する場合、それはUber Eatsサポートセンターの可能性があります。

もし自身の注文した商品がいつになっても届かないなら、Uber Eatsアプリを開いて配達状況を確認してみてください。
たとえば配達パートナーにトラブルが発生し 配達がそのまま遂行できない場合などに、そのまま再配達を行ってもよいかの確認として電話を掛けているようです。

このようなトラブルによる事象は緊急性を伴うため、テキストメッセージ等は利用していないようです。

ビルドしちゃったAndroidアプリのTLS通信をProxy傍受できるようにする

人間誰しも、なんらかの理由でビルドした後のAndroidアプリで通信を傍受したい時がある。そういう時は大概、自宅だったりして手元にはソースコードがない場合だ。
肝となる作業は下記の記事となんら変わらないが、ビルド済みAPKファイルからこの手順を確認する。

blog.s64.jp

1. 正規の方法でapkを取り出す

もし実機にインストールしてあるなら、adbコマンドを用いて手元のPCに持ってくる。

2. apktoolをインストールする

PATHを通しておく。インストール手順はこちら。

3. apkを展開する

apktool d your.built.droid.app-0.apk

4. 構成ファイルを仕込む

やることは3つ。

  • res/xml/nsc.xml のようなパスに network-security-config を作る
  • res/raw/myca.der のようなパスに .der ファイルを置く
  • AndroidManifest.xml 内の <application/> に android:networkSecurityConfig="@xml/nsc" のような設定を加える

4. 再パッケージ

apktool b ./your.built.droid.app -o falsified-unsigned.apk

5. 署名

jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ~/.android/debug.keystore -storepass android -signedjar falsified-signed.apk falsified-unsigned.apk androiddebugkey

~/.android/debug.keystore がない場合、生成することもできる。

6. インストールと起動

adbコマンドを用いてインストールする。
logcatで NetworkSecurityConfig: Using Network Security Config from resource nsc debugBuild:...のようなログが表示されればOK。

モバイルSuicaで "iPhoneをご利用の方は、iPhone以外の携帯情報端末に機種変更および再発行はできません。" というメッセージ(Android)

iPhone(Apple Pay)でSuicaを利用していたユーザがAndroidスマートフォン(Google Pay)のSuicaに機種変更手続きを試みると、以下のようなメッセージが出る:

iPhoneをご利用の方は、iPhone以外の携帯情報端末に機種変更および再発行はできません。

f:id:S64:20190318100849p:plain:w250

このメッセージはそのままの意味を指していて、iPhoneからの引き継ぎができない旨が表示されている。しかたないので新規発行となる。

なお、新規発行時に iPhone側と同じクレジットカード番号を設定しようとするとエラーになる。
よって特別な理由がない限り、iPhone側のモバイルSuicaを解約(払い戻し)することになるだろう。
払い戻しの手数料は220円 なので、ちょっと残念。下回っていれば端数分のみにできるので、ギリギリまで使ってしまおう。


つまづきポイントは上記の「同じクレカ番号を設定するとエラーになる」のところ。 モバイルSuicaの退会(払い戻し)後の再入会は、翌朝4時以降から可能になる。

  • 退会後、再入会される場合は、以下の制限があります
    • 携帯情報端末からの退会の場合:退会完了の翌日以降 4:00~2:00
      (定期券をモバイルSuicaへ切替える場合は、5:00~0:50)
    • パソコンから退会の場合:退会完了の翌日 7:00以降
    • 申請フォームから退会の場合:退会完了メール配信後(申請から2~3日後)

https://www.jreast.co.jp/mobilesuica/procedure/secession/

すなわち、iPhoneで払い戻しを行って即Android側のモバイルSuicaで登録しようとすると(同じクレカ番号で設定しようとすると)エラーになるのである。
おとなしく朝まで待とう。

IT関連職のフリーランスは会社員の上位互換なのか

先日、担当しているある生徒からこんな質問が届いた。

f:id:S64:20190129234210j:plain

曰く、この生徒が志願する業界には所謂 "ブラックな労働環境" が蔓延しており、その中から健全な環境を引き当てるか、経験を積んで独立する道があるのだという。

f:id:S64:20190129234618j:plain

f:id:S64:20190129234705j:plain

話を聞く内に、フリーランスは会社員からのステップアップである、という認識を持っているように感じた。
あくまでこれは私個人の見解でしかないが、必ずしもそうではないと考えている。

そもそもフリーランスとはどのような働き方なのか、会社員とはどう違うのか、という部分の認識は曖昧な場合が多い。
私はフリーランス歴だけでなく 業界歴、もっと言えば社会人歴すらも浅いが、今の自分が思っていることを吐き出してみる。

IT関連技術職へ進むには

この生徒の志願する業界は、私と同様にIT関連の技術職だ。
これらの業種へ進む場合、経路としては、

  • 高等専門学校(いわゆる高専), 専門学校, 大学 などへの進学
  • 各種学校、スクール等への通学

などがイメージされやすい。特に近年のソフトウェアエンジニアの台頭により、後者からの就職はかなりの割合を占めるようになった。
中にはとても優秀な学生を輩出し続ける教育機関も当然存在し、ある学校の出身者ばかりの企業も少なくない。

IT関連という枠はかなり広いくくりなので、各業種へ細分化すると全く異なる事情がある。
ただし共通することとして、必ずしも品質の高い教育が受けられるとは限らないのが現状だ。
私も教育者の端くれながらに できるだけ実態に即したカリキュラムを目指しているが、これだけの期間続けても尚 やはり多くの障壁があり難しい、というのが正直な感想である。

そんな私はどのようにこの業界に入ったかというと、一切ITに関連する教育を受けておらず、完全に独学である。
もともと発達に多少の偏りがあった為に 学校へあまり行かず、自宅でコンピュータを触っているか病院に居るか、という生活をしていて、その後 通信制高校に在学中、あるスタートアップ企業でインターンとしてジョインしたのがきっかけで業界に入る、というパターンだ。

ITは教育できない学問なのか

この項は記事の本題からズレてしまうので深くは追わないが、比較的新しい分野であること、技術の陳腐化が早いこと、要求される範囲が広く短期間で一様に習得する方法が確立されていないこと、学問と実務で大幅に抽象度が異なること、などさまざまな課題が複雑に絡んでいる。

少なくとも言えることは、進学を考える際には 自分が学びたいことが何かを明確にすべきであるということ。
職業訓練校としての教育を求めているなら、それに適切な実務能力を育成する学校が存在する。もしコンピュータサイエンスや理論を学びたいなら、それ相応の学校があるだろう。

組織に属するということ

これはスタートアップ企業で特に顕著に見られる傾向だが、IT関連技術職には、新卒 = 未経験者に対して要求される能力すらも とてつもなく高い場合がある。
が、当然ながら 現実にそれら企業の要求する能力に達した状態で社会人になる人間はほんの一握りである。

そもそも新卒採用をする理由は

  • 低コストな労働力の確保
  • 帰属意識の高い社員の獲得

などが存在し、よほど余剰リソースがない限り、本音を言えば新卒社員への教育なんてしている場合ではないのである。

では会社員になるメリットは何かというと、要は守られるのである。

  • 会社都合で解雇するのは難しく、そう簡単に解雇されることはない
    • 万一能力に不足があっても、支援等を受けて見合った業務を続けられる可能性が高い
  • 安定して給与が支給される
  • 社会的信用が高く、ローン等の金融商品を簡単に契約できる

この企業と求職者間のギャップと前述した職業能力育成の難しさが相まって、結果「放っといても育つ人材」だけが重宝されている現場は少なくない。

高い成果を出し続ける人々

そして驚くことに「放っといても育つ人材」というのは実在している。

彼 / 彼女 らは高い職業能力を持っているが、本質はそこではないと感じている。多くに共通している特徴は、

  • 経験に裏打ちされた基礎を持っている
    • 大半が大学教育を経ており、さらにそれらの知識を実践を通して強固なものにしている
  • 何事からも面白さを見つける能力に長けている
  • モチベーションコントロールが上手いか、モチベーション高く取り組めることのみに集中している
  • 自身の作りたいもの、実現したいことがある

などが挙げられるだろう。まさに願ったり叶ったりだ。
こういった能力を持った人物は、社会人になる以前から 何かしらの創作活動をしていることが多い印象を持っている。

ではそんな、いわば自律している 彼 / 彼女 らにとって、社員として組織に属するメリットはどの程度あるだろうか?
必要に応じて学習ができるため、給与を下げられてまで支援を受ける必要はない。日々十分に業務をこなし成果を出しているため、もともと収入は安定が見込める。
となると、同じ会社内の限られた領域で得られる知識に満足できるだろうか?毎日のように同じオフィスへ通いたいだろうか?

すなわち、フリーランス、個人事業主としての素質が高いのである。

フリーランスの働き方

彼 / 彼女 らがフリーランスとしての素質が高い理由は、その要求される能力にある。

  • 企業同士の取引に当たるため、指揮命令ができない
    • よって、会社側の課題をヒアリングし、それに対する提案を自らしなければならない
  • 人件費ではなく外注費であるため、個別に教育するためのコストはほぼ発生し得ない
    • 要はコストの圧縮を図りたい企業における選択肢なのである

企業が社員に対してしばしば行わざるを得ない様々な行為が不要になるのだ。

フリーランスの働き方の実態は、たとえばソフトウェアエンジニアの場合以下のようなものが大半だ:

  • SES
    • いわばセルフ派遣といったところか。客先オフィスへ出向き、ソフトウェア開発能力とその労働力、時間を提供するもの。
    • 自宅でのリモート作業もままある
    • おそらく、かなりの割合がこれ
  • 受託開発・納品
    • 顧客からヒアリングした内容をもとに製品を提案・開発し、期限内に納品するもの。
    • 多くの人がイメージするフリーランスはこっちかと

実を言うと フリーランスという肩書を持ちながらも、会社員と同じように日々客先の事務所へ通い業務をこなしているケースはかなり多い。
そのような形態で仕事をしているフリーランスが 会社員と異なる点があるとしたら、

  • 自身で仕事を選ぶことができる
    • 会社員の場合、原則指揮命令に従うことが求められる
  • 働く日数や期間をコントロールすることができる
  • 突然仕事がなくなる場合がある
  • 自身で提案・決定することができなければ、仕事を失うことになる

くらいだろうか。

フリーランスは会社員の上位互換ではない

会社員とフリーランスの関係はどちらが上位だとか下位だとかではないと考えている。
理由はごくシンプルに、その人物の適正に依存する部分が多いためだ。

フリーランスはしばしば「好きなことを仕事にしている」と言われがちだが、顧客相手の商売である限りは 会社員と大差ないことがわかる。
では何故それでもフリーランスの人間が「それなりに好きなことをしている」と言いがちというと、ここまで述べたとおり「物事 = 受託した仕事 の中から、何かしら自分にとっての面白さを見つけることができる」人物が多いからだ。

ではその能力はフリーランスだけのものだろうか?いや、会社員でも当然に持つことができ、日々の業務に活用できる能力だ。

実態としてソフトウェアエンジニアの現場では、フリーランス適正の高い人材が強く求められた結果、能力の高い人材が いわばフリーエージェント状態になっている。
著名なソフトウェアエンジニアが業界内で頻繁に転職を繰り返しており、会社員でありながら個人そのものに高いブランド価値が着いているのだ。

そしてフリーランスというのは、しばしば弱い立場になりがちである。あくまで企業にとってはゲスト、外注業者でしかなく、コアな意思決定や事業領域(ドメイン部分)に関わる業務には携われない場合が多く、状況が変われば不要になる、交換可能な部品である場合が多いのだ。
そしてそれは効率の良い正しい経営判断によって発生するものであり、我々は自らその市場を選んでいるのである。

自分の適正は何か、何を身につけるか

盲目的にフリーランスを目指したり、逆に難しそうだという理由だけで会社員のままでいるのは必ずしも正しいとは言えない。
重要なのは、自身はどのような働き方に適正があるか、または目指す先に必要な能力は何かを見極める力だと思う。

とはいえまあ、過酷な労働環境から抜け出すための独立、という選択肢はアリだ。
その日までに、学生の内からトレーニングできることはなんだろうか。

なお、この吐き出し記事に着地点はないです。