package com.mopub.nativeads; import android.support.annotation.NonNull; import com.mopub.common.VisibleForTesting; import com.mopub.network.MoPubNetworkError; import com.mopub.volley.NetworkResponse; import com.mopub.volley.Response; import com.mopub.volley.VolleyError; import com.mopub.volley.toolbox.HttpHeaderParser; import com.mopub.volley.toolbox.JsonRequest; import org.apache.http.HttpStatus; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.UnsupportedEncodingException; import static com.mopub.nativeads.MoPubNativeAdPositioning.MoPubClientPositioning; public class PositioningRequest extends JsonRequest<MoPubClientPositioning> { private static final String FIXED_KEY = "fixed"; private static final String SECTION_KEY = "section"; private static final String POSITION_KEY = "position"; private static final String REPEATING_KEY = "repeating"; private static final String INTERVAL_KEY = "interval"; // Max value to avoid bad integer math calculations. This is 2 ^ 16. private static final int MAX_VALUE = 1 << 16; public PositioningRequest(final String url, final Response.Listener<MoPubClientPositioning> listener, final Response.ErrorListener errorListener) { super(Method.GET, url, null, listener, errorListener); } // This is done just for unit testing visibolity. @Override protected void deliverResponse(final MoPubClientPositioning response) { super.deliverResponse(response); } @Override protected Response<MoPubClientPositioning> parseNetworkResponse(final NetworkResponse response) { if (response.statusCode != HttpStatus.SC_OK) { return Response.error(new VolleyError(response)); } if (response.data.length == 0) { return Response.error(new VolleyError("Empty positioning response", new JSONException("Empty response"))); } try { String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); return Response.success(parseJson(jsonString), HttpHeaderParser.parseCacheHeaders(response)); } catch (UnsupportedEncodingException e) { return Response.error(new VolleyError("Couldn't parse JSON from Charset", e)); } catch (JSONException e) { return Response.error(new VolleyError("JSON Parsing Error", e)); } catch (MoPubNetworkError e) { return Response.error(e); } } @NonNull @VisibleForTesting MoPubClientPositioning parseJson(@NonNull String jsonString) throws JSONException, MoPubNetworkError { JSONObject jsonObject = new JSONObject(jsonString); // If the server returns an error explicitly, throw an exception with the message. String error = jsonObject.optString("error", null); if (error != null) { if (error.equalsIgnoreCase("WARMING_UP")) { throw new MoPubNetworkError(MoPubNetworkError.Reason.WARMING_UP); } throw new JSONException(error); } // Parse fixed and repeating rules. JSONArray fixed = jsonObject.optJSONArray(FIXED_KEY); JSONObject repeating = jsonObject.optJSONObject(REPEATING_KEY); if (fixed == null && repeating == null) { throw new JSONException("Must contain fixed or repeating positions"); } MoPubClientPositioning positioning = new MoPubClientPositioning(); if (fixed != null) { parseFixedJson(fixed, positioning); } if (repeating != null) { parseRepeatingJson(repeating, positioning); } return positioning; } private void parseFixedJson(@NonNull final JSONArray fixed, @NonNull final MoPubClientPositioning positioning) throws JSONException { for (int i = 0; i < fixed.length(); ++i) { JSONObject positionObject = fixed.getJSONObject(i); int section = positionObject.optInt(SECTION_KEY, 0); if (section < 0) { throw new JSONException("Invalid section " + section + " in JSON response"); } if (section > 0) { // Ignore sections > 0. continue; } int position = positionObject.getInt(POSITION_KEY); if (position < 0 || position > MAX_VALUE) { throw new JSONException("Invalid position " + position + " in JSON response"); } positioning.addFixedPosition(position); } } private void parseRepeatingJson(@NonNull final JSONObject repeatingObject, @NonNull final MoPubClientPositioning positioning) throws JSONException { int interval = repeatingObject.getInt(INTERVAL_KEY); if (interval < 2 || interval > MAX_VALUE) { throw new JSONException("Invalid interval " + interval + " in JSON response"); } positioning.enableRepeatingPositions(interval); } }