• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.launcher3.util
18 
19 import android.content.Context
20 import android.content.ContextParams
21 import android.content.ContextWrapper
22 import android.content.pm.ApplicationInfo
23 import android.content.res.Configuration
24 import android.os.Bundle
25 import android.os.IBinder
26 import android.os.UserHandle
27 import android.view.Display
28 import androidx.test.core.app.ApplicationProvider
29 import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext
30 import org.junit.Rule
31 import org.junit.rules.ExternalResource
32 import org.junit.rules.TestRule
33 import org.junit.runner.Description
34 import org.junit.runners.model.Statement
35 
36 /**
37  * Sandbox application where created [Context] instances are still sandboxed within it.
38  *
39  * Tests can declare this application as a [Rule], so that it is set up and destroyed automatically.
40  * Alternatively, they can call [init] and [onDestroy] directly. Either way, these need to be called
41  * for it to work and avoid leaks from created singletons.
42  *
43  * The create [Context] APIs construct a `ContextImpl`, which resets the application to the true
44  * application, thus leaving the sandbox. This implementation wraps the created contexts to
45  * propagate this application (see [SandboxApplicationWrapper]).
46  */
47 class SandboxApplication private constructor(private val base: SandboxApplicationWrapper) :
48     SandboxModelContext(base), TestRule {
49 
50     @JvmOverloads
51     constructor(
52         base: Context = ApplicationProvider.getApplicationContext()
53     ) : this(SandboxApplicationWrapper(base))
54 
55     /**
56      * Initializes the sandbox application propagation logic.
57      *
58      * This function either needs to be called manually or automatically through using [Rule].
59      */
initnull60     fun init() {
61         base.app = this@SandboxApplication
62     }
63 
64     /** Returns `this` if [init] was called, otherwise crashes the test. */
getApplicationContextnull65     override fun getApplicationContext(): Context = base.applicationContext
66 
67     override fun shouldCleanUpOnDestroy(): Boolean {
68         // Defer to the true application to decide whether to clean up. For instance, we do not want
69         // to cleanup under Robolectric.
70         val app = ApplicationProvider.getApplicationContext<Context>()
71         return (app as? SandboxContext)?.shouldCleanUpOnDestroy() ?: true
72     }
73 
applynull74     override fun apply(statement: Statement, description: Description): Statement {
75         return object : ExternalResource() {
76                 override fun before() = init()
77 
78                 override fun after() = onDestroy()
79             }
80             .apply(statement, description)
81     }
82 }
83 
84 private class SandboxApplicationWrapper(base: Context, var app: Context? = null) :
85     ContextWrapper(base) {
86 
getApplicationContextnull87     override fun getApplicationContext(): Context {
88         return checkNotNull(app) { "SandboxApplication accessed before #init() was called." }
89     }
90 
createPackageContextnull91     override fun createPackageContext(packageName: String?, flags: Int): Context {
92         return SandboxApplicationWrapper(super.createPackageContext(packageName, flags), app)
93     }
94 
createPackageContextAsUsernull95     override fun createPackageContextAsUser(
96         packageName: String,
97         flags: Int,
98         user: UserHandle,
99     ): Context {
100         return SandboxApplicationWrapper(
101             super.createPackageContextAsUser(packageName, flags, user),
102             app,
103         )
104     }
105 
createContextAsUsernull106     override fun createContextAsUser(user: UserHandle, flags: Int): Context {
107         return SandboxApplicationWrapper(super.createContextAsUser(user, flags), app)
108     }
109 
createApplicationContextnull110     override fun createApplicationContext(application: ApplicationInfo?, flags: Int): Context {
111         return SandboxApplicationWrapper(super.createApplicationContext(application, flags), app)
112     }
113 
createContextForSdkInSandboxnull114     override fun createContextForSdkInSandbox(sdkInfo: ApplicationInfo, flags: Int): Context {
115         return SandboxApplicationWrapper(super.createContextForSdkInSandbox(sdkInfo, flags), app)
116     }
117 
createContextForSplitnull118     override fun createContextForSplit(splitName: String?): Context {
119         return SandboxApplicationWrapper(super.createContextForSplit(splitName), app)
120     }
121 
createConfigurationContextnull122     override fun createConfigurationContext(overrideConfiguration: Configuration): Context {
123         return SandboxApplicationWrapper(
124             super.createConfigurationContext(overrideConfiguration),
125             app,
126         )
127     }
128 
createDisplayContextnull129     override fun createDisplayContext(display: Display): Context {
130         return SandboxApplicationWrapper(super.createDisplayContext(display), app)
131     }
132 
createDeviceContextnull133     override fun createDeviceContext(deviceId: Int): Context {
134         return SandboxApplicationWrapper(super.createDeviceContext(deviceId), app)
135     }
136 
createWindowContextnull137     override fun createWindowContext(type: Int, options: Bundle?): Context {
138         return SandboxApplicationWrapper(super.createWindowContext(type, options), app)
139     }
140 
createWindowContextnull141     override fun createWindowContext(display: Display, type: Int, options: Bundle?): Context {
142         return SandboxApplicationWrapper(super.createWindowContext(display, type, options), app)
143     }
144 
createContextnull145     override fun createContext(contextParams: ContextParams): Context {
146         return SandboxApplicationWrapper(super.createContext(contextParams), app)
147     }
148 
createAttributionContextnull149     override fun createAttributionContext(attributionTag: String?): Context {
150         return SandboxApplicationWrapper(super.createAttributionContext(attributionTag), app)
151     }
152 
createCredentialProtectedStorageContextnull153     override fun createCredentialProtectedStorageContext(): Context {
154         return SandboxApplicationWrapper(super.createCredentialProtectedStorageContext(), app)
155     }
156 
createDeviceProtectedStorageContextnull157     override fun createDeviceProtectedStorageContext(): Context {
158         return SandboxApplicationWrapper(super.createDeviceProtectedStorageContext(), app)
159     }
160 
createTokenContextnull161     override fun createTokenContext(token: IBinder, display: Display): Context {
162         return SandboxApplicationWrapper(super.createTokenContext(token, display), app)
163     }
164 }
165