android auto instrumentation
TRANSCRIPT
![Page 1: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/1.jpg)
|
Dependency Library injectionHow to extend any app without a single line of code.
![Page 2: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/2.jpg)
|
Przemek JakubczykAndroid Technical LeadApplause
![Page 3: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/3.jpg)
|
Motivation
• Applause ships its SDK which monitors app state• Often clients don’t want to mess with development process• Product/Project owner can do it himself• Or to configure SDK different way
![Page 4: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/4.jpg)
|
The hack
![Page 5: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/5.jpg)
|
Build chain (source)
Dalvik Executable (DEX)
![Page 6: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/6.jpg)
|
Build chain (resources)
Resources binary container
![Page 7: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/7.jpg)
|
Disassemble apk
Dex compiled classes
Binary resources
Smali files aka source code
PNG assets
Other xml based resources
![Page 8: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/8.jpg)
|
Assemble back apk
Dex compiled classes
Binary resources
Smali files aka source code
PNG assets
Other xml based resources
![Page 9: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/9.jpg)
|
Code example (Java)
src/main/res/values/strings.xml<resources> ... <string name="warning">Warning</string></resources>
src/main/java/com/example/MainActivity.java
import com.example.R;@Overrideprotected void onCreate(Bundle savedInstanceState) {
... Toast.makeText(this, R.string.warning, Toast.LENGTH_SHORT).show();}
![Page 10: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/10.jpg)
|
Code example (smali)
.method protected onCreate(Landroid/os/Bundle;)V...
# string constant number const v0, 0x7f060001 # second Toast parameter const/4 v1, 0x0
invoke-static {p0, v0, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;II)Landroid/widget/Toast;
...
.end method
![Page 11: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/11.jpg)
|
<resources> <string name="warning">Warning</string> <string name="no_worries">No worries</string></resources>
Add new resource
res/values/strings.xml
<resources>...
<public type="string" name="warning" id="0x7f060001" /> <public type="string" name="no_worries" id="0x7f060002" /></resources>
res/values/public.xml
![Page 12: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/12.jpg)
|
Modify Smali code
.method protected onCreate(Landroid/os/Bundle;)V...
# string constant number const v0, 0x7f060002 # second Toast parameter const/4 v1, 0x0
invoke-static {p0, v0, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;II)Landroid/widget/Toast;
...
.end method
![Page 13: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/13.jpg)
|
Let’s do it
We have:
Binary Library Snippet
![Page 14: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/14.jpg)
|
Steps
1.Decompile and unpack2.Resources3.Android Manifest4.Merge codebase5.Find place to run the snippet6.Paste the snippet7.Sign the binary8.Limitations
![Page 15: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/15.jpg)
|
apktool d pola.apk
Decompile and unpack
unzip -d library lib.aar
![Page 16: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/16.jpg)
|
Steps
1.Decompile and unpack2.Resources3.Android Manifest4.Merge codebase5.Find place to run the snippet6.Paste the snippet7.Sign the binary8.Limitations
![Page 17: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/17.jpg)
|
cp -r library/res pola-debug/res
Resources
![Page 18: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/18.jpg)
|
Resources - extend references table
pola-debug/res/values/public.xml
<resources> <public type="layout" name="zxing_capture" id="0x7f04002e" /> <public type="layout" name="first_library_layout" id="0x7f03002f" />
... <public type="string" name="yes" id="0x7f080045" /> <public type="string" name="first_library_string" id="0x7f080046" />
... <public type="id" name="action_twitter" id="0x7f0e0099" /> <public type="id" name="first_library_id" id="0x7f0e009a" />
...</resources>
![Page 19: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/19.jpg)
|
Resources - extend R.java
pola/smali/pl/pola_app/R$string.smali
.class public final Lpl/pola_app/R$string;
.super Ljava/lang/Object;
.source "R.java"
# static fields
...
.field public static final zxing_capture:I = 0x7f080045
.field public static final first_library_string:I = 0x7f03002f
![Page 20: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/20.jpg)
|
While compiling a normal application all fields in R.java are public static final
Resources - digression
CreateReportActivity.smaliconst v2, 0x7f080027
R$string.javapublic static int dialog_delete_photo = 0x7f080027;
Java compiler treats them as constants and inserts a value instead of reference
![Page 21: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/21.jpg)
|
R$string.javapublic static int library_titile=0x7f0500d3;
Resources - digression
sget v1, Lcom/example/R$string;->library_titile:I
In library R.java all fields are public static not final! This means that compiler won’t treat them as constant and will leave references.
R$string.smali.field public static library_titile:I = 0x7f050021
![Page 22: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/22.jpg)
|
• No need to modify library code referencing resources.• While compile a normal application all fields in R.java are public static final
so java compiler treats them as constans and inserts a value instead of reference.
R.string.zxing_capture -> 0x7f080045
• In library R.java all fields are public static not final! This means that compiler won’t treat them as constant and will leave references.
• R.string.first_library_string -> R.string.first_library_string
Resources
![Page 23: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/23.jpg)
|
Steps
1.Decompile and unpack2.Resources3.Android Manifest4.Merge codebase5.Find place to run the snippet6.Paste the snippet7.Sign the binary8.Limitations
![Page 24: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/24.jpg)
|
Android Manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="pl.pola_app" platformBuildVersionCode="23" platformBuildVersionName="6.0-2704002">
... <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.BLUETOOTH" />
<application android:icon="@drawable/ic_launcher" android:label="@string/app_name"/> <activity android:name="pl.pola_app.ui.activity.MainActivity"/>
... <activity android:name="com.mylibrary.HelloActivity"/> </application></manifest>
![Page 25: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/25.jpg)
|
Steps
1.Decompile and unpack2.Resources3.Android Manifest4.Merge codebase5.Find place to run the snippet6.Paste the snippet7.Sign the binary8.Limitations
![Page 26: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/26.jpg)
|
• Aar container has classes• Use dx --dex to convert classes to .dex• And use smali.jar to convert to .smali• Merge two smali dirs
Merge codebase
![Page 27: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/27.jpg)
|
Steps
1.Decompile and unpack2.Resources3.Android Manifest4.Merge codebase5.Find place to run the snippet6.Paste the snippet7.Sign the binary8.Limitations
![Page 28: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/28.jpg)
|
• Having all compiled code in one place is a half-success. • We could use apktool to build back the binary but our code isn’t referenced
an application’s code.• Our business requirement was to start Applause as early as it’s possible.• android.app.Application.onCreate()
Find place to run the snippet
![Page 29: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/29.jpg)
|
Find place to run the snippet
app-debug/AndroidManifest.xml
<manifest package="pl.pola_app" >...
<application android:name="pl.pola_app.PolaApplication"... />...
</application>
</manifest>
app-debug/smali/pl/pola_app/PolaApplication.smali
![Page 30: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/30.jpg)
|
Steps
1.Decompile and unpack2.Resources3.Android Manifest4.Merge codebase5.Find place to run the snippet6.Paste the snippet7.Sign the binary8.Limitations
![Page 31: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/31.jpg)
|
• Change onCreate method• Paste our code before applications’ code• Let’s check Pola’s code
Paste the snippet - method I
31
![Page 32: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/32.jpg)
|
@Override public void onCreate() { super.onCreate();
component = PolaComponent.Initializer.init(this); if(BuildConfig.USE_CRASHLYTICS) { Fabric.with(this, new Crashlytics()); } ButterKnife.setDebug(BuildConfig.DEBUG);
if (BuildConfig.DEBUG) { Timber.plant(new Timber.DebugTree()); } else { Timber.plant(new CrashReportingTree()); }
OkHttpClient client = new OkHttpClient(); client.setConnectTimeout(Utils.TIMEOUT_SECONDS, TimeUnit.SECONDS); client.setReadTimeout(Utils.TIMEOUT_SECONDS, TimeUnit.SECONDS);
retrofit = new Retrofit.Builder() .baseUrl(this.getResources().getString(R.string.pola_api_url)) .addConverterFactory(GsonConverterFactory.create()) .client(client) .build();}
32
![Page 33: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/33.jpg)
|
Transforms to ...
![Page 34: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/34.jpg)
|
.method public onCreate()V .locals 6
.prologue const-wide/16 v4, 0x14
.line 26 invoke-super {p0}, Landroid/app/Application;->onCreate()V
.line 28 invoke-static {p0}, Lpl/pola_app/internal/di/PolaComponent$Initializer;->init(Lpl/pola_app/PolaApplication;)Lpl/pola_app/internal/di/PolaComponent; move-result-object v1 iput-object v1, p0, Lpl/pola_app/PolaApplication;->component:Lpl/pola_app/internal/di/PolaComponent;
.line 32 sget-boolean v1, Lpl/pola_app/BuildConfig;->DEBUG:Z invoke-static {v1}, Lbutterknife/ButterKnife;->setDebug(Z)V
Smali #1
34
![Page 35: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/35.jpg)
|
.line 34 sget-boolean v1, Lpl/pola_app/BuildConfig;->DEBUG:Z if-eqz v1, :cond_0
.line 35 new-instance v1, Ltimber/log/Timber$DebugTree; invoke-direct {v1}, Ltimber/log/Timber$DebugTree;-><init>()V invoke-static {v1}, Ltimber/log/Timber;->plant(Ltimber/log/Timber$Tree;)V
.line 40 :goto_0 new-instance v0, Lcom/squareup/okhttp/OkHttpClient; invoke-direct {v0}, Lcom/squareup/okhttp/OkHttpClient;-><init>()V
.line 41 .local v0, "client":Lcom/squareup/okhttp/OkHttpClient; sget-object v1, Ljava/util/concurrent/TimeUnit;->SECONDS:Ljava/util/concurrent/TimeUnit; invoke-virtual {v0, v4, v5, v1}, Lcom/squareup/okhttp/OkHttpClient;->setConnectTimeout(JLjava/util/concurrent/TimeUnit;)V
Smali #2
35
![Page 36: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/36.jpg)
|
.line 42 sget-object v1, Ljava/util/concurrent/TimeUnit;->SECONDS:Ljava/util/concurrent/TimeUnit; invoke-virtual {v0, v4, v5, v1}, Lcom/squareup/okhttp/OkHttpClient;->setReadTimeout(JLjava/util/concurrent/TimeUnit;)V
.line 44 new-instance v1, Lretrofit/Retrofit$Builder; invoke-direct {v1}, Lretrofit/Retrofit$Builder;-><init>()V
.line 45 invoke-virtual {p0}, Lpl/pola_app/PolaApplication;->getResources()Landroid/content/res/Resources; move-result-object v2 const v3, 0x7f08002f invoke-virtual {v2, v3}, Landroid/content/res/Resources;->getString(I)Ljava/lang/String; move-result-object v2 invoke-virtual {v1, v2}, Lretrofit/Retrofit$Builder;->baseUrl(Ljava/lang/String;)Lretrofit/Retrofit$Builder; move-result-object v1
Smali #3
36
![Page 37: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/37.jpg)
|
.line 46 invoke-static {}, Lretrofit/GsonConverterFactory;->create()Lretrofit/GsonConverterFactory; move-result-object v2 invoke-virtual {v1, v2}, Lretrofit/Retrofit$Builder;->addConverterFactory(Lretrofit/Converter$Factory;)Lretrofit/Retrofit$Builder; move-result-object v1
.line 47 invoke-virtual {v1, v0}, Lretrofit/Retrofit$Builder;->client(Lcom/squareup/okhttp/OkHttpClient;)Lretrofit/Retrofit$Builder; move-result-object v1
.line 48 invoke-virtual {v1}, Lretrofit/Retrofit$Builder;->build()Lretrofit/Retrofit; move-result-object v1 sput-object v1, Lpl/pola_app/PolaApplication;->retrofit:Lretrofit/Retrofit;
Smali #4
37
![Page 38: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/38.jpg)
|
.line 49 return-void
.line 37 .end local v0 # "client":Lcom/squareup/okhttp/OkHttpClient; :cond_0 new-instance v1, Lpl/pola_app/PolaApplication$CrashReportingTree; const/4 v2, 0x0 invoke-direct {v1, v2}, Lpl/pola_app/PolaApplication$CrashReportingTree;-><init>(Lpl/pola_app/PolaApplication$1;)V invoke-static {v1}, Ltimber/log/Timber;->plant(Ltimber/log/Timber$Tree;)V goto :goto_0.end method
Smali #5
38
![Page 39: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/39.jpg)
|
• Paste Applause.startNewSession(context, “app_key”) smali equivalent
• Would be easy ...• But need to be very careful on smali registries - variable, fields, params
Paste the snippet - method I
![Page 40: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/40.jpg)
|
• Replace application:name in Android Manifest to LibraryApplication
Paste the snippet - method II
import android.app.Application
class LibraryApplication extends Application {}
import pl.pola_app.PolaApplication
class LibraryApplication extends PolaApplication {}
• Change base class of our LibraryApplication to one found in original Android Manifest
• Ensure all super calls
<application android:name=".PolApplication” ... >
<application android:name=".LibraryApplication” ... >
![Page 41: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/41.jpg)
|
• Replace super class in header to LibraryApplication
Paste the snippet - method II (PolaApplication.smali)
.method public onCreate()V .line 26 invoke-super {p0},
Landroid/app/Application; ->onCreate()V
.method public onCreate()V .line 26 invoke-super {p0},
Lcom/example/LibraryApplication; ->onCreate()V
• Change all super calls
.class public Lpl/pola_app/PolaApplication;.super Landroid/app/Application;.source "PolaApplication.java"
.class public Lpl/pola_app/PolaApplication;.super Lcom/example/LibraryApplication;.source "PolaApplication.java"
![Page 42: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/42.jpg)
|
Steps
1.Decompile and unpack2.Resources3.Android Manifest4.Merge codebase5.Find place to run the snippet6.Paste the snippet7.Sign the binary8.Limitations
![Page 43: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/43.jpg)
|
• The seal is broken
Sign the binary
md5(“pola.apk”) != md5(“new_pola.apk”)
• I am using a dummy certificate in order to run the binary on device• However some of the main application functionalities won’t work• For example Google Service are checking if certificate’s fingerprint equals
the one set in developer console.
![Page 44: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/44.jpg)
|
Steps
1.Decompile and unpack2.Resources3.Android Manifest4.Merge codebase5.Find place to run the snippet6.Paste the snippet7.Sign the binary8.Limitations
![Page 45: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/45.jpg)
|
• 64k method limit. Adding your library might hit the ceiling. A semi-solution is the check if app supports multi-dex and utilize more smali dirs
• Custom attributes are compiled a bit different. We’ve agreed not to use it to have simpler instrumentation script.
• Dexguard uses not genuine aapt compiler where apktool uses the one from Google which is more strict with file naming.
Limitations
![Page 46: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/46.jpg)
|
Run and enjoy!
![Page 47: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/47.jpg)
|
Questions?
![Page 48: Android Auto instrumentation](https://reader034.vdocument.in/reader034/viewer/2022051520/5887e5b21a28abfb678b716b/html5/thumbnails/48.jpg)
|
THANK YOU
Thanks
and catch me on evening beer session