Skip to content

Commit 79b11fc

Browse files
committed
Add custom init logic
1 parent e75d7c9 commit 79b11fc

File tree

3 files changed

+69
-8
lines changed

3 files changed

+69
-8
lines changed

README.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1-
# Android Mock Context
1+
# Android Mocks for `app_process`
22

3-
A custom `Context` implementation that allows for easy customization of common `Context` properties. This allows experimenting with "app level" interactions while using `app_process` instead.
3+
A custom `Context` implementation and other core features that allows for easy customization of common core Android classes. This allows experimenting with "app level" interactions while using `app_process` instead.
4+
5+
## Usage
6+
7+
Set up your process by calling:
8+
9+
```kotlin
10+
Common.initialize(classLoader)
11+
```
12+
13+
This will set up your thread and initialize some core systems set up by `ActivityThread` during a normal app launch.
14+
15+
You can create a custom mocked `Context` using:
16+
17+
```kotlin
18+
MockContext.createWithAppContext(classLoader, mainThread, "com.android.settings")
19+
```
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.penumbraos.appprocessmocks
2+
3+
import android.app.ActivityThread
4+
import android.os.Looper
5+
6+
@Suppress("DEPRECATION")
7+
class Common {
8+
companion object {
9+
/**
10+
* Set up the `app_process` environment to mirror that of a native app. You should call `MockContext.createWithAppContext` first
11+
*/
12+
fun initialize(classLoader: ClassLoader): ActivityThread {
13+
Looper.prepareMainLooper()
14+
15+
// The android.jar plugin currently isn't letting us use these classes directly
16+
val activityThreadClass = classLoader.loadClass("android.app.ActivityThread")
17+
val activityThreadConstructor = activityThreadClass.getDeclaredConstructor()
18+
activityThreadConstructor.isAccessible = true
19+
val mainActivityThread = activityThreadConstructor.newInstance() as ActivityThread
20+
21+
ActivityThread.initializeMainlineModules()
22+
23+
return mainActivityThread
24+
}
25+
}
26+
}

src/main/java/com/penumbraos/appprocessmocks/MockContext.kt

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import android.annotation.SuppressLint
22
import android.app.ActivityThread
3-
import android.app.IApplicationThread
43
import android.app.LoadedApk
54
import android.content.AttributionSource
65
import android.content.BroadcastReceiver
@@ -10,24 +9,25 @@ import android.content.Context
109
import android.content.ContextWrapper
1110
import android.content.Intent
1211
import android.content.IntentFilter
12+
import android.content.pm.ApplicationInfo
1313
import android.content.res.AssetManager
1414
import android.content.res.Resources
1515
import android.os.Looper
1616
import java.io.File
1717

1818
@SuppressLint("DiscouragedPrivateApi", "UnspecifiedRegisterReceiverFlag")
19-
class MockContext(base: Context) : ContextWrapper(base) {
19+
class MockContext(base: Context, basePackageName: String? = null) : ContextWrapper(base) {
2020

2121
var mockAttributionTag: String? = null
2222

2323
/**
2424
* Defaults to com.android.settings and uid 1000
2525
*/
26-
var mockAttributionSource: AttributionSource? = AttributionSource.Builder(1000).setPackageName("com.android.settings").setAttributionTag("*tag*").build()
26+
var mockAttributionSource: AttributionSource? = AttributionSource.Builder(1000).setPackageName(basePackageName ?: "com.android.settings").setAttributionTag("*tag*").build()
2727
private val services = mutableMapOf<String, Any>()
2828
var mockApplicationContext: Context? = null
2929
var mockResources: Resources? = null
30-
var mockPackageName: String? = null
30+
var mockPackageName: String? = basePackageName
3131
var mockPackageResourcePath: String? = null
3232
var mockPackageCodePath: String? = null
3333
var mockAssets: AssetManager? = null
@@ -49,15 +49,34 @@ class MockContext(base: Context) : ContextWrapper(base) {
4949
var mockUnregisterReceiver: ((BroadcastReceiver?) -> Unit)? = null
5050

5151
companion object {
52-
fun createWithAppContext(classLoader: ClassLoader, mainThread: IApplicationThread, loadedApk: LoadedApk): Context {
52+
fun createWithAppContext(classLoader: ClassLoader, mainThread: ActivityThread, packageName: String): Context {
53+
// The android.jar plugin currently isn't letting us use these classes directly
54+
val loadedApkClass = classLoader.loadClass("android.app.LoadedApk")
55+
val loadedApkConstructor = loadedApkClass.getDeclaredConstructor(ActivityThread::class.java)
56+
loadedApkConstructor.isAccessible = true
57+
val loadedApk = loadedApkConstructor.newInstance(mainThread) as LoadedApk
58+
59+
val loadedApkPackageNameField = loadedApkClass.getDeclaredField("mPackageName")
60+
loadedApkPackageNameField.isAccessible = true
61+
loadedApkPackageNameField.set(loadedApk, packageName)
62+
63+
val loadedApkApplicationInfoField = loadedApkClass.getDeclaredField("mApplicationInfo")
64+
loadedApkApplicationInfoField.isAccessible = true
65+
val applicationInfo = loadedApkApplicationInfoField.get(loadedApk) as ApplicationInfo
66+
applicationInfo.packageName = packageName
67+
68+
return createWithAppContext(classLoader, mainThread, loadedApk, packageName)
69+
}
70+
71+
fun createWithAppContext(classLoader: ClassLoader, mainThread: ActivityThread, loadedApk: LoadedApk, packageName: String? = null): Context {
5372
// The android.jar plugin currently isn't letting us use these classes directly
5473
val contextImplClass = classLoader.loadClass("android.app.ContextImpl")
5574
val contextImplConstructor = contextImplClass.getDeclaredMethod("createAppContext",
5675
ActivityThread::class.java, LoadedApk::class.java)
5776
contextImplConstructor.isAccessible = true
5877

5978
val context = contextImplConstructor.invoke(null, mainThread, loadedApk) as Context
60-
return MockContext(context)
79+
return MockContext(context, packageName)
6180
}
6281
}
6382

0 commit comments

Comments
 (0)