1 /* 2 * Copyright (C) 2023 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.sdkprovider; 18 19 import android.annotation.NonNull; 20 import android.app.Activity; 21 import android.os.Binder; 22 import android.os.Build; 23 import android.os.IBinder; 24 import android.util.ArrayMap; 25 26 import androidx.annotation.RequiresApi; 27 28 import com.android.internal.annotations.GuardedBy; 29 30 import java.util.Map; 31 32 /** 33 * It is a Singleton class to store the registered {@link SdkSandboxActivityHandler} instances and 34 * their associated {@link Activity} instances. 35 * 36 * @hide 37 */ 38 public class SdkSandboxActivityRegistry { 39 private static final Object sLock = new Object(); 40 41 @GuardedBy("sLock") 42 private static SdkSandboxActivityRegistry sInstance; 43 44 // A lock to keep all map synchronized 45 private final Object mMapsLock = new Object(); 46 47 @GuardedBy("mMapsLock") 48 private final Map<SdkSandboxActivityHandler, HandlerInfo> mHandlerToHandlerInfoMap = 49 new ArrayMap<>(); 50 51 @GuardedBy("mMapsLock") 52 private final Map<IBinder, HandlerInfo> mTokenToHandlerInfoMap = new ArrayMap<>(); 53 SdkSandboxActivityRegistry()54 private SdkSandboxActivityRegistry() {} 55 56 /** Returns a singleton instance of this class. */ getInstance()57 public static SdkSandboxActivityRegistry getInstance() { 58 synchronized (sLock) { 59 if (sInstance == null) { 60 sInstance = new SdkSandboxActivityRegistry(); 61 } 62 return sInstance; 63 } 64 } 65 66 /** 67 * Registers the passed {@link SdkSandboxActivityHandler} and returns a {@link IBinder} token 68 * that identifies it. 69 * 70 * <p>If {@link SdkSandboxActivityHandler} is already registered, its {@link IBinder} identifier 71 * will be returned. 72 * 73 * @param sdkName is the name of the SDK registering {@link SdkSandboxActivityHandler} 74 * @param handler is the {@link SdkSandboxActivityHandler} to register. 75 */ 76 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) 77 @NonNull register(@onNull String sdkName, @NonNull SdkSandboxActivityHandler handler)78 public IBinder register(@NonNull String sdkName, @NonNull SdkSandboxActivityHandler handler) { 79 synchronized (mMapsLock) { 80 if (mHandlerToHandlerInfoMap.containsKey(handler)) { 81 HandlerInfo handlerInfo = mHandlerToHandlerInfoMap.get(handler); 82 return handlerInfo.getToken(); 83 } 84 85 IBinder token = new Binder(); 86 HandlerInfo handlerInfo = new HandlerInfo(sdkName, handler, token); 87 mHandlerToHandlerInfoMap.put(handlerInfo.getHandler(), handlerInfo); 88 mTokenToHandlerInfoMap.put(handlerInfo.getToken(), handlerInfo); 89 return token; 90 } 91 } 92 93 /** 94 * Unregisters the passed {@link SdkSandboxActivityHandler}. 95 * 96 * @param handler is the {@link SdkSandboxActivityHandler} to unregister. 97 */ 98 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) unregister(@onNull SdkSandboxActivityHandler handler)99 public void unregister(@NonNull SdkSandboxActivityHandler handler) { 100 synchronized (mMapsLock) { 101 HandlerInfo handlerInfo = mHandlerToHandlerInfoMap.get(handler); 102 if (handlerInfo == null) { 103 return; 104 } 105 mHandlerToHandlerInfoMap.remove(handlerInfo.getHandler()); 106 mTokenToHandlerInfoMap.remove(handlerInfo.getToken()); 107 } 108 } 109 110 /** 111 * It notifies the SDK about {@link Activity} creation. 112 * 113 * <p>This should be called by the sandbox {@link Activity} while being created to notify the 114 * SDK that registered the {@link SdkSandboxActivityHandler} that identified by the passed 115 * {@link IBinder} token. 116 * 117 * @param token is the {@link IBinder} identifier for the {@link SdkSandboxActivityHandler}. 118 * @param activity is the {@link Activity} is being created. 119 * @throws IllegalArgumentException if there is no registered handler identified by the passed 120 * {@link IBinder} token (that mostly would mean that the handler is de-registered before 121 * the passed {@link Activity} is created), or the {@link SdkSandboxActivityHandler} is 122 * already notified about a previous {@link Activity}, in both cases the passed {@link 123 * Activity} will not start. 124 */ 125 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) notifyOnActivityCreation(@onNull IBinder token, @NonNull Activity activity)126 public void notifyOnActivityCreation(@NonNull IBinder token, @NonNull Activity activity) { 127 synchronized (mMapsLock) { 128 HandlerInfo handlerInfo = mTokenToHandlerInfoMap.get(token); 129 if (handlerInfo == null) { 130 throw new IllegalArgumentException( 131 "There is no registered SdkSandboxActivityHandler to notify"); 132 } 133 handlerInfo.getHandler().onActivityCreated(activity); 134 } 135 } 136 137 /** 138 * Holds the information about {@link SdkSandboxActivityHandler}. 139 * 140 * @hide 141 */ 142 private static class HandlerInfo { 143 private final String mSdkName; 144 private final SdkSandboxActivityHandler mHandler; 145 private final IBinder mToken; 146 147 HandlerInfo(String sdkName, SdkSandboxActivityHandler handler, IBinder token)148 HandlerInfo(String sdkName, SdkSandboxActivityHandler handler, IBinder token) { 149 this.mSdkName = sdkName; 150 this.mHandler = handler; 151 this.mToken = token; 152 } 153 154 @NonNull getSdkName()155 public String getSdkName() { 156 return mSdkName; 157 } 158 159 @NonNull getHandler()160 public SdkSandboxActivityHandler getHandler() { 161 return mHandler; 162 } 163 164 @NonNull getToken()165 public IBinder getToken() { 166 return mToken; 167 } 168 } 169 } 170