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.app.sdksandbox.sdkprovider.SdkSandboxClientImportanceListener;
21 import android.app.sdksandbox.sdkprovider.SdkSandboxController.SdkSandboxClientImportanceListenerProxy;
22 import android.os.IBinder;
23 import android.os.RemoteException;
24 import android.util.Log;
25 
26 import com.android.internal.annotations.GuardedBy;
27 import com.android.internal.annotations.VisibleForTesting;
28 
29 import java.util.ArrayList;
30 import java.util.Objects;
31 
32 /**
33  * Singleton for a privacy sandbox, which is initialised when the sandbox is created.
34  *
35  * @hide
36  */
37 public class SdkSandboxLocalSingleton {
38 
39     private static final String TAG = "SandboxLocalSingleton";
40     private static SdkSandboxLocalSingleton sInstance = null;
41     private final ISdkToServiceCallback mSdkToServiceCallback;
42 
43     private final Object mLock = new Object();
44 
45     @GuardedBy("mLock")
46     private final ArrayList<SdkSandboxClientImportanceListenerProxy>
47             mSdkSandboxClientImportanceListeners = new ArrayList<>();
48 
SdkSandboxLocalSingleton(ISdkToServiceCallback sdkToServiceCallback)49     private SdkSandboxLocalSingleton(ISdkToServiceCallback sdkToServiceCallback) {
50         mSdkToServiceCallback = sdkToServiceCallback;
51     }
52 
53     /**
54      * Returns a singleton instance of this class. TODO(b/247313241): Fix parameter once aidl issues
55      * are fixed.
56      *
57      * @param sdkToServiceBinder callback to support communication with the {@link
58      *     com.android.server.sdksandbox.SdkSandboxManagerService}
59      * @throws IllegalStateException if singleton is already initialised
60      * @throws UnsupportedOperationException if the interface passed is not of type {@link
61      *     ISdkToServiceCallback}
62      */
initInstance(@onNull IBinder sdkToServiceBinder)63     public static synchronized void initInstance(@NonNull IBinder sdkToServiceBinder) {
64         if (sInstance != null) {
65             Log.d(TAG, "Already Initialised");
66             return;
67         }
68         try {
69             if (Objects.nonNull(sdkToServiceBinder)
70                     && sdkToServiceBinder
71                             .getInterfaceDescriptor()
72                             .equals(ISdkToServiceCallback.DESCRIPTOR)) {
73                 sInstance =
74                         new SdkSandboxLocalSingleton(
75                                 ISdkToServiceCallback.Stub.asInterface(sdkToServiceBinder));
76                 return;
77             }
78         } catch (RemoteException e) {
79             // Fall through to the failure case.
80         }
81         throw new UnsupportedOperationException("IBinder not supported");
82     }
83 
84     /** Returns an already initialised singleton instance of this class. */
getExistingInstance()85     public static SdkSandboxLocalSingleton getExistingInstance() {
86         if (sInstance == null) {
87             throw new IllegalStateException("SdkSandboxLocalSingleton not found");
88         }
89         return sInstance;
90     }
91 
92     /** To reset the singleton. Only for Testing. */
93     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
destroySingleton()94     public static void destroySingleton() {
95         sInstance = null;
96     }
97 
98     /** Gets the callback to the {@link com.android.server.sdksandbox.SdkSandboxManagerService} */
getSdkToServiceCallback()99     public ISdkToServiceCallback getSdkToServiceCallback() {
100         return mSdkToServiceCallback;
101     }
102 
103     /** Registers a listener to listen to client app foreground state change. */
registerSdkSandboxClientImportanceListener( @onNull SdkSandboxClientImportanceListenerProxy listener)104     public void registerSdkSandboxClientImportanceListener(
105             @NonNull SdkSandboxClientImportanceListenerProxy listener) {
106         synchronized (mLock) {
107             mSdkSandboxClientImportanceListeners.add(listener);
108         }
109     }
110 
111     /**
112      * Unregisters a listener previously registered using {@link
113      * SdkSandboxLocalSingleton#registerSdkSandboxClientImportanceListener(
114      * SdkSandboxClientImportanceListenerProxy)}.
115      */
unregisterSdkSandboxClientImportanceListener( @onNull SdkSandboxClientImportanceListener listener)116     public void unregisterSdkSandboxClientImportanceListener(
117             @NonNull SdkSandboxClientImportanceListener listener) {
118         synchronized (mLock) {
119             for (int i = mSdkSandboxClientImportanceListeners.size() - 1; i >= 0; --i) {
120                 if (mSdkSandboxClientImportanceListeners.get(i).listener == listener) {
121                     mSdkSandboxClientImportanceListeners.remove(i);
122                 }
123             }
124         }
125     }
126 
127     /**
128      * Notifies all listeners registered using {@link
129      * SdkSandboxLocalSingleton#registerSdkSandboxClientImportanceListener(
130      * SdkSandboxClientImportanceListenerProxy)} about a client's foreground state change.
131      */
notifySdkSandboxClientImportanceChange(boolean isForeground)132     public void notifySdkSandboxClientImportanceChange(boolean isForeground) {
133         synchronized (mLock) {
134             for (int i = 0; i < mSdkSandboxClientImportanceListeners.size(); ++i) {
135                 mSdkSandboxClientImportanceListeners
136                         .get(i)
137                         .onForegroundImportanceChanged(isForeground);
138             }
139         }
140     }
141 }
142