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 package android.adservices.adid; 17 18 import static android.adservices.common.AdServicesPermissions.ACCESS_ADSERVICES_AD_ID; 19 import static android.adservices.common.AdServicesStatusUtils.SERVICE_UNAVAILABLE_ERROR_MESSAGE; 20 import static android.adservices.common.AndroidRCommonUtil.invokeCallbackOnErrorOnRvc; 21 22 import android.adservices.common.AdServicesOutcomeReceiver; 23 import android.adservices.common.AdServicesStatusUtils; 24 import android.adservices.common.CallerMetadata; 25 import android.adservices.common.OutcomeReceiverConverter; 26 import android.adservices.common.SandboxedSdkContextUtils; 27 import android.annotation.CallbackExecutor; 28 import android.annotation.FlaggedApi; 29 import android.annotation.NonNull; 30 import android.annotation.RequiresPermission; 31 import android.app.sdksandbox.SandboxedSdkContext; 32 import android.content.Context; 33 import android.os.Build; 34 import android.os.OutcomeReceiver; 35 import android.os.RemoteException; 36 import android.os.SystemClock; 37 38 import androidx.annotation.RequiresApi; 39 40 import com.android.adservices.AdServicesCommon; 41 import com.android.adservices.LogUtil; 42 import com.android.adservices.ServiceBinder; 43 import com.android.adservices.flags.Flags; 44 import com.android.adservices.shared.common.exception.ServiceUnavailableException; 45 46 import java.util.Objects; 47 import java.util.concurrent.Executor; 48 49 /** 50 * AdId Manager provides APIs for app and ad-SDKs to access advertising ID. The advertising ID is a 51 * unique, per-device, user-resettable ID for advertising. It gives users better controls and 52 * provides developers with a simple, standard system to continue to monetize their apps via 53 * personalized ads (formerly known as interest-based ads). 54 */ 55 public class AdIdManager { 56 /** 57 * Service used for registering AdIdManager in the system service registry. 58 * 59 * @hide 60 */ 61 public static final String ADID_SERVICE = "adid_service"; 62 63 // When an app calls the AdId API directly, it sets the SDK name to empty string. 64 static final String EMPTY_SDK = ""; 65 66 private Context mContext; 67 private ServiceBinder<IAdIdService> mServiceBinder; 68 69 /** 70 * Factory method for creating an instance of AdIdManager. 71 * 72 * @param context The {@link Context} to use 73 * @return A {@link AdIdManager} instance 74 */ 75 @NonNull get(@onNull Context context)76 public static AdIdManager get(@NonNull Context context) { 77 // On T+, context.getSystemService() does more than just call constructor. 78 return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) 79 ? context.getSystemService(AdIdManager.class) 80 : new AdIdManager(context); 81 } 82 83 /** 84 * Create AdIdManager 85 * 86 * @hide 87 */ AdIdManager(Context context)88 public AdIdManager(Context context) { 89 // In case the AdIdManager is initiated from inside a sdk_sandbox process the fields 90 // will be immediately rewritten by the initialize method below. 91 initialize(context); 92 } 93 94 /** 95 * Initializes {@link AdIdManager} with the given {@code context}. 96 * 97 * <p>This method is called by the {@link SandboxedSdkContext} to propagate the correct context. 98 * For more information check the javadoc on the {@link 99 * android.app.sdksandbox.SdkSandboxSystemServiceRegistry}. 100 * 101 * @hide 102 * @see android.app.sdksandbox.SdkSandboxSystemServiceRegistry 103 */ initialize(Context context)104 public AdIdManager initialize(Context context) { 105 mContext = context; 106 mServiceBinder = 107 ServiceBinder.getServiceBinder( 108 context, 109 AdServicesCommon.ACTION_ADID_SERVICE, 110 IAdIdService.Stub::asInterface); 111 return this; 112 } 113 114 @SuppressWarnings("NewApi") getService( @allbackExecutor Executor executor, OutcomeReceiver<AdId, Exception> callback)115 private IAdIdService getService( 116 @CallbackExecutor Executor executor, OutcomeReceiver<AdId, Exception> callback) { 117 IAdIdService service = null; 118 try { 119 service = mServiceBinder.getService(); 120 121 // Throw ServiceUnavailableException and set it to the callback. 122 if (service == null) { 123 throw new ServiceUnavailableException(SERVICE_UNAVAILABLE_ERROR_MESSAGE); 124 } 125 } catch (RuntimeException e) { 126 LogUtil.e(e, "Failed binding to AdId service"); 127 executor.execute(() -> callback.onError(e)); 128 } 129 130 return service; 131 } 132 getContext()133 private Context getContext() { 134 return mContext; 135 } 136 137 /** 138 * Return the AdId. 139 * 140 * @param executor The executor to run callback. 141 * @param callback The callback that's called after adid are available or an error occurs. 142 */ 143 @RequiresApi(Build.VERSION_CODES.S) 144 @RequiresPermission(ACCESS_ADSERVICES_AD_ID) 145 @NonNull getAdId( @onNull @allbackExecutor Executor executor, @NonNull OutcomeReceiver<AdId, Exception> callback)146 public void getAdId( 147 @NonNull @CallbackExecutor Executor executor, 148 @NonNull OutcomeReceiver<AdId, Exception> callback) { 149 Objects.requireNonNull(executor, "executor must not be null"); 150 Objects.requireNonNull(callback, "callback must not be null"); 151 CallerMetadata callerMetadata = 152 new CallerMetadata.Builder() 153 .setBinderElapsedTimestamp(SystemClock.elapsedRealtime()) 154 .build(); 155 String appPackageName = ""; 156 String sdkPackageName = ""; 157 // First check if context is SandboxedSdkContext or not 158 Context getAdIdRequestContext = getContext(); 159 SandboxedSdkContext requestContext = 160 SandboxedSdkContextUtils.getAsSandboxedSdkContext(getAdIdRequestContext); 161 if (requestContext != null) { 162 sdkPackageName = requestContext.getSdkPackageName(); 163 appPackageName = requestContext.getClientPackageName(); 164 } else { // This is the case without the Sandbox. 165 appPackageName = getAdIdRequestContext.getPackageName(); 166 } 167 168 try { 169 IAdIdService service = getService(executor, callback); 170 if (service == null) { 171 LogUtil.w("Unable to find AdId service"); 172 return; 173 } 174 175 service.getAdId( 176 new GetAdIdParam.Builder() 177 .setAppPackageName(appPackageName) 178 .setSdkPackageName(sdkPackageName) 179 .build(), 180 callerMetadata, 181 new IGetAdIdCallback.Stub() { 182 @Override 183 public void onResult(GetAdIdResult resultParcel) { 184 executor.execute( 185 () -> { 186 if (resultParcel.isSuccess()) { 187 callback.onResult( 188 new AdId( 189 resultParcel.getAdId(), 190 resultParcel.isLatEnabled())); 191 } else { 192 callback.onError( 193 AdServicesStatusUtils.asException( 194 resultParcel)); 195 } 196 }); 197 } 198 199 @Override 200 public void onError(int resultCode) { 201 executor.execute( 202 () -> 203 callback.onError( 204 AdServicesStatusUtils.asException(resultCode))); 205 } 206 }); 207 } catch (RemoteException e) { 208 LogUtil.e(e, "RemoteException"); 209 callback.onError(e); 210 } 211 } 212 213 /** 214 * Return the AdId. For use on Android R or lower. 215 * 216 * @param executor The executor to run callback. 217 * @param callback The callback that's called after adid are available or an error occurs. 218 * @deprecated use {@link #getAdId(Executor, OutcomeReceiver)} instead. Android R is no longer 219 * supported. 220 */ 221 @RequiresPermission(ACCESS_ADSERVICES_AD_ID) 222 @Deprecated 223 @FlaggedApi(Flags.FLAG_ADSERVICES_OUTCOMERECEIVER_R_API_DEPRECATED) 224 @SuppressWarnings("NewApi") 225 @NonNull getAdId( @onNull @allbackExecutor Executor executor, @NonNull AdServicesOutcomeReceiver<AdId, Exception> callback)226 public void getAdId( 227 @NonNull @CallbackExecutor Executor executor, 228 @NonNull AdServicesOutcomeReceiver<AdId, Exception> callback) { 229 if (invokeCallbackOnErrorOnRvc(callback)) { 230 return; 231 } 232 233 getAdId(executor, OutcomeReceiverConverter.toOutcomeReceiver(callback)); 234 } 235 236 /** 237 * If the service is in an APK (as opposed to the system service), unbind it from the service to 238 * allow the APK process to die. 239 * 240 * @hide 241 */ 242 // TODO: change to @VisibleForTesting unbindFromService()243 public void unbindFromService() { 244 mServiceBinder.unbindFromService(); 245 } 246 } 247