• 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 android.platform.test.ravenwood;
18 
19 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK;
20 
21 import android.content.ClipboardManager;
22 import android.content.Context;
23 import android.content.res.AssetManager;
24 import android.content.res.Resources;
25 import android.content.res.Resources.Theme;
26 import android.hardware.ISerialManager;
27 import android.hardware.SerialManager;
28 import android.os.Handler;
29 import android.os.HandlerThread;
30 import android.os.Looper;
31 import android.os.PermissionEnforcer;
32 import android.os.ServiceManager;
33 import android.os.UserHandle;
34 import android.ravenwood.example.BlueManager;
35 import android.ravenwood.example.RedManager;
36 import android.util.ArrayMap;
37 import android.util.Singleton;
38 
39 import com.android.internal.annotations.GuardedBy;
40 
41 import java.io.File;
42 import java.io.IOException;
43 import java.util.Objects;
44 import java.util.concurrent.Executor;
45 import java.util.function.Supplier;
46 
47 public class RavenwoodContext extends RavenwoodBaseContext {
48     private static final String TAG = com.android.ravenwood.common.RavenwoodCommonUtils.TAG;
49 
50     private final Object mLock = new Object();
51     private final String mPackageName;
52     private final HandlerThread mMainThread;
53 
54     private final RavenwoodPermissionEnforcer mEnforcer = new RavenwoodPermissionEnforcer();
55 
56     private final ArrayMap<Class<?>, String> mClassToName = new ArrayMap<>();
57     private final ArrayMap<String, Supplier<?>> mNameToFactory = new ArrayMap<>();
58 
59     private final File mFilesDir;
60     private final File mCacheDir;
61     private final Supplier<Resources> mResourcesSupplier;
62 
63     private RavenwoodContext mAppContext;
64 
65     @GuardedBy("mLock")
66     private Resources mResources;
67 
68     @GuardedBy("mLock")
69     private Resources.Theme mTheme;
70 
registerService(Class<?> serviceClass, String serviceName, Supplier<?> serviceSupplier)71     private void registerService(Class<?> serviceClass, String serviceName,
72             Supplier<?> serviceSupplier) {
73         mClassToName.put(serviceClass, serviceName);
74         mNameToFactory.put(serviceName, serviceSupplier);
75     }
76 
RavenwoodContext(String packageName, HandlerThread mainThread, Supplier<Resources> resourcesSupplier)77     public RavenwoodContext(String packageName, HandlerThread mainThread,
78             Supplier<Resources> resourcesSupplier) throws IOException {
79         mPackageName = packageName;
80         mMainThread = mainThread;
81         mResourcesSupplier = resourcesSupplier;
82         mFilesDir = createTempDir(packageName + "_files-dir");
83         mCacheDir = createTempDir(packageName + "_cache-dir");
84 
85         // Services provided by a typical shipping device
86         registerService(ClipboardManager.class,
87                 Context.CLIPBOARD_SERVICE, memoize(() ->
88                         new ClipboardManager(this, getMainThreadHandler())));
89         registerService(PermissionEnforcer.class,
90                 Context.PERMISSION_ENFORCER_SERVICE, () -> mEnforcer);
91         registerService(SerialManager.class,
92                 Context.SERIAL_SERVICE, memoize(() ->
93                         new SerialManager(this, ISerialManager.Stub.asInterface(
94                                 ServiceManager.getService(Context.SERIAL_SERVICE)))
95                 ));
96 
97         // Additional services we provide for testing purposes
98         registerService(BlueManager.class,
99                 BlueManager.SERVICE_NAME, memoize(() -> new BlueManager()));
100         registerService(RedManager.class,
101                 RedManager.SERVICE_NAME, memoize(() -> new RedManager()));
102     }
103 
104     @Override
getSystemService(String serviceName)105     public Object getSystemService(String serviceName) {
106         // TODO: pivot to using SystemServiceRegistry
107         final Supplier<?> serviceSupplier = mNameToFactory.get(serviceName);
108         if (serviceSupplier != null) {
109             return serviceSupplier.get();
110         } else {
111             throw new UnsupportedOperationException(
112                     "Service " + serviceName + " not yet supported under Ravenwood");
113         }
114     }
115 
cleanUp()116     void cleanUp() {
117         deleteDir(mFilesDir);
118         deleteDir(mCacheDir);
119     }
120 
121     @Override
getSystemServiceName(Class<?> serviceClass)122     public String getSystemServiceName(Class<?> serviceClass) {
123         // TODO: pivot to using SystemServiceRegistry
124         final String serviceName = mClassToName.get(serviceClass);
125         if (serviceName != null) {
126             return serviceName;
127         } else {
128             throw new UnsupportedOperationException(
129                     "Service " + serviceClass + " not yet supported under Ravenwood");
130         }
131     }
132 
133     @Override
getMainLooper()134     public Looper getMainLooper() {
135         return mMainThread.getLooper();
136     }
137 
138     @Override
getMainThreadHandler()139     public Handler getMainThreadHandler() {
140         return mMainThread.getThreadHandler();
141     }
142 
143     @Override
getMainExecutor()144     public Executor getMainExecutor() {
145         return mMainThread.getThreadExecutor();
146     }
147 
148     @Override
getPackageName()149     public String getPackageName() {
150         return mPackageName;
151     }
152 
153     @Override
getOpPackageName()154     public String getOpPackageName() {
155         return mPackageName;
156     }
157 
158     @Override
getAttributionTag()159     public String getAttributionTag() {
160         return null;
161     }
162 
163     @Override
getUser()164     public UserHandle getUser() {
165         return android.os.UserHandle.of(android.os.UserHandle.myUserId());
166     }
167 
168     @Override
getUserId()169     public int getUserId() {
170         return android.os.UserHandle.myUserId();
171     }
172 
173     @Override
getDeviceId()174     public int getDeviceId() {
175         return Context.DEVICE_ID_DEFAULT;
176     }
177 
178     @Override
getFilesDir()179     public File getFilesDir() {
180         return mFilesDir;
181     }
182 
183     @Override
getCacheDir()184     public File getCacheDir() {
185         return mCacheDir;
186     }
187 
188     @Override
deleteFile(String name)189     public boolean deleteFile(String name) {
190         File f = new File(name);
191         return f.delete();
192     }
193 
194     @Override
getResources()195     public Resources getResources() {
196         synchronized (mLock) {
197             if (mResources == null) {
198                 mResources = mResourcesSupplier.get();
199             }
200             return mResources;
201         }
202     }
203 
204     @Override
getAssets()205     public AssetManager getAssets() {
206         return getResources().getAssets();
207     }
208 
209     @Override
getTheme()210     public Theme getTheme() {
211         synchronized (mLock) {
212             if (mTheme == null) {
213                 mTheme = getResources().newTheme();
214             }
215             return mTheme;
216         }
217     }
218 
219     @Override
getPackageResourcePath()220     public String getPackageResourcePath() {
221         return new File(RAVENWOOD_RESOURCE_APK).getAbsolutePath();
222     }
223 
setApplicationContext(RavenwoodContext appContext)224     public void setApplicationContext(RavenwoodContext appContext) {
225         mAppContext = appContext;
226     }
227 
228     @Override
getApplicationContext()229     public Context getApplicationContext() {
230         return mAppContext;
231     }
232 
233     @Override
isRestricted()234     public boolean isRestricted() {
235         return false;
236     }
237 
238     @Override
canLoadUnsafeResources()239     public boolean canLoadUnsafeResources() {
240         return true;
241     }
242 
243     /**
244      * Wrap the given {@link Supplier} to become memoized.
245      *
246      * The underlying {@link Supplier} will only be invoked once, and that result will be cached
247      * and returned for any future requests.
248      */
memoize(ThrowingSupplier<T> supplier)249     private static <T> Supplier<T> memoize(ThrowingSupplier<T> supplier) {
250         final Singleton<T> singleton = new Singleton<>() {
251             @Override
252             protected T create() {
253                 try {
254                     return supplier.get();
255                 } catch (Exception e) {
256                     throw new RuntimeException(e);
257                 }
258             }
259         };
260         return () -> {
261             return singleton.get();
262         };
263     }
264 
265     public interface ThrowingSupplier<T> {
get()266         T get() throws Exception;
267     }
268 
269 
createTempDir(String prefix)270     static File createTempDir(String prefix) throws IOException {
271         // Create a temp file, delete it and recreate it as a directory.
272         final File dir = File.createTempFile(prefix + "-", "");
273         dir.delete();
274         dir.mkdirs();
275         return dir;
276     }
277 
deleteDir(File dir)278     static void deleteDir(File dir) {
279         File[] children = dir.listFiles();
280         if (children != null) {
281             for (File child : children) {
282                 if (child.isDirectory()) {
283                     deleteDir(child);
284                 } else {
285                     child.delete();
286                 }
287             }
288         }
289     }
290 }
291