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 android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.Context;
22 import android.util.ArrayMap;
23 
24 import com.android.internal.annotations.GuardedBy;
25 import com.android.internal.annotations.VisibleForTesting;
26 
27 import java.util.Map;
28 
29 /**
30  * Maintains a set of services which {@code Manager} classes should have a {@link
31  * SandboxedSdkContext} in case they are running inside a {@code sdk_sandbox} process.
32  *
33  * <p>This class is required to work around the fact that {@link
34  * android.content.ContextWrapper#getSystemService(String)} delegates the call to the base context,
35  * and reimplementing this method in the {@link SandboxedSdkContext} will require accessing hidden
36  * APIs, which is forbidden for Mainline modules.
37  *
38  * <p>Manager classes that want to behave differently in case they are initiated from inside a
39  * {@code sdk_sandbox} process are expected to call {@link #registerServiceMutator(String,
40  * ServiceMutator)} in their initialization code, e.g. {@link
41  * android.adservices.AdServicesFrameworkInitializer#registerServiceWrappers()}. When a {@code SDK}
42  * running inside a {@code sdk_sandbox} process requests a "sdk sandbox aware" manager via {@link
43  * SandboxedSdkContext#getSystemService(String)} the code inside {@link
44  * SandboxedSdkContext#getSystemService(String)} will use the registered {@link ServiceMutator} to
45  * set the correct context.
46  *
47  * @hide
48  */
49 // TODO(b/242889021): limit this class only to Android T, on U+ we should implement the proper
50 //  platform support.
51 public final class SdkSandboxSystemServiceRegistry {
52 
53     @VisibleForTesting
SdkSandboxSystemServiceRegistry()54     public SdkSandboxSystemServiceRegistry() {}
55 
56     private static final Object sLock = new Object();
57 
58     @GuardedBy("sLock")
59     private static SdkSandboxSystemServiceRegistry sInstance = null;
60 
61     /** Returns an instance of {@link SdkSandboxSystemServiceRegistry}. */
62     @NonNull
getInstance()63     public static SdkSandboxSystemServiceRegistry getInstance() {
64         synchronized (sLock) {
65             if (sInstance == null) {
66                 sInstance = new SdkSandboxSystemServiceRegistry();
67             }
68             return sInstance;
69         }
70     }
71 
72     @GuardedBy("mServiceMutators")
73     private final Map<String, ServiceMutator> mServiceMutators = new ArrayMap<>();
74 
75     /**
76      * Adds a {@code mutator} for the service with given {@code serviceName}.
77      *
78      * <p>This {@code mutator} will be applied inside the {@link
79      * SandboxedSdkContext#getSystemService(String)} method.
80      */
registerServiceMutator( @onNull String serviceName, @NonNull ServiceMutator mutator)81     public void registerServiceMutator(
82             @NonNull String serviceName, @NonNull ServiceMutator mutator) {
83         synchronized (mServiceMutators) {
84             mServiceMutators.put(serviceName, mutator);
85         }
86     }
87 
88     /**
89      * Returns a {@link ServiceMutator} for the given {@code serviceName}, or {@code null} if this
90      * {@code serviceName} doesn't have a mutator registered.
91      */
92     @Nullable
getServiceMutator(@onNull String serviceName)93     public ServiceMutator getServiceMutator(@NonNull String serviceName) {
94         synchronized (mServiceMutators) {
95             return mServiceMutators.get(serviceName);
96         }
97     }
98 
99     /**
100      * A functional interface representing a method on a {@code Manager} class to set the context.
101      *
102      * <p>This interface is required in order to break the circular dependency between {@code
103      * framework-sdsksandbox} and {@code framework-adservices} build targets.
104      */
105     public interface ServiceMutator {
106 
107         /** Sets a {@code context} on the given {@code service}. */
108         @NonNull
setContext(@onNull Object service, @NonNull Context context)109         Object setContext(@NonNull Object service, @NonNull Context context);
110     }
111 }
112