From 51f8f6899e945fe320adc257547955f8724f3632 Mon Sep 17 00:00:00 2001 From: Abhas Vohra Date: Tue, 5 Jan 2021 19:31:51 +0530 Subject: [PATCH 1/3] Removed AsyncTasks, replaced with executor implementation --- .../main/java/org/prebid/mobile/AdUnit.java | 11 +- .../org/prebid/mobile/AdvertisingIDUtil.java | 32 +++-- .../java/org/prebid/mobile/DemandFetcher.java | 21 ++- .../org/prebid/mobile/DownloadImageTask.java | 56 ++++++++ .../prebid/mobile/PrebidServerAdapter.java | 114 +++++++++-------- .../src/main/java/org/prebid/mobile/Util.java | 2 + .../java/org/prebid/mobile/http/HTTPGet.java | 120 ++++++++++++++++++ .../org/prebid/mobile/http/HTTPResponse.java | 68 ++++++++++ .../org/prebid/mobile/http/HttpErrorCode.java | 26 ++++ .../BackgroundThreadExecutor.java | 75 +++++++++++ .../tasksmanager/CancellableExecutor.java | 23 ++++ .../tasksmanager/MainThreadExecutor.java | 41 ++++++ .../mobile/tasksmanager/TasksManager.java | 77 +++++++++++ .../org/prebid/mobile/DemandFetcherTest.java | 54 ++++++++ .../prebid/mobile/PrebidNativeNativeTest.java | 0 .../mobile/PrebidServerAdapterTest.java | 106 +++++++++++++++- .../org/prebid/mobile/ResultCodeTest.java | 45 ++++++- 17 files changed, 795 insertions(+), 76 deletions(-) create mode 100644 PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/DownloadImageTask.java create mode 100644 PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HTTPGet.java create mode 100644 PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HTTPResponse.java create mode 100644 PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HttpErrorCode.java create mode 100644 PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/BackgroundThreadExecutor.java create mode 100644 PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/CancellableExecutor.java create mode 100644 PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/MainThreadExecutor.java create mode 100644 PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/TasksManager.java create mode 100644 PrebidMobile/src/test/java/org/prebid/mobile/PrebidNativeNativeTest.java diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/AdUnit.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/AdUnit.java index a59e5fd2a..4de2d6184 100644 --- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/AdUnit.java +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/AdUnit.java @@ -24,6 +24,8 @@ import android.support.annotation.NonNull; import android.text.TextUtils; +import org.prebid.mobile.tasksmanager.TasksManager; + import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -79,8 +81,13 @@ public void fetchDemand(@NonNull final OnCompleteListener2 listener) { fetchDemand(keywordsMap, new OnCompleteListener() { @Override - public void onComplete(ResultCode resultCode) { - listener.onComplete(resultCode, keywordsMap.size() != 0 ? Collections.unmodifiableMap(keywordsMap) : null); + public void onComplete(final ResultCode resultCode) { + TasksManager.getInstance().executeOnMainThread(new Runnable() { + @Override + public void run() { + listener.onComplete(resultCode, keywordsMap.size() != 0 ? Collections.unmodifiableMap(keywordsMap) : null); + } + }); } }); } diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/AdvertisingIDUtil.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/AdvertisingIDUtil.java index d495b5912..e087cc4cb 100644 --- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/AdvertisingIDUtil.java +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/AdvertisingIDUtil.java @@ -18,10 +18,11 @@ import android.annotation.SuppressLint; import android.content.Context; -import android.os.AsyncTask; -import android.os.Build; +import android.os.Looper; import android.text.TextUtils; +import org.prebid.mobile.tasksmanager.TasksManager; + import java.lang.ref.WeakReference; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -56,7 +57,7 @@ private enum STATE { private static STATE state = STATE.NOT_FETCHED; /** - * Starts an AsyncTask to retrieve and set the AAID. + * Fetch a background executor to retrieve and set the AAID. * Does nothing if PrebidServerSettings.aaid is already set for the SDK. * * @param context context to retrieve the AAID on. @@ -68,11 +69,7 @@ static void retrieveAndSetAAID(Context context) { } AAIDTask getAAIDTask = new AAIDTask(context); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - getAAIDTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } else { - getAAIDTask.execute(); - } + getAAIDTask.execute(); } /** @@ -80,7 +77,7 @@ static void retrieveAndSetAAID(Context context) { * Sets the SDK's aaid value to the result if successful, * or null if failed. */ - private static class AAIDTask extends AsyncTask { + private static class AAIDTask { private static final String cAdvertisingIdClientName = "com.google.android.gms.ads.identifier.AdvertisingIdClient"; private static final String cAdvertisingIdClientInfoName @@ -92,8 +89,20 @@ private AAIDTask(Context context) { this.context = new WeakReference(context); } - @Override - protected Void doInBackground(Void... params) { + protected void execute() { + if (Looper.myLooper() == Looper.getMainLooper()) { + TasksManager.getInstance().executeOnBackgroundThread(new Runnable() { + @Override + public void run() { + fetchAAID(); + } + }); + } else { + fetchAAID(); + } + } + + private void fetchAAID() { state = STATE.FETCHING; // attempt to retrieve AAID from GooglePlayServices via reflection // Setting aaid in the backend thread @@ -129,7 +138,6 @@ protected Void doInBackground(Void... params) { } else { state = STATE.FETCHED; } - return null; } } } diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/DemandFetcher.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/DemandFetcher.java index e0db71d0b..e1287106b 100644 --- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/DemandFetcher.java +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/DemandFetcher.java @@ -22,6 +22,8 @@ import android.support.annotation.NonNull; import android.support.annotation.VisibleForTesting; +import org.prebid.mobile.tasksmanager.TasksManager; + import java.util.HashMap; import java.util.UUID; @@ -131,12 +133,19 @@ private void notifyListener(final ResultCode resultCode) { LogUtil.d("notifyListener:" + resultCode); if (listener != null) { - listener.onComplete(resultCode); - } - // for single request, if done, finish current fetcher, - // let ad unit create a new fetcher for next request - if (periodMillis <= 0) { - destroy(); + TasksManager.getInstance().executeOnMainThread(new Runnable() { + @Override + public void run() { + if (listener != null) { + listener.onComplete(resultCode); + } + // for single request, if done, finish current fetcher, + // let ad unit create a new fetcher for next request + if (periodMillis <= 0) { + destroy(); + } + } + }); } } diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/DownloadImageTask.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/DownloadImageTask.java new file mode 100644 index 000000000..b4ecb9994 --- /dev/null +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/DownloadImageTask.java @@ -0,0 +1,56 @@ +package org.prebid.mobile; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Looper; +import android.widget.ImageView; + +import org.prebid.mobile.tasksmanager.TasksManager; + +import java.io.InputStream; +import java.lang.ref.WeakReference; + +public class DownloadImageTask { + + WeakReference imageRef; + + protected DownloadImageTask(ImageView image) { + this.imageRef = new WeakReference<>(image); + } + + protected void execute(final String url) { + if (Looper.myLooper() == Looper.getMainLooper()) { + TasksManager.getInstance().executeOnBackgroundThread(new Runnable() { + @Override + public void run() { + fetchAndProcessImage(url); + } + }); + } else { + fetchAndProcessImage(url); + } + } + + private void fetchAndProcessImage(String url) { + Bitmap bitmap = null; + try { + InputStream in = new java.net.URL(url).openStream(); + bitmap = BitmapFactory.decodeStream(in); + } catch (Exception e) { + LogUtil.e("Error", e.getMessage()); + } + processImage(bitmap); + } + + private void processImage(final Bitmap result) { + TasksManager.getInstance().executeOnMainThread(new Runnable() { + @Override + public void run() { + ImageView image = imageRef.get(); + if (image != null) { + image.setImageBitmap(result); + } + } + }); + } +} diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/PrebidServerAdapter.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/PrebidServerAdapter.java index 4bc1ac8b5..a5989f68c 100644 --- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/PrebidServerAdapter.java +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/PrebidServerAdapter.java @@ -22,13 +22,13 @@ import android.location.LocationManager; import android.net.ConnectivityManager; import android.net.NetworkInfo; -import android.os.AsyncTask; import android.os.Build; import android.os.CountDownTimer; +import android.os.Handler; +import android.os.Looper; import android.support.annotation.MainThread; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.annotation.WorkerThread; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.webkit.CookieManager; @@ -37,6 +37,7 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.prebid.mobile.tasksmanager.TasksManager; import java.io.BufferedReader; import java.io.IOException; @@ -68,9 +69,20 @@ class PrebidServerAdapter implements DemandAdapter { @Override public void requestDemand(RequestParams params, DemandAdapterListener listener, String auctionId) { - ServerConnector connector = new ServerConnector(this, listener, params, auctionId); + final ServerConnector connector = new ServerConnector(this, listener, params, auctionId); serverConnectors.add(connector); - connector.execute(); + if (Looper.myLooper() == Looper.getMainLooper()) { + TasksManager.getInstance().executeOnBackgroundThread(new Runnable() { + @Override + public void run() { + ServerConnector.TaskResult result = connector.execute(); + connector.processResult(result); + } + }); + } else { + ServerConnector.TaskResult result = connector.execute(); + connector.processResult(result); + } } @Override @@ -85,7 +97,7 @@ public void stopRequest(String auctionId) { serverConnectors.removeAll(toRemove); } - static class ServerConnector extends AsyncTask> { + static class ServerConnector { private static final int TIMEOUT_COUNT_DOWN_INTERVAL = 500; @@ -99,6 +111,7 @@ static class ServerConnector extends AsyncTask(prebidServerAdapter); @@ -106,20 +119,11 @@ static class ServerConnector extends AsyncTask execute() { timeoutCountDownTimer.start(); - } - - @Override - @WorkerThread - protected AsyncTaskResult doInBackground(Object... objects) { try { long demandFetchStartTime = System.currentTimeMillis(); @@ -191,7 +195,7 @@ protected AsyncTaskResult doInBackground(Object... objects) { BidLog.getInstance().setLastEntry(entry); - return new AsyncTaskResult<>(response); + return new TaskResult<>(response); } else if (httpResult >= HttpURLConnection.HTTP_BAD_REQUEST) { StringBuilder builder = new StringBuilder(); InputStream is = conn.getErrorStream(); @@ -217,57 +221,53 @@ protected AsyncTaskResult doInBackground(Object... objects) { BidLog.getInstance().setLastEntry(entry); if (m.find() || result.contains("No stored request")) { - return new AsyncTaskResult<>(ResultCode.INVALID_ACCOUNT_ID); + return new TaskResult<>(ResultCode.INVALID_ACCOUNT_ID); } else if (m3.find() || result.contains("No stored imp")) { - return new AsyncTaskResult<>(ResultCode.INVALID_CONFIG_ID); + return new TaskResult<>(ResultCode.INVALID_CONFIG_ID); } else if (m2.find() || m4.find() || result.contains("Request imp[0].banner.format")) { - return new AsyncTaskResult<>(ResultCode.INVALID_SIZE); + return new TaskResult<>(ResultCode.INVALID_SIZE); } else { - return new AsyncTaskResult<>(ResultCode.PREBID_SERVER_ERROR); + return new TaskResult<>(ResultCode.PREBID_SERVER_ERROR); } } } catch (MalformedURLException e) { - return new AsyncTaskResult<>(e); + return new TaskResult<>(e); } catch (UnsupportedEncodingException e) { - return new AsyncTaskResult<>(e); + return new TaskResult<>(e); } catch (SocketTimeoutException ex) { - return new AsyncTaskResult<>(ResultCode.TIMEOUT); + return new TaskResult<>(ResultCode.TIMEOUT); } catch (IOException e) { - return new AsyncTaskResult<>(e); + return new TaskResult<>(e); } catch (JSONException e) { - return new AsyncTaskResult<>(e); + return new TaskResult<>(e); } catch (NoContextException ex) { - return new AsyncTaskResult<>(ResultCode.INVALID_CONTEXT); + return new TaskResult<>(ResultCode.INVALID_CONTEXT); } catch (Exception e) { - return new AsyncTaskResult<>(e); + return new TaskResult<>(e); } - return new AsyncTaskResult<>(new RuntimeException("ServerConnector exception")); + return new TaskResult<>(new RuntimeException("ServerConnector exception")); } - @Override - @MainThread - protected void onPostExecute(AsyncTaskResult asyncTaskResult) { - super.onPostExecute(asyncTaskResult); - + private void processResult(TaskResult taskResult) { timeoutCountDownTimer.cancel(); - if (asyncTaskResult.getError() != null) { - asyncTaskResult.getError().printStackTrace(); + if (taskResult.getError() != null) { + taskResult.getError().printStackTrace(); //Default error notifyDemandFailed(ResultCode.PREBID_SERVER_ERROR); removeThisTask(); return; - } else if (asyncTaskResult.getResultCode() != null) { - notifyDemandFailed(asyncTaskResult.getResultCode()); + } else if (taskResult.getResultCode() != null) { + notifyDemandFailed(taskResult.getResultCode()); removeThisTask(); return; } - JSONObject jsonObject = asyncTaskResult.getResult(); + JSONObject jsonObject = taskResult.getResult(); HashMap keywords = new HashMap<>(); boolean containTopBid = false; @@ -328,19 +328,29 @@ protected void onPostExecute(AsyncTaskResult asyncTaskResult) { removeThisTask(); } - @Override - @MainThread - protected void onCancelled() { - super.onCancelled(); - + private void cancel() { + isCancelled = true; if (timeoutFired) { notifyDemandFailed(ResultCode.TIMEOUT); } else { timeoutCountDownTimer.cancel(); } - removeThisTask(); +// removeThisTask(); } +// @Override +// @MainThread +// protected void onCancelled() { +// super.onCancelled(); +// +// if (timeoutFired) { +// notifyDemandFailed(ResultCode.TIMEOUT); +// } else { +// timeoutCountDownTimer.cancel(); +// } +// removeThisTask(); +// } + private void removeThisTask() { @Nullable PrebidServerAdapter prebidServerAdapter = this.prebidServerAdapter.get(); @@ -356,7 +366,7 @@ String getAuctionId() { } void destroy() { - this.cancel(true); + this.cancel(); this.listener = null; } @@ -1108,7 +1118,7 @@ private boolean canIAccessDeviceData() { private static class NoContextException extends Exception { } - private static class AsyncTaskResult { + private static class TaskResult { @Nullable private T result; @Nullable @@ -1131,15 +1141,15 @@ public Exception getError() { return error; } - private AsyncTaskResult(@NonNull T result) { + private TaskResult(@NonNull T result) { this.result = result; } - private AsyncTaskResult(@NonNull ResultCode resultCode) { + private TaskResult(@NonNull ResultCode resultCode) { this.resultCode = resultCode; } - private AsyncTaskResult(@NonNull Exception error) { + private TaskResult(@NonNull Exception error) { this.error = error; } } @@ -1160,7 +1170,7 @@ public TimeoutCountDownTimer(long millisInFuture, long countDownInterval) { @Override public void onTick(long millisUntilFinished) { - if (ServerConnector.this.isCancelled()) { + if (isCancelled) { TimeoutCountDownTimer.this.cancel(); } } @@ -1168,12 +1178,12 @@ public void onTick(long millisUntilFinished) { @Override public void onFinish() { - if (ServerConnector.this.isCancelled()) { + if (isCancelled) { return; } timeoutFired = true; - ServerConnector.this.cancel(true); + ServerConnector.this.cancel(); } } diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/Util.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/Util.java index 8686c9fe8..c0da18d36 100644 --- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/Util.java +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/Util.java @@ -53,6 +53,8 @@ public class Util { static final String MOPUB_INTERSTITIAL_CLASS = "com.mopub.mobileads.MoPubInterstitial"; static final String AD_MANAGER_REQUEST_CLASS = "com.google.android.gms.ads.doubleclick.PublisherAdRequest"; static final String AD_MANAGER_REQUEST_BUILDER_CLASS = "com.google.android.gms.ads.doubleclick.PublisherAdRequest$Builder"; + public static final int HTTP_CONNECTION_TIMEOUT = 15000; + public static final int HTTP_SOCKET_TIMEOUT = 20000; private static final Random RANDOM = new Random(); private static final HashSet reservedKeys; private static final int MoPubQueryStringLimit = 4000; diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HTTPGet.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HTTPGet.java new file mode 100644 index 000000000..8fc4d08b8 --- /dev/null +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HTTPGet.java @@ -0,0 +1,120 @@ +/* + * Copyright 2020-2021 Prebid.org, Inc. + * + * 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 org.prebid.mobile.http; + +import android.os.Looper; + +import org.prebid.mobile.Util; +import org.prebid.mobile.tasksmanager.TasksManager; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.ProtocolException; +import java.net.URL; + +public abstract class HTTPGet { + + public HTTPGet() { + super(); + } + + abstract protected void onPostExecute(HTTPResponse response); + + public void execute() { + if (Looper.myLooper() == Looper.getMainLooper()) { + TasksManager.getInstance().executeOnBackgroundThread(new Runnable() { + @Override + public void run() { + HTTPResponse response = makeHttpRequest(); + onPostExecute(response); + } + }); + } else { + HTTPResponse response = makeHttpRequest(); + onPostExecute(response); + } + } + + protected abstract String getUrl(); + + private HttpURLConnection createConnection(URL url) throws IOException { + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setDoOutput(false); + connection.setDoInput(true); + connection.setUseCaches(false); + connection.setRequestMethod("GET"); + return connection; + } + + private void setConnectionParams(HttpURLConnection connection) throws ProtocolException { + connection.setConnectTimeout(Util.HTTP_CONNECTION_TIMEOUT); + connection.setReadTimeout(Util.HTTP_SOCKET_TIMEOUT); + } + + protected HTTPResponse makeHttpRequest() { + HTTPResponse out = new HTTPResponse(); + HttpURLConnection connection = null; + try { + URL reqUrl = new URL(getUrl()); + if (reqUrl.getHost() == null) { + out.setSucceeded(false); + return out; + } + // Create and connect to HTTP service + connection = createConnection(reqUrl); + setConnectionParams(connection); + connection.connect(); + + + //Response parsing + StringBuilder builder = new StringBuilder(); + InputStream is = connection.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8")); + String line; + while ((line = reader.readLine()) != null) { + builder.append(line); + } + reader.close(); + is.close(); + String responseString = builder.toString(); + + + out.setHeaders(connection.getHeaderFields()); + out.setResponseBody(responseString); + boolean isStatusOK = (connection.getResponseCode() + == HttpURLConnection.HTTP_OK); + out.setSucceeded(isStatusOK); + + } catch (MalformedURLException e) { + out.setSucceeded(false); + out.setErrorCode(HttpErrorCode.URI_SYNTAX_ERROR); + } catch (IOException e) { + out.setSucceeded(false); + out.setErrorCode(HttpErrorCode.TRANSPORT_ERROR); + } catch (Exception e) { + out.setSucceeded(false); + out.setErrorCode(HttpErrorCode.UNKNOWN_ERROR); + e.printStackTrace(); + } + return out; + } +} + diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HTTPResponse.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HTTPResponse.java new file mode 100644 index 000000000..bc4c99d69 --- /dev/null +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HTTPResponse.java @@ -0,0 +1,68 @@ +/* + * Copyright 2013 APPNEXUS INC + * + * 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 org.prebid.mobile.http; + +import java.util.List; +import java.util.Map; + +public class HTTPResponse { + private boolean succeeded; + private String responseBody; + private Map> headers; + private HttpErrorCode errorCode; + + public HTTPResponse() { + + } + + public HTTPResponse(boolean succeeded, String responseBody, Map> headers) { + this.succeeded = succeeded; + this.responseBody = responseBody; + this.headers = headers; + } + + public boolean getSucceeded() { + return succeeded; + } + + public void setSucceeded(boolean succeeded) { + this.succeeded = succeeded; + } + + public void setErrorCode(HttpErrorCode errorCode) { + this.errorCode = errorCode; + } + + public HttpErrorCode getErrorCode() { + return errorCode; + } + + public String getResponseBody() { + return responseBody; + } + + public void setResponseBody(String responseBody) { + this.responseBody = responseBody; + } + + public Map> getHeaders() { + return headers; + } + + public void setHeaders(Map> headers) { + this.headers = headers; + } +} diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HttpErrorCode.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HttpErrorCode.java new file mode 100644 index 000000000..9e1fbeebf --- /dev/null +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HttpErrorCode.java @@ -0,0 +1,26 @@ +/* + * Copyright 2015 APPNEXUS INC + * + * 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 org.prebid.mobile.http; + +public enum HttpErrorCode { + CONNECTION_FAILURE, + URI_SYNTAX_ERROR, + NO_HTTP_RESPONSE, + HTTP_PROTOCOL_ERROR, + TRANSPORT_ERROR, + UNKNOWN_ERROR +} diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/BackgroundThreadExecutor.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/BackgroundThreadExecutor.java new file mode 100644 index 000000000..40e7b54ed --- /dev/null +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/BackgroundThreadExecutor.java @@ -0,0 +1,75 @@ +/* + * Copyright 2020 APPNEXUS INC + * + * 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 org.prebid.mobile.tasksmanager; + +import android.os.Handler; +import android.os.HandlerThread; +import android.support.annotation.VisibleForTesting; + +public class BackgroundThreadExecutor implements CancellableExecutor { + + private Handler h; + private boolean running = false; + private final int HANDLER_COUNT = 3; + private int index = 0; + + BackgroundThreadExecutor() { + HandlerThread backgroundThread = new HandlerThread("BackgroundThread"); + backgroundThread.start(); + h = new Handler(backgroundThread.getLooper()); + running = true; + } + + + @Override + public void execute(Runnable runnable) { + if (running) { + h.post(runnable); + } + } + + @Override + public boolean cancel(Runnable runnable) { + if (running) { + h.removeCallbacks(runnable); + return true; + } + return false; + } + + public void shutdown() { + if (running) { + h.getLooper().quit(); + h = null; + running = false; + } + } + + public void startThread() { + if (!running) { + HandlerThread backgroundThread = new HandlerThread("BackgroundThread"); + backgroundThread.start(); + h = new Handler(backgroundThread.getLooper()); + running = true; + } + } + + @VisibleForTesting + public Handler getBackgroundHandler() { + return h; + } +} diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/CancellableExecutor.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/CancellableExecutor.java new file mode 100644 index 000000000..8b7351e9a --- /dev/null +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/CancellableExecutor.java @@ -0,0 +1,23 @@ +/* + * Copyright 2020 APPNEXUS INC + * + * 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 org.prebid.mobile.tasksmanager; + +import java.util.concurrent.Executor; + +public interface CancellableExecutor extends Executor { + boolean cancel(Runnable runnable); +} diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/MainThreadExecutor.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/MainThreadExecutor.java new file mode 100644 index 000000000..72cc4b497 --- /dev/null +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/MainThreadExecutor.java @@ -0,0 +1,41 @@ +/* + * Copyright 2020 APPNEXUS INC + * + * 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 org.prebid.mobile.tasksmanager; + +import android.os.Handler; +import android.os.Looper; +import android.support.annotation.VisibleForTesting; + +public class MainThreadExecutor implements CancellableExecutor { + private final Handler mHandler = new Handler(Looper.getMainLooper()); + + @Override + public void execute(Runnable runnable) { + mHandler.post(runnable); + } + + @Override + public boolean cancel(Runnable runnable) { + mHandler.removeCallbacks(runnable); + return true; + } + + @VisibleForTesting + public Handler getMainExecutor() { + return mHandler; + } +} diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/TasksManager.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/TasksManager.java new file mode 100644 index 000000000..f5f517947 --- /dev/null +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/TasksManager.java @@ -0,0 +1,77 @@ +/* + * Copyright 2020 APPNEXUS INC + * + * 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 org.prebid.mobile.tasksmanager; + +import java.util.concurrent.Executor; + +public class TasksManager { + + private static TasksManager instance = null; + public Executor mainThreadExecutor; + public Executor backgroundThreadExecutor; + + private TasksManager() { + mainThreadExecutor = new MainThreadExecutor(); + backgroundThreadExecutor = new BackgroundThreadExecutor(); + } + + /** + * Factory method to obtain the Singleton instance of the TasksManager + * */ + public synchronized static TasksManager getInstance() { + if (instance == null) { + synchronized (TasksManager.class) { + instance = new TasksManager(); + } + + } + return instance; + } + + /** + * This API can be used to execute code block on the main thread. + * @param task takes in task (to be executed on main thread) as a runnable + * */ + public void executeOnMainThread(Runnable task) { + mainThreadExecutor.execute(task); + } + + /** + * Utility method to cancel an ongoing main thread task + * @param task takes in task to be cancelled + * */ + public void cancelTaskOnMainThread(Runnable task) { + ((CancellableExecutor) mainThreadExecutor).cancel(task); + } + + /** + * This API can be used to execute code block on the background thread. + * @param task takes in task (to be executed on background thread) as a runnable + * */ + public void executeOnBackgroundThread(Runnable task) { + backgroundThreadExecutor.execute(task); + } + + /** + * Utility method to cancel an ongoing background thread task + * @param task takes in task to be cancelled + * */ + public void cancelTaksOnBackgroundThread(Runnable task) { + ((CancellableExecutor) backgroundThreadExecutor).cancel(task); + + } +} diff --git a/PrebidMobile/src/test/java/org/prebid/mobile/DemandFetcherTest.java b/PrebidMobile/src/test/java/org/prebid/mobile/DemandFetcherTest.java index 5b6861f80..20e8ab988 100644 --- a/PrebidMobile/src/test/java/org/prebid/mobile/DemandFetcherTest.java +++ b/PrebidMobile/src/test/java/org/prebid/mobile/DemandFetcherTest.java @@ -26,6 +26,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; +import org.prebid.mobile.tasksmanager.BackgroundThreadExecutor; +import org.prebid.mobile.tasksmanager.MainThreadExecutor; +import org.prebid.mobile.tasksmanager.TasksManager; import org.prebid.mobile.testutils.BaseSetup; import org.prebid.mobile.testutils.MockPrebidServerResponses; import org.robolectric.Robolectric; @@ -49,6 +52,18 @@ @Config(sdk = BaseSetup.testSDK) public class DemandFetcherTest extends BaseSetup { + @Override + public void setup() { + super.setup(); + ((BackgroundThreadExecutor)TasksManager.getInstance().backgroundThreadExecutor).startThread(); + } + + @Override + public void tearDown() { + super.tearDown(); + ((BackgroundThreadExecutor)TasksManager.getInstance().backgroundThreadExecutor).shutdown(); + } + @Test public void testBaseConditions() throws Exception { PublisherAdRequest.Builder builder = new PublisherAdRequest.Builder(); @@ -64,6 +79,10 @@ public void testBaseConditions() throws Exception { assertEquals(DemandFetcher.STATE.RUNNING, FieldUtils.readField(demandFetcher, "state", true)); ShadowLooper fetcherLooper = Shadows.shadowOf(demandFetcher.getHandler().getLooper()); fetcherLooper.runOneTask(); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); demandFetcher.destroy(); @@ -95,6 +114,10 @@ public void testSingleRequestNoBidsResponse() throws Exception { fetcherLooper.runOneTask(); ShadowLooper demandLooper = Shadows.shadowOf(demandFetcher.getDemandHandler().getLooper()); demandLooper.runOneTask(); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onComplete(ResultCode.NO_BIDS); @@ -128,6 +151,10 @@ public void testDestroyAutoRefresh() throws Exception { fetcherLooper.runOneTask(); ShadowLooper demandLooper = Shadows.shadowOf(demandFetcher.getDemandHandler().getLooper()); demandLooper.runOneTask(); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); assertEquals(DemandFetcher.STATE.RUNNING, FieldUtils.readField(demandFetcher, "state", true)); @@ -163,6 +190,10 @@ public void testSingleRequestOneBidResponseForDFPAdObject() throws Exception { fetcherLooper.runOneTask(); ShadowLooper demandLooper = Shadows.shadowOf(demandFetcher.getDemandHandler().getLooper()); demandLooper.runOneTask(); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onComplete(ResultCode.SUCCESS); @@ -216,6 +247,10 @@ public void testSingleRequestOneBidRubiconResponseForDFPAdObject() throws Except fetcherLooper.runOneTask(); ShadowLooper demandLooper = Shadows.shadowOf(demandFetcher.getDemandHandler().getLooper()); demandLooper.runOneTask(); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onComplete(ResultCode.SUCCESS); @@ -300,6 +335,10 @@ public void testSingleRequestOneBidResponseForMoPubAdObject() throws Exception { fetcherLooper.runOneTask(); ShadowLooper demandLooper = Shadows.shadowOf(demandFetcher.getDemandHandler().getLooper()); demandLooper.runOneTask(); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onComplete(ResultCode.SUCCESS); @@ -333,8 +372,12 @@ public void testSingleRequestOneBidRubiconResponseForMoPubAdObject() throws Exce fetcherLooper.runOneTask(); ShadowLooper demandLooper = Shadows.shadowOf(demandFetcher.getDemandHandler().getLooper()); demandLooper.runOneTask(); + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); + verify(mockListener).onComplete(ResultCode.SUCCESS); assertEquals(DemandFetcher.STATE.DESTROYED, FieldUtils.readField(demandFetcher, "state", true)); String adViewKeywords = adView.getKeywords(); @@ -367,6 +410,10 @@ public void testAutoRefreshForMoPubAdObject() throws Exception { fetcherLooper.runOneTask(); ShadowLooper demandLooper = Shadows.shadowOf(demandFetcher.getDemandHandler().getLooper()); demandLooper.runOneTask(); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onComplete(ResultCode.SUCCESS); @@ -375,6 +422,8 @@ public void testAutoRefreshForMoPubAdObject() throws Exception { assertEquals("hb_pb:0.50,hb_env:mobile-app,hb_pb_appnexus:0.50,hb_size:300x250,hb_bidder_appnexus:appnexus,hb_bidder:appnexus,hb_cache_id:df4aba04-5e69-44b8-8608-058ab21600b8,hb_env_appnexus:mobile-app,hb_size_appnexus:300x250,hb_cache_id_appnexus:df4aba04-5e69-44b8-8608-058ab21600b8,", adViewKeywords); fetcherLooper.runOneTask(); demandLooper.runOneTask(); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onComplete(ResultCode.NO_BIDS); @@ -409,6 +458,10 @@ public void testAutoRefreshForDFPAdObject() throws Exception { fetcherLooper.runOneTask(); ShadowLooper demandLooper = Shadows.shadowOf(demandFetcher.getDemandHandler().getLooper()); demandLooper.runOneTask(); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onComplete(ResultCode.SUCCESS); @@ -437,6 +490,7 @@ public void testAutoRefreshForDFPAdObject() throws Exception { assertEquals("300x250", bundle.get("hb_size_appnexus")); fetcherLooper.runOneTask(); demandLooper.runOneTask(); + bgLooper.runToEndOfTasks(); Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onComplete(ResultCode.NO_BIDS); diff --git a/PrebidMobile/src/test/java/org/prebid/mobile/PrebidNativeNativeTest.java b/PrebidMobile/src/test/java/org/prebid/mobile/PrebidNativeNativeTest.java new file mode 100644 index 000000000..e69de29bb diff --git a/PrebidMobile/src/test/java/org/prebid/mobile/PrebidServerAdapterTest.java b/PrebidMobile/src/test/java/org/prebid/mobile/PrebidServerAdapterTest.java index fec3c05f1..572dfd192 100644 --- a/PrebidMobile/src/test/java/org/prebid/mobile/PrebidServerAdapterTest.java +++ b/PrebidMobile/src/test/java/org/prebid/mobile/PrebidServerAdapterTest.java @@ -33,10 +33,14 @@ import org.junit.Test; import org.junit.rules.ErrorCollector; import org.junit.runner.RunWith; +import org.prebid.mobile.tasksmanager.BackgroundThreadExecutor; +import org.prebid.mobile.tasksmanager.MainThreadExecutor; +import org.prebid.mobile.tasksmanager.TasksManager; import org.prebid.mobile.testutils.BaseSetup; import org.prebid.mobile.testutils.MockPrebidServerResponses; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; +import org.robolectric.Shadows; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowLooper; @@ -83,10 +87,18 @@ public class PrebidServerAdapterTest extends BaseSetup { @Rule public ErrorCollector errorCollector = new ErrorCollector(); + @Override + public void setup() { + super.setup(); + ((BackgroundThreadExecutor)TasksManager.getInstance().backgroundThreadExecutor).startThread(); + } + @Override public void tearDown() { super.tearDown(); + ((BackgroundThreadExecutor)TasksManager.getInstance().backgroundThreadExecutor).shutdown(); + TargetingParams.clearAccessControlList(); TargetingParams.clearUserData(); TargetingParams.clearContextData(); @@ -128,6 +140,10 @@ public void testInvalidPrebidServerAccountIdForAppNexusHostedPrebidServer() { RequestParams requestParams = new RequestParams("6ace8c7d-88c0-4623-8117-75bc3f0a2e45", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onDemandFailed(ResultCode.INVALID_ACCOUNT_ID, uuid); @@ -150,11 +166,14 @@ public void testInvalidPrebidServerAccountIdForRubiconHostedPrebidServer() { RequestParams requestParams = new RequestParams("1001-1", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onDemandFailed(ResultCode.INVALID_ACCOUNT_ID, uuid); - } @Test @@ -170,6 +189,10 @@ public void testInvalidPrebidServerConfigIdForAppNexusHostedPrebidServer() { RequestParams requestParams = new RequestParams("6ace8c7d-88c0-4623-8117-ffffffffffff", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onDemandFailed(ResultCode.INVALID_CONFIG_ID, uuid); @@ -192,9 +215,12 @@ public void testInvalidPrebidServerConfigIdForRubiconHostedPrebidServer() { RequestParams requestParams = new RequestParams("1001-1_INVALID_CONFIG_ID", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); - verify(mockListener).onDemandFailed(ResultCode.INVALID_CONFIG_ID, uuid); } @@ -211,6 +237,10 @@ public void testInvalidPrebidServerIdSyntaxForAppNexusHostedPrebidServer() { RequestParams requestParams = new RequestParams("6ace8c7d-88c0-4623-8117-75bc3f0a2e45", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onDemandFailed(ResultCode.INVALID_ACCOUNT_ID, uuid); @@ -229,6 +259,10 @@ public void testInvalidPrebidServerIdSyntaxForAppNexusHostedPrebidServer2() { RequestParams requestParams = new RequestParams("6ace8c7d-88c0-4623-8117-75bc3f0a2e", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onDemandFailed(ResultCode.INVALID_CONFIG_ID, uuid); @@ -249,6 +283,10 @@ public void testUpdateTimeoutMillis() { RequestParams requestParams = new RequestParams("e2edc23f-0b3b-4203-81b5-7cc97132f418", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onDemandFailed(ResultCode.NO_BIDS, uuid); @@ -273,11 +311,19 @@ public void testUpdateTimeoutMillis2() { RequestParams requestParams = new RequestParams("67890", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); assertEquals("Actual Prebid Mobile timeout is " + PrebidMobile.getTimeoutMillis(), 2000, PrebidMobile.getTimeoutMillis()); assertTrue(!PrebidMobile.timeoutMillisUpdated); adapter.requestDemand(requestParams, mockListener, uuid); + + bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); assertEquals("Actual Prebid Mobile timeout is " + PrebidMobile.getTimeoutMillis(), 2000, PrebidMobile.getTimeoutMillis()); @@ -300,6 +346,10 @@ public void testNoBidResponse() { RequestParams requestParams = new RequestParams("67890", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onDemandFailed(ResultCode.NO_BIDS, uuid); @@ -321,6 +371,10 @@ public void testNoBidRubiconResponse() { RequestParams requestParams = new RequestParams("67890", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onDemandFailed(ResultCode.NO_BIDS, uuid); @@ -343,6 +397,10 @@ public void testSuccessfulBidResponse() { RequestParams requestParams = new RequestParams("67890", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); HashMap bids = new HashMap(); @@ -375,6 +433,10 @@ public void testSuccessfulBidRubiconResponse() { RequestParams requestParams = new RequestParams("67890", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); HashMap bids = new HashMap(); @@ -415,6 +477,10 @@ public void testSuccessfulBidResponseWithoutCacheId() { RequestParams requestParams = new RequestParams("67890", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onDemandFailed(ResultCode.NO_BIDS, uuid); @@ -436,6 +502,10 @@ public void testSuccessfulBidRubiconResponseWithoutCacheId() { RequestParams requestParams = new RequestParams("67890", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onDemandFailed(ResultCode.NO_BIDS, uuid); @@ -457,6 +527,10 @@ public void testSuccessfulBidResponseTwoBidsOnTheSameSeat() { RequestParams requestParams = new RequestParams("67890", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); HashMap bids = new HashMap(); @@ -489,6 +563,10 @@ public void testSuccessfulBidResponseWithoutCacheId2() { RequestParams requestParams = new RequestParams("67890", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); HashMap bids = new HashMap(); @@ -521,6 +599,10 @@ public void testSuccessfulBidResponseWithoutCacheId3() { RequestParams requestParams = new RequestParams("67890", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onDemandFailed(ResultCode.NO_BIDS, uuid); @@ -542,6 +624,10 @@ public void testMergingBidsFromDifferentSeats() { RequestParams requestParams = new RequestParams("67890", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); HashMap bids = new HashMap(); @@ -585,6 +671,10 @@ public void testListenerMapping() { String uuid2 = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener1, uuid1); adapter.requestDemand(requestParams, mockListener2, uuid2); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener1).onDemandFailed(ResultCode.NO_BIDS, uuid1); @@ -652,6 +742,10 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio fetcherLooper.runOneTask(); ShadowLooper demandLooper = shadowOf(fetcher.getDemandHandler().getLooper()); demandLooper.runOneTask(); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); Host.CUSTOM.setHostUrl(server.url("/clearKeywords").toString()); @@ -665,6 +759,10 @@ public MockResponse dispatch(RecordedRequest request) throws InterruptedExceptio fetcherLooper.runOneTask(); demandLooper = shadowOf(fetcher.getDemandHandler().getLooper()); demandLooper.runOneTask(); + + bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener, times(1)).onComplete(ResultCode.NO_BIDS); @@ -765,6 +863,10 @@ public void testRubiconDefaultError() { RequestParams requestParams = new RequestParams("67890", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onDemandFailed(ResultCode.PREBID_SERVER_ERROR, uuid); diff --git a/PrebidMobile/src/test/java/org/prebid/mobile/ResultCodeTest.java b/PrebidMobile/src/test/java/org/prebid/mobile/ResultCodeTest.java index b8fb5ff2b..3a15d61e3 100644 --- a/PrebidMobile/src/test/java/org/prebid/mobile/ResultCodeTest.java +++ b/PrebidMobile/src/test/java/org/prebid/mobile/ResultCodeTest.java @@ -26,10 +26,14 @@ import org.apache.commons.lang3.reflect.FieldUtils; import org.junit.Test; import org.junit.runner.RunWith; +import org.prebid.mobile.tasksmanager.BackgroundThreadExecutor; +import org.prebid.mobile.tasksmanager.MainThreadExecutor; +import org.prebid.mobile.tasksmanager.TasksManager; import org.prebid.mobile.testutils.BaseSetup; import org.prebid.mobile.testutils.MockPrebidServerResponses; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; +import org.robolectric.Shadows; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowLooper; import org.robolectric.shadows.ShadowNetworkInfo; @@ -52,6 +56,19 @@ @RunWith(RobolectricTestRunner.class) @Config(sdk = BaseSetup.testSDK) public class ResultCodeTest extends BaseSetup { + + @Override + public void setup() { + super.setup(); + ((BackgroundThreadExecutor)TasksManager.getInstance().backgroundThreadExecutor).startThread(); + } + + @Override + public void tearDown() { + super.tearDown(); + ((BackgroundThreadExecutor)TasksManager.getInstance().backgroundThreadExecutor).shutdown(); + } + @Test public void testInvalidContext() throws Exception { @@ -88,6 +105,10 @@ public void testSuccessForMoPub() throws Exception { fetcherLooper.runOneTask(); ShadowLooper demandLooper = shadowOf(fetcher.getDemandHandler().getLooper()); demandLooper.runOneTask(); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onComplete(ResultCode.SUCCESS); @@ -113,6 +134,10 @@ public void testSuccessForDFP() throws Exception { fetcherLooper.runOneTask(); ShadowLooper demandLooper = shadowOf(fetcher.getDemandHandler().getLooper()); demandLooper.runOneTask(); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onComplete(ResultCode.SUCCESS); @@ -158,6 +183,10 @@ public void testSuccessRubiconForDFP() throws Exception { fetcherLooper.runOneTask(); ShadowLooper demandLooper = shadowOf(fetcher.getDemandHandler().getLooper()); demandLooper.runOneTask(); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); @@ -272,9 +301,13 @@ public void testNoBids() throws Exception { DemandFetcher fetcher = (DemandFetcher) FieldUtils.readField(adUnit, "fetcher", true); PrebidMobile.setTimeoutMillis(Integer.MAX_VALUE); ShadowLooper fetcherLooper = shadowOf(fetcher.getHandler().getLooper()); - fetcherLooper.runOneTask(); + fetcherLooper.runToEndOfTasks(); ShadowLooper demandLooper = shadowOf(fetcher.getDemandHandler().getLooper()); - demandLooper.runOneTask(); + demandLooper.runToEndOfTasks(); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onComplete(ResultCode.NO_BIDS); @@ -299,6 +332,10 @@ public void testNoBidsRubicon() throws Exception { fetcherLooper.runOneTask(); ShadowLooper demandLooper = shadowOf(fetcher.getDemandHandler().getLooper()); demandLooper.runOneTask(); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onComplete(ResultCode.NO_BIDS); @@ -373,6 +410,10 @@ public void testInvalidSizeForBanner() { RequestParams requestParams = new RequestParams("e2edc23f-0b3b-4203-81b5-7cc97132f418", AdType.BANNER, sizes); String uuid = UUID.randomUUID().toString(); adapter.requestDemand(requestParams, mockListener, uuid); + + ShadowLooper bgLooper = Shadows.shadowOf(((BackgroundThreadExecutor) TasksManager.getInstance().backgroundThreadExecutor).getBackgroundHandler().getLooper()); + bgLooper.runToEndOfTasks(); + Robolectric.flushBackgroundThreadScheduler(); Robolectric.flushForegroundThreadScheduler(); verify(mockListener).onDemandFailed(ResultCode.INVALID_SIZE, uuid); From ee0fab642a79d02e14db1cbb602e1fe47e40e2ec Mon Sep 17 00:00:00 2001 From: Abhas Vohra Date: Fri, 8 Jan 2021 19:36:53 +0530 Subject: [PATCH 2/3] Moved POST request code to HTTPPost --- .../prebid/mobile/PrebidServerAdapter.java | 228 +++--------------- .../java/org/prebid/mobile/http/HTTPPost.java | 227 +++++++++++++++++ .../mobile/http/NoContextException.java | 20 ++ .../org/prebid/mobile/http/TaskResult.java | 59 +++++ 4 files changed, 336 insertions(+), 198 deletions(-) create mode 100644 PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HTTPPost.java create mode 100644 PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/NoContextException.java create mode 100644 PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/TaskResult.java diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/PrebidServerAdapter.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/PrebidServerAdapter.java index a5989f68c..3e69ee6bd 100644 --- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/PrebidServerAdapter.java +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/PrebidServerAdapter.java @@ -24,10 +24,7 @@ import android.net.NetworkInfo; import android.os.Build; import android.os.CountDownTimer; -import android.os.Handler; -import android.os.Looper; import android.support.annotation.MainThread; -import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -37,19 +34,11 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import org.prebid.mobile.tasksmanager.TasksManager; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.UnsupportedEncodingException; +import org.prebid.mobile.http.HTTPPost; +import org.prebid.mobile.http.NoContextException; +import org.prebid.mobile.http.TaskResult; + import java.lang.ref.WeakReference; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.SocketTimeoutException; -import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -57,8 +46,6 @@ import java.util.Locale; import java.util.Map; import java.util.UUID; -import java.util.regex.Matcher; -import java.util.regex.Pattern; class PrebidServerAdapter implements DemandAdapter { private ArrayList serverConnectors; @@ -71,18 +58,7 @@ class PrebidServerAdapter implements DemandAdapter { public void requestDemand(RequestParams params, DemandAdapterListener listener, String auctionId) { final ServerConnector connector = new ServerConnector(this, listener, params, auctionId); serverConnectors.add(connector); - if (Looper.myLooper() == Looper.getMainLooper()) { - TasksManager.getInstance().executeOnBackgroundThread(new Runnable() { - @Override - public void run() { - ServerConnector.TaskResult result = connector.execute(); - connector.processResult(result); - } - }); - } else { - ServerConnector.TaskResult result = connector.execute(); - connector.processResult(result); - } + connector.execute(); } @Override @@ -97,7 +73,7 @@ public void stopRequest(String auctionId) { serverConnectors.removeAll(toRemove); } - static class ServerConnector { + static class ServerConnector extends HTTPPost { private static final int TIMEOUT_COUNT_DOWN_INTERVAL = 500; @@ -122,131 +98,30 @@ static class ServerConnector { adType = requestParams.getAdType(); } - private TaskResult execute() { - timeoutCountDownTimer.start(); - try { - long demandFetchStartTime = System.currentTimeMillis(); - - BidLog.BidLogEntry entry = new BidLog.BidLogEntry(); + @Override + protected void onPostExecute(TaskResult response) { + processResult(response); - URL url = new URL(getHost()); - entry.setRequestUrl(getHost()); + } - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setDoOutput(true); - conn.setDoInput(true); - conn.setRequestProperty("Content-Type", "application/json"); - conn.setRequestProperty("Accept", "application/json"); - if(canIAccessDeviceData()) { - String existingCookie = getExistingCookie(); - if (existingCookie != null) { - conn.setRequestProperty(PrebidServerSettings.COOKIE_HEADER, existingCookie); - } // todo still pass cookie if limit ad tracking? - } - conn.setRequestMethod("POST"); - conn.setConnectTimeout(PrebidMobile.getTimeoutMillis()); - - // Add post data - OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream(), "UTF-8"); - JSONObject postData = getPostData(); - String postString = postData.toString(); - LogUtil.d("Sending request for auction " + auctionId + " with post data: " + postString); - wr.write(postString); - wr.flush(); - - entry.setRequestBody(postString); - - // Start the connection - conn.connect(); - - // Read request response - int httpResult = conn.getResponseCode(); - long demandFetchEndTime = System.currentTimeMillis(); - - entry.setResponseCode(httpResult); - - if (httpResult == HttpURLConnection.HTTP_OK) { - StringBuilder builder = new StringBuilder(); - InputStream is = conn.getInputStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8")); - String line; - while ((line = reader.readLine()) != null) { - builder.append(line); - } - reader.close(); - is.close(); - String result = builder.toString(); - entry.setResponse(result); - JSONObject response = new JSONObject(result); - httpCookieSync(conn.getHeaderFields()); - // in the future, this can be improved to parse response base on request versions - if (!PrebidMobile.timeoutMillisUpdated) { - int tmaxRequest = -1; - try { - tmaxRequest = response.getJSONObject("ext").getInt("tmaxrequest"); - } catch (JSONException e) { - // ignore this - } - if (tmaxRequest >= 0) { - PrebidMobile.setTimeoutMillis(Math.min((int) (demandFetchEndTime - demandFetchStartTime) + tmaxRequest + 200, 2000)); // adding 200ms as safe time - PrebidMobile.timeoutMillisUpdated = true; - } - } + public void execute() { + timeoutCountDownTimer.start(); + super.execute(); + } - BidLog.getInstance().setLastEntry(entry); + @Override + protected String getUrl() { + return PrebidMobile.getPrebidServerHost().getHostUrl(); + } - return new TaskResult<>(response); - } else if (httpResult >= HttpURLConnection.HTTP_BAD_REQUEST) { - StringBuilder builder = new StringBuilder(); - InputStream is = conn.getErrorStream(); - BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8")); - String line; - while ((line = reader.readLine()) != null) { - builder.append(line); - } - reader.close(); - is.close(); - String result = builder.toString(); - entry.setResponse(result); - LogUtil.d("Getting response for auction " + getAuctionId() + ": " + result); - Pattern storedRequestNotFound = Pattern.compile("^Invalid request: Stored Request with ID=\".*\" not found."); - Pattern storedImpNotFound = Pattern.compile("^Invalid request: Stored Imp with ID=\".*\" not found."); - Pattern invalidBannerSize = Pattern.compile("^Invalid request: Request imp\\[\\d\\].banner.format\\[\\d\\] must define non-zero \"h\" and \"w\" properties."); - Pattern invalidInterstitialSize = Pattern.compile("Invalid request: Unable to set interstitial size list"); - Matcher m = storedRequestNotFound.matcher(result); - Matcher m2 = invalidBannerSize.matcher(result); - Matcher m3 = storedImpNotFound.matcher(result); - Matcher m4 = invalidInterstitialSize.matcher(result); - - BidLog.getInstance().setLastEntry(entry); - - if (m.find() || result.contains("No stored request")) { - return new TaskResult<>(ResultCode.INVALID_ACCOUNT_ID); - } else if (m3.find() || result.contains("No stored imp")) { - return new TaskResult<>(ResultCode.INVALID_CONFIG_ID); - } else if (m2.find() || m4.find() || result.contains("Request imp[0].banner.format")) { - return new TaskResult<>(ResultCode.INVALID_SIZE); - } else { - return new TaskResult<>(ResultCode.PREBID_SERVER_ERROR); - } - } + @Override + protected void setTimeoutMillisUpdated(boolean b) { + PrebidMobile.timeoutMillisUpdated = b; + } - } catch (MalformedURLException e) { - return new TaskResult<>(e); - } catch (UnsupportedEncodingException e) { - return new TaskResult<>(e); - } catch (SocketTimeoutException ex) { - return new TaskResult<>(ResultCode.TIMEOUT); - } catch (IOException e) { - return new TaskResult<>(e); - } catch (JSONException e) { - return new TaskResult<>(e); - } catch (NoContextException ex) { - return new TaskResult<>(ResultCode.INVALID_CONTEXT); - } catch (Exception e) { - return new TaskResult<>(e); - } - return new TaskResult<>(new RuntimeException("ServerConnector exception")); + @Override + protected boolean isTimeoutMillisUpdated() { + return PrebidMobile.timeoutMillisUpdated; } private void processResult(TaskResult taskResult) { @@ -361,7 +236,7 @@ private void removeThisTask() { prebidServerAdapter.serverConnectors.remove(this); } - String getAuctionId() { + protected String getAuctionId() { return auctionId; } @@ -395,10 +270,6 @@ private void notifyContainsTopBid(boolean contains) { } } - private String getHost() { - return PrebidMobile.getPrebidServerHost().getHostUrl(); - } - /** * Synchronize the uuid2 cookie to the Webview Cookie Jar * This is only done if there is no present cookie. @@ -406,7 +277,7 @@ private String getHost() { * @param headers headers to extract cookies from for syncing */ @SuppressWarnings("deprecation") - private void httpCookieSync(Map> headers) { + protected void httpCookieSync(Map> headers) { if (headers == null || headers.isEmpty()) return; CookieManager cm = CookieManager.getInstance(); if (cm == null) { @@ -448,7 +319,7 @@ private void httpCookieSync(Map> headers) { } } - private String getExistingCookie() { + protected String getExistingCookie() { try { CookieSyncManager.createInstance(PrebidMobile.getApplicationContext()); CookieManager cm = CookieManager.getInstance(); @@ -469,7 +340,7 @@ private String getExistingCookie() { } - private JSONObject getPostData() throws NoContextException { + protected JSONObject getPostData() throws NoContextException { JSONObject postData = new JSONObject(); try { @@ -1091,7 +962,7 @@ private JSONObject getRegsObject() { return regs; } - private boolean canIAccessDeviceData() { + protected boolean canIAccessDeviceData() { //fetch advertising identifier based TCF 2.0 Purpose1 value //truth table /* @@ -1115,45 +986,6 @@ private boolean canIAccessDeviceData() { return setDeviceId; } - private static class NoContextException extends Exception { - } - - private static class TaskResult { - @Nullable - private T result; - @Nullable - private ResultCode resultCode; - @Nullable - private Exception error; - - @Nullable - public T getResult() { - return result; - } - - @Nullable - public ResultCode getResultCode() { - return resultCode; - } - - @Nullable - public Exception getError() { - return error; - } - - private TaskResult(@NonNull T result) { - this.result = result; - } - - private TaskResult(@NonNull ResultCode resultCode) { - this.resultCode = resultCode; - } - - private TaskResult(@NonNull Exception error) { - this.error = error; - } - } - class TimeoutCountDownTimer extends CountDownTimer { /** diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HTTPPost.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HTTPPost.java new file mode 100644 index 000000000..7863a4413 --- /dev/null +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HTTPPost.java @@ -0,0 +1,227 @@ +/* + * Copyright 2020-2021 Prebid.org, Inc. + * + * 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 org.prebid.mobile.http; + +import android.os.Looper; + +import org.json.JSONException; +import org.json.JSONObject; +import org.prebid.mobile.BidLog; +import org.prebid.mobile.LogUtil; +import org.prebid.mobile.PrebidMobile; +import org.prebid.mobile.ResultCode; +import org.prebid.mobile.Util; +import org.prebid.mobile.tasksmanager.TasksManager; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.ProtocolException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public abstract class HTTPPost { + + static final String COOKIE_HEADER = "Cookie"; + + public HTTPPost() { + super(); + } + + abstract protected void onPostExecute(TaskResult response); + + public void execute() { + if (Looper.myLooper() == Looper.getMainLooper()) { + TasksManager.getInstance().executeOnBackgroundThread(new Runnable() { + @Override + public void run() { + TaskResult response = makeHttpRequest(); + onPostExecute(response); + } + }); + } else { + TaskResult response = makeHttpRequest(); + onPostExecute(response); + } + } + + protected abstract String getUrl(); + + private HttpURLConnection createConnection(URL url) throws IOException { + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setDoOutput(false); + connection.setDoInput(true); + connection.setUseCaches(false); + connection.setRequestMethod("GET"); + return connection; + } + + private void setConnectionParams(HttpURLConnection connection) throws ProtocolException { + connection.setConnectTimeout(Util.HTTP_CONNECTION_TIMEOUT); + connection.setReadTimeout(Util.HTTP_SOCKET_TIMEOUT); + } + + protected TaskResult makeHttpRequest() { + try { + long demandFetchStartTime = System.currentTimeMillis(); + + BidLog.BidLogEntry entry = new BidLog.BidLogEntry(); + + URL url = new URL(getUrl()); + entry.setRequestUrl(getUrl()); + + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setDoOutput(true); + conn.setDoInput(true); + conn.setRequestProperty("Content-Type", "application/json"); + conn.setRequestProperty("Accept", "application/json"); + if(canIAccessDeviceData()) { + String existingCookie = getExistingCookie(); + if (existingCookie != null) { + conn.setRequestProperty(COOKIE_HEADER, existingCookie); + } // todo still pass cookie if limit ad tracking? + } + conn.setRequestMethod("POST"); + conn.setConnectTimeout(PrebidMobile.getTimeoutMillis()); + + // Add post data + OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream(), "UTF-8"); + JSONObject postData = getPostData(); + String postString = postData.toString(); + LogUtil.d("Sending request for auction " + getAuctionId() + " with post data: " + postString); + wr.write(postString); + wr.flush(); + + entry.setRequestBody(postString); + + // Start the connection + conn.connect(); + + // Read request response + int httpResult = conn.getResponseCode(); + long demandFetchEndTime = System.currentTimeMillis(); + + entry.setResponseCode(httpResult); + + if (httpResult == HttpURLConnection.HTTP_OK) { + StringBuilder builder = new StringBuilder(); + InputStream is = conn.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8")); + String line; + while ((line = reader.readLine()) != null) { + builder.append(line); + } + reader.close(); + is.close(); + String result = builder.toString(); + entry.setResponse(result); + JSONObject response = new JSONObject(result); + httpCookieSync(conn.getHeaderFields()); + // in the future, this can be improved to parse response base on request versions + if (!isTimeoutMillisUpdated()) { + int tmaxRequest = -1; + try { + tmaxRequest = response.getJSONObject("ext").getInt("tmaxrequest"); + } catch (JSONException e) { + // ignore this + } + if (tmaxRequest >= 0) { + PrebidMobile.setTimeoutMillis(Math.min((int) (demandFetchEndTime - demandFetchStartTime) + tmaxRequest + 200, 2000)); // adding 200ms as safe time + setTimeoutMillisUpdated(true); + } + } + + BidLog.getInstance().setLastEntry(entry); + + return new TaskResult<>(response); + } else if (httpResult >= HttpURLConnection.HTTP_BAD_REQUEST) { + StringBuilder builder = new StringBuilder(); + InputStream is = conn.getErrorStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(is, "utf-8")); + String line; + while ((line = reader.readLine()) != null) { + builder.append(line); + } + reader.close(); + is.close(); + String result = builder.toString(); + entry.setResponse(result); + LogUtil.d("Getting response for auction " + getAuctionId() + ": " + result); + Pattern storedRequestNotFound = Pattern.compile("^Invalid request: Stored Request with ID=\".*\" not found."); + Pattern storedImpNotFound = Pattern.compile("^Invalid request: Stored Imp with ID=\".*\" not found."); + Pattern invalidBannerSize = Pattern.compile("^Invalid request: Request imp\\[\\d\\].banner.format\\[\\d\\] must define non-zero \"h\" and \"w\" properties."); + Pattern invalidInterstitialSize = Pattern.compile("Invalid request: Unable to set interstitial size list"); + Matcher m = storedRequestNotFound.matcher(result); + Matcher m2 = invalidBannerSize.matcher(result); + Matcher m3 = storedImpNotFound.matcher(result); + Matcher m4 = invalidInterstitialSize.matcher(result); + + BidLog.getInstance().setLastEntry(entry); + + if (m.find() || result.contains("No stored request")) { + return new TaskResult<>(ResultCode.INVALID_ACCOUNT_ID); + } else if (m3.find() || result.contains("No stored imp")) { + return new TaskResult<>(ResultCode.INVALID_CONFIG_ID); + } else if (m2.find() || m4.find() || result.contains("Request imp[0].banner.format")) { + return new TaskResult<>(ResultCode.INVALID_SIZE); + } else { + return new TaskResult<>(ResultCode.PREBID_SERVER_ERROR); + } + } + + } catch (MalformedURLException e) { + return new TaskResult<>(e); + } catch (UnsupportedEncodingException e) { + return new TaskResult<>(e); + } catch (SocketTimeoutException ex) { + return new TaskResult<>(ResultCode.TIMEOUT); + } catch (IOException e) { + return new TaskResult<>(e); + } catch (JSONException e) { + return new TaskResult<>(e); + } catch (NoContextException ex) { + return new TaskResult<>(ResultCode.INVALID_CONTEXT); + } catch (Exception e) { + return new TaskResult<>(e); + } + return new TaskResult<>(new RuntimeException("ServerConnector exception")); + } + + protected abstract void setTimeoutMillisUpdated(boolean b); + + protected abstract boolean isTimeoutMillisUpdated(); + + protected abstract String getAuctionId(); + + protected abstract JSONObject getPostData() throws NoContextException; + + protected abstract boolean canIAccessDeviceData(); + + protected abstract String getExistingCookie(); + + protected abstract void httpCookieSync(Map> headerFields); +} + diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/NoContextException.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/NoContextException.java new file mode 100644 index 000000000..4203325df --- /dev/null +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/NoContextException.java @@ -0,0 +1,20 @@ +/* + * Copyright 2020-2021 Prebid.org, Inc. + * + * 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 org.prebid.mobile.http; + +public class NoContextException extends Exception { +} diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/TaskResult.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/TaskResult.java new file mode 100644 index 000000000..9001dfe69 --- /dev/null +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/TaskResult.java @@ -0,0 +1,59 @@ +/* + * Copyright 2020-2021 Prebid.org, Inc. + * + * 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 org.prebid.mobile.http; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import org.prebid.mobile.ResultCode; + +public class TaskResult { + @Nullable + private T result; + @Nullable + private ResultCode resultCode; + @Nullable + private Exception error; + + @Nullable + public T getResult() { + return result; + } + + @Nullable + public ResultCode getResultCode() { + return resultCode; + } + + @Nullable + public Exception getError() { + return error; + } + + protected TaskResult(@NonNull T result) { + this.result = result; + } + + protected TaskResult(@NonNull ResultCode resultCode) { + this.resultCode = resultCode; + } + + protected TaskResult(@NonNull Exception error) { + this.error = error; + } +} + From 560f676c51c5f8b6b07e1fd46814d6c8cdac6aed Mon Sep 17 00:00:00 2001 From: Abhas Vohra Date: Mon, 8 Feb 2021 19:56:40 +0530 Subject: [PATCH 3/3] Updated document license for the requested files, replaced h, mHandler with handler --- .../org/prebid/mobile/PrebidServerAdapter.java | 16 +--------------- .../org/prebid/mobile/http/HTTPResponse.java | 3 ++- .../org/prebid/mobile/http/HttpErrorCode.java | 2 +- .../tasksmanager/BackgroundThreadExecutor.java | 18 +++++++++--------- .../tasksmanager/CancellableExecutor.java | 2 +- .../tasksmanager/MainThreadExecutor.java | 10 +++++----- .../mobile/tasksmanager/TasksManager.java | 2 +- 7 files changed, 20 insertions(+), 33 deletions(-) diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/PrebidServerAdapter.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/PrebidServerAdapter.java index 3e69ee6bd..8efbc3a64 100644 --- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/PrebidServerAdapter.java +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/PrebidServerAdapter.java @@ -210,22 +210,8 @@ private void cancel() { } else { timeoutCountDownTimer.cancel(); } -// removeThisTask(); } - -// @Override -// @MainThread -// protected void onCancelled() { -// super.onCancelled(); -// -// if (timeoutFired) { -// notifyDemandFailed(ResultCode.TIMEOUT); -// } else { -// timeoutCountDownTimer.cancel(); -// } -// removeThisTask(); -// } - + private void removeThisTask() { @Nullable PrebidServerAdapter prebidServerAdapter = this.prebidServerAdapter.get(); diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HTTPResponse.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HTTPResponse.java index bc4c99d69..7357aff9f 100644 --- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HTTPResponse.java +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HTTPResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 APPNEXUS INC + * Copyright 2020-2021 Prebid.org, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.prebid.mobile.http; import java.util.List; diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HttpErrorCode.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HttpErrorCode.java index 9e1fbeebf..7fb20f49b 100644 --- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HttpErrorCode.java +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/http/HttpErrorCode.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 APPNEXUS INC + * Copyright 2020-2021 Prebid.org, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/BackgroundThreadExecutor.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/BackgroundThreadExecutor.java index 40e7b54ed..894a4a537 100644 --- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/BackgroundThreadExecutor.java +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/BackgroundThreadExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 APPNEXUS INC + * Copyright 2020-2021 Prebid.org, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ public class BackgroundThreadExecutor implements CancellableExecutor { - private Handler h; + private Handler handler; private boolean running = false; private final int HANDLER_COUNT = 3; private int index = 0; @@ -30,7 +30,7 @@ public class BackgroundThreadExecutor implements CancellableExecutor { BackgroundThreadExecutor() { HandlerThread backgroundThread = new HandlerThread("BackgroundThread"); backgroundThread.start(); - h = new Handler(backgroundThread.getLooper()); + handler = new Handler(backgroundThread.getLooper()); running = true; } @@ -38,14 +38,14 @@ public class BackgroundThreadExecutor implements CancellableExecutor { @Override public void execute(Runnable runnable) { if (running) { - h.post(runnable); + handler.post(runnable); } } @Override public boolean cancel(Runnable runnable) { if (running) { - h.removeCallbacks(runnable); + handler.removeCallbacks(runnable); return true; } return false; @@ -53,8 +53,8 @@ public boolean cancel(Runnable runnable) { public void shutdown() { if (running) { - h.getLooper().quit(); - h = null; + handler.getLooper().quit(); + handler = null; running = false; } } @@ -63,13 +63,13 @@ public void startThread() { if (!running) { HandlerThread backgroundThread = new HandlerThread("BackgroundThread"); backgroundThread.start(); - h = new Handler(backgroundThread.getLooper()); + handler = new Handler(backgroundThread.getLooper()); running = true; } } @VisibleForTesting public Handler getBackgroundHandler() { - return h; + return handler; } } diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/CancellableExecutor.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/CancellableExecutor.java index 8b7351e9a..d724e271c 100644 --- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/CancellableExecutor.java +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/CancellableExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 APPNEXUS INC + * Copyright 2020-2021 Prebid.org, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/MainThreadExecutor.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/MainThreadExecutor.java index 72cc4b497..f152a6831 100644 --- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/MainThreadExecutor.java +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/MainThreadExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 APPNEXUS INC + * Copyright 2020-2021 Prebid.org, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,21 +21,21 @@ import android.support.annotation.VisibleForTesting; public class MainThreadExecutor implements CancellableExecutor { - private final Handler mHandler = new Handler(Looper.getMainLooper()); + private final Handler handler = new Handler(Looper.getMainLooper()); @Override public void execute(Runnable runnable) { - mHandler.post(runnable); + handler.post(runnable); } @Override public boolean cancel(Runnable runnable) { - mHandler.removeCallbacks(runnable); + handler.removeCallbacks(runnable); return true; } @VisibleForTesting public Handler getMainExecutor() { - return mHandler; + return handler; } } diff --git a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/TasksManager.java b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/TasksManager.java index f5f517947..e9e90e842 100644 --- a/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/TasksManager.java +++ b/PrebidMobile/PrebidMobile-core/src/main/java/org/prebid/mobile/tasksmanager/TasksManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2020 APPNEXUS INC + * Copyright 2020-2021 Prebid.org, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License.