com.appnexus.opensdk.AdRequest.java Source code

Java tutorial

Introduction

Here is the source code for com.appnexus.opensdk.AdRequest.java

Source

/*
 *    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 com.appnexus.opensdk;

import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import android.util.Pair;
import com.appnexus.opensdk.InterstitialAdView.Size;
import com.appnexus.opensdk.utils.Clog;
import com.appnexus.opensdk.utils.HashingFunctions;
import com.appnexus.opensdk.utils.Settings;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.HttpHostConnectException;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.util.ArrayList;

class AdRequest extends AsyncTask<Void, Integer, AdResponse> {

    private AdView owner;
    private final AdRequester requester;
    private AdListener adListener;
    private Context context;
    private String hidmd5;
    private String hidsha1;
    private String devMake;
    private String devModel;
    private String carrier;
    private boolean firstlaunch;
    private String lat;
    private String lon;
    private String locDataAge;
    private String locDataPrecision;
    private String ua;
    private String orientation;
    private String allowedSizes;
    private String mcc;
    private String mnc;
    private String connection_type;
    private String dev_time; // Set at the time of the request
    private String dev_timezone;
    private String language;
    private final String placementId;
    private String nativeBrowser;
    private String psa;
    private int width = -1;
    private int height = -1;
    private int maxWidth = -1;
    private int maxHeight = -1;
    private boolean shouldRetry = true; // true by default
    private float reserve = 0.00f;
    private String age;
    private String gender;
    private ArrayList<Pair<String, String>> customKeywords;

    private final Handler retryHandler = new Handler();

    private int httpRetriesLeft = 0;
    private int blankRetriesLeft = 0;

    private static final AdResponse HTTP_ERROR = new AdResponse(true, false, false);
    private static final AdResponse CONNECTIVITY_RETRY = new AdResponse(false, true, false);
    private static final AdResponse BLANK_RETRY = new AdResponse(false, false, true);

    /**
     * Creates a new AdRequest with the given parameters
     *
     * @param requester       The instance of AdRequester which is filing this request.
     * @param aid             The ANDROID_ID to hash and pass.
     * @param lat             The lattitude to pass.
     * @param lon             The longistude to pass.
     * @param placementId     The AppNexus placement id to use
     * @param orientation     The device orientation to pass, 'portrait' or 'landscape'
     * @param carrier         The carrier to pass, such as 'AT&T'
     * @param width           The width to request, in pixels. -1 for none.
     * @param height          The height to request, in pixels. -1 for none.
     * @param maxWidth        The maximum width, if no width is specified.
     * @param maxHeight       The maximum height, if no height is specified.
     * @param mcc             The MCC to pass.
     * @param mnc             The MNC to pass
     * @param connectionType  The type of connection, 'wifi' or 'wan'
     * @param isNativeBrowser Whether this ad space will open the landing page in the native
     *                        browser ('1') or the in-app browser ('0').
     * @param adListener      The instance of AdListener to use.
     * @param shouldServePSAs Whether this ad space accepts PSAs ('1') or only wants ads
     *                        ('0')
     */
    public AdRequest(AdRequester requester, String aid, String lat, String lon, String placementId,
            String orientation, String carrier, int width, int height, int maxWidth, int maxHeight, String mcc,
            String mnc, String connectionType, boolean isNativeBrowser, AdListener adListener,
            boolean shouldServePSAs, boolean shouldRetry) {
        this.adListener = adListener;
        this.requester = requester;
        this.httpRetriesLeft = Settings.getSettings().MAX_CONNECTIVITY_RETRIES;
        this.blankRetriesLeft = Settings.getSettings().MAX_BLANK_RETRIES;
        if (aid != null) {
            hidmd5 = HashingFunctions.md5(aid);
            hidsha1 = HashingFunctions.sha1(aid);
        }
        devMake = Settings.getSettings().deviceMake;
        devModel = Settings.getSettings().deviceModel;

        // Get firstlaunch and convert it to a string
        firstlaunch = Settings.getSettings().first_launch;
        // Get ua, the user agent...
        ua = Settings.getSettings().ua;

        this.lat = lat;
        this.lon = lon;

        this.carrier = carrier;

        this.mnc = mnc;
        this.mcc = mcc;

        this.width = width;
        this.height = height;
        this.maxWidth = maxWidth;
        this.maxHeight = maxHeight;

        this.connection_type = connectionType;
        this.dev_time = "" + System.currentTimeMillis();

        this.dev_timezone = Settings.getSettings().dev_timezone;
        this.language = Settings.getSettings().language;

        this.placementId = placementId;
        this.psa = shouldServePSAs ? "1" : "0";

        this.nativeBrowser = isNativeBrowser ? "1" : "0";

        this.shouldRetry = shouldRetry;
    }

    public AdRequest(AdRequester adRequester) {
        this(adRequester, Settings.getSettings().MAX_CONNECTIVITY_RETRIES,
                Settings.getSettings().MAX_BLANK_RETRIES);
    }

    private AdRequest(AdRequester adRequester, int httpRetriesLeft, int blankRetriesLeft) {
        owner = adRequester.getOwner();
        this.requester = adRequester;
        this.httpRetriesLeft = httpRetriesLeft;
        this.blankRetriesLeft = blankRetriesLeft;
        this.placementId = owner.getPlacementID();
        context = owner.getContext();
        String aid = android.provider.Settings.Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);

        // Do we have access to location?
        if (context.checkCallingOrSelfPermission(
                "android.permission.ACCESS_FINE_LOCATION") == PackageManager.PERMISSION_GRANTED
                || context.checkCallingOrSelfPermission(
                        "android.permission.ACCESS_COARSE_LOCATION") == PackageManager.PERMISSION_GRANTED) {
            // Get lat, long from any GPS information that might be currently
            // available
            LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
            Location lastLocation = lm.getLastKnownLocation(lm.getBestProvider(new Criteria(), false));
            if (lastLocation != null) {
                lat = "" + lastLocation.getLatitude();
                lon = "" + lastLocation.getLongitude();
                locDataAge = "" + (System.currentTimeMillis() - lastLocation.getTime());
                locDataPrecision = "" + lastLocation.getAccuracy();
            }
        } else {
            Clog.w(Clog.baseLogTag, Clog.getString(R.string.permissions_missing_location));
        }

        // Do we have permission ACCESS_NETWORK_STATE?
        if (context.checkCallingOrSelfPermission(
                "android.permission.ACCESS_NETWORK_STATE") != PackageManager.PERMISSION_GRANTED) {
            Clog.e(Clog.baseLogTag, Clog.getString(R.string.permissions_missing_network_state));
            fail();
            this.cancel(true);
            return;
        }

        // Get orientation, the current rotation of the device
        orientation = context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE
                ? "h"
                : "v";
        // Get hidmd5, hidsha1, the device ID hashed
        if (Settings.getSettings().hidmd5 == null) {
            Settings.getSettings().hidmd5 = HashingFunctions.md5(aid);
        }
        hidmd5 = Settings.getSettings().hidmd5;
        if (Settings.getSettings().hidsha1 == null) {
            Settings.getSettings().hidsha1 = HashingFunctions.sha1(aid);
        }
        hidsha1 = Settings.getSettings().hidsha1;
        // Get devMake, devModel, the Make and Model of the current device
        devMake = Settings.getSettings().deviceMake;
        devModel = Settings.getSettings().deviceModel;
        // Get carrier
        if (Settings.getSettings().carrierName == null) {
            Settings.getSettings().carrierName = ((TelephonyManager) context
                    .getSystemService(Context.TELEPHONY_SERVICE)).getNetworkOperatorName();
        }
        carrier = Settings.getSettings().carrierName;
        // Get firstlaunch and convert it to a string
        firstlaunch = Settings.getSettings().first_launch;
        // Get ua, the user agent...
        ua = Settings.getSettings().ua;
        // Get wxh

        if (owner.isBanner()) {
            this.width = ((BannerAdView) owner).getAdWidth();
            this.height = ((BannerAdView) owner).getAdHeight();
        }

        maxHeight = owner.getContainerHeight();
        maxWidth = owner.getContainerWidth();

        if (Settings.getSettings().mcc == null || Settings.getSettings().mnc == null) {
            TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            String networkOperator = tm.getNetworkOperator();
            if (networkOperator != null && networkOperator.length() >= 6) {
                Settings.getSettings().mcc = networkOperator.substring(0, 3);
                Settings.getSettings().mnc = networkOperator.substring(3);
            }
        }
        mcc = Settings.getSettings().mcc;
        mnc = Settings.getSettings().mnc;

        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo wifi = cm.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
        connection_type = wifi.isConnected() ? "wifi" : "wan";
        dev_time = "" + System.currentTimeMillis();

        if (owner instanceof InterstitialAdView) {
            // Make string for allowed_sizes
            allowedSizes = "";
            ArrayList<Size> sizes = ((InterstitialAdView) owner).getAllowedSizes();
            for (Size s : sizes) {
                allowedSizes += "" + s.width() + "x" + s.height();
                // If not last size, add a comma
                if (sizes.indexOf(s) != sizes.size() - 1)
                    allowedSizes += ",";
            }
        }

        nativeBrowser = owner.getOpensNativeBrowser() ? "1" : "0";

        //Reserve price
        reserve = owner.getReserve();
        if (reserve <= 0) {
            this.psa = owner.shouldServePSAs ? "1" : "0";
        } else {
            this.psa = "0";
        }

        age = owner.getAge();
        if (owner.getGender() != null) {
            if (owner.getGender() == AdView.GENDER.MALE) {
                gender = "m";
            } else if (owner.getGender() == AdView.GENDER.FEMALE) {
                gender = "f";
            } else {
                gender = null;
            }
        }
        customKeywords = owner.getCustomKeywords();

        mcc = Settings.getSettings().mcc;
        mnc = Settings.getSettings().mnc;
        language = Settings.getSettings().language;
    }

    private void fail() {
        if (requester != null)
            requester.failed(this);
        if (adListener != null)
            adListener.onAdRequestFailed(this.owner);
        Clog.clearLastResponse();
    }

    String getRequestUrl() {
        StringBuilder sb;
        sb = new StringBuilder(Settings.getSettings().BASE_URL);
        sb.append("id=");
        if (placementId != null) {
            sb.append(Uri.encode(placementId));
        } else {
            sb.append("NO-PLACEMENT-ID");
        }
        if (!isEmpty(hidmd5))
            sb.append("&md5udid=").append(Uri.encode(hidmd5));
        if (!isEmpty(hidsha1))
            sb.append("&sha1udid=").append(Uri.encode(hidsha1));
        if (!isEmpty(devMake))
            sb.append("&devmake=").append(Uri.encode(devMake));
        if (!isEmpty(devModel))
            sb.append("&devmodel=").append(Uri.encode(devModel));
        if (!isEmpty(carrier))
            sb.append("&carrier=").append(Uri.encode(carrier));
        sb.append("&appid=");
        if (!isEmpty(Settings.getSettings().app_id)) {
            sb.append(Uri.encode(Settings.getSettings().app_id));
        } else {
            sb.append("NO-APP-ID");
        }
        if (firstlaunch)
            sb.append("&firstlaunch=true");
        if (!isEmpty(lat) && !isEmpty(lon))
            sb.append("&loc=").append(lat).append(",").append(lon);
        if (!isEmpty(locDataAge))
            sb.append("&loc_age=").append(locDataAge);
        if (!isEmpty(locDataPrecision))
            sb.append("&loc_prec=").append(locDataPrecision);
        if (Settings.getSettings().test_mode)
            sb.append("&istest=true");
        if (!isEmpty(ua))
            sb.append("&ua=").append(Uri.encode(ua));
        if (!isEmpty(orientation))
            sb.append("&orientation=").append(orientation);
        if (width > 0 && height > 0)
            sb.append("&size=").append(width).append("x").append(height);
        // complicated, don't change
        if (owner != null) {
            if (maxHeight > 0 && maxWidth > 0) {
                if (!(owner instanceof InterstitialAdView) && (width < 0 || height < 0)) {
                    sb.append("&max_size=").append(maxWidth).append("x").append(maxHeight);
                } else if (owner instanceof InterstitialAdView) {
                    sb.append("&size=").append(maxWidth).append("x").append(maxHeight);
                }
            }
        }
        if (!isEmpty(allowedSizes))
            sb.append("&promo_sizes=").append(allowedSizes);
        if (!isEmpty(mcc))
            sb.append("&mcc=").append(Uri.encode(mcc));
        if (!isEmpty(mnc))
            sb.append("&mnc=").append(Uri.encode(mnc));
        if (!isEmpty(language))
            sb.append("&language=").append(Uri.encode(language));
        if (!isEmpty(dev_timezone))
            sb.append("&devtz=").append(Uri.encode(dev_timezone));
        if (!isEmpty(dev_time))
            sb.append("&devtime=").append(Uri.encode(dev_time));
        if (!isEmpty(connection_type))
            sb.append("&connection_type=").append(Uri.encode(connection_type));
        if (!isEmpty(nativeBrowser))
            sb.append("&native_browser=").append(nativeBrowser);
        if (!isEmpty(psa))
            sb.append("&psa=").append(psa);
        if (reserve > 0)
            sb.append("&reserve=").append(reserve);
        if (!isEmpty(age))
            sb.append("&age=").append(Uri.encode(age));
        if (!isEmpty(gender))
            sb.append("&gender=").append(Uri.encode(gender));
        sb.append("&format=json");
        sb.append("&st=mobile_app");
        sb.append("&sdkver=").append(Uri.encode(Settings.getSettings().sdkVersion));

        // add custom parameters if there are any
        if (customKeywords != null) {
            for (Pair<String, String> pair : customKeywords) {
                if (!isEmpty(pair.first) && (pair.second != null)) {
                    sb.append("&").append(pair.first).append("=").append(Uri.encode(pair.second));
                }
            }
        }

        return sb.toString();
    }

    @Override
    protected AdResponse doInBackground(Void... params) {
        if (!hasNetwork(context)) {
            Clog.e(Clog.httpReqLogTag, Clog.getString(R.string.no_connectivity));
            if (!shouldRetry)
                return doRequest();
            return AdRequest.CONNECTIVITY_RETRY;
        }

        return doRequest();

    }

    private AdResponse doRequest() {
        String query_string = getRequestUrl();

        Clog.setLastRequest(query_string);

        Clog.d(Clog.httpReqLogTag, Clog.getString(R.string.fetch_url, query_string));

        HttpResponse r = null;
        String out = null;
        try {
            HttpParams p = new BasicHttpParams();
            HttpConnectionParams.setConnectionTimeout(p, Settings.getSettings().HTTP_CONNECTION_TIMEOUT);
            HttpConnectionParams.setSoTimeout(p, Settings.getSettings().HTTP_SOCKET_TIMEOUT);
            HttpConnectionParams.setSocketBufferSize(p, 8192);
            DefaultHttpClient h = new DefaultHttpClient(p);
            r = h.execute(new HttpGet(query_string));
            if (!httpShouldContinue(r.getStatusLine())) {
                return AdRequest.HTTP_ERROR;
            }
            out = EntityUtils.toString(r.getEntity());
        } catch (ClientProtocolException e) {
            Clog.e(Clog.httpReqLogTag, Clog.getString(R.string.http_unknown));
            return AdRequest.CONNECTIVITY_RETRY;
        } catch (ConnectTimeoutException e) {
            Clog.e(Clog.httpReqLogTag, Clog.getString(R.string.http_timeout));
            return AdRequest.CONNECTIVITY_RETRY;
        } catch (HttpHostConnectException he) {
            Clog.e(Clog.httpReqLogTag,
                    Clog.getString(R.string.http_unreachable, he.getHost().getHostName(), he.getHost().getPort()));
            return AdRequest.CONNECTIVITY_RETRY;
        } catch (IOException e) {
            Clog.e(Clog.httpReqLogTag, Clog.getString(R.string.http_io));
            return AdRequest.CONNECTIVITY_RETRY;
        } catch (SecurityException se) {
            Clog.e(Clog.baseLogTag, Clog.getString(R.string.permissions_internet));
            return null;
        } catch (Exception e) {
            e.printStackTrace();
            Clog.e(Clog.baseLogTag, Clog.getString(R.string.unknown_exception));
            return AdRequest.CONNECTIVITY_RETRY;
        }
        if (out.equals("")) {
            Clog.e(Clog.httpRespLogTag, Clog.getString(R.string.response_blank));
            return AdRequest.BLANK_RETRY;
        }
        return new AdResponse(out, r.getAllHeaders());
    }

    private boolean hasNetwork(Context context) {
        if (context != null) {
            NetworkInfo ninfo = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE))
                    .getActiveNetworkInfo();
            if (ninfo != null && ninfo.isConnectedOrConnecting()) {
                return true;
            }
        }
        return false;
    }

    private boolean httpShouldContinue(StatusLine statusLine) {
        if (statusLine == null)
            return false;

        int http_error_code = statusLine.getStatusCode();
        switch (http_error_code) {
        default:
            Clog.d(Clog.httpRespLogTag, Clog.getString(R.string.http_bad_status, http_error_code));
            return false;
        case 200:
            return true;
        }

    }

    @Override
    protected void onPostExecute(AdResponse result) {
        if (requester != null)
            requester.setAdRequest(null);
        if (result == null) {
            Clog.v(Clog.httpRespLogTag, Clog.getString(R.string.no_response));
            fail();
            // Don't call fail again!
            return; // http request failed
        }
        if (result.isHttpError()) {
            fail();
            return;
        }

        if (shouldRetry) {
            if ((httpRetriesLeft < 1) || (blankRetriesLeft < 1)) {
                // return if we have exceeded the max number of tries
                fail();
                return;
            }
            boolean resultIsRetry = false;

            if (result.isConnectivityRetry()) {
                httpRetriesLeft--;
                resultIsRetry = true;
            } else if (result.isBlankRetry()) {
                blankRetriesLeft--;
                resultIsRetry = true;
            }

            if (resultIsRetry) {
                // don't fail, but clear the last response
                Clog.clearLastResponse();
                final AdRequest retry = new AdRequest(requester, httpRetriesLeft, blankRetriesLeft);
                if (requester != null)
                    requester.setAdRequest(retry);
                retry.retryHandler.postDelayed(new RetryRunnable(retry),
                        Settings.getSettings().HTTP_RETRY_INTERVAL);
                return; // The request failed and should be retried.
            }
            // else let it continue to process the valid result
        }
        if (requester != null)
            requester.onReceiveResponse(result);
        // for unit testing
        if (adListener != null)
            adListener.onAdLoaded(owner);
    }

    @Override
    protected void onCancelled(AdResponse adResponse) {
        super.onCancelled(adResponse);
        Clog.w(Clog.httpRespLogTag, Clog.getString(R.string.cancel_request));
        if (requester != null)
            requester.setAdRequest(null);
        // remove pending retry requests if the requester cancels the ad request
        retryHandler.removeCallbacksAndMessages(null);
    }

    private static boolean isEmpty(String str) {
        return (str == null) || str.equals("");
    }

    class RetryRunnable implements Runnable {
        final AdRequest retry;

        RetryRunnable(AdRequest retry) {
            this.retry = retry;
        }

        @Override
        public void run() {
            if (retry.isCancelled()) {
                Clog.w(Clog.httpRespLogTag, Clog.getString(R.string.retry_already_cancelled));
                return;
            }

            // Spawn an AdRequest
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                retry.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
            } else {
                retry.execute();
            }
        }
    }

    //   // Uncomment for unit tests
    //   public void setContext(Context context) {
    //       this.context = context;
    //   }
}