6

I have successfully extracted the class files from an android emulator and replaced them into my android.jar file residing inside my \platforms\android-21\android.jar and I still cannot access the hidden methods. I have previously used android 19, but to keep my builds up to date. I know I can use reflection, but its so much easier having it natively supported inside Android Studio.

Using JD-GUI I can confirm that the classes and methods are inside the android.jar file yet Android Studio "cannot resolve symbol" on every one of them. I am confused majorly here. Thanks everyone.

EDIT: I do have repo setup so if compiling the android.jar file from source is possible, I would not mind doing that.

Seth
  • 1,769
  • 4
  • 26
  • 39
  • 1
    I followed the procedure mentioned here: https://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-1-introduction/ but the jar file I got is not recognisable in eclipse. ADT throwed error that android.jar is not found..Can you please share what steps you followed – user531069 Aug 26 '15 at 19:40
  • @user531069 the link you provided is private – Denis Dmitrienko Feb 07 '20 at 06:49

5 Answers5

4

Here's an alternative that does not require building the entire android SDK JAR or modifying your android SDK at all. If you have a JAR with some of the @hide android SDK classes and/or methods in it you can add this hunk to your standard android project build.gradle file:

allprojects {
  gradle.projectsEvaluated {
    tasks.withType(JavaCompile) {
      options.compilerArgs << "-Xbootclasspath/p:<FULL_PATH_TO_CUSTOM_ANDROID_API>.jar"
    }
  }
}

This will prepend your JAR to the bootclasspath which has the android.jar from the selected SDK in it. The java compiler will prefer to use classes in the prepended bootclasspath when compiling thus overriding the android.jar from the SDK.

See http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javac.html

The drawback is that IntelliJ/Android Studio doesn't seem to understand this at the higher level so methods appear red in the editor but the build works fine. You can actually combine this with the provided solution to at least get additional classes to show up as valid in the editor but exposed @hide methods are still unknown to the IDE.

dependencies {
  compile fileTree(dir: 'libs', include: ['*.jar'], 'exclude': ['<PATH_TO_CUSTOM_ANDROID_API>.jar'])
  provided files('libs/<PATH_TO_CUSTOM_ANDROID_API>.jar')
}

I haven't played with it long enough to find how to reference the relative path in the compilerArgs but it should be easy enough.

satur9nine
  • 13,927
  • 5
  • 80
  • 123
2

File > Invalidate Caches and Restart > Invalidate and Restart

I knew something was weird. I knew the methods were there in the jar, I knew it was something with Studio. Here it caches the methods :)

Doing what I posted in the first line fixes it! So happy! Hope others benefit!

Seth
  • 1,769
  • 4
  • 26
  • 39
  • You have to decompile the image and extract the unincluded classes then repack them. Its not as simple as just doing above. – Seth Dec 03 '17 at 18:25
1
  1. Create folder under ../app, named as 'systemlibs'
  2. Copy your library under 'systemlibs'
  3. add provided files('systemlibs/android.jar') in app/build.gradle dependencies
  4. sync gradle and build


    dependencies {
        provided files('systemlibs/android.jar')
        compile fileTree(dir: 'libs', include: ['*.jar'])
        testCompile 'junit:junit:4.12'
        compile 'com.android.support:appcompat-v7:23.4.0'
        compile 'com.android.support:design:23.4.0'
    }

emotionfxxk
  • 339
  • 4
  • 5
1

For a while I used the Xbootclasspath trick but Android Studio doesn't really like it (highlighting was broken) and it doesn't work at all if you are trying to access hidden APIs that have changed across SDK levels (these APIs are hidden so they are unstable).

Eventually I found a solution that involves creating pure java libraries that are used in my Android project. I call this trick "platform-unhidden" aka P-U. The main annoyance is there is more boilerplate code and the library structure is complex.

If you need to support two levels (lets say API level 25 and lower, and API level 26 and higher) then you will need 5 java libraries. The "stubs" libraries contain Android platform code copied from AOSP but with everything stubbed out like the android.jar in the SDK usually is but with the hidden methods you want exposed. The "wrap" libraries use the stub libraries and expose methods/classes with unique names. The P-U library selects the appropriate wrapping library based on the detected SDK level with Build.SDK_INT.

Visually this looks like:

                     +---------+                 +----------+
            +--api-->+ wrap-25 +--compile-only-->+ stubs-25 +--+
+-------+   |        +---------+                 +----------+  |   +---------+
|       +---+                                                  +-->+         |
|  P-U  |                                                          | android |
|       +---+                                                  +-->+         |
+-------+   |        +---------+                 +----------+  |   +---------+
            +--api-->+ wrap-26 +--compile-only-->+ stubs-26 +--+
                     +---------+                 +----------+

Setting up the java libraries required creating a shared gradle file like so:

platform-shared.gradle:

// All the platform-* projects apply this gradle file
apply plugin: 'java'
apply plugin: 'java-library'

sourceCompatibility = 1.8

apply from: file("${androidBuild}/versions.gradle")

// ANDROID_HOME can override ANDROID_SDK_ROOT
// see https://developer.android.com/studio/command-line/variables

def androidHome = System.getenv('ANDROID_HOME')
def androidSdkRoot = System.getenv('ANDROID_SDK_ROOT')

if (androidHome?.trim() && new File(androidHome).canRead()) {
  androidSdkRoot = androidHome
}

if (!androidSdkRoot?.trim() || !new File(androidSdkRoot).canRead()) {
  def props = new Properties()
  def file = new File("${rootDir}/local.properties")
  if (file.exists()) {
    file.withInputStream { props.load(it) }
    if (props.getProperty("sdk.dir")) {
      androidSdkRoot = props.getProperty("sdk.dir")
    }
  }
  else {
    throw new GradleException('Android SDK root not usable')
  }
}

repositories {
  // Need an Android SDK jar to compile against even though this is a pure java project
  repositories {
    flatDir {
      dirs "${androidSdkRoot}/platforms/android-${COMPILE_SDK_VERSION}"
    }
  }

  mavenCentral()
}

// Make CI happy by implementing these tasks

task assembleRelease {
  dependsOn 'jar'
}

task assembleDebug {
  dependsOn 'jar'
}

task assembleAndroidTest {
  // Ignored
}

task lintRelease {
  // Ignored
}

task lintDebug {
  // Ignored
}

task testReleaseUnitTest {
  // Ignored
}

task testDebugUnitTest {
  // Ignored
}

Then you'll need gradle files for each of the libraries like so (not showing 26, it looks the same as 25). Add more levels as needed.

stubs-25.gradle:

apply from: file("../platform-shared.gradle")

dependencies {
    api name: "android"
}

wrap-25.gradle:

apply from: file("../platform-shared.gradle")

dependencies {
    compileOnly project(':platform-stubs-25')
}

platform-unhidden.gradle:

apply from: file("../platform-shared.gradle")

dependencies {
    compileOnly project(':platform-stubs-25')
    compileOnly name: "android"
    api project(':platform-wrap-25')
    api project(':platform-wrap-26')
}
satur9nine
  • 13,927
  • 5
  • 80
  • 123
0

You don`t have to replace \platforms\android-21\android.jar.
It destroy the SDK and you may not submit those changes onto git.

Below way may be more easier:

  1. See How do I build the Android SDK with hidden and internal APIs available? and get framework_all.jar

  2. Put framework_all.jar in system_libraries besides app in your projects.

  3. change app/build.gradle like

    dependencies { compile fileTree(dir: 'libs', include: ['.jar']) provided fileTree(dir: '../system_libraries', include: ['.jar']) }

  4. AS will gives a tips like "Gradle files have changed since .... Sync Now", click Sync Now.

  5. Goto Project view and you will see platform_all.jar in External Libraries.

  6. Each time you changed any .jar under system_libraries, you need to do some change in app/build.gradle and re-sync it.

Community
  • 1
  • 1
Hui.Li
  • 399
  • 4
  • 18
  • I'm not sure what you are talking about. Replacing the android.jar for whichever android version you are exposing does NOT destroy the SDK. It works perfectly fine. However, others editing your code with a modded android.jar will also need the exposed android.jar file which if you post to git you should include in a build folder. – Seth Sep 06 '15 at 23:14
  • The method you posted takes forever as you need to download the entire sdk which even on a great connection (1 mBps) takes days. Modding the jar is much quicker and easier. You can even create a new platform labeld "android-21_exposed" to allow compiling back and forth between regular and exposed versions. – Seth Sep 06 '15 at 23:15
  • If you changed some files in SDK ( android.jar here), how could others make it work when he download your code? copy it as you did? Then what should he do if he need to work on some other apps? roll back the copy? All changes for one projects should better only effects on itself, but not all on your work env. – Hui.Li Sep 14 '15 at 08:57
  • I'm an indie developer. Nobody else has my code but me. Nobody else works on my code but me. So your comment is irrelevant to the question asked. However, if one needs to work on code with hidden api's, they could just include the modified .jar file in the code when it gets downloaded and probably use gradle or other tools to reference it. – Seth Oct 10 '15 at 15:40
  • It should be provided fileTree(dir: '../system_libraries', include: [' * .jar'] (without the spaces) the "*" is missing. – Vasile Jureschi Oct 15 '15 at 20:53
  • 1
    I tried this method, it seems only hidden classes can be seen now, e.g. SystemProperties. However, when I tried to access the hidden field or methods of a class, this method still does not work. Any idea? – Robin Jan 05 '16 at 01:36