157 lines
6.5 KiB
Java
157 lines
6.5 KiB
Java
/*
|
|
* Copyright (C) 2022 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package android.adservices.ondevicepersonalization;
|
|
|
|
import android.adservices.ondevicepersonalization.aidl.IDataAccessService;
|
|
import android.adservices.ondevicepersonalization.aidl.IDataAccessServiceCallback;
|
|
import android.annotation.FlaggedApi;
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.annotation.WorkerThread;
|
|
import android.net.Uri;
|
|
import android.os.Bundle;
|
|
import android.os.PersistableBundle;
|
|
import android.os.RemoteException;
|
|
|
|
import com.android.adservices.ondevicepersonalization.flags.Flags;
|
|
import com.android.ondevicepersonalization.internal.util.LoggerFactory;
|
|
|
|
import java.util.Objects;
|
|
import java.util.concurrent.ArrayBlockingQueue;
|
|
import java.util.concurrent.BlockingQueue;
|
|
|
|
/**
|
|
* Generates event tracking URLs for a request. The service can embed these URLs within the
|
|
* HTML output as needed. When the HTML is rendered within an ODP WebView, ODP will intercept
|
|
* requests to these URLs, call
|
|
* {@code IsolatedWorker#onEvent(EventInput, android.os.OutcomeReceiver)}, and log the returned
|
|
* output in the EVENTS table.
|
|
*
|
|
*/
|
|
@FlaggedApi(Flags.FLAG_ON_DEVICE_PERSONALIZATION_APIS_ENABLED)
|
|
public class EventUrlProvider {
|
|
private static final LoggerFactory.Logger sLogger = LoggerFactory.getLogger();
|
|
private static final String TAG = EventUrlProvider.class.getSimpleName();
|
|
private static final long ASYNC_TIMEOUT_MS = 1000;
|
|
|
|
@NonNull private final IDataAccessService mDataAccessService;
|
|
|
|
/** @hide */
|
|
public EventUrlProvider(@NonNull IDataAccessService binder) {
|
|
mDataAccessService = Objects.requireNonNull(binder);
|
|
}
|
|
|
|
/**
|
|
* Creates an event tracking URL that returns the provided response. Returns HTTP Status
|
|
* 200 (OK) if the response data is not empty. Returns HTTP Status 204 (No Content) if the
|
|
* response data is empty.
|
|
*
|
|
* @param eventParams The data to be passed to
|
|
* {@code IsolatedWorker#onEvent(EventInput, android.os.OutcomeReceiver)}
|
|
* when the event occurs.
|
|
* @param responseData The content to be returned to the WebView when the URL is fetched.
|
|
* @param mimeType The Mime Type of the URL response.
|
|
* @return An ODP event URL that can be inserted into a WebView.
|
|
*/
|
|
@WorkerThread
|
|
@NonNull public Uri createEventTrackingUrlWithResponse(
|
|
@NonNull PersistableBundle eventParams,
|
|
@Nullable byte[] responseData,
|
|
@Nullable String mimeType) {
|
|
final long startTimeMillis = System.currentTimeMillis();
|
|
Bundle params = new Bundle();
|
|
params.putParcelable(Constants.EXTRA_EVENT_PARAMS, eventParams);
|
|
params.putByteArray(Constants.EXTRA_RESPONSE_DATA, responseData);
|
|
params.putString(Constants.EXTRA_MIME_TYPE, mimeType);
|
|
return getUrl(params, Constants.API_NAME_EVENT_URL_CREATE_WITH_RESPONSE, startTimeMillis);
|
|
}
|
|
|
|
/**
|
|
* Creates an event tracking URL that redirects to the provided destination URL when it is
|
|
* clicked in an ODP webview.
|
|
*
|
|
* @param eventParams The data to be passed to
|
|
* {@code IsolatedWorker#onEvent(EventInput, android.os.OutcomeReceiver)}
|
|
* when the event occurs
|
|
* @param destinationUrl The URL to redirect to.
|
|
* @return An ODP event URL that can be inserted into a WebView.
|
|
*/
|
|
@WorkerThread
|
|
@NonNull public Uri createEventTrackingUrlWithRedirect(
|
|
@NonNull PersistableBundle eventParams,
|
|
@Nullable Uri destinationUrl) {
|
|
final long startTimeMillis = System.currentTimeMillis();
|
|
Bundle params = new Bundle();
|
|
params.putParcelable(Constants.EXTRA_EVENT_PARAMS, eventParams);
|
|
params.putString(Constants.EXTRA_DESTINATION_URL, destinationUrl.toString());
|
|
return getUrl(params, Constants.API_NAME_EVENT_URL_CREATE_WITH_REDIRECT, startTimeMillis);
|
|
}
|
|
|
|
@NonNull private Uri getUrl(
|
|
@NonNull Bundle params, int apiName, long startTimeMillis) {
|
|
int responseCode = Constants.STATUS_SUCCESS;
|
|
try {
|
|
BlockingQueue<CallbackResult> asyncResult = new ArrayBlockingQueue<>(1);
|
|
|
|
mDataAccessService.onRequest(
|
|
Constants.DATA_ACCESS_OP_GET_EVENT_URL,
|
|
params,
|
|
new IDataAccessServiceCallback.Stub() {
|
|
@Override
|
|
public void onSuccess(@NonNull Bundle result) {
|
|
asyncResult.add(new CallbackResult(result, 0));
|
|
}
|
|
@Override
|
|
public void onError(int errorCode) {
|
|
asyncResult.add(new CallbackResult(null, errorCode));
|
|
}
|
|
});
|
|
CallbackResult callbackResult = asyncResult.take();
|
|
Objects.requireNonNull(callbackResult);
|
|
if (callbackResult.mErrorCode != 0) {
|
|
throw new IllegalStateException("Error: " + callbackResult.mErrorCode);
|
|
}
|
|
Bundle result = Objects.requireNonNull(callbackResult.mResult);
|
|
Uri url = Objects.requireNonNull(
|
|
result.getParcelable(Constants.EXTRA_RESULT, Uri.class));
|
|
return url;
|
|
} catch (InterruptedException | RemoteException e) {
|
|
responseCode = Constants.STATUS_INTERNAL_ERROR;
|
|
throw new RuntimeException(e);
|
|
} finally {
|
|
try {
|
|
mDataAccessService.logApiCallStats(
|
|
apiName,
|
|
System.currentTimeMillis() - startTimeMillis,
|
|
responseCode);
|
|
} catch (Exception e) {
|
|
sLogger.d(e, TAG + ": failed to log metrics");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static class CallbackResult {
|
|
final Bundle mResult;
|
|
final int mErrorCode;
|
|
|
|
CallbackResult(Bundle result, int errorCode) {
|
|
mResult = result;
|
|
mErrorCode = errorCode;
|
|
}
|
|
}
|
|
}
|