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