1 /* 2 * Copyright (C) 2022 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 android.app.sdksandbox; 18 19 import static android.app.sdksandbox.SdkSandboxSystemServiceRegistry.ServiceMutator; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.content.Context; 24 import android.content.ContextWrapper; 25 import android.content.pm.ApplicationInfo; 26 import android.content.res.AssetManager; 27 import android.content.res.Resources; 28 29 import com.android.internal.annotations.VisibleForTesting; 30 import com.android.modules.utils.build.SdkLevel; 31 32 import java.io.File; 33 34 /** 35 * Refers to the context of the SDK loaded in the SDK sandbox process. 36 * 37 * <p>It is a wrapper of the client application (which loading SDK to the sandbox) context, to 38 * represent the context of the SDK loaded by that application. 39 * 40 * <p>An instance of the {@link SandboxedSdkContext} will be created by the SDK sandbox, and then 41 * attached to the {@link SandboxedSdkProvider} after the SDK is loaded. 42 * 43 * <p>Each sdk will get their own private storage directories and the file storage API on this 44 * object will utilize those areas. 45 * 46 * @hide 47 */ 48 public final class SandboxedSdkContext extends ContextWrapper { 49 50 private final Resources mResources; 51 private final AssetManager mAssets; 52 private final String mClientPackageName; 53 private final String mSdkName; 54 private final ApplicationInfo mSdkProviderInfo; 55 @Nullable private final File mCeDataDir; 56 @Nullable private final File mDeDataDir; 57 private final SdkSandboxSystemServiceRegistry mSdkSandboxSystemServiceRegistry; 58 private final ClassLoader mClassLoader; 59 60 /** 61 * Starting from Android U, we customize the base context directly instead of adding information 62 * to the ContextWrapper. 63 */ 64 private final boolean mIsCustomizedSdkContextEnabled = SdkLevel.isAtLeastU(); 65 SandboxedSdkContext( @onNull Context baseContext, @NonNull ClassLoader classLoader, @NonNull String clientPackageName, @NonNull ApplicationInfo info, @NonNull String sdkName, @Nullable String sdkCeDataDir, @Nullable String sdkDeDataDir)66 public SandboxedSdkContext( 67 @NonNull Context baseContext, 68 @NonNull ClassLoader classLoader, 69 @NonNull String clientPackageName, 70 @NonNull ApplicationInfo info, 71 @NonNull String sdkName, 72 @Nullable String sdkCeDataDir, 73 @Nullable String sdkDeDataDir) { 74 this( 75 baseContext, 76 classLoader, 77 clientPackageName, 78 info, 79 sdkName, 80 sdkCeDataDir, 81 sdkDeDataDir, 82 SdkSandboxSystemServiceRegistry.getInstance()); 83 } 84 85 @VisibleForTesting SandboxedSdkContext( @onNull Context baseContext, @NonNull ClassLoader classLoader, @NonNull String clientPackageName, @NonNull ApplicationInfo info, @NonNull String sdkName, @Nullable String sdkCeDataDir, @Nullable String sdkDeDataDir, SdkSandboxSystemServiceRegistry sdkSandboxSystemServiceRegistry)86 public SandboxedSdkContext( 87 @NonNull Context baseContext, 88 @NonNull ClassLoader classLoader, 89 @NonNull String clientPackageName, 90 @NonNull ApplicationInfo info, 91 @NonNull String sdkName, 92 @Nullable String sdkCeDataDir, 93 @Nullable String sdkDeDataDir, 94 SdkSandboxSystemServiceRegistry sdkSandboxSystemServiceRegistry) { 95 super(baseContext); 96 mClientPackageName = clientPackageName; 97 mSdkName = sdkName; 98 mSdkProviderInfo = info; 99 Resources resources = null; 100 try { 101 resources = baseContext.getPackageManager().getResourcesForApplication(info); 102 } catch (Exception ignored) { 103 } 104 105 if (resources != null) { 106 mResources = resources; 107 mAssets = resources.getAssets(); 108 } else { 109 mResources = null; 110 mAssets = null; 111 } 112 113 mCeDataDir = (sdkCeDataDir != null) ? new File(sdkCeDataDir) : null; 114 mDeDataDir = (sdkDeDataDir != null) ? new File(sdkDeDataDir) : null; 115 116 mSdkSandboxSystemServiceRegistry = sdkSandboxSystemServiceRegistry; 117 mClassLoader = classLoader; 118 } 119 120 /** 121 * Return a new Context object for the current SandboxedSdkContext but whose storage APIs are 122 * backed by sdk specific credential-protected storage. 123 * 124 * @see Context#isCredentialProtectedStorage() 125 */ 126 @Override 127 @NonNull createCredentialProtectedStorageContext()128 public Context createCredentialProtectedStorageContext() { 129 Context newBaseContext = getBaseContext().createCredentialProtectedStorageContext(); 130 return createContextWithNewBase(newBaseContext); 131 } 132 133 /** 134 * Return a new Context object for the current SandboxedSdkContext but whose storage 135 * APIs are backed by sdk specific device-protected storage. 136 * 137 * @see Context#isDeviceProtectedStorage() 138 */ 139 @Override 140 @NonNull createDeviceProtectedStorageContext()141 public Context createDeviceProtectedStorageContext() { 142 Context newBaseContext = getBaseContext().createDeviceProtectedStorageContext(); 143 return createContextWithNewBase(newBaseContext); 144 } 145 146 /** 147 * Returns a new SandboxedSdkContext object with the same fields of the caller {@link 148 * SandboxedSdkContext} but with new base {@link Context}. 149 * 150 * @param newBaseContext The new base {@link Context} to use. 151 */ 152 @NonNull createContextWithNewBase(@onNull Context newBaseContext)153 public SandboxedSdkContext createContextWithNewBase(@NonNull Context newBaseContext) { 154 return new SandboxedSdkContext( 155 newBaseContext, 156 mClassLoader, 157 mClientPackageName, 158 mSdkProviderInfo, 159 mSdkName, 160 (mCeDataDir != null) ? mCeDataDir.toString() : null, 161 (mDeDataDir != null) ? mDeDataDir.toString() : null); 162 } 163 164 /** 165 * Returns the SDK name defined in the SDK's manifest. 166 */ 167 @NonNull getSdkName()168 public String getSdkName() { 169 return mSdkName; 170 } 171 172 /** 173 * Returns the SDK package name defined in the SDK's manifest. 174 * 175 * @hide 176 */ 177 @NonNull getSdkPackageName()178 public String getSdkPackageName() { 179 return mSdkProviderInfo.packageName; 180 } 181 182 /** 183 * Returns the package name of the client application corresponding to the sandbox. 184 * 185 */ 186 @NonNull getClientPackageName()187 public String getClientPackageName() { 188 return mClientPackageName; 189 } 190 191 /** Returns the resources defined in the SDK's .apk file. */ 192 @Override 193 @Nullable getResources()194 public Resources getResources() { 195 if (mIsCustomizedSdkContextEnabled) { 196 return getBaseContext().getResources(); 197 } 198 return mResources; 199 } 200 201 /** Returns the assets defined in the SDK's .apk file. */ 202 @Override 203 @Nullable getAssets()204 public AssetManager getAssets() { 205 if (mIsCustomizedSdkContextEnabled) { 206 return getBaseContext().getAssets(); 207 } 208 return mAssets; 209 } 210 211 /** Returns sdk-specific internal storage directory. */ 212 @Override 213 @Nullable getDataDir()214 public File getDataDir() { 215 if (mIsCustomizedSdkContextEnabled) { 216 return getBaseContext().getDataDir(); 217 } 218 219 File res = null; 220 if (isCredentialProtectedStorage()) { 221 res = mCeDataDir; 222 } else if (isDeviceProtectedStorage()) { 223 res = mDeDataDir; 224 } 225 if (res == null) { 226 throw new RuntimeException("No data directory found for sdk: " + getSdkName()); 227 } 228 return res; 229 } 230 231 @Override 232 @Nullable getSystemService(String name)233 public Object getSystemService(String name) { 234 if (name == null) { 235 return null; 236 } 237 Object service = getBaseContext().getSystemService(name); 238 ServiceMutator serviceMutator = mSdkSandboxSystemServiceRegistry.getServiceMutator(name); 239 if (serviceMutator != null) { 240 service = serviceMutator.setContext(service, this); 241 } 242 return service; 243 } 244 245 @Override getClassLoader()246 public ClassLoader getClassLoader() { 247 if (mIsCustomizedSdkContextEnabled) { 248 return getBaseContext().getClassLoader(); 249 } 250 return mClassLoader; 251 } 252 } 253