Android: Add helper function for converting Integer -> rtc::Optional<int>

Bug: webrtc:8278
Change-Id: I1c499a35c5fd77304ed2906ea61ef2322ec98cea
No-Tree-Checks: true
Reviewed-on: https://webrtc-review.googlesource.com/20876
Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20750}
diff --git a/sdk/android/src/java/org/webrtc/IntegerWrapper.java b/sdk/android/src/java/org/webrtc/IntegerWrapper.java
new file mode 100644
index 0000000..5df9221
--- /dev/null
+++ b/sdk/android/src/java/org/webrtc/IntegerWrapper.java
@@ -0,0 +1,24 @@
+/*
+ *  Copyright 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.webrtc;
+
+/** This class contains the Java glue code for JNI generation of Integer handling. */
+class IntegerWrapper {
+  @CalledByNative
+  static Integer create(int i) {
+    return Integer.valueOf(i);
+  }
+
+  @CalledByNative
+  static int getIntValue(Integer i) {
+    return i;
+  }
+}
diff --git a/sdk/android/src/java/org/webrtc/VideoEncoderWrapper.java b/sdk/android/src/java/org/webrtc/VideoEncoderWrapper.java
index c7fd6cf..b2bc081 100644
--- a/sdk/android/src/java/org/webrtc/VideoEncoderWrapper.java
+++ b/sdk/android/src/java/org/webrtc/VideoEncoderWrapper.java
@@ -62,11 +62,6 @@
   }
 
   @CalledByNative
