[go: nahoru, domu]

Merge "Fix flaky FragmentScenarioTest.fragmentWithOptionsMenu" into androidx-master-dev
diff --git a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
index d43bbf3..89ab155 100644
--- a/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/dependencies/Dependencies.kt
@@ -40,6 +40,7 @@
 const val KOTLINPOET = "com.squareup:kotlinpoet:1.0.0"
 const val KOTLIN_STDLIB = "org.jetbrains.kotlin:kotlin-stdlib:1.3.0"
 const val KOTLIN_METADATA = "me.eugeniomarletti.kotlin.metadata:kotlin-metadata:1.4.0"
+const val KOTLIN_METADATA_JVM = "org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.0.5"
 const val KOTLIN_COROUTINES = "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.0"
 const val LEAKCANARY_INSTRUMENTATION =
         "com.squareup.leakcanary:leakcanary-android-instrumentation:1.6.2"
diff --git a/fragment/src/androidTest/java/androidx/fragment/app/FragmentLifecycleTest.java b/fragment/src/androidTest/java/androidx/fragment/app/FragmentLifecycleTest.java
index fb260b2..a52ef71 100644
--- a/fragment/src/androidTest/java/androidx/fragment/app/FragmentLifecycleTest.java
+++ b/fragment/src/androidTest/java/androidx/fragment/app/FragmentLifecycleTest.java
@@ -17,6 +17,11 @@
 
 package androidx.fragment.app;
 
+import static androidx.fragment.app.FragmentTestUtil.HostCallbacks;
+import static androidx.fragment.app.FragmentTestUtil.restartFragmentController;
+import static androidx.fragment.app.FragmentTestUtil.shutdownFragmentController;
+import static androidx.fragment.app.FragmentTestUtil.startupFragmentController;
+
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
@@ -44,20 +49,17 @@
 import android.view.Menu;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.Window;
 import android.widget.TextView;
 
 import androidx.annotation.ContentView;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.core.app.ActivityCompat;
 import androidx.core.view.ViewCompat;
 import androidx.fragment.app.test.EmptyFragmentTestActivity;
 import androidx.fragment.app.test.FragmentTestActivity;
 import androidx.fragment.test.R;
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.ViewModelStore;
-import androidx.lifecycle.ViewModelStoreOwner;
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
@@ -70,8 +72,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
 import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
