7

Recently I analyzed crash reports form my app and found several stack traces which points to okhttp

My app doesn't depend on okhttp explicitly.

AFAIK okhttp version depends on Android OS version, and okhttp library by itself placed on device

To help with troubleshooting I decided to log okhttp library version, and looks like I found several useful classes for this

  1. com.squareup.okhttp.internal.Version
  2. okhttp3.internal.Version

Just to make sure that I didn't mistake I took com.android.okhttp.internal.http.HttpURLConnectionImpl class form stack-trace and tried to Class.forName it - success

Also I noticed that com.squareup.okhttp transformed to com.android.okhttp looks like at build-time, so totally I tried such variants

  1. Class.forName("com.android.okhttp.internal.Version") -> java.lang.ClassNotFoundException
  2. Class.forName("com.squareup.okhttp.internal.Version") -> java.lang.ClassNotFoundException
  3. Class.forName("okhttp3.internal.Version") -> java.lang.ClassNotFoundException
  4. Class.forName("com.android.okhttp.internal.http.HttpURLConnectionImpl") -> success

Can anyone explain why? What I missed?

Update

I have pulled okhttp.jar from my device adb pull /system/framework/okhttp.jar but it contains MANIFEST.MF only

CAMOBAP
  • 5,523
  • 8
  • 58
  • 93
  • In crash reports you know the version of your app, and assuming you have at least some simple version control in place, you should know what version of okhttp that version of your app shipped with. – laalto Mar 24 '16 at 09:15
  • My app doesn't depend on `okhttp` explicitly – CAMOBAP Mar 24 '16 at 10:03

1 Answers1

2

from 4.xx google is using okhttp part of squareup

/**
* This implementation uses HttpEngine to send requests and receive responses. This class may use
* multiple HttpEngines to follow redirects, authentication retries, etc. to retrieve the final
* response body.
*
* <h3>What does 'connected' mean?</h3> This class inherits a {@code connected} field from the
* superclass. That field is <strong>not</strong> used to indicate whether this URLConnection is
* currently connected. Instead, it indicates whether a connection has ever been attempted. Once a
* connection has been attempted, certain properties (request header fields, request method, etc.)
* are immutable.
*/
public class HttpURLConnectionImpl extends HttpURLConnection {


  private String defaultUserAgent() {
    String agent = System.getProperty("http.agent");
    return agent != null ? Util.toHumanReadableAscii(agent) : Version.userAgent();
  }

https://github.com/square/okhttp/blob/master/okhttp-urlconnection/src/main/java/okhttp3/internal/huc/HttpURLConnectionImpl.java

http://square.github.io/okhttp/

everything depends on device - what os version u using because api is evolving, u can use reflections but u need know what field is on specific api

see https://github.com/square/okhttp/blob/master/CHANGELOG.md

to compare diffrent api versions use: https://android.googlesource.com/platform/external/okhttp/

u can try at the beginning

System.getProperty("http.agent");

edit:

via reflections

 HttpURLConnection  connection = (HttpURLConnection) new URL("http://google.com")
         .openConnection();
 Method method = connection.getClass().getDeclaredMethod("defaultUserAgent");
 method.setAccessible(true);
 String okEngineVersion = (String) method.invoke(connection, new Object[]{});

same as

String okEngineVersion = System.getProperty("http.agent");

and if u want to bother:

  • every class is treated the same way - > as equals ( no versioning - u can only check magic minor major number - java compiler version from class)
  • manifest of /system/framework/okhttp.jar doesn't contain version properties

if u want okhttp.internal.Version class then:

 File file = new File("/system/framework/okhttp.jar");

 // using javaxt-core lib        
 Jar jar = new Jar(file);
 jar.getVersion();

// load dex 
DexFile dexfile = DexFile.loadDex(file.getAbsolutePath(),
                   File.createTempFile("opt", "dex", _context.getCacheDir()).getPath(), 0);

Enumeration<String> dexEntries = dexfile.entries();
ClassLoader systemClassLoader = DexClassLoader.getSystemClassLoader();

while (dexEntries.hasMoreElements()) {
  String className = dexEntries.nextElement();
  Class<?> aClass = systemClassLoader.loadClass(className);
}

conclusion: If you want to avoid crash of app from library changes delivery own version of library and load classes on the fly or compile with apk

ceph3us
  • 7,326
  • 3
  • 36
  • 43
  • What about solution for `okhttp 2.x` ? And why I cannot access `okhttp3.internal.Version` directly? – CAMOBAP Mar 24 '16 at 11:05
  • thank you for update, but I need version, not `User-Agent`. According code I know that `Version.userAgent` contains actual library version, `HttpURLConnectionImpl.defaultUserAgent` doesn't – CAMOBAP Mar 24 '16 at 11:35
  • @CAMOBAP - so get via reflection Version field – ceph3us Mar 25 '16 at 23:02
  • @CAMOBAP - yes if u dont know how to use reflections see edit and then learn more about reflections – ceph3us Mar 26 '16 at 05:33
  • thanks for reply, didn't help because jar didn't contains `dex` file `odex` placed separately – CAMOBAP Apr 28 '16 at 10:19