-  static int getIntValue(Integer i) {
-    return i.intValue();
-  }
-
-  @CalledByNative
   static VideoEncoder.Callback createEncoderCallback(final long nativeEncoder) {
     return (EncodedImage frame, VideoEncoder.CodecSpecificInfo info)
                -> nativeOnEncodedFrame(nativeEncoder, frame.buffer, frame.encodedWidth,
diff --git a/sdk/android/src/jni/classreferenceholder.cc b/sdk/android/src/jni/classreferenceholder.cc
index 29295b6..25cb38c 100644
--- a/sdk/android/src/jni/classreferenceholder.cc
+++ b/sdk/android/src/jni/classreferenceholder.cc
@@ -53,7 +53,6 @@
   LoadClass(jni, "android/graphics/SurfaceTexture");
   LoadClass(jni, "java/lang/Boolean");
   LoadClass(jni, "java/lang/Double");
-  LoadClass(jni, "java/lang/Integer");
   LoadClass(jni, "java/lang/Long");
   LoadClass(jni, "java/lang/String");
   LoadClass(jni, "java/math/BigInteger");
diff --git a/sdk/android/src/jni/jni_helpers.cc b/sdk/android/src/jni/jni_helpers.cc
index 4c3724f..6331a64 100644
--- a/sdk/android/src/jni/jni_helpers.cc
+++ b/sdk/android/src/jni/jni_helpers.cc
@@ -15,6 +15,8 @@
 #include <unistd.h>
 #include <vector>
 
+#include "sdk/android/generated_base_jni/jni/IntegerWrapper_jni.h"
+#include "sdk/android/src/jni/class_loader.h"
 #include "sdk/android/src/jni/classreferenceholder.h"
 
 namespace webrtc {
@@ -273,6 +275,21 @@
   return converted_list;
 }
 
+rtc::Optional<int32_t> JavaIntegerToOptionalInt(JNIEnv* jni, jobject integer) {
+  if (IsNull(jni, integer))
+    return rtc::nullopt;
+  return Java_IntegerWrapper_getIntValue(jni, integer);
+}
+
+jobject JavaIntegerFromOptionalInt(JNIEnv* jni,
+                                   const rtc::Optional<int32_t>& optional_int) {
+  return optional_int ? JavaIntegerFromInt(jni, *optional_int) : nullptr;
+}
+
+jobject JavaIntegerFromInt(JNIEnv* jni, int32_t i) {
+  return Java_IntegerWrapper_create(jni, i);
+}
+
 // Return the (singleton) Java Enum object corresponding to |index|;
 jobject JavaEnumFromIndex(JNIEnv* jni, jclass state_class,
                           const std::string& state_class_name, int index) {
diff --git a/sdk/android/src/jni/jni_helpers.h b/sdk/android/src/jni/jni_helpers.h
index 920bf46..e08e4b2 100644
--- a/sdk/android/src/jni/jni_helpers.h
+++ b/sdk/android/src/jni/jni_helpers.h
@@ -19,6 +19,7 @@
 #include <string>
 #include <vector>
 
+#include "api/optional.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/constructormagic.h"
 #include "rtc_base/refcount.h"
@@ -107,6 +108,13 @@
 // return a new vector of UTF-8 native strings.
 std::vector<std::string> JavaToStdVectorStrings(JNIEnv* jni, jobject list);
 
+rtc::Optional<int32_t> JavaIntegerToOptionalInt(JNIEnv* jni, jobject integer);
+
+jobject JavaIntegerFromOptionalInt(JNIEnv* jni,
+                                   const rtc::Optional<int32_t>& optional_int);
+
+jobject JavaIntegerFromInt(JNIEnv* jni, int32_t i);
+
 // Return the (singleton) Java Enum object corresponding to |index|;
 jobject JavaEnumFromIndex(JNIEnv* jni, jclass state_class,
                           const std::string& state_class_name, int index);
diff --git a/sdk/android/src/jni/pc/java_native_conversion.cc b/sdk/android/src/jni/pc/java_native_conversion.cc
index 280d36e..1eadfa3 100644
--- a/sdk/android/src/jni/pc/java_native_conversion.cc
+++ b/sdk/android/src/jni/pc/java_native_conversion.cc
@@ -459,8 +459,6 @@
 
   jfieldID j_ice_check_min_interval_id = GetFieldID(
       jni, j_rtc_config_class, "iceCheckMinInterval", "Ljava/lang/Integer;");
-  jclass j_integer_class = jni->FindClass("java/lang/Integer");
-  jmethodID int_value_id = GetMethodID(jni, j_integer_class, "intValue", "()I");
 
   jfieldID j_disable_ipv6_on_wifi_id =
       GetFieldID(jni, j_rtc_config_class, "disableIPv6OnWifi", "Z");
@@ -514,12 +512,8 @@
       jni, j_rtc_config, j_presume_writable_when_fully_relayed_id);
   jobject j_ice_check_min_interval =
       GetNullableObjectField(jni, j_rtc_config, j_ice_check_min_interval_id);
-  if (!IsNull(jni, j_ice_check_min_interval)) {
-    int ice_check_min_interval_value =
-        jni->CallIntMethod(j_ice_check_min_interval, int_value_id);
-    rtc_config->ice_check_min_interval =
-        rtc::Optional<int>(ice_check_min_interval_value);
-  }
+  rtc_config->ice_check_min_interval =
+      JavaIntegerToOptionalInt(jni, j_ice_check_min_interval);
   rtc_config->disable_ipv6_on_wifi =
       GetBooleanField(jni, j_rtc_config, j_disable_ipv6_on_wifi_id);
   rtc_config->max_ipv6_networks =
@@ -558,9 +552,7 @@
                                    "maxBitrateBps", "Ljava/lang/Integer;");
   jfieldID ssrc_id =
       GetFieldID(jni, j_encoding_parameters_class, "ssrc", "Ljava/lang/Long;");
-  jclass j_integer_class = jni->FindClass("java/lang/Integer");
   jclass j_long_class = jni->FindClass("java/lang/Long");
-  jmethodID int_value_id = GetMethodID(jni, j_integer_class, "intValue", "()I");
   jmethodID long_value_id = GetMethodID(jni, j_long_class, "longValue", "()J");
 
   for (jobject j_encoding_parameters : Iterable(jni, j_encodings)) {
@@ -568,11 +560,7 @@
     encoding.active = GetBooleanField(jni, j_encoding_parameters, active_id);
     jobject j_bitrate =
         GetNullableObjectField(jni, j_encoding_parameters, bitrate_id);
-    if (!IsNull(jni, j_bitrate)) {
-      int bitrate_value = jni->CallIntMethod(j_bitrate, int_value_id);
-      CHECK_EXCEPTION(jni) << "error during CallIntMethod";
-      encoding.max_bitrate_bps = rtc::Optional<int>(bitrate_value);
-    }
+    encoding.max_bitrate_bps = JavaIntegerToOptionalInt(jni, j_bitrate);
     jobject j_ssrc =
         GetNullableObjectField(jni, j_encoding_parameters, ssrc_id);
     if (!IsNull(jni, j_ssrc)) {
@@ -602,18 +590,10 @@
     codec.kind =
         JavaToNativeMediaType(jni, GetObjectField(jni, j_codec, kind_id));
     jobject j_clock_rate = GetNullableObjectField(jni, j_codec, clock_rate_id);
-    if (!IsNull(jni, j_clock_rate)) {
-      int clock_rate_value = jni->CallIntMethod(j_clock_rate, int_value_id);
-      CHECK_EXCEPTION(jni) << "error during CallIntMethod";
-      codec.clock_rate = rtc::Optional<int>(clock_rate_value);
-    }
+    codec.clock_rate = JavaIntegerToOptionalInt(jni, j_clock_rate);
     jobject j_num_channels =
         GetNullableObjectField(jni, j_codec, num_channels_id);
-    if (!IsNull(jni, j_num_channels)) {
-      int num_channels_value = jni->CallIntMethod(j_num_channels, int_value_id);
-      CHECK_EXCEPTION(jni) << "error during CallIntMethod";
-      codec.num_channels = rtc::Optional<int>(num_channels_value);
-    }
+    codec.num_channels = JavaIntegerToOptionalInt(jni, j_num_channels);
     parameters->codecs.push_back(codec);
   }
 }
@@ -640,9 +620,7 @@
   jfieldID ssrc_id =
       GetFieldID(jni, encoding_class, "ssrc", "Ljava/lang/Long;");
 
-  jclass integer_class = jni->FindClass("java/lang/Integer");
   jclass long_class = jni->FindClass("java/lang/Long");
-  jmethodID integer_ctor = GetMethodID(jni, integer_class, "<init>", "(I)V");
   jmethodID long_ctor = GetMethodID(jni, long_class, "<init>", "(J)V");
 
   for (const RtpEncodingParameters& encoding : parameters.encodings) {
@@ -651,13 +629,9 @@
     CHECK_EXCEPTION(jni) << "error during NewObject";
     jni->SetBooleanField(j_encoding_parameters, active_id, encoding.active);
     CHECK_EXCEPTION(jni) << "error during SetBooleanField";
-    if (encoding.max_bitrate_bps) {
-      jobject j_bitrate_value = jni->NewObject(integer_class, integer_ctor,
-                                               *(encoding.max_bitrate_bps));
-      CHECK_EXCEPTION(jni) << "error during NewObject";
-      jni->SetObjectField(j_encoding_parameters, bitrate_id, j_bitrate_value);
-      CHECK_EXCEPTION(jni) << "error during SetObjectField";
-    }
+    jni->SetObjectField(
+        j_encoding_parameters, bitrate_id,
+        JavaIntegerFromOptionalInt(jni, encoding.max_bitrate_bps));
     if (encoding.ssrc) {
       jobject j_ssrc_value = jni->NewObject(long_class, long_ctor,
                                             static_cast<jlong>(*encoding.ssrc));
@@ -699,20 +673,10 @@
     jni->SetObjectField(j_codec, kind_id,
                         NativeToJavaMediaType(jni, codec.kind));
     CHECK_EXCEPTION(jni) << "error during SetObjectField";
-    if (codec.clock_rate) {
-      jobject j_clock_rate_value =
-          jni->NewObject(integer_class, integer_ctor, *(codec.clock_rate));
-      CHECK_EXCEPTION(jni) << "error during NewObject";
-      jni->SetObjectField(j_codec, clock_rate_id, j_clock_rate_value);
-      CHECK_EXCEPTION(jni) << "error during SetObjectField";
-    }
-    if (codec.num_channels) {
-      jobject j_num_channels_value =
-          jni->NewObject(integer_class, integer_ctor, *(codec.num_channels));
-      CHECK_EXCEPTION(jni) << "error during NewObject";
-      jni->SetObjectField(j_codec, num_channels_id, j_num_channels_value);
-      CHECK_EXCEPTION(jni) << "error during SetObjectField";
-    }
+    jni->SetObjectField(j_codec, clock_rate_id,
+                        JavaIntegerFromOptionalInt(jni, codec.clock_rate));
+    jni->SetObjectField(j_codec, num_channels_id,
+                        JavaIntegerFromOptionalInt(jni, codec.num_channels));
     jboolean added = jni->CallBooleanMethod(j_codecs, codecs_add, j_codec);
     CHECK_EXCEPTION(jni) << "error during CallBooleanMethod";
     RTC_CHECK(added);
diff --git a/sdk/android/src/jni/pc/peerconnection_jni.cc b/sdk/android/src/jni/pc/peerconnection_jni.cc
index 9c09634..aea277d 100644
--- a/sdk/android/src/jni/pc/peerconnection_jni.cc
+++ b/sdk/android/src/jni/pc/peerconnection_jni.cc
@@ -365,20 +365,9 @@
                          jobject j_current,
                          jobject j_max) {
   PeerConnectionInterface::BitrateParameters params;
-  jclass j_integer_class = jni->FindClass("java/lang/Integer");
-  jmethodID int_value_id = GetMethodID(jni, j_integer_class, "intValue", "()I");
-  if (!IsNull(jni, j_min)) {
-    int min_value = jni->CallIntMethod(j_min, int_value_id);
-    params.min_bitrate_bps = rtc::Optional<int>(min_value);
-  }
-  if (!IsNull(jni, j_current)) {
-    int current_value = jni->CallIntMethod(j_current, int_value_id);
-    params.current_bitrate_bps = rtc::Optional<int>(current_value);
-  }
-  if (!IsNull(jni, j_max)) {
-    int max_value = jni->CallIntMethod(j_max, int_value_id);
-    params.max_bitrate_bps = rtc::Optional<int>(max_value);
-  }
+  params.min_bitrate_bps = JavaIntegerToOptionalInt(jni, j_min);
+  params.current_bitrate_bps = JavaIntegerToOptionalInt(jni, j_current);
+  params.max_bitrate_bps = JavaIntegerToOptionalInt(jni, j_max);
   return ExtractNativePC(jni, j_pc)->SetBitrate(params).ok();
 }
 
diff --git a/sdk/android/src/jni/pc/rtcstatscollectorcallbackwrapper.cc b/sdk/android/src/jni/pc/rtcstatscollectorcallbackwrapper.cc
index f383b4d..e7e6495 100644
--- a/sdk/android/src/jni/pc/rtcstatscollectorcallbackwrapper.cc
+++ b/sdk/android/src/jni/pc/rtcstatscollectorcallbackwrapper.cc
@@ -44,8 +44,7 @@
           "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;")),
       j_boolean_class_(FindClass(jni, "java/lang/Boolean")),
       j_boolean_ctor_(GetMethodID(jni, j_boolean_class_, "<init>", "(Z)V")),
