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