@@ -343,7 +343,8 @@
     public void testSavedInstanceStateAfterRestore() {
 
         final ViewModelStore viewModelStore = new ViewModelStore();
-        final FragmentController fc1 = startupFragmentController(null, viewModelStore);
+        final FragmentController fc1 =
+                startupFragmentController(mActivityRule.getActivity(), null, viewModelStore);
         final FragmentManager fm1 = fc1.getSupportFragmentManager();
 
         // Add the initial state
@@ -369,7 +370,8 @@
         fc1.dispatchDestroy();
 
         // Create the new controller and restore state
-        final FragmentController fc2 = startupFragmentController(savedState, viewModelStore);
+        final FragmentController fc2 =
+                startupFragmentController(mActivityRule.getActivity(), savedState, viewModelStore);
         final FragmentManager fm2 = fc2.getSupportFragmentManager();
 
         final StrictFragment restoredParentFragment = (StrictFragment) fm2
@@ -694,7 +696,8 @@
     @UiThreadTest
     public void saveAnimationState() throws Throwable {
         ViewModelStore viewModelStore = new ViewModelStore();
-        FragmentController fc = startupFragmentController(null, viewModelStore);
+        FragmentController fc = startupFragmentController(mActivityRule.getActivity(), null,
+                viewModelStore);
         FragmentManager fm = fc.getSupportFragmentManager();
 
         fm.beginTransaction()
@@ -707,7 +710,7 @@
         assertAnimationsMatch(fm, 0, R.anim.fade_out, R.anim.fade_in, R.anim.fade_out);
 
         // Causes save and restore of fragments and back stack
-        fc = restartFragmentController(fc, viewModelStore);
+        fc = restartFragmentController(mActivityRule.getActivity(), fc, viewModelStore);
         fm = fc.getSupportFragmentManager();
 
         assertAnimationsMatch(fm, 0, R.anim.fade_out, R.anim.fade_in, R.anim.fade_out);
@@ -722,7 +725,7 @@
         assertAnimationsMatch(fm, R.anim.fade_in, R.anim.fade_out, 0, 0);
 
         // Causes save and restore of fragments and back stack
-        fc = restartFragmentController(fc, viewModelStore);
+        fc = restartFragmentController(mActivityRule.getActivity(), fc, viewModelStore);
         fm = fc.getSupportFragmentManager();
 
         assertAnimationsMatch(fm, R.anim.fade_in, R.anim.fade_out, 0, 0);
@@ -841,7 +844,8 @@
     @UiThreadTest
     public void fragmentDestroyedOnFinish() throws Throwable {
         ViewModelStore viewModelStore = new ViewModelStore();
-        FragmentController fc = startupFragmentController(null, viewModelStore);
+        FragmentController fc = startupFragmentController(mActivityRule.getActivity(), null,
+                viewModelStore);
         FragmentManager fm = fc.getSupportFragmentManager();
 
         StrictViewFragment fragmentA = StrictViewFragment.create(R.layout.fragment_a);
@@ -882,7 +886,7 @@
             public void run() {
                 final ViewModelStore viewModelStore = new ViewModelStore();
                 final FragmentController fc1 = startupFragmentController(
-                        null, viewModelStore);
+                        mActivityRule.getActivity(), null, viewModelStore);
 
                 final FragmentManager fm1 = fc1.getSupportFragmentManager();
 
@@ -924,7 +928,8 @@
                 // is being restored as the fragment controller state is being brought up.
 
                 try {
-                    startupFragmentController(savedState, viewModelStore);
+                    startupFragmentController(mActivityRule.getActivity(), savedState,
+                            viewModelStore);
 
                     fail("Expected IllegalStateException when moving from "
                             + StrictFragment.stateToString(fromState) + " to "
@@ -944,14 +949,15 @@
     @UiThreadTest
     public void noPrematureStateChange() throws Throwable {
         ViewModelStore viewModelStore = new ViewModelStore();
-        FragmentController fc = startupFragmentController(null, viewModelStore);
+        FragmentController fc = startupFragmentController(mActivityRule.getActivity(), null,
+                viewModelStore);
         FragmentManager fm = fc.getSupportFragmentManager();
 
         fm.beginTransaction()
                 .add(new StrictFragment(), "1")
                 .commitNow();
 
-        fc = restartFragmentController(fc, viewModelStore);
+        fc = restartFragmentController(mActivityRule.getActivity(), fc, viewModelStore);
 
         fm = fc.getSupportFragmentManager();
 
@@ -971,7 +977,8 @@
     @UiThreadTest
     public void testIsStateSaved() throws Throwable {
         ViewModelStore viewModelStore = new ViewModelStore();
-        FragmentController fc = startupFragmentController(null, viewModelStore);
+        FragmentController fc = startupFragmentController(mActivityRule.getActivity(), null,
+                viewModelStore);
         FragmentManager fm = fc.getSupportFragmentManager();
 
         Fragment f = new StrictFragment();
@@ -1000,7 +1007,8 @@
     @UiThreadTest
     public void testSetArgumentsLifecycle() throws Throwable {
         ViewModelStore viewModelStore = new ViewModelStore();
-        FragmentController fc = startupFragmentController(null, viewModelStore);
+        FragmentController fc = startupFragmentController(mActivityRule.getActivity(), null,
+                viewModelStore);
         FragmentManager fm = fc.getSupportFragmentManager();
 
         Fragment f = new StrictFragment();
@@ -1077,7 +1085,8 @@
         fc1.execPendingActions();
 
         // Simulate an activity restart
-        final FragmentController fc2 = restartFragmentController(fc1, viewModelStore);
+        final FragmentController fc2 =
+                restartFragmentController(mActivityRule.getActivity(), fc1, viewModelStore);
 
         // Bring the state back down to destroyed before we finish the test
         shutdownFragmentController(fc2, viewModelStore);
@@ -1116,7 +1125,8 @@
         fc1.execPendingActions();
 
         // Simulate an activity restart
-        final FragmentController fc2 = restartFragmentController(fc1, viewModelStore);
+        final FragmentController fc2 =
+                restartFragmentController(mActivityRule.getActivity(), fc1, viewModelStore);
 
         // Bring the state back down to destroyed before we finish the test
         shutdownFragmentController(fc2, viewModelStore);
@@ -1126,7 +1136,8 @@
     @UiThreadTest
     public void targetFragmentClearedWhenSetToNull() {
         ViewModelStore viewModelStore = new ViewModelStore();
-        final FragmentController fc = startupFragmentController(null, viewModelStore);
+        final FragmentController fc =
+                startupFragmentController(mActivityRule.getActivity(), null, viewModelStore);
 
         final FragmentManager fm = fc.getSupportFragmentManager();
 
@@ -1169,7 +1180,8 @@
     @UiThreadTest
     public void targetFragmentOnlyTargetAdded() {
         ViewModelStore viewModelStore = new ViewModelStore();
-        final FragmentController fc = startupFragmentController(null, viewModelStore);
+        final FragmentController fc =
+                startupFragmentController(mActivityRule.getActivity(), null, viewModelStore);
 
         final FragmentManager fm = fc.getSupportFragmentManager();
 
@@ -1209,7 +1221,8 @@
     @UiThreadTest
     public void targetFragmentNonRetainedNonRetained() {
         ViewModelStore viewModelStore = new ViewModelStore();
-        final FragmentController fc = startupFragmentController(null, viewModelStore);
+        final FragmentController fc =
+                startupFragmentController(mActivityRule.getActivity(), null, viewModelStore);
 
         final FragmentManager fm = fc.getSupportFragmentManager();
 
@@ -1250,7 +1263,8 @@
     @UiThreadTest
     public void targetFragmentRetainedNonRetained() {
         ViewModelStore viewModelStore = new ViewModelStore();
-        final FragmentController fc = startupFragmentController(null, viewModelStore);
+        final FragmentController fc =
+                startupFragmentController(mActivityRule.getActivity(), null, viewModelStore);
 
         final FragmentManager fm = fc.getSupportFragmentManager();
 
@@ -1292,7 +1306,8 @@
     @UiThreadTest
     public void targetFragmentNonRetainedRetained() {
         ViewModelStore viewModelStore = new ViewModelStore();
-        final FragmentController fc = startupFragmentController(null, viewModelStore);
+        final FragmentController fc =
+                startupFragmentController(mActivityRule.getActivity(), null, viewModelStore);
 
         final FragmentManager fm = fc.getSupportFragmentManager();
 
@@ -1330,7 +1345,8 @@
     @UiThreadTest
     public void targetFragmentRetainedRetained() {
         ViewModelStore viewModelStore = new ViewModelStore();
-        final FragmentController fc = startupFragmentController(null, viewModelStore);
+        final FragmentController fc =
+                startupFragmentController(mActivityRule.getActivity(), null, viewModelStore);
 
         final FragmentManager fm = fc.getSupportFragmentManager();
 
@@ -1760,38 +1776,6 @@
         Assert.assertEquals(popExit, record.mPopExitAnim);
     }
 
-    private FragmentController restartFragmentController(FragmentController fc,
-            ViewModelStore viewModelStore) {
-        Parcelable savedState = shutdownFragmentController(fc, viewModelStore);
-        return startupFragmentController(savedState, viewModelStore);
-    }
-
-    private FragmentController startupFragmentController(Parcelable savedState,
-            ViewModelStore viewModelStore) {
-        final FragmentController fc = FragmentController.createController(
-                new HostCallbacks(mActivityRule.getActivity(), viewModelStore));
-        fc.attachHost(null);
-        fc.restoreSaveState(savedState);
-        fc.dispatchCreate();
-        fc.dispatchActivityCreated();
-        fc.noteStateNotSaved();
-        fc.execPendingActions();
-        fc.dispatchStart();
-        fc.dispatchResume();
-        fc.execPendingActions();
-        return fc;
-    }
-
-    private Parcelable shutdownFragmentController(FragmentController fc,
-            ViewModelStore viewModelStore) {
-        fc.dispatchPause();
-        final Parcelable savedState = fc.saveAllState();
-        fc.dispatchStop();
-        viewModelStore.clear();
-        fc.dispatchDestroy();
-        return savedState;
-    }
-
     private void executePendingTransactions(final FragmentManager fm) throws Throwable {
         mActivityRule.runOnUiThread(new Runnable() {
             @Override
@@ -1902,100 +1886,6 @@
         }
     }
 
-    static class HostCallbacks extends FragmentHostCallback<FragmentActivity>
-            implements ViewModelStoreOwner {
-        private final FragmentActivity mActivity;
-        private final ViewModelStore mViewModelStore;
-
-        HostCallbacks(FragmentActivity activity, ViewModelStore viewModelStore) {
-            super(activity);
-            mActivity = activity;
-            mViewModelStore = viewModelStore;
-        }
-
-        @NonNull
-        @Override
-        public ViewModelStore getViewModelStore() {
-            return mViewModelStore;
-        }
-
-        @Override
-        public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        }
-
-        @Override
-        public boolean onShouldSaveFragmentState(Fragment fragment) {
-            return !mActivity.isFinishing();
-        }
-
-        @Override
-        @NonNull
-        public LayoutInflater onGetLayoutInflater() {
-            return mActivity.getLayoutInflater().cloneInContext(mActivity);
-        }
-
-        @Override
-        public FragmentActivity onGetHost() {
-            return mActivity;
-        }
-
-        @Override
-        public void onSupportInvalidateOptionsMenu() {
-            mActivity.supportInvalidateOptionsMenu();
-        }
-
-        @Override
-        public void onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode) {
-            mActivity.startActivityFromFragment(fragment, intent, requestCode);
-        }
-
-        @Override
-        public void onStartActivityFromFragment(
-                Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) {
-            mActivity.startActivityFromFragment(fragment, intent, requestCode, options);
-        }
-
-        @Override
-        public void onRequestPermissionsFromFragment(@NonNull Fragment fragment,
-                @NonNull String[] permissions, int requestCode) {
-            throw new UnsupportedOperationException();
-        }
-
-        @Override
-        public boolean onShouldShowRequestPermissionRationale(@NonNull String permission) {
-            return ActivityCompat.shouldShowRequestPermissionRationale(
-                    mActivity, permission);
-        }
-
-        @Override
-        public boolean onHasWindowAnimations() {
-            return mActivity.getWindow() != null;
-        }
-
-        @Override
-        public int onGetWindowAnimations() {
-            final Window w = mActivity.getWindow();
-            return (w == null) ? 0 : w.getAttributes().windowAnimations;
-        }
-
-        @Override
-        public void onAttachFragment(Fragment fragment) {
-            mActivity.onAttachFragment(fragment);
-        }
-
-        @Nullable
-        @Override
-        public View onFindViewById(int id) {
-            return mActivity.findViewById(id);
-        }
-
-        @Override
-        public boolean onHasView() {
-            final Window w = mActivity.getWindow();
-            return (w != null && w.peekDecorView() != null);
-        }
-    }
-
     public static class SimpleFragment extends Fragment {
         private int mLayoutId;
         private static final String LAYOUT_ID = "layoutId";
diff --git a/fragment/src/androidTest/java/androidx/fragment/app/FragmentTestUtil.java b/fragment/src/androidTest/java/androidx/fragment/app/FragmentTestUtil.java
index f220673..5dad36f 100644
--- a/fragment/src/androidTest/java/androidx/fragment/app/FragmentTestUtil.java
+++ b/fragment/src/androidTest/java/androidx/fragment/app/FragmentTestUtil.java
@@ -19,23 +19,36 @@
 
 import android.app.Activity;
 import android.app.Instrumentation;
+import android.content.Intent;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.util.Pair;
+import android.view.LayoutInflater;
+import android.view.View;
 import android.view.ViewGroup;
+import android.view.Window;
 import android.view.animation.Animation;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.app.ActivityCompat;
 import androidx.fragment.app.test.FragmentTestActivity;
+import androidx.lifecycle.ViewModelStore;
+import androidx.lifecycle.ViewModelStoreOwner;
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
 public class FragmentTestUtil {
+
     private static final Runnable DO_NOTHING = new Runnable() {
         @Override
         public void run() {
@@ -155,7 +168,8 @@
             @Override
             public void run() {
                 Handler handler = new Handler();
-                HostCallbacks hostCallbacks = new HostCallbacks(activity, handler, 0);
+                androidx.fragment.app.HostCallbacks hostCallbacks =
+                        new androidx.fragment.app.HostCallbacks(activity, handler, 0);
                 controller[0] = FragmentController.createController(hostCallbacks);
             }
         });
@@ -241,4 +255,130 @@
             }
         }
     }
+
+    static class HostCallbacks extends FragmentHostCallback<FragmentActivity>
+            implements ViewModelStoreOwner {
+        private final FragmentActivity mActivity;
+        private final ViewModelStore mViewModelStore;
+
+        HostCallbacks(FragmentActivity activity, ViewModelStore viewModelStore) {
+            super(activity);
+            mActivity = activity;
+            mViewModelStore = viewModelStore;
+        }
+
+        @NonNull
+        @Override
+        public ViewModelStore getViewModelStore() {
+            return mViewModelStore;
+        }
+
+        @Override
+        public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        }
+
+        @Override
+        public boolean onShouldSaveFragmentState(Fragment fragment) {
+            return !mActivity.isFinishing();
+        }
+
+        @Override
+        @NonNull
+        public LayoutInflater onGetLayoutInflater() {
+            return mActivity.getLayoutInflater().cloneInContext(mActivity);
+        }
+
+        @Override
+        public FragmentActivity onGetHost() {
+            return mActivity;
+        }
+
+        @Override
+        public void onSupportInvalidateOptionsMenu() {
+            mActivity.supportInvalidateOptionsMenu();
+        }
+
+        @Override
+        public void onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode) {
+            mActivity.startActivityFromFragment(fragment, intent, requestCode);
+        }
+
+        @Override
+        public void onStartActivityFromFragment(
+                Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) {
+            mActivity.startActivityFromFragment(fragment, intent, requestCode, options);
+        }
+
+        @Override
+        public void onRequestPermissionsFromFragment(@NonNull Fragment fragment,
+                @NonNull String[] permissions, int requestCode) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean onShouldShowRequestPermissionRationale(@NonNull String permission) {
+            return ActivityCompat.shouldShowRequestPermissionRationale(
+                    mActivity, permission);
+        }
+
+        @Override
+        public boolean onHasWindowAnimations() {
+            return mActivity.getWindow() != null;
+        }
+
+        @Override
+        public int onGetWindowAnimations() {
+            final Window w = mActivity.getWindow();
+            return (w == null) ? 0 : w.getAttributes().windowAnimations;
+        }
+
+        @Override
+        public void onAttachFragment(Fragment fragment) {
+            mActivity.onAttachFragment(fragment);
+        }
+
+        @Nullable
+        @Override
+        public View onFindViewById(int id) {
+            return mActivity.findViewById(id);
+        }
+
+        @Override
+        public boolean onHasView() {
+            final Window w = mActivity.getWindow();
+            return (w != null && w.peekDecorView() != null);
+        }
+    }
+
+    public static FragmentController startupFragmentController(FragmentActivity activity,
+            Parcelable savedState, ViewModelStore viewModelStore) {
+        final FragmentController fc = FragmentController.createController(
+                new HostCallbacks(activity, viewModelStore));
+        fc.attachHost(null);
+        fc.restoreSaveState(savedState);
+        fc.dispatchCreate();
+        fc.dispatchActivityCreated();
+        fc.noteStateNotSaved();
+        fc.execPendingActions();
+        fc.dispatchStart();
+        fc.dispatchResume();
+        fc.execPendingActions();
+        return fc;
+    }
+
+    public static FragmentController restartFragmentController(FragmentActivity activity,
+            FragmentController fc, ViewModelStore viewModelStore) {
+        Parcelable savedState = shutdownFragmentController(fc, viewModelStore);
+        return startupFragmentController(activity, savedState, viewModelStore);
+    }
+
+    public static Parcelable shutdownFragmentController(FragmentController fc,
+            ViewModelStore viewModelStore) {
+        fc.dispatchPause();
+        final Parcelable savedState = fc.saveAllState();
+        fc.dispatchStop();
+        viewModelStore.clear();
+        fc.dispatchDestroy();
+        return savedState;
+    }
 }
diff --git a/jetifier/jetifier/source-transformer/rewritePackageNames.py b/jetifier/jetifier/source-transformer/rewritePackageNames.py
index 99326f9..448e092 100755
--- a/jetifier/jetifier/source-transformer/rewritePackageNames.py
+++ b/jetifier/jetifier/source-transformer/rewritePackageNames.py
@@ -7,22 +7,11 @@
 #   import androidx.annotation.RequiresApi;
 
 # See also b/74074903
+import argparse
 import json
 import os.path
 import subprocess
 
-HARDCODED_RULES_REVERSE = [
-  "s|androidx.core.os.ResultReceiver|android.support.v4.os.ResultsReceiver|g\n",
-  "s|androidx.core.media.MediaBrowserCompat|android.support.v4.media.MediaBrowserCompat|g\n",
-  "s|androidx.core.media.MediaDescriptionCompat|android.support.v4.media.MediaDescriptionCompat|g\n",
-  "s|androidx.core.media.MediaMetadataCompat|android.support.v4.media.MediaMetadataCompat|g\n",
-  "s|androidx.core.media.RatingCompat|android.support.v4.media.RatingCompat|g\n",
-  "s|androidx.core.media.session.MediaControllerCompat|android.support.v4.media.session.MediaControllerCompat|g\n",
-  "s|androidx.core.media.session.MediaSessionCompat|android.support.v4.media.session.MediaSessionCompat|g\n",
-  "s|androidx.core.media.session.ParcelableVolumeInfo|android.support.v4.media.session.ParcelableVolumeInfo|g\n",
-  "s|androidx.core.media.session.PlaybackStateCompat|android.support.v4.media.session.PlaybackStateCompat|g\n",
-]
-
 class StringBuilder(object):
   def __init__(self, item=None):
     self.items = []
@@ -36,17 +25,6 @@
   def __str__(self):
     return "".join(self.items)
 
-class ExecutionConfig(object):
-  """Stores configuration about this execution of the package renaming.
-     For example, file paths of the source code.
-     This config could potentially be affected by command-line arguments.
-  """
-  def __init__(self, jetifierConfig, sourceRoots, excludeDirs):
-    self.jetifierConfig = jetifierConfig
-    self.sourceRoots = sourceRoots
-    self.excludeDirs = excludeDirs
-
-
 class SourceRewriteRule(object):
   def __init__(self, fromName, toName):
     self.fromName = fromName
@@ -56,8 +34,8 @@
     return self.fromName + ":" + self.toName
 
 class JetifierConfig(object):
-  """Stores configuration about the renaming itself, such as package rename rules.
-     This config isn't supposed to be affected by command-line arguments.
+  """
+  Stores configuration about the renaming itself, such as package rename rules.
   """
   @staticmethod
   def parse(filePath):
@@ -66,78 +44,63 @@
       nonCommentLines = [line for line in lines if not line.strip().startswith("#")]
       parsed = json.loads("".join(nonCommentLines))
       return JetifierConfig(parsed)
-    
+
   def __init__(self, parsedJson):
     self.json = parsedJson
 
-  def getTypesMap(self):
+  def getTypesMap(self, reverse):
     rules = []
     for rule in self.json["rules"]:
       fromName = rule["from"].replace("/", ".").replace("(.*)", "")
       toName = rule["to"].replace("/", ".").replace("{0}", "")
       if not toName.startswith("ignore"):
-        rules.append(SourceRewriteRule(fromName, toName))
+        if reverse:
+          # Dejetify instead, so toName becomes fromName and vice versa.
+          rules.append(SourceRewriteRule(toName, fromName))
+        else:
+          rules.append(SourceRewriteRule(fromName, toName))
 
     return rules
 
 
-def createRewriteCommand(executionConfig):
-  # create command to find source files
-  finderTextBuilder = StringBuilder("find")
-  for sourceRoot in executionConfig.sourceRoots:
-    finderTextBuilder.add(" ").add(sourceRoot)
-  for exclusion in executionConfig.excludeDirs:
-    finderTextBuilder.add(" -name ").add(exclusion).add(" -prune -o")
-  finderTextBuilder.add(" -iregex '.*\.java\|.*\.xml\|.*\.cfg\|.*\.flags' -print")
-
-  # create command to rewrite one source
-  print("Building sed instructions")
+def createSourceJetificationSedCommand(args, jetifierConfig):
   rewriterTextBuilder = StringBuilder()
-  rewriteRules = executionConfig.jetifierConfig.getTypesMap()
+  rewriteRules = jetifierConfig.getTypesMap(args.reverse)
+  # Append substitution rules, this will most probably never exceed the shell
+  # characters per line limit which should be 131072.
+  # In the weird case where the user somehow modified their ARG_MAX,
+  # they'll know what to do when they see a /bin/sed: Argument list too long
+  # error.
   for rule in rewriteRules:
-    rewriterTextBuilder.add("s|").add(rule.fromName.replace(".", "\.")).add("|").add(rule.toName).add("|g\n")
-  for rule in HARDCODED_RULES_REVERSE:
-    rewriterTextBuilder.add(rule)
-  scriptPath = "/tmp/jetifier-sed-script.txt"
-  print("Writing " + scriptPath)
-  with open(scriptPath, 'w') as scriptFile:
-    scriptFile.write(str(rewriterTextBuilder))
-  
-  # create the command to do the rewrites
-  fullCommand = "time " + str(finderTextBuilder) + " | xargs -n 1 --no-run-if-empty -P 64 sed -i -f " + scriptPath
+    rewriterTextBuilder.add("-e \'s|").add(rule.fromName.replace(".", "\.")).add("|").add(rule.toName).add("|g\' ")
 
-  return fullCommand  
+  # sed command containing substitutions and applied to the output file.
+  sedCommand = "sed %s %s > %s" % (rewriterTextBuilder, args.infile, args.outfile)
+  return sedCommand
 
-def processConfig(executionConfig):
-  print("Building rewrite command")
-  rewriteCommand = createRewriteCommand(executionConfig)
-  commandLength = len(rewriteCommand)
-  print("""
-Will run command:
-
-""" + rewriteCommand + """
-
-""")
-  response = raw_input("Ok? [y/n]")
-  if response == "y":
-    subprocess.check_output(rewriteCommand, shell=True)  
+def jetifySource(args):
+  # If config file is not specified, look for the config file in the
+  # same folder.
+  jetifierConfigPath = args.config
+  if not jetifierConfigPath:
+    jetifierConfigPath = os.path.join(os.path.realpath(__file__), "default.config")
+  jetifierConfig = JetifierConfig.parse(jetifierConfigPath)
+  command = createSourceJetificationSedCommand(args, jetifierConfig)
+  subprocess.check_output(command, shell=True)
 
 
 def main():
-  pathOfThisFile = os.path.realpath(__file__)
-  jetifierPath = os.path.abspath(os.path.join(pathOfThisFile, "..", ".."))
+  # Set up input arguments
+  parser = argparse.ArgumentParser()
+  parser.add_argument("-c", "--config", help="path to optional custom config file.")
+  parser.add_argument("-r", "--reverse", help="operate in reverse mode (\"de-jetification\")",
+        action="store_true")
+  parser.add_argument("-i", "--infile",
+        required=True, help="path to input source (java or xml)")
+  parser.add_argument("-o", "--outfile",
+        required=True, help="path to the output file, overriden if exists.")
+  args = parser.parse_args()
+  jetifySource(args)
 
-  jetifierConfigPath = os.path.join(jetifierPath, "core/src/main/resources", "default.generated.config")
-  print("Parsing " + jetifierConfigPath)
-  jetifierConfig = JetifierConfig.parse(jetifierConfigPath)
-
-  sourceRoot = os.getcwd()
-  excludeDirs = ["out", ".git", ".repo"]
-
-  executionConfig = ExecutionConfig(jetifierConfig, [sourceRoot], excludeDirs)
-
-  processConfig(executionConfig)
-
-main()
-
-
+if __name__ == "__main__":
+    main()
diff --git a/media/api/1.1.0-alpha02.txt b/media/api/1.1.0-alpha02.txt
index c4e14c0..881051e 100644
--- a/media/api/1.1.0-alpha02.txt
+++ b/media/api/1.1.0-alpha02.txt
@@ -285,6 +285,7 @@
     method public abstract void sendCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction!, android.os.Bundle!);
     method public abstract void sendCustomAction(String!, android.os.Bundle!);
     method public abstract void setCaptioningEnabled(boolean);
+    method public void setPlaybackSpeed(float);
     method public abstract void setRating(android.support.v4.media.RatingCompat!);
     method public abstract void setRating(android.support.v4.media.RatingCompat!, android.os.Bundle!);
     method public abstract void setRepeatMode(int);
@@ -363,6 +364,7 @@
     method public void onRewind();
     method public void onSeekTo(long);
     method public void onSetCaptioningEnabled(boolean);
+    method public void onSetPlaybackSpeed(float);
     method public void onSetRating(android.support.v4.media.RatingCompat!);
     method public void onSetRating(android.support.v4.media.RatingCompat!, android.os.Bundle!);
     method public void onSetRepeatMode(int);
diff --git a/media/api/current.txt b/media/api/current.txt
index c4e14c0..881051e 100644
--- a/media/api/current.txt
+++ b/media/api/current.txt
@@ -285,6 +285,7 @@
     method public abstract void sendCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction!, android.os.Bundle!);
     method public abstract void sendCustomAction(String!, android.os.Bundle!);
     method public abstract void setCaptioningEnabled(boolean);
+    method public void setPlaybackSpeed(float);
     method public abstract void setRating(android.support.v4.media.RatingCompat!);
     method public abstract void setRating(android.support.v4.media.RatingCompat!, android.os.Bundle!);
     method public abstract void setRepeatMode(int);
@@ -363,6 +364,7 @@
     method public void onRewind();
     method public void onSeekTo(long);
     method public void onSetCaptioningEnabled(boolean);
+    method public void onSetPlaybackSpeed(float);
     method public void onSetRating(android.support.v4.media.RatingCompat!);
     method public void onSetRating(android.support.v4.media.RatingCompat!, android.os.Bundle!);
     method public void onSetRepeatMode(int);
diff --git a/media/src/main/aidl/android/support/v4/media/session/IMediaSession.aidl b/media/src/main/aidl/android/support/v4/media/session/IMediaSession.aidl
index f32f449..0fcd811 100644
--- a/media/src/main/aidl/android/support/v4/media/session/IMediaSession.aidl
+++ b/media/src/main/aidl/android/support/v4/media/session/IMediaSession.aidl
@@ -34,7 +34,7 @@
  * @hide
  */
 interface IMediaSession {
-    // Next ID: 48
+    // Next ID: 49
     void sendCommand(String command, in Bundle args, in MediaSessionCompat.ResultReceiverWrapper cb) = 0;
     boolean sendMediaButton(in KeyEvent mediaButton) = 1;
     void registerCallbackListener(in IMediaControllerCallback cb) = 2;
@@ -81,6 +81,7 @@
     void seekTo(long pos) = 23;
     void rate(in RatingCompat rating) = 24;
     void rateWithExtras(in RatingCompat rating, in Bundle extras) = 50;
+    void setPlaybackSpeed(float speed) = 48;
     void setCaptioningEnabled(boolean enabled) = 45;
     void setRepeatMode(int repeatMode) = 38;
     void setShuffleModeEnabledRemoved(boolean shuffleMode) = 39;
diff --git a/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java b/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
index e7963eb..49956a5 100644
--- a/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
+++ b/media/src/main/java/android/support/v4/media/session/MediaControllerCompat.java
@@ -1301,6 +1301,13 @@
         public abstract void setRating(RatingCompat rating, Bundle extras);
 
         /**
+         * Set the playback speed.
+         *
+         * @param speed The playback speed
+         */
+        public void setPlaybackSpeed(float speed) {}
+
+        /**
          * Enables/disables captioning for this session.
          *
          * @param enabled {@code true} to enable captioning, {@code false} to disable.
@@ -1938,6 +1945,15 @@
         }
 
         @Override
+        public void setPlaybackSpeed(float speed) {
+            try {
+                mBinder.setPlaybackSpeed(speed);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in setPlaybackSpeed.", e);
+            }
+        }
+
+        @Override
         public void setCaptioningEnabled(boolean enabled) {
             try {
                 mBinder.setCaptioningEnabled(enabled);
@@ -2429,6 +2445,13 @@
         }
 
         @Override
+        public void setPlaybackSpeed(float speed) {
+            Bundle bundle = new Bundle();
+            bundle.putFloat(MediaSessionCompat.ACTION_ARGUMENT_PLAYBACK_SPEED, speed);
+            sendCustomAction(MediaSessionCompat.ACTION_SET_PLAYBACK_SPEED, bundle);
+        }
+
+        @Override
         public void setCaptioningEnabled(boolean enabled) {
             Bundle bundle = new Bundle();
             bundle.putBoolean(MediaSessionCompat.ACTION_ARGUMENT_CAPTIONING_ENABLED, enabled);
diff --git a/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java b/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
index 4b468c6..95eff3c 100644
--- a/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
+++ b/media/src/main/java/android/support/v4/media/session/MediaSessionCompat.java
@@ -328,6 +328,15 @@
             "android.support.v4.media.session.action.SET_RATING";
 
     /**
+     * Custom action to invoke setPlaybackSpeed() with extra fields.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY)
+    public static final String ACTION_SET_PLAYBACK_SPEED =
+            "android.support.v4.media.session.action.SET_PLAYBACK_SPEED";
+
+    /**
      * Argument for use with {@link #ACTION_PREPARE_FROM_MEDIA_ID} indicating media id to play.
      *
      * @hide
@@ -365,6 +374,15 @@
             "android.support.v4.media.session.action.ARGUMENT_RATING";
 
     /**
+     * Argument for use with {@link #ACTION_SET_PLAYBACK_SPEED} indicating the speed to be set.
+     *
+     * @hide
+     */
+    @RestrictTo(LIBRARY)
+    public static final String ACTION_ARGUMENT_PLAYBACK_SPEED =
+            "android.support.v4.media.session.action.ARGUMENT_PLAYBACK_SPEED";
+
+    /**
      * Argument for use with various actions indicating extra bundle.
      *
      * @hide
@@ -1259,6 +1277,19 @@
         }
 
         /**
+         * Override to handle the playback speed change.
+         * To update the new playback speed, create a new {@link PlaybackStateCompat} by using
+         * {@link PlaybackStateCompat.Builder#setState(int, long, float)}, and set it with
+         * {@link #setPlaybackState(PlaybackStateCompat)}.
+         *
+         * @param speed the playback speed
+         * @see #setPlaybackState(PlaybackStateCompat)
+         * @see PlaybackStateCompat.Builder#setState(int, long, float)
+         */
+        public void onSetPlaybackSpeed(float speed) {
+        }
+
+        /**
          * Override to handle requests to enable/disable captioning.
          *
          * @param enabled {@code true} to enable captioning, {@code false} to disable.
@@ -1579,6 +1610,9 @@
                 } else if (action.equals(ACTION_SET_RATING)) {
                     RatingCompat rating = extras.getParcelable(ACTION_ARGUMENT_RATING);
                     Callback.this.onSetRating(rating, bundle);
+                } else if (action.equals(ACTION_SET_PLAYBACK_SPEED)) {
+                    float speed = extras.getFloat(ACTION_ARGUMENT_PLAYBACK_SPEED, 1.0f);
+                    Callback.this.onSetPlaybackSpeed(speed);
                 } else {
                     Callback.this.onCustomAction(action, extras);
                 }
@@ -2947,6 +2981,11 @@
             }
 
             @Override
+            public void setPlaybackSpeed(float speed) throws RemoteException {
+                postToHandler(MessageHandler.MSG_SET_PLAYBACK_SPEED, speed);
+            }
+
+            @Override
             public void setCaptioningEnabled(boolean enabled) throws RemoteException {
                 postToHandler(MessageHandler.MSG_SET_CAPTIONING_ENABLED, enabled);
             }
@@ -3095,6 +3134,7 @@
         }
 
         class MessageHandler extends Handler {
+            // Next ID: 33
             private static final int MSG_COMMAND = 1;
             private static final int MSG_ADJUST_VOLUME = 2;
             private static final int MSG_PREPARE = 3;
@@ -3115,6 +3155,7 @@
             private static final int MSG_SEEK_TO = 18;
             private static final int MSG_RATE = 19;
             private static final int MSG_RATE_EXTRA = 31;
+            private static final int MSG_SET_PLAYBACK_SPEED = 32;
             private static final int MSG_CUSTOM_ACTION = 20;
             private static final int MSG_MEDIA_BUTTON = 21;
             private static final int MSG_SET_VOLUME = 22;
@@ -3219,6 +3260,9 @@
                         case MSG_RATE_EXTRA:
                             cb.onSetRating((RatingCompat) msg.obj, extras);
                             break;
+                        case MSG_SET_PLAYBACK_SPEED:
+                            cb.onSetPlaybackSpeed((Float) msg.obj);
+                            break;
                         case MSG_CUSTOM_ACTION:
                             cb.onCustomAction((String) msg.obj, extras);
                             break;
@@ -3911,6 +3955,12 @@
             }
 
             @Override
+            public void setPlaybackSpeed(float speed) throws RemoteException {
+                // Will not be called.
+                throw new AssertionError();
+            }
+
+            @Override
             public void setCaptioningEnabled(boolean enabled) throws RemoteException {
                 // Will not be called.
                 throw new AssertionError();
diff --git a/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/ClientBroadcastReceiver.java b/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/ClientBroadcastReceiver.java
index 1baba08..0f4d8e8 100644
--- a/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/ClientBroadcastReceiver.java
+++ b/media/version-compat-tests/current/client/src/androidTest/java/android/support/mediacompat/client/ClientBroadcastReceiver.java
@@ -41,6 +41,7 @@
 import static android.support.mediacompat.testlib.MediaControllerConstants
         .SEND_CUSTOM_ACTION_PARCELABLE;
 import static android.support.mediacompat.testlib.MediaControllerConstants.SET_CAPTIONING_ENABLED;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SET_PLAYBACK_SPEED;
 import static android.support.mediacompat.testlib.MediaControllerConstants.SET_RATING;
 import static android.support.mediacompat.testlib.MediaControllerConstants.SET_REPEAT_MODE;
 import static android.support.mediacompat.testlib.MediaControllerConstants.SET_SHUFFLE_MODE;
@@ -231,6 +232,9 @@
                 case SET_SHUFFLE_MODE:
                     controls.setShuffleMode(extras.getInt(KEY_ARGUMENT));
                     break;
+                case SET_PLAYBACK_SPEED:
+                    controls.setPlaybackSpeed(extras.getFloat(KEY_ARGUMENT));
+                    break;
             }
         }
     }
diff --git a/media/version-compat-tests/current/service/src/androidTest/java/android/support/mediacompat/service/MediaSessionCompatCallbackTest.java b/media/version-compat-tests/current/service/src/androidTest/java/android/support/mediacompat/service/MediaSessionCompatCallbackTest.java
index 38ac323..56a99c2 100644
--- a/media/version-compat-tests/current/service/src/androidTest/java/android/support/mediacompat/service/MediaSessionCompatCallbackTest.java
+++ b/media/version-compat-tests/current/service/src/androidTest/java/android/support/mediacompat/service/MediaSessionCompatCallbackTest.java
@@ -37,6 +37,7 @@
 import static android.support.mediacompat.testlib.MediaControllerConstants.SEND_CUSTOM_ACTION;
 import static android.support.mediacompat.testlib.MediaControllerConstants.SEND_CUSTOM_ACTION_PARCELABLE;
 import static android.support.mediacompat.testlib.MediaControllerConstants.SET_CAPTIONING_ENABLED;
+import static android.support.mediacompat.testlib.MediaControllerConstants.SET_PLAYBACK_SPEED;
 import static android.support.mediacompat.testlib.MediaControllerConstants.SET_RATING;
 import static android.support.mediacompat.testlib.MediaControllerConstants.SET_REPEAT_MODE;
 import static android.support.mediacompat.testlib.MediaControllerConstants.SET_SHUFFLE_MODE;
@@ -696,6 +697,26 @@
     }
 
     /**
+     * Tests {@link MediaSessionCompat.Callback#onSetPlaybackSpeed(float)}.
+     */
+    @Test
+    @SmallTest
+    public void testCallback_onSetPlaybackSpeed() {
+        if (!TextUtils.equals(VERSION_TOT, mClientVersion)) {
+            // In previous versions, MediaControllerCompat#setPlaybackSpeed() does not exist.
+            return;
+        }
+
+        mCallback.reset(1);
+        final float testSpeed = 2.0f;
+        callTransportControlsMethod(
+                SET_PLAYBACK_SPEED, testSpeed, getApplicationContext(), mSession.getSessionToken());
+        assertTrue(mCallback.await(TIME_OUT_MS));
+        assertTrue(mCallback.mOnSetPlaybackSpeedCalled);
+        assertEquals(testSpeed, mCallback.mSpeed, 0.0f);
+    }
+
+    /**
      * Tests {@link MediaSessionCompat.Callback#onMediaButtonEvent}.
      */
     @Test
@@ -1098,6 +1119,7 @@
         private int mQueueIndex;
         private MediaDescriptionCompat mQueueDescription;
         private List<MediaSessionCompat.QueueItem> mQueue = new ArrayList<>();
+        private float mSpeed;
 
         private int mOnPlayCalledCount;
         private boolean mOnPauseCalled;
@@ -1124,6 +1146,7 @@
         private boolean mOnAddQueueItemCalled;
         private boolean mOnAddQueueItemAtCalled;
         private boolean mOnRemoveQueueItemCalled;
+        private boolean mOnSetPlaybackSpeedCalled;
 
         public void reset(int count) {
             mLatch = new CountDownLatch(count);
@@ -1147,6 +1170,7 @@
             mShuffleMode = PlaybackStateCompat.SHUFFLE_MODE_NONE;
             mQueueIndex = -1;
             mQueueDescription = null;
+            mSpeed = -1.0f;
 
             mRemoteUserInfoForStop = null;
             mOnPlayCalledCount = 0;
@@ -1174,6 +1198,7 @@
             mOnAddQueueItemCalled = false;
             mOnAddQueueItemAtCalled = false;
             mOnRemoveQueueItemCalled = false;
+            mOnSetPlaybackSpeedCalled = false;
         }
 
         public void reset(int count, String expectedCallerPackageName) {
@@ -1485,6 +1510,17 @@
             mLatch.countDown();
         }
 
+        @Override
+        public void onSetPlaybackSpeed(float speed) {
+            if (!isCallerTestClient()) {
+                // Ignore
+                return;
+            }
+            mOnSetPlaybackSpeedCalled = true;
+            mSpeed = speed;
+            mLatch.countDown();
+        }
+
         private boolean isCallerTestClient() {
             RemoteUserInfo info = mSession.getCurrentControllerInfo();
             assertNotNull(info);
diff --git a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaControllerConstants.java b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaControllerConstants.java
index 3041315..7bdd2fa 100644
--- a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaControllerConstants.java
+++ b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/MediaControllerConstants.java
@@ -54,6 +54,7 @@
     public static final int SET_CAPTIONING_ENABLED = 320;
     public static final int SET_REPEAT_MODE = 321;
     public static final int SET_SHUFFLE_MODE = 322;
+    public static final int SET_PLAYBACK_SPEED = 323;
 
     private MediaControllerConstants() {
     }
diff --git a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/IntentUtil.java b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/IntentUtil.java
index 14ba14c..532b2a7 100644
--- a/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/IntentUtil.java
+++ b/media/version-compat-tests/lib/src/main/java/android/support/mediacompat/testlib/util/IntentUtil.java
@@ -116,6 +116,8 @@
             intent.putExtra(KEY_ARGUMENT, (long) arg);
         } else if (arg instanceof Boolean) {
             intent.putExtra(KEY_ARGUMENT, (boolean) arg);
+        } else if (arg instanceof Float) {
+            intent.putExtra(KEY_ARGUMENT, (float) arg);
         } else if (arg instanceof Parcelable) {
             intent.putExtra(KEY_ARGUMENT, (Parcelable) arg);
         } else if (arg instanceof ArrayList<?>) {
diff --git a/media2/src/main/java/androidx/media2/MediaControllerImplLegacy.java b/media2/src/main/java/androidx/media2/MediaControllerImplLegacy.java
index 855835b..d88a058 100644
--- a/media2/src/main/java/androidx/media2/MediaControllerImplLegacy.java
+++ b/media2/src/main/java/androidx/media2/MediaControllerImplLegacy.java
@@ -513,12 +513,6 @@
     }
 
     @Override
-    public ListenableFuture<SessionResult> setPlaybackSpeed(float speed) {
-        // Unsupported action
-        return createFutureWithResult(RESULT_ERROR_NOT_SUPPORTED);
-    }
-
-    @Override
     public @BuffState int getBufferingState() {
         synchronized (mLock) {
             if (!mConnected) {
@@ -571,6 +565,18 @@
     }
 
     @Override
+    public ListenableFuture<SessionResult> setPlaybackSpeed(float speed) {
+        synchronized (mLock) {
+            if (!mConnected) {
+                Log.w(TAG, "Session isn't active", new IllegalStateException());
+                return createFutureWithResult(RESULT_ERROR_SESSION_DISCONNECTED);
+            }
+            mControllerCompat.getTransportControls().setPlaybackSpeed(speed);
+        }
+        return createFutureWithResult(RESULT_SUCCESS);
+    }
+
+    @Override
     public ListenableFuture<SessionResult> sendCustomCommand(@NonNull SessionCommand command,
             @Nullable Bundle args) {
         synchronized (mLock) {
diff --git a/media2/src/main/java/androidx/media2/MediaSessionLegacyStub.java b/media2/src/main/java/androidx/media2/MediaSessionLegacyStub.java
index 42be686..f1a0b4b 100644
--- a/media2/src/main/java/androidx/media2/MediaSessionLegacyStub.java
+++ b/media2/src/main/java/androidx/media2/MediaSessionLegacyStub.java
@@ -299,6 +299,16 @@
     }
 
     @Override
+    public void onSetPlaybackSpeed(final float speed) {
+        dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_SET_SPEED, new SessionTask() {
+            @Override
+            public void run(ControllerInfo controller) throws RemoteException {
+                mSessionImpl.setPlaybackSpeed(speed);
+            }
+        });
+    }
+
+    @Override
     public void onSkipToQueueItem(final long queueId) {
         dispatchSessionTask(SessionCommand.COMMAND_CODE_PLAYER_SKIP_TO_PLAYLIST_ITEM,
                 new SessionTask() {
diff --git a/media2/version-compat-tests/common/src/main/aidl/androidx/media2/test/common/IRemoteMediaControllerCompat.aidl b/media2/version-compat-tests/common/src/main/aidl/androidx/media2/test/common/IRemoteMediaControllerCompat.aidl
index 53a2e99..335fc00 100644
--- a/media2/version-compat-tests/common/src/main/aidl/androidx/media2/test/common/IRemoteMediaControllerCompat.aidl
+++ b/media2/version-compat-tests/common/src/main/aidl/androidx/media2/test/common/IRemoteMediaControllerCompat.aidl
@@ -44,6 +44,7 @@
     void pause(String controllerId);
     void stop(String controllerId);
     void seekTo(String controllerId, long pos);
+    void setPlaybackSpeed(String controllerId, float speed);
     void fastForward(String controllerId);
     void skipToNext(String controllerId);
     void rewind(String controllerId);
diff --git a/media2/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/MediaControllerCompatProviderService.java b/media2/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/MediaControllerCompatProviderService.java
index 213ca3b..3100d5c 100644
--- a/media2/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/MediaControllerCompatProviderService.java
+++ b/media2/version-compat-tests/current/client/src/androidTest/java/androidx/media2/test/client/MediaControllerCompatProviderService.java
@@ -240,6 +240,12 @@
         }
 
         @Override
+        public void setPlaybackSpeed(String controllerId, float speed) throws RemoteException {
+            MediaControllerCompat controller = mMediaControllerCompatMap.get(controllerId);
+            controller.getTransportControls().setPlaybackSpeed(speed);
+        }
+
+        @Override
         public void fastForward(String controllerId) throws RemoteException {
             MediaControllerCompat controller = mMediaControllerCompatMap.get(controllerId);
             controller.getTransportControls().fastForward();
diff --git a/media2/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/RemoteMediaControllerCompat.java b/media2/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/RemoteMediaControllerCompat.java
index faf93f4..d13e523 100644
--- a/media2/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/RemoteMediaControllerCompat.java
+++ b/media2/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/RemoteMediaControllerCompat.java
@@ -248,6 +248,14 @@
             }
         }
 
+        public void setPlaybackSpeed(float speed) {
+            try {
+                mBinder.setPlaybackSpeed(mControllerId, speed);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Failed to call setPlaybackSpeed()");
+            }
+        }
+
         public void fastForward() {
             try {
                 mBinder.fastForward(mControllerId);
diff --git a/media2/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionLegacyCallbackTest.java b/media2/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionCallbackTestWithMediaControllerCompat.java
similarity index 97%
rename from media2/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionLegacyCallbackTest.java
rename to media2/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionCallbackTestWithMediaControllerCompat.java
index 8b93d02..912c37d 100644
--- a/media2/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionLegacyCallbackTest.java
+++ b/media2/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionCallbackTestWithMediaControllerCompat.java
@@ -79,8 +79,8 @@
  */
 @RunWith(AndroidJUnit4.class)
 @LargeTest
-public class MediaSessionLegacyCallbackTest extends MediaSessionTestBase {
-    private static final String TAG = "MediaSessionLegacyCallbackTest";
+public class MediaSessionCallbackTestWithMediaControllerCompat extends MediaSessionTestBase {
+    private static final String TAG = "MediaSessionCallbackTestWithMediaControllerCompat";
 
     private static final String EXPECTED_CONTROLLER_PACKAGE_NAME =
             (Build.VERSION.SDK_INT < 21 || Build.VERSION.SDK_INT >= 24)
@@ -214,6 +214,20 @@
     }
 
     @Test
+    public void testSetPlaybackSpeed() {
+        prepareLooper();
+        final float testSpeed = 2.0f;
+        mController.getTransportControls().setPlaybackSpeed(testSpeed);
+        try {
+            assertTrue(mPlayer.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        }
+        assertTrue(mPlayer.mSetPlaybackSpeedCalled);
+        assertEquals(testSpeed, mPlayer.mPlaybackSpeed, 0.0f);
+    }
+
+    @Test
     public void testAddQueueItem() throws InterruptedException {
         prepareLooper();
         final int playlistSize = 10;
diff --git a/media2/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionCompatCallbackTestWithMediaController.java b/media2/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionCompatCallbackTestWithMediaController.java
index 758a680..59349bf 100644
--- a/media2/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionCompatCallbackTestWithMediaController.java
+++ b/media2/version-compat-tests/current/service/src/androidTest/java/androidx/media2/test/service/tests/MediaSessionCompatCallbackTestWithMediaController.java
@@ -173,6 +173,19 @@
     }
 
     @Test
+    public void testSetPlaybackSpeed() throws Exception {
+        prepareLooper();
+        RemoteMediaController controller = createControllerAndWaitConnection();
+        mSessionCallback.reset(1);
+
+        final float testSpeed = 2.0f;
+        controller.setPlaybackSpeed(testSpeed);
+        assertTrue(mSessionCallback.await(TIME_OUT_MS));
+        assertTrue(mSessionCallback.mOnSetPlaybackSpeedCalled);
+        assertEquals(testSpeed, mSessionCallback.mSpeed, 0.0f);
+    }
+
+    @Test
     public void testAddPlaylistItem() throws Exception {
         prepareLooper();
         final List<MediaItem> testList = MediaTestUtils.createPlaylist(2);
@@ -612,6 +625,7 @@
     private class MediaSessionCallback extends MediaSessionCompat.Callback {
         private CountDownLatch mLatch = new CountDownLatch(1);
         private long mSeekPosition;
+        private float mSpeed;
         private long mQueueItemId;
         private RatingCompat mRating;
         private String mMediaId;
@@ -636,6 +650,7 @@
         private boolean mOnSkipToPreviousCalled;
         private boolean mOnSkipToNextCalled;
         private boolean mOnSeekToCalled;
+        private boolean mOnSetPlaybackSpeedCalled;
         private boolean mOnSkipToQueueItemCalled;
         private boolean mOnSetRatingCalled;
         private boolean mOnPlayFromMediaIdCalled;
@@ -657,6 +672,7 @@
         public void reset(int count) {
             mLatch = new CountDownLatch(count);
             mSeekPosition = -1;
+            mSpeed = -1.0f;
             mQueueItemId = -1;
             mRating = null;
             mMediaId = null;
@@ -682,6 +698,7 @@
             mOnSkipToNextCalled = false;
             mOnSkipToQueueItemCalled = false;
             mOnSeekToCalled = false;
+            mOnSetPlaybackSpeedCalled = false;
             mOnSetRatingCalled = false;
             mOnPlayFromMediaIdCalled = false;
             mOnPlayFromSearchCalled = false;
@@ -761,6 +778,13 @@
         }
 
         @Override
+        public void onSetPlaybackSpeed(float speed) {
+            mOnSetPlaybackSpeedCalled = true;
+            mSpeed = speed;
+            mLatch.countDown();
+        }
+
+        @Override
         public void onSetRating(RatingCompat rating) {
             mOnSetRatingCalled = true;
             mRating = rating;
diff --git a/preference/api/1.1.0-alpha04.txt b/preference/api/1.1.0-alpha04.txt
index d3b0755..0edd4f7 100644
--- a/preference/api/1.1.0-alpha04.txt
+++ b/preference/api/1.1.0-alpha04.txt
@@ -481,12 +481,16 @@
     method public int getMax();
     method public int getMin();
     method public final int getSeekBarIncrement();
+    method public boolean getShowSeekBarValue();
+    method public boolean getUpdatesContinuously();
     method public int getValue();
     method public boolean isAdjustable();
     method public void setAdjustable(boolean);
     method public final void setMax(int);
     method public void setMin(int);
     method public final void setSeekBarIncrement(int);
+    method public void setShowSeekBarValue(boolean);
+    method public void setUpdatesContinuously(boolean);
     method public void setValue(int);
   }
 
diff --git a/preference/api/current.txt b/preference/api/current.txt
index d3b0755..0edd4f7 100644
--- a/preference/api/current.txt
+++ b/preference/api/current.txt
@@ -481,12 +481,16 @@
     method public int getMax();
     method public int getMin();
     method public final int getSeekBarIncrement();
+    method public boolean getShowSeekBarValue();
+    method public boolean getUpdatesContinuously();
     method public int getValue();
     method public boolean isAdjustable();
     method public void setAdjustable(boolean);
     method public final void setMax(int);
     method public void setMin(int);
     method public final void setSeekBarIncrement(int);
+    method public void setShowSeekBarValue(boolean);
+    method public void setUpdatesContinuously(boolean);
     method public void setValue(int);
   }
 
diff --git a/preference/res/values/attrs.xml b/preference/res/values/attrs.xml
index 8b1d83c..50aac79 100644
--- a/preference/res/values/attrs.xml
+++ b/preference/res/values/attrs.xml
@@ -324,6 +324,11 @@
         value will be displayed. If true, the view is VISIBLE; if false, the view will be GONE.
         By default, this view is GONE. -->
         <attr name="showSeekBarValue" format="boolean" />
+        <!-- Flag indicating whether the SeekBarPreference should continuously save the Seekbar
+        value while the Seekbar is being dragged. If true, the SeekBarPreference should continuously
+        save the Seekbar value while it is being dragged. If false, the Seekbar value is only saved
+        when released. By default, this boolean is false. -->
+        <attr name="updatesContinuously" format="boolean" />
     </declare-styleable>
 
     <declare-styleable name="SwitchPreference">
diff --git a/preference/res/values/styles.xml b/preference/res/values/styles.xml
index 3eaada8..001e097 100644
--- a/preference/res/values/styles.xml
+++ b/preference/res/values/styles.xml
@@ -55,6 +55,7 @@
         <item name="android:layout">@layout/preference_widget_seekbar</item>
         <item name="adjustable">true</item>
         <item name="showSeekBarValue">true</item>
+        <item name="updatesContinuously">false</item>
     </style>
 
     <style name="Preference.PreferenceScreen">
diff --git a/preference/src/main/java/androidx/preference/SeekBarPreference.java b/preference/src/main/java/androidx/preference/SeekBarPreference.java
index 920fdd7..6be1b40 100644
--- a/preference/src/main/java/androidx/preference/SeekBarPreference.java
+++ b/preference/src/main/java/androidx/preference/SeekBarPreference.java
@@ -64,13 +64,17 @@
     boolean mAdjustable;
     // Whether to show the SeekBar value TextView next to the bar
     private boolean mShowSeekBarValue;
+    // Whether the SeekBarPreference should continuously save the Seekbar value while it is being
+    // dragged.
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
+    boolean mUpdatesContinuously;
     /**
      * Listener reacting to the {@link SeekBar} changing value by the user
      */
     private OnSeekBarChangeListener mSeekBarChangeListener = new OnSeekBarChangeListener() {
         @Override
         public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
-            if (fromUser && !mTrackingTouch) {
+            if (fromUser && (mUpdatesContinuously || !mTrackingTouch)) {
                 syncValueInternal(seekBar);
             }
         }
@@ -136,6 +140,8 @@
         setSeekBarIncrement(a.getInt(R.styleable.SeekBarPreference_seekBarIncrement, 0));
         mAdjustable = a.getBoolean(R.styleable.SeekBarPreference_adjustable, true);
         mShowSeekBarValue = a.getBoolean(R.styleable.SeekBarPreference_showSeekBarValue, false);
+        mUpdatesContinuously = a.getBoolean(R.styleable.SeekBarPreference_updatesContinuously,
+                false);
         a.recycle();
     }
 
@@ -291,6 +297,56 @@
         mAdjustable = adjustable;
     }
 
+    /**
+     * Gets whether the {@link SeekBarPreference} should continuously save the {@link SeekBar} value
+     * while it is being dragged. Note that when the value is true,
+     * {@link Preference#OnPreferenceChangeListener} will be called continuously as well.
+     *
+     * {@see #setUpdatesContinuously()}
+     *
+     * @return Whether the {@link SeekBarPreference} should continuously save the {@link SeekBar}
+     * value while it is being dragged
+     */
+    public boolean getUpdatesContinuously() {
+        return mUpdatesContinuously;
+    }
+
+    /**
+     * Sets whether the {@link SeekBarPreference} should continuously save the {@link SeekBar} value
+     * while it is being dragged.
+     *
+     * {@see #getUpdatesContinuously()}
+     *
+     * @param updatesContinuously Whether the {@link SeekBarPreference} should continuously save
+     *                           the {@link SeekBar} value while it is being dragged
+     */
+    public void setUpdatesContinuously(boolean updatesContinuously) {
+        mUpdatesContinuously = updatesContinuously;
+    }
+
+    /**
+     * Gets whether the current {@link SeekBar} value is displayed to the user.
+     *
+     * {@see #setShowSeekBarValue()}
+     *
+     * @return Whether the current {@link SeekBar} value is displayed to the user
+     */
+    public boolean getShowSeekBarValue() {
+        return mShowSeekBarValue;
+    }
+
+    /**
+     * Sets whether the current {@link SeekBar} value is displayed to the user.
+     *
+     * {@see #getShowSeekBarValue()}
+     *
+     * @param showSeekBarValue Whether the current {@link SeekBar} value is displayed to the user
+     */
+    public void setShowSeekBarValue(boolean showSeekBarValue) {
+        mShowSeekBarValue = showSeekBarValue;
+        notifyChanged();
+    }
+
     private void setValueInternal(int seekBarValue, boolean notifyChanged) {
         if (seekBarValue < mMin) {
             seekBarValue = mMin;
diff --git a/room/compiler/build.gradle b/room/compiler/build.gradle
index 4d15cf6..4c74d26 100644
--- a/room/compiler/build.gradle
+++ b/room/compiler/build.gradle
@@ -51,7 +51,7 @@
     compile(JAVAPOET)
     compile(ANTLR)
     compile(XERIAL)
-    compile(KOTLIN_METADATA)
+    compile(KOTLIN_METADATA_JVM)
     compile(APACHE_COMMONS_CODEC)
     testCompile(GOOGLE_COMPILE_TESTING)
     testCompile project(":paging:paging-common")
diff --git a/room/compiler/src/main/kotlin/androidx/room/ext/KotlinMetadataElement.kt b/room/compiler/src/main/kotlin/androidx/room/ext/KotlinMetadataElement.kt
deleted file mode 100644
index 8eae79d..0000000
--- a/room/compiler/src/main/kotlin/androidx/room/ext/KotlinMetadataElement.kt
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * 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 androidx.room.ext
-
-import androidx.room.processor.Context
-import me.eugeniomarletti.kotlin.metadata.KotlinClassMetadata
-import me.eugeniomarletti.kotlin.metadata.KotlinMetadataUtils
-import me.eugeniomarletti.kotlin.metadata.isPrimary
-import me.eugeniomarletti.kotlin.metadata.isSuspend
-import me.eugeniomarletti.kotlin.metadata.jvm.getJvmConstructorSignature
-import me.eugeniomarletti.kotlin.metadata.kotlinMetadata
-import me.eugeniomarletti.kotlin.metadata.shadow.metadata.ProtoBuf
-import me.eugeniomarletti.kotlin.metadata.shadow.serialization.deserialization.getName
-import javax.annotation.processing.ProcessingEnvironment
-import javax.lang.model.element.Element
-import javax.lang.model.element.ExecutableElement
-
-/**
- * Utility class for processors that wants to run kotlin specific code.
- */
-class KotlinMetadataElement private constructor(
-    val context: Context,
-    val element: Element,
-    private val classMetadata: KotlinClassMetadata
-) : KotlinMetadataUtils {
-
-    override val processingEnv: ProcessingEnvironment
-        get() = context.processingEnv
-
-    /**
-     * Returns the parameter names of the function or constructor if all have names embedded in the
-     * metadata.
-     */
-    fun getParameterNames(method: ExecutableElement): List<String>? {
-        val valueParameterList = classMetadata.data.getFunctionOrNull(method)?.valueParameterList
-            ?: findConstructor(method)?.valueParameterList
-            ?: return null
-        return if (valueParameterList.all { it.hasName() }) {
-            valueParameterList.map {
-                classMetadata.data.nameResolver.getName(it.name)
-                    .asString()
-                    .replace("`", "")
-                    .removeSuffix("?")
-                    .trim()
-            }
-        } else {
-            null
-        }
-    }
-
-    /**
-     * Finds the kotlin metadata for a constructor.
-     */
-    private fun findConstructor(
-        executableElement: ExecutableElement
-    ): ProtoBuf.Constructor? = classMetadata?.let { metadata ->
-        val (nameResolver, classProto) = metadata.data
-        val jvmSignature = executableElement.jvmMethodSignature
-        // find constructor
-        return classProto.constructorList.singleOrNull {
-            it.getJvmConstructorSignature(nameResolver, classProto.typeTable) == jvmSignature
-        }
-    }
-
-    /**
-     * Finds the primary constructor signature of the class.
-     */
-    fun findPrimaryConstructorSignature() = classMetadata.data.let { data ->
-        data.classProto
-            .constructorList.first { it.isPrimary }
-            .getJvmConstructorSignature(
-                data.nameResolver,
-                data.classProto.typeTable
-            )
-    }
-
-    fun getMethodSignature(executableElement: ExecutableElement) =
-        executableElement.jvmMethodSignature
-
-    /**
-     * Checks if a method is a suspend function.
-     */
-    fun isSuspendFunction(method: ExecutableElement) =
-        classMetadata.data.getFunctionOrNull(method)?.isSuspend == true
-
-    companion object {
-
-        /**
-         * Creates a [KotlinMetadataElement] for the given element if it contains Kotlin metadata,
-         * otherwise this method returns null.
-         *
-         * Usually the [element] passed must represent a class. For example, if kotlin metadata is
-         * desired for a method, then the containing method should be used as parameter.
-         */
-        fun createFor(context: Context, element: Element): KotlinMetadataElement? {
-            val metadata = try {
-                element.kotlinMetadata
-            } catch (throwable: Throwable) {
-                context.logger.d(element, "failed to read get kotlin metadata from %s", element)
-            } as? KotlinClassMetadata
-            return if (metadata != null) {
-                KotlinMetadataElement(context, element, metadata)
-            } else {
-                null
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/androidx/room/ext/element_ext.kt b/room/compiler/src/main/kotlin/androidx/room/ext/element_ext.kt
index 05cbde9..f7809d5 100644
--- a/room/compiler/src/main/kotlin/androidx/room/ext/element_ext.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/ext/element_ext.kt
@@ -22,7 +22,6 @@
 import com.google.auto.common.AnnotationMirrors
 import com.google.auto.common.MoreElements
 import com.google.auto.common.MoreTypes
-import me.eugeniomarletti.kotlin.metadata.shadow.load.java.JvmAbi
 import java.lang.reflect.Proxy
 import javax.annotation.processing.ProcessingEnvironment
 import javax.lang.model.element.AnnotationMirror
@@ -342,6 +341,11 @@
 }
 
 /**
+ * Suffix of the Kotlin synthetic class created interface method implementations.
+ */
+const val DEFAULT_IMPLS_CLASS_NAME = "DefaultImpls"
+
+/**
  * Finds the default implementation method corresponding to this Kotlin interface method.
  */
 fun Element.findKotlinDefaultImpl(typeUtils: Types): Element? {
@@ -360,7 +364,7 @@
 
     val parent = this.enclosingElement as TypeElement
     val innerClass = parent.enclosedElements.find {
-        it.kind == ElementKind.CLASS && it.simpleName.contentEquals(JvmAbi.DEFAULT_IMPLS_CLASS_NAME)
+        it.kind == ElementKind.CLASS && it.simpleName.contentEquals(DEFAULT_IMPLS_CLASS_NAME)
     } ?: return null
     return innerClass.enclosedElements.find {
         it.kind == ElementKind.METHOD && it.simpleName == this.simpleName &&
diff --git a/room/compiler/src/main/kotlin/androidx/room/kotlin/JvmDescriptorUtils.kt b/room/compiler/src/main/kotlin/androidx/room/kotlin/JvmDescriptorUtils.kt
new file mode 100644
index 0000000..8e043d1
--- /dev/null
+++ b/room/compiler/src/main/kotlin/androidx/room/kotlin/JvmDescriptorUtils.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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 androidx.room.kotlin
+
+import com.google.auto.common.MoreElements
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.NestingKind
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.ArrayType
+import javax.lang.model.type.DeclaredType
+import javax.lang.model.type.ErrorType
+import javax.lang.model.type.ExecutableType
+import javax.lang.model.type.NoType
+import javax.lang.model.type.NullType
+import javax.lang.model.type.PrimitiveType
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+import javax.lang.model.type.TypeVariable
+import javax.lang.model.type.WildcardType
+import javax.lang.model.util.AbstractTypeVisitor6
+import javax.lang.model.util.Types
+
+/**
+ * Returns the method descriptor of this [ExecutableElement].
+ *
+ * For reference, see the [JVM specification, section 4.3.3](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.3)
+ */
+fun ExecutableElement.descriptor(typeUtils: Types) =
+    "$simpleName${asType().descriptor(typeUtils)}"
+
+/**
+ * Returns the name of this [TypeElement] in its "internal form".
+ *
+ * For reference, see the [JVM specification, section 4.2](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.2).
+ */
+internal val TypeElement.internalName: String
+    get() = when (nestingKind) {
+        NestingKind.TOP_LEVEL ->
+            qualifiedName.toString().replace('.', '/')
+        NestingKind.MEMBER ->
+            MoreElements.asType(enclosingElement).internalName + "$" + simpleName
+        NestingKind.LOCAL, NestingKind.ANONYMOUS ->
+            error("Unsupported nesting $nestingKind")
+    }
+
+internal val NoType.descriptor: String
+    get() = "V"
+
+internal val DeclaredType.descriptor: String
+    get() = "L" + MoreElements.asType(asElement()).internalName + ";"
+
+internal val PrimitiveType.descriptor: String
+    get() = when (this.kind) {
+        TypeKind.BYTE -> "B"
+        TypeKind.CHAR -> "C"
+        TypeKind.DOUBLE -> "D"
+        TypeKind.FLOAT -> "F"
+        TypeKind.INT -> "I"
+        TypeKind.LONG -> "J"
+        TypeKind.SHORT -> "S"
+        TypeKind.BOOLEAN -> "Z"
+        else -> error("Unknown primitive type $this")
+    }
+
+fun TypeMirror.descriptor(typeUtils: Types): String =
+    accept(JvmDescriptorTypeVisitor, typeUtils)
+
+internal fun WildcardType.descriptor(typeUtils: Types): String =
+    typeUtils.erasure(this).descriptor(typeUtils)
+
+internal fun TypeVariable.descriptor(typeUtils: Types): String =
+    typeUtils.erasure(this).descriptor(typeUtils)
+
+internal fun ArrayType.descriptor(typeUtils: Types): String =
+    "[" + componentType.descriptor(typeUtils)
+
+internal fun ExecutableType.descriptor(typeUtils: Types): String {
+    val parameterDescriptors =
+        parameterTypes.joinToString(separator = "") { it.descriptor(typeUtils) }
+    val returnDescriptor = returnType.descriptor(typeUtils)
+    return "($parameterDescriptors)$returnDescriptor"
+}
+
+/**
+ * When applied over a type, it returns either:
+ * + a "field descriptor", for example: `Ljava/lang/Object;`
+ * + a "method descriptor", for example: `(Ljava/lang/Object;)Z`
+ *
+ * The easiest way to use this is through [TypeMirror.descriptor][JvmDescriptorUtils.descriptor] in [JvmDescriptorUtils].
+ *
+ * For reference, see the [JVM specification, section 4.3](http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3).
+ */
+internal object JvmDescriptorTypeVisitor : AbstractTypeVisitor6<String, Types>() {
+    override fun visitNoType(t: NoType, typeUtils: Types): String = t.descriptor
+
+    override fun visitDeclared(t: DeclaredType, typeUtils: Types): String = t.descriptor
+
+    override fun visitPrimitive(t: PrimitiveType, typeUtils: Types): String = t.descriptor
+
+    override fun visitArray(t: ArrayType, typeUtils: Types): String = t.descriptor(typeUtils)
+
+    override fun visitWildcard(t: WildcardType, typeUtils: Types): String = t.descriptor(typeUtils)
+
+    override fun visitExecutable(t: ExecutableType, typeUtils: Types): String =
+        t.descriptor(typeUtils)
+
+    override fun visitTypeVariable(t: TypeVariable, typeUtils: Types): String =
+        t.descriptor(typeUtils)
+
+    override fun visitNull(t: NullType, typeUtils: Types): String =
+        visitUnknown(t, typeUtils)
+
+    override fun visitError(t: ErrorType, typeUtils: Types): String =
+        visitUnknown(t, typeUtils)
+
+    override fun visitUnknown(t: TypeMirror, typeUtils: Types): String =
+        error("Unsupported type $t")
+}
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/androidx/room/kotlin/KotlinClassMetadataUtils.kt b/room/compiler/src/main/kotlin/androidx/room/kotlin/KotlinClassMetadataUtils.kt
new file mode 100644
index 0000000..9a7effe
--- /dev/null
+++ b/room/compiler/src/main/kotlin/androidx/room/kotlin/KotlinClassMetadataUtils.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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 androidx.room.kotlin
+
+import kotlinx.metadata.Flag
+import kotlinx.metadata.Flags
+import kotlinx.metadata.KmClassVisitor
+import kotlinx.metadata.KmConstructorExtensionVisitor
+import kotlinx.metadata.KmConstructorVisitor
+import kotlinx.metadata.KmExtensionType
+import kotlinx.metadata.KmFunctionExtensionVisitor
+import kotlinx.metadata.KmFunctionVisitor
+import kotlinx.metadata.KmValueParameterVisitor
+import kotlinx.metadata.jvm.JvmConstructorExtensionVisitor
+import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor
+import kotlinx.metadata.jvm.JvmMethodSignature
+import kotlinx.metadata.jvm.KotlinClassMetadata
+
+/**
+ * Represents the kotlin metadata of a function
+ */
+data class KmFunction(
+    val descriptor: String,
+    private val flags: Flags,
+    val parameters: List<KmValueParameter>
+) {
+    fun isSuspend() = Flag.Function.IS_SUSPEND(flags)
+}
+
+/**
+ * Represents the kotlin metadata of a constructor
+ */
+data class KmConstructor(
+    val descriptor: String,
+    private val flags: Flags,
+    val parameters: List<KmValueParameter>
+) {
+    fun isPrimary() = Flag.Constructor.IS_PRIMARY(flags)
+}
+
+/**
+ * Represents the kotlin metadata of a parameter
+ */
+data class KmValueParameter(val name: String, private val flags: Flags)
+
+internal fun KotlinClassMetadata.Class.readFunctions(): List<KmFunction> =
+    mutableListOf<KmFunction>().apply { accept(FunctionReader(this)) }
+
+private class FunctionReader(val result: MutableList<KmFunction>) : KmClassVisitor() {
+    override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor? {
+        return object : KmFunctionVisitor() {
+
+            lateinit var descriptor: String
+            val parameters = mutableListOf<KmValueParameter>()
+
+            override fun visitValueParameter(
+                flags: Flags,
+                name: String
+            ): KmValueParameterVisitor? {
+                parameters.add(KmValueParameter(name, flags))
+                return super.visitValueParameter(flags, name)
+            }
+
+            override fun visitExtensions(type: KmExtensionType): KmFunctionExtensionVisitor? {
+                if (type != JvmFunctionExtensionVisitor.TYPE) {
+                    error("Unsupported extension type: $type")
+                }
+                return object : JvmFunctionExtensionVisitor() {
+                    override fun visit(desc: JvmMethodSignature?) {
+                        descriptor = desc!!.asString()
+                    }
+                }
+            }
+
+            override fun visitEnd() {
+                result.add(KmFunction(descriptor, flags, parameters))
+            }
+        }
+    }
+}
+
+internal fun KotlinClassMetadata.Class.readConstructors(): List<KmConstructor> =
+    mutableListOf<KmConstructor>().apply { accept(ConstructorReader(this)) }
+
+private class ConstructorReader(val result: MutableList<KmConstructor>) : KmClassVisitor() {
+    override fun visitConstructor(flags: Flags): KmConstructorVisitor? {
+        return object : KmConstructorVisitor() {
+
+            lateinit var descriptor: String
+            val parameters = mutableListOf<KmValueParameter>()
+
+            override fun visitValueParameter(
+                flags: Flags,
+                name: String
+            ): KmValueParameterVisitor? {
+                parameters.add(KmValueParameter(name, flags))
+                return super.visitValueParameter(flags, name)
+            }
+
+            override fun visitExtensions(type: KmExtensionType): KmConstructorExtensionVisitor? {
+                if (type != JvmConstructorExtensionVisitor.TYPE) {
+                    error("Unsupported extension type: $type")
+                }
+                return object : JvmConstructorExtensionVisitor() {
+                    override fun visit(desc: JvmMethodSignature?) {
+                        descriptor = desc!!.asString()
+                    }
+                }
+            }
+
+            override fun visitEnd() {
+                result.add(KmConstructor(descriptor, flags, parameters))
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/androidx/room/kotlin/KotlinMetadataElement.kt b/room/compiler/src/main/kotlin/androidx/room/kotlin/KotlinMetadataElement.kt
new file mode 100644
index 0000000..1db7d37
--- /dev/null
+++ b/room/compiler/src/main/kotlin/androidx/room/kotlin/KotlinMetadataElement.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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 androidx.room.kotlin
+
+import androidx.room.processor.Context
+import kotlinx.metadata.jvm.KotlinClassHeader
+import kotlinx.metadata.jvm.KotlinClassMetadata
+import javax.lang.model.element.Element
+import javax.lang.model.element.ExecutableElement
+
+/**
+ * Utility class for processors that wants to run kotlin specific code.
+ */
+class KotlinMetadataElement(
+    val context: Context,
+    val element: Element,
+    private val classMetadata: KotlinClassMetadata.Class
+) {
+
+    private val functionList: List<KmFunction> by lazy { classMetadata.readFunctions() }
+    private val constructorList: List<KmConstructor> by lazy { classMetadata.readConstructors() }
+
+    private val ExecutableElement.descriptor: String
+        get() = descriptor(context.processingEnv.typeUtils)
+
+    /**
+     * Returns the parameter names of the function or constructor if all have names embedded in the
+     * metadata.
+     */
+    fun getParameterNames(method: ExecutableElement): List<String>? {
+        val methodSignature = method.descriptor
+        val paramList =
+            functionList.firstOrNull { it.descriptor == methodSignature }?.parameters
+                ?: constructorList.firstOrNull { it.descriptor == methodSignature }?.parameters
+        return paramList?.map { it.name }
+    }
+
+    /**
+     * Finds the primary constructor descriptor of the class.
+     */
+    fun findPrimaryConstructorSignature() = constructorList.first { it.isPrimary() }.descriptor
+
+    /**
+     * Checks if a method is a suspend function.
+     */
+    fun isSuspendFunction(method: ExecutableElement) = functionList.firstOrNull {
+        it.descriptor == method.descriptor
+    }?.isSuspend() ?: false
+
+    companion object {
+
+        /**
+         * Creates a [KotlinMetadataElement] for the given element if it contains Kotlin metadata,
+         * otherwise this method returns null.
+         *
+         * Usually the [element] passed must represent a class. For example, if kotlin metadata is
+         * desired for a method, then the containing method should be used as parameter.
+         */
+        fun createFor(context: Context, element: Element): KotlinMetadataElement? {
+            val metadata = getMetadataAnnotation(element)?.run {
+                KotlinClassHeader(
+                    kind = kind,
+                    metadataVersion = metadataVersion,
+                    bytecodeVersion = bytecodeVersion,
+                    data1 = data1,
+                    data2 = data2,
+                    extraString = extraString,
+                    packageName = packageName,
+                    extraInt = extraInt
+                ).let {
+                    // TODO: Support more metadata kind (file facade, synthetic class, etc...)
+                    KotlinClassMetadata.read(it) as? KotlinClassMetadata.Class
+                }
+            }
+            return if (metadata != null) {
+                KotlinMetadataElement(context, element, metadata)
+            } else {
+                context.logger.d(
+                    element, "Failed to read get kotlin metadata for %s", element)
+                null
+            }
+        }
+
+        /**
+         * Search for Kotlin's Metadata annotation across the element's hierarchy.
+         */
+        private fun getMetadataAnnotation(element: Element?): Metadata? =
+            if (element != null) {
+                element.getAnnotation(Metadata::class.java)
+                    ?: getMetadataAnnotation(element.enclosingElement)
+            } else {
+                null
+            }
+    }
+}
\ No newline at end of file
diff --git a/room/compiler/src/main/kotlin/androidx/room/processor/MethodProcessorDelegate.kt b/room/compiler/src/main/kotlin/androidx/room/processor/MethodProcessorDelegate.kt
index cbadf55..2098ade 100644
--- a/room/compiler/src/main/kotlin/androidx/room/processor/MethodProcessorDelegate.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/processor/MethodProcessorDelegate.kt
@@ -16,13 +16,13 @@
 
 package androidx.room.processor
 
-import androidx.room.ext.KotlinMetadataElement
 import androidx.room.ext.KotlinTypeNames
 import androidx.room.ext.L
 import androidx.room.ext.N
 import androidx.room.ext.RoomCoroutinesTypeNames
 import androidx.room.ext.T
 import androidx.room.ext.getSuspendFunctionReturnType
+import androidx.room.kotlin.KotlinMetadataElement
 import androidx.room.parser.ParsedQuery
 import androidx.room.solver.prepared.binder.CallablePreparedQueryResultBinder.Companion.createPreparedBinder
 import androidx.room.solver.prepared.binder.PreparedQueryResultBinder
diff --git a/room/compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt b/room/compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt
index 45ebe64..3f4fea9 100644
--- a/room/compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/processor/PojoProcessor.kt
@@ -20,7 +20,6 @@
 import androidx.room.Embedded
 import androidx.room.Ignore
 import androidx.room.Relation
-import androidx.room.ext.KotlinMetadataElement
 import androidx.room.ext.extendsBoundOrSelf
 import androidx.room.ext.getAllFieldsIncludingPrivateSupers
 import androidx.room.ext.hasAnnotation
@@ -29,6 +28,8 @@
 import androidx.room.ext.isCollection
 import androidx.room.ext.toAnnotationBox
 import androidx.room.ext.typeName
+import androidx.room.kotlin.KotlinMetadataElement
+import androidx.room.kotlin.descriptor
 import androidx.room.processor.ProcessorErrors.CANNOT_FIND_GETTER_FOR_FIELD
 import androidx.room.processor.ProcessorErrors.CANNOT_FIND_SETTER_FOR_FIELD
 import androidx.room.processor.ProcessorErrors.CANNOT_FIND_TYPE
@@ -375,7 +376,7 @@
                 val primaryConstructor =
                     kotlinMetadata?.findPrimaryConstructorSignature()?.let { signature ->
                         goodConstructors.firstOrNull {
-                            kotlinMetadata.getMethodSignature(it.element) == signature
+                            it.element.descriptor(context.processingEnv.typeUtils) == signature
                     }
                 }
                 if (primaryConstructor != null) {
diff --git a/room/compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt b/room/compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
index e5bf595..8b95edb 100644
--- a/room/compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
+++ b/room/compiler/src/main/kotlin/androidx/room/writer/DaoWriter.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.writer
 
+import androidx.room.ext.DEFAULT_IMPLS_CLASS_NAME
 import androidx.room.ext.L
 import androidx.room.ext.N
 import androidx.room.ext.RoomTypeNames
@@ -40,7 +41,6 @@
 import com.squareup.javapoet.ParameterSpec
 import com.squareup.javapoet.TypeName
 import com.squareup.javapoet.TypeSpec
-import me.eugeniomarletti.kotlin.metadata.shadow.load.java.JvmAbi
 import stripNonJava
 import javax.annotation.processing.ProcessingEnvironment
 import javax.lang.model.element.ElementKind
@@ -217,7 +217,7 @@
                 TransactionMethod.CallType.DEFAULT_KOTLIN -> {
                     append("$N.$N.$N(this")
                     params.add(element.enclosingElement.simpleName)
-                    params.add(JvmAbi.DEFAULT_IMPLS_CLASS_NAME)
+                    params.add(DEFAULT_IMPLS_CLASS_NAME)
                     params.add(element.simpleName)
                 }
             }
diff --git a/room/compiler/src/test/kotlin/androidx/room/kotlin/JvmDescriptorUtilsTest.kt b/room/compiler/src/test/kotlin/androidx/room/kotlin/JvmDescriptorUtilsTest.kt
new file mode 100644
index 0000000..f5f1052
--- /dev/null
+++ b/room/compiler/src/test/kotlin/androidx/room/kotlin/JvmDescriptorUtilsTest.kt
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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 androidx.room.kotlin
+
+import androidx.room.testing.TestProcessor
+import com.google.auto.common.MoreElements
+import com.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
+import com.google.testing.compile.CompileTester
+import com.google.testing.compile.JavaSourcesSubjectFactory
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import toJFO
+import javax.tools.JavaFileObject
+
+@RunWith(JUnit4::class)
+class JvmDescriptorUtilsTest {
+
+    private val describeAnnotation =
+        """
+        package androidx.room.test;
+
+        import java.lang.annotation.ElementType;
+        import java.lang.annotation.Target;
+
+        @Target({ElementType.METHOD, ElementType.CONSTRUCTOR})
+        public @interface Describe { }
+        """.toJFO("androidx.room.test.Describe")
+
+    @Test
+    fun descriptor_method_simple() {
+        singleRun(
+            """
+            package androidx.room.test;
+
+            public class DummyClass {
+                @Describe
+                public void emptyMethod() {
+                }
+            }
+            """.toJFO("androidx.room.test.DummyClass")
+        ) { descriptors ->
+            assertThat(descriptors.first())
+                .isEqualTo("emptyMethod()V")
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun descriptor_method_primitiveParams() {
+        singleRun(
+            """
+            package androidx.room.test;
+
+            class DummyClass {
+                @Describe
+                void method1(boolean yesOrNo, int number) { }
+
+                @Describe
+                byte method2(char letter) { return 0; }
+
+                @Describe
+                void method3(double realNumber1, float realNummber2) { }
+
+                @Describe
+                void method4(long bigNumber, short littlerNumber) { }
+            }
+            """.toJFO("androidx.room.test.DummyClass")
+        ) { descriptors ->
+            assertThat(descriptors)
+                .isEqualTo(setOf("method1(ZI)V", "method2(C)B", "method3(DF)V", "method4(JS)V"))
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun descriptor_method_classParam_javaTypes() {
+        singleRun(
+            """
+            package androidx.room.test;
+
+            import java.util.ArrayList;
+            import java.util.List;
+
+            class DummyClass {
+                @Describe
+                void method1(Object something) { }
+
+                @Describe
+                Object method2() { return null; }
+
+                @Describe
+                List<String> method3(ArrayList<Integer> list) { return null; }
+            }
+            """.toJFO("androidx.room.test.DummyClass")
+        ) { descriptors ->
+            assertThat(descriptors).isEqualTo(
+                setOf(
+                    "method1(Ljava/lang/Object;)V",
+                    "method2()Ljava/lang/Object;",
+                    "method3(Ljava/util/ArrayList;)Ljava/util/List;"
+                )
+            )
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun descriptor_method_classParam_testClass() {
+        val extraJfo =
+            """
+            package androidx.room.test;
+
+            class DataClass { }
+            """.toJFO("androidx.room.test.DataClass")
+
+        singleRun(
+            """
+            package androidx.room.test;
+
+            class DummyClass {
+                @Describe
+                void method1(DataClass data) { }
+
+                @Describe
+                DataClass method2() { return null; }
+            }
+            """.toJFO("androidx.room.test.DummyClass"), extraJfo
+        ) { descriptors ->
+            assertThat(descriptors).isEqualTo(
+                setOf(
+                    "method1(Landroidx/room/test/DataClass;)V",
+                    "method2()Landroidx/room/test/DataClass;"
+                )
+            )
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun descriptor_method_classParam_innerTestClass() {
+        val extraJfo =
+            """
+            package androidx.room.test;
+
+            class DataClass {
+
+                class MemberInnerData { }
+
+                static class StaticInnerData { }
+
+                enum EnumData {
+                    VALUE1, VALUE2
+                }
+            }
+            """.toJFO("androidx.room.test.DataClass")
+
+        singleRun(
+            """
+            package androidx.room.test;
+
+            class DummyClass {
+                @Describe
+                void method1(DataClass.MemberInnerData data) { }
+
+                @Describe
+                void method2(DataClass.StaticInnerData data) { }
+
+                @Describe
+                void method3(DataClass.EnumData enumData) { }
+
+                @Describe
+                DataClass.StaticInnerData method4() { return null; }
+            }
+            """.toJFO("androidx.room.test.DummyClass"), extraJfo
+        ) { descriptors ->
+            assertThat(descriptors).isEqualTo(
+                setOf(
+                    "method1(Landroidx/room/test/DataClass\$MemberInnerData;)V",
+                    "method2(Landroidx/room/test/DataClass\$StaticInnerData;)V",
+                    "method3(Landroidx/room/test/DataClass\$EnumData;)V",
+                    "method4()Landroidx/room/test/DataClass\$StaticInnerData;"
+                )
+            )
+        }.compilesWithoutError()
+    }
+
+    @Test
+    fun descriptor_method_arrayParams() {
+        val extraJfo =
+            """
+            package androidx.room.test;
+
+            class DataClass { }
+            """.toJFO("androidx.room.test.DataClass")
+
+        singleRun(
+            """
+            package androidx.room.test;
+
+            class DummyClass {
+                @Describe
+                void method1(DataClass[] data) { }
+
+                @Describe
+                DataClass[] method2() { return null; }
+
+                @Describe
+                void method3(int[] array) { }
+
+                @Describe
+                void method4(int... array) { }
+            }
+            """.toJFO("androidx.room.test.DummyClass"), extraJfo
+        ) { descriptors ->
+            assertThat(descriptors).isEqualTo(
+                setOf(
+                    "method1([Landroidx/room/test/DataClass;)V",
+                    "method2()[Landroidx/room/test/DataClass;",
+                    "method3([I)V",
+                    "method4([I)V"
+                )
+            )
+        }.compilesWithoutError()
+    }
+
+    private fun singleRun(
+        vararg jfo: JavaFileObject,
+        handler: (Set<String>) -> Unit
+    ): CompileTester = Truth.assertAbout(JavaSourcesSubjectFactory.javaSources())
+        .that(listOf(describeAnnotation) + jfo)
+        .processedWith(TestProcessor.builder()
+            .nextRunHandler {
+                it.roundEnv.getElementsAnnotatedWith(it.annotations.first()).map { element ->
+                    MoreElements.asExecutable(element).descriptor(it.processingEnv.typeUtils)
+                }.toSet().let(handler)
+                true
+            }
+            .forAnnotations("androidx.room.test.Describe")
+            .build())
+}
\ No newline at end of file
diff --git a/room/compiler/src/test/kotlin/androidx/room/kotlin/KotlinMetadataElementTest.kt b/room/compiler/src/test/kotlin/androidx/room/kotlin/KotlinMetadataElementTest.kt
new file mode 100644
index 0000000..a10de87
--- /dev/null
+++ b/room/compiler/src/test/kotlin/androidx/room/kotlin/KotlinMetadataElementTest.kt
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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 androidx.room.kotlin
+
+import androidx.room.processor.Context
+import androidx.room.testing.TestInvocation
+import com.google.auto.common.MoreElements
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import simpleRun
+import javax.lang.model.util.ElementFilter
+
+@RunWith(JUnit4::class)
+class KotlinMetadataElementTest {
+
+    @Test
+    fun getParameterNames() {
+        simpleRun { invocation ->
+            val (testClassElement, metadataElement) = getMetadataElement(invocation)
+            assertThat(ElementFilter.methodsIn(testClassElement.enclosedElements)
+                .first { it.simpleName.toString() == "functionWithParams" }
+                .let { metadataElement.getParameterNames(MoreElements.asExecutable(it)) }
+            ).isEqualTo(
+                listOf("param1", "yesOrNo", "number")
+            )
+        }
+    }
+
+    @Test
+    fun findPrimaryConstructorSignature() {
+        simpleRun { invocation ->
+            val (testClassElement, metadataElement) = getMetadataElement(invocation)
+            assertThat(
+                ElementFilter.constructorsIn(testClassElement.enclosedElements).map {
+                    val desc = MoreElements.asExecutable(it).descriptor(invocation.typeUtils)
+                    desc to (desc == metadataElement.findPrimaryConstructorSignature())
+                }.toSet()
+            ).isEqualTo(
+                setOf(
+                    "TestData(Ljava/lang/String;)Landroidx/room/kotlin/" +
+                            "KotlinMetadataElementTest\$TestData" to true,
+                    "TestData(Landroidx/room/kotlin/KotlinMetadataElementTest\$TestData" to false
+                )
+            )
+        }
+    }
+
+    @Test
+    fun isSuspendFunction() {
+        simpleRun { invocation ->
+            val (testClassElement, metadataElement) = getMetadataElement(invocation)
+            assertThat(ElementFilter.constructorsIn(testClassElement.enclosedElements).map {
+                val executableElement = MoreElements.asExecutable(it)
+                executableElement.simpleName.toString() to metadataElement.isSuspendFunction(
+                    executableElement
+                )
+            }.toSet()).isEqualTo(
+                setOf(
+                    "emptyFunction" to false,
+                    "suspendFunction" to true,
+                    "functionWithParams" to false
+                )
+            )
+        }
+    }
+
+    private fun getMetadataElement(invocation: TestInvocation) =
+        invocation.typeElement(TestData::class.java.canonicalName).let {
+            it to KotlinMetadataElement.createFor(Context(invocation.processingEnv), it)!!
+        }
+
+    private class TestData(val constructorParam: String) {
+
+        constructor() : this("anything")
+
+        fun emptyFunction() {}
+
+        suspend fun suspendFunction() {}
+
+        fun functionWithParams(param1: String, yesOrNo: Boolean, number: Int) {}
+    }
+}
\ No newline at end of file
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BooksDao.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BooksDao.kt
index 51f4184..541834d 100644
--- a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BooksDao.kt
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/dao/BooksDao.kt
@@ -25,6 +25,7 @@
 import androidx.room.Transaction
 import androidx.room.TypeConverters
 import androidx.room.Update
+import androidx.room.integration.kotlintestapp.vo.AnswerConverter
 import androidx.room.integration.kotlintestapp.vo.Author
 import androidx.room.integration.kotlintestapp.vo.Book
 import androidx.room.integration.kotlintestapp.vo.BookAuthor
@@ -45,7 +46,7 @@
 import java.util.Date
 
 @Dao
-@TypeConverters(DateConverter::class)
+@TypeConverters(DateConverter::class, AnswerConverter::class)
 interface BooksDao {
 
     @Insert
@@ -330,4 +331,12 @@
 
     @Query("SELECT dateOfBirth FROM author WHERE authorId = :authorId")
     suspend fun getAuthorDateOfBirths(authorId: String): Date
+
+    // see: b/123767877, suspend function with inner class as parameter issues.
+    @Query("SELECT 0 FROM book WHERE bookId = :param")
+    suspend fun getZero(param: AnswerConverter.Answer): Int
+
+    // see: b/123767877, suspend function with inner class as parameter issues.
+    @Query("SELECT 'YES' FROM book")
+    suspend fun getAnswer(): AnswerConverter.Answer
 }
diff --git a/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/AnswerConverter.kt b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/AnswerConverter.kt
new file mode 100644
index 0000000..2fd788d
--- /dev/null
+++ b/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/vo/AnswerConverter.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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 androidx.room.integration.kotlintestapp.vo
+
+import androidx.room.TypeConverter
+
+class AnswerConverter {
+
+    enum class Answer {
+        YES,
+        NO
+    }
+
+    @TypeConverter
+    fun toAnswer(value: String?) = value?.let { Answer.valueOf(it) }
+
+    @TypeConverter
+    fun fromAnswer(value: Answer?) = value?.name
+}
\ No newline at end of file
diff --git a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageView.kt b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageView.kt
index 0e76607..aaea628 100644
--- a/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageView.kt
+++ b/viewpager2/src/androidTest/java/androidx/viewpager2/widget/swipe/PageView.kt
@@ -17,14 +17,15 @@
 package androidx.viewpager2.widget.swipe
 
 import android.app.Activity
+import android.graphics.Color
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.widget.TextView
 import androidx.viewpager2.test.R
 
-private const val PAGE_COLOR_EVEN = 0xFFFF0000.toInt()
-private const val PAGE_COLOR_ODD = 0xFF0000FF.toInt()
+private val PAGE_COLOR_EVEN = Color.parseColor("#FFAAAA")
+private val PAGE_COLOR_ODD = Color.parseColor("#AAAAFF")
 
 object PageView {
     fun inflatePage(parent: ViewGroup): View =
diff --git a/viewpager2/src/androidTest/res/layout/item_test_layout.xml b/viewpager2/src/androidTest/res/layout/item_test_layout.xml
index d5e021b..74a0e03 100644
--- a/viewpager2/src/androidTest/res/layout/item_test_layout.xml
+++ b/viewpager2/src/androidTest/res/layout/item_test_layout.xml
@@ -19,4 +19,5 @@
     android:id="@+id/text_view"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:textColor="@color/primary_text_default_material_light"
     android:gravity="center"/>