-      j_integer_class_(FindClass(jni, "java/lang/Integer")),
-      j_integer_ctor_(GetMethodID(jni, j_integer_class_, "<init>", "(I)V")),
+      j_integer_class_(jni->FindClass("java/lang/Integer")),
       j_long_class_(FindClass(jni, "java/lang/Long")),
       j_long_ctor_(GetMethodID(jni, j_long_class_, "<init>", "(J)V")),
       j_big_integer_class_(FindClass(jni, "java/math/BigInteger")),
@@ -124,11 +123,8 @@
       return value;
     }
     case RTCStatsMemberInterface::kInt32: {
-      jobject value =
-          jni->NewObject(j_integer_class_, j_integer_ctor_,
-                         *member->cast_to<RTCStatsMember<int32_t>>());
-      CHECK_EXCEPTION(jni) << "error during NewObject";
-      return value;
+      return JavaIntegerFromInt(jni,
+                                *member->cast_to<RTCStatsMember<int32_t>>());
     }
     case RTCStatsMemberInterface::kUint32: {
       jobject value =
@@ -183,9 +179,8 @@
           jni->NewObjectArray(values.size(), j_integer_class_, nullptr);
       CHECK_EXCEPTION(jni) << "error during NewObjectArray";
       for (size_t i = 0; i < values.size(); ++i) {
-        jobject value =
-            jni->NewObject(j_integer_class_, j_integer_ctor_, values[i]);
-        jni->SetObjectArrayElement(j_values, i, value);
+        jni->SetObjectArrayElement(j_values, i,
+                                   JavaIntegerFromInt(jni, values[i]));
         CHECK_EXCEPTION(jni) << "error during SetObjectArrayElement";
       }
       return j_values;
diff --git a/sdk/android/src/jni/pc/rtcstatscollectorcallbackwrapper.h b/sdk/android/src/jni/pc/rtcstatscollectorcallbackwrapper.h
index 3a86f98..e3b56df 100644
--- a/sdk/android/src/jni/pc/rtcstatscollectorcallbackwrapper.h
+++ b/sdk/android/src/jni/pc/rtcstatscollectorcallbackwrapper.h
@@ -48,7 +48,6 @@
   const jclass j_boolean_class_;
   const jmethodID j_boolean_ctor_;
   const jclass j_integer_class_;
-  const jmethodID j_integer_ctor_;
   const jclass j_long_class_;
   const jmethodID j_long_ctor_;
   const jclass j_big_integer_class_;
diff --git a/sdk/android/src/jni/videodecoderwrapper.cc b/sdk/android/src/jni/videodecoderwrapper.cc
index 164cb66..1bc75b0 100644
--- a/sdk/android/src/jni/videodecoderwrapper.cc
+++ b/sdk/android/src/jni/videodecoderwrapper.cc
@@ -15,12 +15,21 @@
 #include "modules/video_coding/utility/vp8_header_parser.h"
 #include "modules/video_coding/utility/vp9_uncompressed_header_parser.h"
 #include "rtc_base/logging.h"
+#include "rtc_base/safe_conversions.h"
 #include "rtc_base/timeutils.h"
 #include "sdk/android/src/jni/classreferenceholder.h"
 
 namespace webrtc {
 namespace jni {
 
+namespace {
+template <typename Dst, typename Src>
+inline rtc::Optional<Dst> cast_optional(const rtc::Optional<Src>& value) {
+  return value ? rtc::Optional<Dst>(rtc::dchecked_cast<Dst, Src>(*value))
+               : rtc::nullopt;
+}
+}  // namespace
+
 VideoDecoderWrapper::VideoDecoderWrapper(JNIEnv* jni, jobject decoder)
     : decoder_(jni, decoder),
       encoded_image_class_(jni, FindClass(jni, "org/webrtc/EncodedImage")),
@@ -69,9 +78,6 @@
   get_number_method_ =
       jni->GetMethodID(*video_codec_status_class_, "getNumber", "()I");
 
-  integer_constructor_ = jni->GetMethodID(*integer_class_, "<init>", "(I)V");
-  int_value_method_ = jni->GetMethodID(*integer_class_, "intValue", "()I");
-
   initialized_ = false;
   // QP parsing starts enabled and we disable it if the decoder provides frames.
   qp_parsing_enabled_ = true;
@@ -189,24 +195,16 @@
   VideoFrame frame =
       JavaToNativeFrame(jni, jframe, frame_extra_info.timestamp_rtp);
 
-  rtc::Optional<int32_t> decoding_time_ms;
-  if (jdecode_time_ms != nullptr) {
-    decoding_time_ms = jni->CallIntMethod(jdecode_time_ms, int_value_method_);
-  }
+  rtc::Optional<int32_t> decoding_time_ms =
+      JavaIntegerToOptionalInt(jni, jdecode_time_ms);
 
-  rtc::Optional<uint8_t> qp;
-  if (jqp != nullptr) {
-    qp = jni->CallIntMethod(jqp, int_value_method_);
-    // The decoder provides QP values itself, no need to parse the bitstream.
-    qp_parsing_enabled_ = false;
-  } else {
-    qp = frame_extra_info.qp;
-    // The decoder doesn't provide QP values, ensure bitstream parsing is
-    // enabled.
-    qp_parsing_enabled_ = true;
-  }
-
-  callback_->Decoded(frame, decoding_time_ms, qp);
+  rtc::Optional<uint8_t> decoder_qp =
+      cast_optional<uint8_t, int32_t>(JavaIntegerToOptionalInt(jni, jqp));
+  // If the decoder provides QP values itself, no need to parse the bitstream.
+  // Enable QP parsing if decoder does not provide QP values itself.
+  qp_parsing_enabled_ = !decoder_qp.has_value();
+  callback_->Decoded(frame, decoding_time_ms,
+                     decoder_qp ? decoder_qp : frame_extra_info.qp);
 }
 
 jobject VideoDecoderWrapper::ConvertEncodedImageToJavaEncodedImage(
@@ -232,7 +230,7 @@
       jni->GetStaticObjectField(*frame_type_class_, frame_type_field);
   jobject qp = nullptr;
   if (image.qp_ != -1) {
-    qp = jni->NewObject(*integer_class_, integer_constructor_, image.qp_);
+    qp = JavaIntegerFromInt(jni, image.qp_);
   }
   return jni->NewObject(
       *encoded_image_class_, encoded_image_constructor_, buffer,
diff --git a/sdk/android/src/jni/videodecoderwrapper.h b/sdk/android/src/jni/videodecoderwrapper.h
index f56b49e..0e4e96e 100644
--- a/sdk/android/src/jni/videodecoderwrapper.h
+++ b/sdk/android/src/jni/videodecoderwrapper.h
@@ -110,9 +110,6 @@
 
   jmethodID get_number_method_;
 
-  jmethodID integer_constructor_;
-  jmethodID int_value_method_;
-
   jobject ConvertEncodedImageToJavaEncodedImage(JNIEnv* jni,
                                                 const EncodedImage& image);
 };
diff --git a/sdk/android/src/jni/videoencoderwrapper.cc b/sdk/android/src/jni/videoencoderwrapper.cc
index 958d029..360a5c6 100644
--- a/sdk/android/src/jni/videoencoderwrapper.cc
+++ b/sdk/android/src/jni/videoencoderwrapper.cc
@@ -167,22 +167,18 @@
   ScopedLocalRefFrame local_ref_frame(jni);
   jobject j_scaling_settings =
       Java_VideoEncoder_getScalingSettings(jni, *encoder_);
-  bool on =
+  bool isOn =
       Java_VideoEncoderWrapper_getScalingSettingsOn(jni, j_scaling_settings);
-  jobject j_low =
-      Java_VideoEncoderWrapper_getScalingSettingsLow(jni, j_scaling_settings);
-  jobject j_high =
-      Java_VideoEncoderWrapper_getScalingSettingsHigh(jni, j_scaling_settings);
 
-  if (j_low != nullptr || j_high != nullptr) {
-    RTC_DCHECK(j_low != nullptr);
-    RTC_DCHECK(j_high != nullptr);
-    int low = Java_VideoEncoderWrapper_getIntValue(jni, j_low);
-    int high = Java_VideoEncoderWrapper_getIntValue(jni, j_high);
-    return ScalingSettings(on, low, high);
-  } else {
-    return ScalingSettings(on);
-  }
+  rtc::Optional<int> low = JavaIntegerToOptionalInt(
+      jni,
+      Java_VideoEncoderWrapper_getScalingSettingsLow(jni, j_scaling_settings));
+  rtc::Optional<int> high = JavaIntegerToOptionalInt(
+      jni,
+      Java_VideoEncoderWrapper_getScalingSettingsHigh(jni, j_scaling_settings));
+
+  return (low && high) ? ScalingSettings(isOn, *low, *high)
+                       : ScalingSettings(isOn);
 }
 
 const char* VideoEncoderWrapper::ImplementationName() const {
@@ -205,10 +201,7 @@
 
   std::vector<uint8_t> buffer_copy(buffer_size);
   memcpy(buffer_copy.data(), buffer, buffer_size);
-  int qp = -1;
-  if (j_qp != nullptr) {
-    qp = Java_VideoEncoderWrapper_getIntValue(jni, j_qp);
-  }
+  const int qp = JavaIntegerToOptionalInt(jni, j_qp).value_or(-1);
 
   encoder_queue_->PostTask(
       [