/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.app.sdksandbox; import static android.app.sdksandbox.SdkSandboxSystemServiceRegistry.ServiceMutator; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.ContextWrapper; import android.content.pm.ApplicationInfo; import android.content.res.AssetManager; import android.content.res.Resources; import com.android.internal.annotations.VisibleForTesting; import java.io.File; /** * Refers to the context of the SDK loaded in the SDK sandbox process. * *

It is a wrapper of the client application (which loading SDK to the sandbox) context, to * represent the context of the SDK loaded by that application. * *

An instance of the {@link SandboxedSdkContext} will be created by the SDK sandbox, and then * attached to the {@link SandboxedSdkProvider} after the SDK is loaded. * *

Each sdk will get their own private storage directory and the file storage API on this object * will utilize those area. * *

Note: All APIs defined in this class are not stable and subject to change. * * @hide */ public final class SandboxedSdkContext extends ContextWrapper { private final Resources mResources; private final AssetManager mAssets; private final String mClientPackageName; private final String mSdkName; private final ApplicationInfo mSdkProviderInfo; @Nullable private final File mCeDataDir; @Nullable private final File mDeDataDir; private final SdkSandboxSystemServiceRegistry mSdkSandboxSystemServiceRegistry; private final ClassLoader mClassLoader; public SandboxedSdkContext( @NonNull Context baseContext, @NonNull ClassLoader classLoader, @NonNull String clientPackageName, @NonNull ApplicationInfo info, @NonNull String sdkName, @Nullable String sdkCeDataDir, @Nullable String sdkDeDataDir) { this( baseContext, classLoader, clientPackageName, info, sdkName, sdkCeDataDir, sdkDeDataDir, SdkSandboxSystemServiceRegistry.getInstance()); } @VisibleForTesting public SandboxedSdkContext( @NonNull Context baseContext, @NonNull ClassLoader classLoader, @NonNull String clientPackageName, @NonNull ApplicationInfo info, @NonNull String sdkName, @Nullable String sdkCeDataDir, @Nullable String sdkDeDataDir, SdkSandboxSystemServiceRegistry sdkSandboxSystemServiceRegistry) { super(baseContext); mClientPackageName = clientPackageName; mSdkName = sdkName; mSdkProviderInfo = info; Resources resources = null; try { resources = baseContext.getPackageManager().getResourcesForApplication(info); } catch (Exception ignored) { } if (resources != null) { mResources = resources; mAssets = resources.getAssets(); } else { mResources = null; mAssets = null; } mCeDataDir = (sdkCeDataDir != null) ? new File(sdkCeDataDir) : null; mDeDataDir = (sdkDeDataDir != null) ? new File(sdkDeDataDir) : null; mSdkSandboxSystemServiceRegistry = sdkSandboxSystemServiceRegistry; mClassLoader = classLoader; } /** * Return a new Context object for the current SandboxedSdkContext but whose storage APIs are * backed by sdk specific credential-protected storage. * * @see Context#isCredentialProtectedStorage() */ @Override @NonNull public Context createCredentialProtectedStorageContext() { Context newBaseContext = getBaseContext().createCredentialProtectedStorageContext(); return new SandboxedSdkContext( newBaseContext, mClassLoader, mClientPackageName, mSdkProviderInfo, mSdkName, (mCeDataDir != null) ? mCeDataDir.toString() : null, (mDeDataDir != null) ? mDeDataDir.toString() : null); } /** * Return a new Context object for the current SandboxedSdkContext but whose storage * APIs are backed by sdk specific device-protected storage. * * @see Context#isDeviceProtectedStorage() */ @Override @NonNull public Context createDeviceProtectedStorageContext() { Context newBaseContext = getBaseContext().createDeviceProtectedStorageContext(); return new SandboxedSdkContext( newBaseContext, mClassLoader, mClientPackageName, mSdkProviderInfo, mSdkName, (mCeDataDir != null) ? mCeDataDir.toString() : null, (mDeDataDir != null) ? mDeDataDir.toString() : null); } /** * Returns the SDK name defined in the SDK's manifest. */ @NonNull public String getSdkName() { return mSdkName; } /** * Returns the SDK package name defined in the SDK's manifest. * * @hide */ @NonNull public String getSdkPackageName() { return mSdkProviderInfo.packageName; } /** * Returns the package name of the client application corresponding to the sandbox. * */ @NonNull public String getClientPackageName() { return mClientPackageName; } /** Returns the resources defined in the SDK's .apk file. */ @Override @Nullable public Resources getResources() { return mResources; } /** Returns the assets defined in the SDK's .apk file. */ @Override @Nullable public AssetManager getAssets() { return mAssets; } /** Returns sdk-specific internal storage directory. */ @Override @Nullable public File getDataDir() { File res = null; if (isCredentialProtectedStorage()) { res = mCeDataDir; } else if (isDeviceProtectedStorage()) { res = mDeDataDir; } if (res == null) { throw new RuntimeException("No data directory found for sdk: " + getSdkName()); } return res; } @Override @Nullable public Object getSystemService(String name) { if (name == null) { return null; } Object service = getBaseContext().getSystemService(name); ServiceMutator serviceMutator = mSdkSandboxSystemServiceRegistry.getServiceMutator(name); if (serviceMutator != null) { service = serviceMutator.setContext(service, this); } return service; } @Override public ClassLoader getClassLoader() { return mClassLoader; } }