Import Android SDK Platform P [4344336]

/google/data/ro/projects/android/fetch_artifact \
    --bid 4344336 \
    --target sdk_phone_armv7-win_sdk \
    sdk-repo-linux-sources-4344336.zip

AndroidVersion.ApiLevel has been modified to appear as 28

Change-Id: If482fcd4cfaf6c5e544e5574926be25a293e9a6d
diff --git a/android/app/FragmentManager.java b/android/app/FragmentManager.java
index ba5ea21..0d5cd02 100644
--- a/android/app/FragmentManager.java
+++ b/android/app/FragmentManager.java
@@ -1380,8 +1380,13 @@
                                     @Override
                                     public void onAnimationEnd(Animator anim) {
                                         container.endViewTransition(view);
-                                        if (fragment.getAnimatingAway() != null) {
-                                            fragment.setAnimatingAway(null);
+                                        Animator animator = f.getAnimatingAway();
+                                        f.setAnimatingAway(null);
+                                        // If the animation finished immediately, the fragment's
+                                        // view will still be there. If so, we can just pretend
+                                        // there was no animation and skip the moveToState()
+                                        if (container.indexOfChild(view) == -1
+                                                && animator != null) {
                                             moveToState(fragment, fragment.getStateAfterAnimating(),
                                                     0, 0, false);
                                         }
diff --git a/android/app/LoadedApk.java b/android/app/LoadedApk.java
index b38be66..f6d9710 100644
--- a/android/app/LoadedApk.java
+++ b/android/app/LoadedApk.java
@@ -625,17 +625,31 @@
         final List<String> zipPaths = new ArrayList<>(10);
         final List<String> libPaths = new ArrayList<>(10);
 
-        final boolean isBundledApp = mApplicationInfo.isSystemApp()
+        boolean isBundledApp = mApplicationInfo.isSystemApp()
                 && !mApplicationInfo.isUpdatedSystemApp();
 
+        // Vendor apks are treated as bundled only when /vendor/lib is in the default search
+        // paths. If not, they are treated as unbundled; access to system libs is limited.
+        // Having /vendor/lib in the default search paths means that all system processes
+        // are allowed to use any vendor library, which in turn means that system is dependent
+        // on vendor partition. In the contrary, not having /vendor/lib in the default search
+        // paths mean that the two partitions are separated and thus we can treat vendor apks
+        // as unbundled.
+        final String defaultSearchPaths = System.getProperty("java.library.path");
+        final boolean treatVendorApkAsUnbundled = !defaultSearchPaths.contains("/vendor/lib");
+        if (mApplicationInfo.getCodePath() != null
+                && mApplicationInfo.getCodePath().startsWith("/vendor/")
+                && treatVendorApkAsUnbundled) {
+            isBundledApp = false;
+        }
+
         makePaths(mActivityThread, isBundledApp, mApplicationInfo, zipPaths, libPaths);
 
         String libraryPermittedPath = mDataDir;
         if (isBundledApp) {
             // This is necessary to grant bundled apps access to
             // libraries located in subdirectories of /system/lib
-            libraryPermittedPath += File.pathSeparator +
-                                    System.getProperty("java.library.path");
+            libraryPermittedPath += File.pathSeparator + defaultSearchPaths;
         }
 
         final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths);
diff --git a/android/app/Notification.java b/android/app/Notification.java
index 841b961..ee6c1cb 100644
--- a/android/app/Notification.java
+++ b/android/app/Notification.java
@@ -67,6 +67,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.NotificationHeaderView;
 import android.view.View;
@@ -3905,6 +3906,7 @@
             if (p.title != null) {
                 contentView.setViewVisibility(R.id.title, View.VISIBLE);
                 contentView.setTextViewText(R.id.title, processTextSpans(p.title));
+                updateTextSizePrimary(contentView, R.id.title);
                 if (!p.ambient) {
                     setTextViewColorPrimary(contentView, R.id.title);
                 }
@@ -3916,6 +3918,7 @@
                 int textId = showProgress ? com.android.internal.R.id.text_line_1
                         : com.android.internal.R.id.text;
                 contentView.setTextViewText(textId, processTextSpans(p.text));
+                updateTextSizeSecondary(contentView, textId);
                 if (!p.ambient) {
                     setTextViewColorSecondary(contentView, textId);
                 }
@@ -3927,6 +3930,25 @@
             return contentView;
         }
 
+        private void updateTextSizeSecondary(RemoteViews contentView, int textId) {
+            updateTextSizeColorized(contentView, textId,
+                    com.android.internal.R.dimen.notification_text_size_colorized,
+                    com.android.internal.R.dimen.notification_text_size);
+        }
+
+        private void updateTextSizePrimary(RemoteViews contentView, int textId) {
+            updateTextSizeColorized(contentView, textId,
+                    com.android.internal.R.dimen.notification_title_text_size_colorized,
+                    com.android.internal.R.dimen.notification_title_text_size);
+        }
+
+        private void updateTextSizeColorized(RemoteViews contentView, int textId,
+                int colorizedDimen, int normalDimen) {
+            int size = mContext.getResources().getDimensionPixelSize(isColorized()
+                    ? colorizedDimen : normalDimen);
+            contentView.setTextViewTextSize(textId, TypedValue.COMPLEX_UNIT_PX, size);
+        }
+
         private CharSequence processTextSpans(CharSequence text) {
             if (hasForegroundColor()) {
                 return NotificationColorUtil.clearColorSpans(text);
@@ -5852,6 +5874,7 @@
             builder.setTextViewColorSecondary(contentView, R.id.big_text);
             contentView.setViewVisibility(R.id.big_text,
                     TextUtils.isEmpty(bigTextText) ? View.GONE : View.VISIBLE);
+            builder.updateTextSizeSecondary(contentView, R.id.big_text);
             contentView.setBoolean(R.id.big_text, "setHasImage", builder.mN.hasLargeIcon());
         }
     }
@@ -6185,6 +6208,7 @@
                 contentView.setViewVisibility(rowId, View.VISIBLE);
                 contentView.setTextViewText(rowId, mBuilder.processTextSpans(
                         makeMessageLine(m, mBuilder)));
+                mBuilder.updateTextSizeSecondary(contentView, rowId);
                 mBuilder.setTextViewColorSecondary(contentView, rowId);
 
                 if (contractedMessage == m) {
@@ -6552,6 +6576,7 @@
                     contentView.setViewVisibility(rowIds[i], View.VISIBLE);
                     contentView.setTextViewText(rowIds[i],
                             mBuilder.processTextSpans(mBuilder.processLegacyText(str)));
+                    mBuilder.updateTextSizeSecondary(contentView, rowIds[i]);
                     mBuilder.setTextViewColorSecondary(contentView, rowIds[i]);
                     contentView.setViewPadding(rowIds[i], 0, topPadding, 0, 0);
                     handleInboxImageMargin(contentView, rowIds[i], first);
@@ -8522,8 +8547,15 @@
 
         final StandardTemplateParams fillTextsFrom(Builder b) {
             Bundle extras = b.mN.extras;
-            title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE), ambient);
-            text = b.processLegacyText(extras.getCharSequence(EXTRA_TEXT), ambient);
+            this.title = b.processLegacyText(extras.getCharSequence(EXTRA_TITLE), ambient);
+
+            // Big text notifications should contain their content when viewed in ambient mode.
+            CharSequence text = extras.getCharSequence(EXTRA_BIG_TEXT);
+            if (!ambient || TextUtils.isEmpty(text)) {
+                text = extras.getCharSequence(EXTRA_TEXT);
+            }
+            this.text = b.processLegacyText(text, ambient);
+
             return this;
         }
     }
diff --git a/android/app/RemoteInput.java b/android/app/RemoteInput.java
index 8ab19c0..02a0124 100644
--- a/android/app/RemoteInput.java
+++ b/android/app/RemoteInput.java
@@ -33,8 +33,8 @@
  * an intent inside a {@link android.app.PendingIntent} that is sent.
  * Always use {@link RemoteInput.Builder} to create instances of this class.
  * <p class="note"> See
- * <a href="{@docRoot}wear/notifications/remote-input.html">Receiving Voice Input from
- * a Notification</a> for more information on how to use this class.
+ * <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#direct">Replying
+ * to notifications</a> for more information on how to use this class.
  *
  * <p>The following example adds a {@code RemoteInput} to a {@link Notification.Action},
  * sets the result key as {@code quick_reply}, and sets the label as {@code Quick reply}.
diff --git a/android/arch/lifecycle/ComputableLiveData.java b/android/arch/lifecycle/ComputableLiveData.java
index f135244..fe18243 100644
--- a/android/arch/lifecycle/ComputableLiveData.java
+++ b/android/arch/lifecycle/ComputableLiveData.java
@@ -1,9 +1,136 @@
-//ComputableLiveData interface for tests
+/*
+ * Copyright (C) 2017 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 android.arch.lifecycle;
-import android.arch.lifecycle.LiveData;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.support.annotation.MainThread;
+import android.support.annotation.NonNull;
+import android.support.annotation.RestrictTo;
+import android.support.annotation.VisibleForTesting;
+import android.support.annotation.WorkerThread;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * A LiveData class that can be invalidated & computed on demand.
+ * <p>
+ * This is an internal class for now, might be public if we see the necessity.
+ *
+ * @param <T> The type of the live data
+ * @hide internal
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
 public abstract class ComputableLiveData<T> {
-    public ComputableLiveData(){}
-    abstract protected T compute();
-    public LiveData<T> getLiveData() {return null;}
-    public void invalidate() {}
+
+    private final LiveData<T> mLiveData;
+
+    private AtomicBoolean mInvalid = new AtomicBoolean(true);
+    private AtomicBoolean mComputing = new AtomicBoolean(false);
+
+    /**
+     * Creates a computable live data which is computed when there are active observers.
+     * <p>
+     * It can also be invalidated via {@link #invalidate()} which will result in a call to
+     * {@link #compute()} if there are active observers (or when they start observing)
+     */
+    @SuppressWarnings("WeakerAccess")
+    public ComputableLiveData() {
+        mLiveData = new LiveData<T>() {
+            @Override
+            protected void onActive() {
+                // TODO if we make this class public, we should accept an executor
+                AppToolkitTaskExecutor.getInstance().executeOnDiskIO(mRefreshRunnable);
+            }
+        };
+    }
+
+    /**
+     * Returns the LiveData managed by this class.
+     *
+     * @return A LiveData that is controlled by ComputableLiveData.
+     */
+    @SuppressWarnings("WeakerAccess")
+    @NonNull
+    public LiveData<T> getLiveData() {
+        return mLiveData;
+    }
+
+    @VisibleForTesting
+    final Runnable mRefreshRunnable = new Runnable() {
+        @WorkerThread
+        @Override
+        public void run() {
+            boolean computed;
+            do {
+                computed = false;
+                // compute can happen only in 1 thread but no reason to lock others.
+                if (mComputing.compareAndSet(false, true)) {
+                    // as long as it is invalid, keep computing.
+                    try {
+                        T value = null;
+                        while (mInvalid.compareAndSet(true, false)) {
+                            computed = true;
+                            value = compute();
+                        }
+                        if (computed) {
+                            mLiveData.postValue(value);
+                        }
+                    } finally {
+                        // release compute lock
+                        mComputing.set(false);
+                    }
+                }
+                // check invalid after releasing compute lock to avoid the following scenario.
+                // Thread A runs compute()
+                // Thread A checks invalid, it is false
+                // Main thread sets invalid to true
+                // Thread B runs, fails to acquire compute lock and skips
+                // Thread A releases compute lock
+                // We've left invalid in set state. The check below recovers.
+            } while (computed && mInvalid.get());
+        }
+    };
+
+    // invalidation check always happens on the main thread
+    @VisibleForTesting
+    final Runnable mInvalidationRunnable = new Runnable() {
+        @MainThread
+        @Override
+        public void run() {
+            boolean isActive = mLiveData.hasActiveObservers();
+            if (mInvalid.compareAndSet(false, true)) {
+                if (isActive) {
+                    // TODO if we make this class public, we should accept an executor.
+                    AppToolkitTaskExecutor.getInstance().executeOnDiskIO(mRefreshRunnable);
+                }
+            }
+        }
+    };
+
+    /**
+     * Invalidates the LiveData.
+     * <p>
+     * When there are active observers, this will trigger a call to {@link #compute()}.
+     */
+    public void invalidate() {
+        AppToolkitTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable);
+    }
+
+    @SuppressWarnings("WeakerAccess")
+    @WorkerThread
+    protected abstract T compute();
 }
diff --git a/android/arch/lifecycle/LiveData.java b/android/arch/lifecycle/LiveData.java
index 3aea6ac..99d859c 100644
--- a/android/arch/lifecycle/LiveData.java
+++ b/android/arch/lifecycle/LiveData.java
@@ -1,4 +1,411 @@
-//LiveData interface for tests
+/*
+ * Copyright (C) 2017 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 android.arch.lifecycle;
-public class LiveData<T> {
+
+import static android.arch.lifecycle.Lifecycle.State.DESTROYED;
+import static android.arch.lifecycle.Lifecycle.State.STARTED;
+
+import android.arch.core.executor.AppToolkitTaskExecutor;
+import android.arch.core.internal.SafeIterableMap;
+import android.arch.lifecycle.Lifecycle.State;
+import android.support.annotation.MainThread;
+import android.support.annotation.Nullable;
+
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * LiveData is a data holder class that can be observed within a given lifecycle.
+ * This means that an {@link Observer} can be added in a pair with a {@link LifecycleOwner}, and
+ * this observer will be notified about modifications of the wrapped data only if the paired
+ * LifecycleOwner is in active state. LifecycleOwner is considered as active, if its state is
+ * {@link Lifecycle.State#STARTED} or {@link Lifecycle.State#RESUMED}. An observer added via
+ * {@link #observeForever(Observer)} is considered as always active and thus will be always notified
+ * about modifications. For those observers, you should manually call
+ * {@link #removeObserver(Observer)}.
+ *
+ * <p> An observer added with a Lifecycle will be automatically removed if the corresponding
+ * Lifecycle moves to {@link Lifecycle.State#DESTROYED} state. This is especially useful for
+ * activities and fragments where they can safely observe LiveData and not worry about leaks:
+ * they will be instantly unsubscribed when they are destroyed.
+ *
+ * <p>
+ * In addition, LiveData has {@link LiveData#onActive()} and {@link LiveData#onInactive()} methods
+ * to get notified when number of active {@link Observer}s change between 0 and 1.
+ * This allows LiveData to release any heavy resources when it does not have any Observers that
+ * are actively observing.
+ * <p>
+ * This class is designed to hold individual data fields of {@link ViewModel},
+ * but can also be used for sharing data between different modules in your application
+ * in a decoupled fashion.
+ *
+ * @param <T> The type of data hold by this instance
+ * @see ViewModel
+ */
+@SuppressWarnings({"WeakerAccess", "unused"})
+// TODO: Thread checks are too strict right now, we may consider automatically moving them to main
+// thread.
+public abstract class LiveData<T> {
+    private final Object mDataLock = new Object();
+    static final int START_VERSION = -1;
+    private static final Object NOT_SET = new Object();
+
+    private static final LifecycleOwner ALWAYS_ON = new LifecycleOwner() {
+
+        private LifecycleRegistry mRegistry = init();
+
+        private LifecycleRegistry init() {
+            LifecycleRegistry registry = new LifecycleRegistry(this);
+            registry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
+            registry.handleLifecycleEvent(Lifecycle.Event.ON_START);
+            registry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
+            return registry;
+        }
+
+        @Override
+        public Lifecycle getLifecycle() {
+            return mRegistry;
+        }
+    };
+
+    private SafeIterableMap<Observer<T>, LifecycleBoundObserver> mObservers =
+            new SafeIterableMap<>();
+
+    // how many observers are in active state
+    private int mActiveCount = 0;
+    private volatile Object mData = NOT_SET;
+    // when setData is called, we set the pending data and actual data swap happens on the main
+    // thread
+    private volatile Object mPendingData = NOT_SET;
+    private int mVersion = START_VERSION;
+
+    private boolean mDispatchingValue;
+    @SuppressWarnings("FieldCanBeLocal")
+    private boolean mDispatchInvalidated;
+    private final Runnable mPostValueRunnable = new Runnable() {
+        @Override
+        public void run() {
+            Object newValue;
+            synchronized (mDataLock) {
+                newValue = mPendingData;
+                mPendingData = NOT_SET;
+            }
+            //noinspection unchecked
+            setValue((T) newValue);
+        }
+    };
+
+    private void considerNotify(LifecycleBoundObserver observer) {
+        if (!observer.active) {
+            return;
+        }
+        // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
+        //
+        // we still first check observer.active to keep it as the entrance for events. So even if
+        // the observer moved to an active state, if we've not received that event, we better not
+        // notify for a more predictable notification order.
+        if (!isActiveState(observer.owner.getLifecycle().getCurrentState())) {
+            return;
+        }
+        if (observer.lastVersion >= mVersion) {
+            return;
+        }
+        observer.lastVersion = mVersion;
+        //noinspection unchecked
+        observer.observer.onChanged((T) mData);
+    }
+
+    private void dispatchingValue(@Nullable LifecycleBoundObserver initiator) {
+        if (mDispatchingValue) {
+            mDispatchInvalidated = true;
+            return;
+        }
+        mDispatchingValue = true;
+        do {
+            mDispatchInvalidated = false;
+            if (initiator != null) {
+                considerNotify(initiator);
+                initiator = null;
+            } else {
+                for (Iterator<Map.Entry<Observer<T>, LifecycleBoundObserver>> iterator =
+                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
+                    considerNotify(iterator.next().getValue());
+                    if (mDispatchInvalidated) {
+                        break;
+                    }
+                }
+            }
+        } while (mDispatchInvalidated);
+        mDispatchingValue = false;
+    }
+
+    /**
+     * Adds the given observer to the observers list within the lifespan of the given
+     * owner. The events are dispatched on the main thread. If LiveData already has data
+     * set, it will be delivered to the observer.
+     * <p>
+     * The observer will only receive events if the owner is in {@link Lifecycle.State#STARTED}
+     * or {@link Lifecycle.State#RESUMED} state (active).
+     * <p>
+     * If the owner moves to the {@link Lifecycle.State#DESTROYED} state, the observer will
+     * automatically be removed.
+     * <p>
+     * When data changes while the {@code owner} is not active, it will not receive any updates.
+     * If it becomes active again, it will receive the last available data automatically.
+     * <p>
+     * LiveData keeps a strong reference to the observer and the owner as long as the
+     * given LifecycleOwner is not destroyed. When it is destroyed, LiveData removes references to
+     * the observer &amp; the owner.
+     * <p>
+     * If the given owner is already in {@link Lifecycle.State#DESTROYED} state, LiveData
+     * ignores the call.
+     * <p>
+     * If the given owner, observer tuple is already in the list, the call is ignored.
+     * If the observer is already in the list with another owner, LiveData throws an
+     * {@link IllegalArgumentException}.
+     *
+     * @param owner    The LifecycleOwner which controls the observer
+     * @param observer The observer that will receive the events
+     */
+    @MainThread
+    public void observe(LifecycleOwner owner, Observer<T> observer) {
+        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
+            // ignore
+            return;
+        }
+        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
+        LifecycleBoundObserver existing = mObservers.putIfAbsent(observer, wrapper);
+        if (existing != null && existing.owner != wrapper.owner) {
+            throw new IllegalArgumentException("Cannot add the same observer"
+                    + " with different lifecycles");
+        }
+        if (existing != null) {
+            return;
+        }
+        owner.getLifecycle().addObserver(wrapper);
+        wrapper.activeStateChanged(isActiveState(owner.getLifecycle().getCurrentState()));
+    }
+
+    /**
+     * Adds the given observer to the observers list. This call is similar to
+     * {@link LiveData#observe(LifecycleOwner, Observer)} with a LifecycleOwner, which
+     * is always active. This means that the given observer will receive all events and will never
+     * be automatically removed. You should manually call {@link #removeObserver(Observer)} to stop
+     * observing this LiveData.
+     * While LiveData has one of such observers, it will be considered
+     * as active.
+     * <p>
+     * If the observer was already added with an owner to this LiveData, LiveData throws an
+     * {@link IllegalArgumentException}.
+     *
+     * @param observer The observer that will receive the events
+     */
+    @MainThread
+    public void observeForever(Observer<T> observer) {
+        observe(ALWAYS_ON, observer);
+    }
+
+    /**
+     * Removes the given observer from the observers list.
+     *
+     * @param observer The Observer to receive events.
+     */
+    @MainThread
+    public void removeObserver(final Observer<T> observer) {
+        assertMainThread("removeObserver");
+        LifecycleBoundObserver removed = mObservers.remove(observer);
+        if (removed == null) {
+            return;
+        }
+        removed.owner.getLifecycle().removeObserver(removed);
+        removed.activeStateChanged(false);
+    }
+
+    /**
+     * Removes all observers that are tied to the given {@link LifecycleOwner}.
+     *
+     * @param owner The {@code LifecycleOwner} scope for the observers to be removed.
+     */
+    @MainThread
+    public void removeObservers(final LifecycleOwner owner) {
+        assertMainThread("removeObservers");
+        for (Map.Entry<Observer<T>, LifecycleBoundObserver> entry : mObservers) {
+            if (entry.getValue().owner == owner) {
+                removeObserver(entry.getKey());
+            }
+        }
+    }
+
+    /**
+     * Posts a task to a main thread to set the given value. So if you have a following code
+     * executed in the main thread:
+     * <pre class="prettyprint">
+     * liveData.postValue("a");
+     * liveData.setValue("b");
+     * </pre>
+     * The value "b" would be set at first and later the main thread would override it with
+     * the value "a".
+     * <p>
+     * If you called this method multiple times before a main thread executed a posted task, only
+     * the last value would be dispatched.
+     *
+     * @param value The new value
+     */
+    protected void postValue(T value) {
+        boolean postTask;
+        synchronized (mDataLock) {
+            postTask = mPendingData == NOT_SET;
+            mPendingData = value;
+        }
+        if (!postTask) {
+            return;
+        }
+        AppToolkitTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
+    }
+
+    /**
+     * Sets the value. If there are active observers, the value will be dispatched to them.
+     * <p>
+     * This method must be called from the main thread. If you need set a value from a background
+     * thread, you can use {@link #postValue(Object)}
+     *
+     * @param value The new value
+     */
+    @MainThread
+    protected void setValue(T value) {
+        assertMainThread("setValue");
+        mVersion++;
+        mData = value;
+        dispatchingValue(null);
+    }
+
+    /**
+     * Returns the current value.
+     * Note that calling this method on a background thread does not guarantee that the latest
+     * value set will be received.
+     *
+     * @return the current value
+     */
+    @Nullable
+    public T getValue() {
+        Object data = mData;
+        if (data != NOT_SET) {
+            //noinspection unchecked
+            return (T) data;
+        }
+        return null;
+    }
+
+    int getVersion() {
+        return mVersion;
+    }
+
+    /**
+     * Called when the number of active observers change to 1 from 0.
+     * <p>
+     * This callback can be used to know that this LiveData is being used thus should be kept
+     * up to date.
+     */
+    protected void onActive() {
+
+    }
+
+    /**
+     * Called when the number of active observers change from 1 to 0.
+     * <p>
+     * This does not mean that there are no observers left, there may still be observers but their
+     * lifecycle states aren't {@link Lifecycle.State#STARTED} or {@link Lifecycle.State#RESUMED}
+     * (like an Activity in the back stack).
+     * <p>
+     * You can check if there are observers via {@link #hasObservers()}.
+     */
+    protected void onInactive() {
+
+    }
+
+    /**
+     * Returns true if this LiveData has observers.
+     *
+     * @return true if this LiveData has observers
+     */
+    public boolean hasObservers() {
+        return mObservers.size() > 0;
+    }
+
+    /**
+     * Returns true if this LiveData has active observers.
+     *
+     * @return true if this LiveData has active observers
+     */
+    public boolean hasActiveObservers() {
+        return mActiveCount > 0;
+    }
+
+    class LifecycleBoundObserver implements LifecycleObserver {
+        public final LifecycleOwner owner;
+        public final Observer<T> observer;
+        public boolean active;
+        public int lastVersion = START_VERSION;
+
+        LifecycleBoundObserver(LifecycleOwner owner, Observer<T> observer) {
+            this.owner = owner;
+            this.observer = observer;
+        }
+
+        @SuppressWarnings("unused")
+        @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
+        void onStateChange() {
+            if (owner.getLifecycle().getCurrentState() == DESTROYED) {
+                removeObserver(observer);
+                return;
+            }
+            // immediately set active state, so we'd never dispatch anything to inactive
+            // owner
+            activeStateChanged(isActiveState(owner.getLifecycle().getCurrentState()));
+
+        }
+
+        void activeStateChanged(boolean newActive) {
+            if (newActive == active) {
+                return;
+            }
+            active = newActive;
+            boolean wasInactive = LiveData.this.mActiveCount == 0;
+            LiveData.this.mActiveCount += active ? 1 : -1;
+            if (wasInactive && active) {
+                onActive();
+            }
+            if (LiveData.this.mActiveCount == 0 && !active) {
+                onInactive();
+            }
+            if (active) {
+                dispatchingValue(this);
+            }
+        }
+    }
+
+    static boolean isActiveState(State state) {
+        return state.isAtLeast(STARTED);
+    }
+
+    private void assertMainThread(String methodName) {
+        if (!AppToolkitTaskExecutor.getInstance().isMainThread()) {
+            throw new IllegalStateException("Cannot invoke " + methodName + " on a background"
+                    + " thread");
+        }
+    }
 }
diff --git a/android/arch/paging/LivePagedListProvider.java b/android/arch/paging/LivePagedListProvider.java
index 821eac2..b7c68dd 100644
--- a/android/arch/paging/LivePagedListProvider.java
+++ b/android/arch/paging/LivePagedListProvider.java
@@ -16,112 +16,5 @@
 
 package android.arch.paging;
 
-import android.arch.core.executor.AppToolkitTaskExecutor;
-import android.arch.lifecycle.ComputableLiveData;
-import android.arch.lifecycle.LiveData;
-import android.support.annotation.Nullable;
-import android.support.annotation.WorkerThread;
-
-/**
- * Provides a {@code LiveData<PagedList>}, given a means to construct a DataSource.
- * <p>
- * Return type for data-loading system of an application or library to produce a
- * {@code LiveData<PagedList>}, while leaving the details of the paging mechanism up to the
- * consumer.
- *
- * @param <Key> Tyep of input valued used to load data from the DataSource. Must be integer if
- *             you're using TiledDataSource.
- * @param <Value> Data type produced by the DataSource, and held by the PagedLists.
- *
- * @see PagedListAdapter
- * @see DataSource
- * @see PagedList
- */
-public abstract class LivePagedListProvider<Key, Value> {
-
-    /**
-     * Construct a new data source to be wrapped in a new PagedList, which will be returned
-     * through the LiveData.
-     *
-     * @return The data source.
-     */
-    @WorkerThread
-    protected abstract DataSource<Key, Value> createDataSource();
-
-    /**
-     * Creates a LiveData of PagedLists, given the page size.
-     * <p>
-     * This LiveData can be passed to a {@link PagedListAdapter} to be displayed with a
-     * {@link android.support.v7.widget.RecyclerView}.
-     *
-     * @param initialLoadKey Initial key used to load initial data from the data source.
-     * @param pageSize Page size defining how many items are loaded from a data source at a time.
-     *                 Recommended to be multiple times the size of item displayed at once.
-     *
-     * @return The LiveData of PagedLists.
-     */
-    public LiveData<PagedList<Value>> create(@Nullable Key initialLoadKey, int pageSize) {
-        return create(initialLoadKey,
-                new PagedList.Config.Builder()
-                        .setPageSize(pageSize)
-                        .build());
-    }
-
-    /**
-     * Creates a LiveData of PagedLists, given the PagedList.Config.
-     * <p>
-     * This LiveData can be passed to a {@link PagedListAdapter} to be displayed with a
-     * {@link android.support.v7.widget.RecyclerView}.
-     *
-     * @param initialLoadKey Initial key to pass to the data source to initialize data with.
-     * @param config PagedList.Config to use with created PagedLists. This specifies how the
-     *               lists will load data.
-     *
-     * @return The LiveData of PagedLists.
-     */
-    public LiveData<PagedList<Value>> create(@Nullable final Key initialLoadKey,
-            final PagedList.Config config) {
-        return new ComputableLiveData<PagedList<Value>>() {
-            @Nullable
-            private PagedList<Value> mList;
-            @Nullable
-            private DataSource<Key, Value> mDataSource;
-
-            private final DataSource.InvalidatedCallback mCallback =
-                    new DataSource.InvalidatedCallback() {
-                @Override
-                public void onInvalidated() {
-                    invalidate();
-                }
-            };
-
-            @Override
-            protected PagedList<Value> compute() {
-                @Nullable Key initializeKey = initialLoadKey;
-                if (mList != null) {
-                    //noinspection unchecked
-                    initializeKey = (Key) mList.getLastKey();
-                }
-
-                do {
-                    if (mDataSource != null) {
-                        mDataSource.removeInvalidatedCallback(mCallback);
-                    }
-
-                    mDataSource = createDataSource();
-                    mDataSource.addInvalidatedCallback(mCallback);
-
-                    mList = new PagedList.Builder<Key, Value>()
-                            .setDataSource(mDataSource)
-                            .setMainThreadExecutor(AppToolkitTaskExecutor.getMainThreadExecutor())
-                            .setBackgroundThreadExecutor(
-                                    AppToolkitTaskExecutor.getIOThreadExecutor())
-                            .setConfig(config)
-                            .setInitialKey(initializeKey)
-                            .build();
-                } while (mList.isDetached());
-                return mList;
-            }
-        }.getLiveData();
-    }
-}
+abstract public class LivePagedListProvider<K, T> {
+}
\ No newline at end of file
diff --git a/android/arch/paging/PagedListAdapter.java b/android/arch/paging/PagedListAdapter.java
index 73c1b64..19a0c55 100644
--- a/android/arch/paging/PagedListAdapter.java
+++ b/android/arch/paging/PagedListAdapter.java
@@ -44,7 +44,7 @@
  * {@literal @}Dao
  * interface UserDao {
  *     {@literal @}Query("SELECT * FROM user ORDER BY lastName ASC")
- *     public abstract LivePagedListProvider&lt;User> usersByLastName();
+ *     public abstract LivePagedListProvider&lt;Integer, User> usersByLastName();
  * }
  *
  * class MyViewModel extends ViewModel {
@@ -58,7 +58,7 @@
  *     }
  * }
  *
- * class MyActivity extends Activity implements LifecycleRegistryOwner {
+ * class MyActivity extends AppCompatActivity {
  *     {@literal @}Override
  *     public void onCreate(Bundle savedState) {
  *         super.onCreate(savedState);
diff --git a/android/arch/paging/PagedListAdapterHelper.java b/android/arch/paging/PagedListAdapterHelper.java
index 7d7a922..4bff5fc 100644
--- a/android/arch/paging/PagedListAdapterHelper.java
+++ b/android/arch/paging/PagedListAdapterHelper.java
@@ -50,7 +50,7 @@
  * {@literal @}Dao
  * interface UserDao {
  *     {@literal @}Query("SELECT * FROM user ORDER BY lastName ASC")
- *     public abstract LivePagedListProvider&lt;User> usersByLastName();
+ *     public abstract LivePagedListProvider&lt;Integer, User> usersByLastName();
  * }
  *
  * class MyViewModel extends ViewModel {
@@ -64,7 +64,7 @@
  *     }
  * }
  *
- * class MyActivity extends Activity implements LifecycleRegistryOwner {
+ * class MyActivity extends AppCompatActivity {
  *     {@literal @}Override
  *     public void onCreate(Bundle savedState) {
  *         super.onCreate(savedState);
diff --git a/android/arch/paging/integration/testapp/PagedListSampleActivity.java b/android/arch/paging/integration/testapp/PagedListSampleActivity.java
index f002218..5d0117d 100644
--- a/android/arch/paging/integration/testapp/PagedListSampleActivity.java
+++ b/android/arch/paging/integration/testapp/PagedListSampleActivity.java
@@ -17,7 +17,6 @@
 package android.arch.paging.integration.testapp;
 
 import android.arch.lifecycle.LifecycleRegistry;
-import android.arch.lifecycle.LifecycleRegistryOwner;
 import android.arch.lifecycle.Observer;
 import android.arch.lifecycle.ViewModelProviders;
 import android.arch.paging.PagedList;
@@ -31,7 +30,7 @@
 /**
  * Sample NullPaddedList activity with artificial data source.
  */
-public class PagedListSampleActivity extends AppCompatActivity implements LifecycleRegistryOwner {
+public class PagedListSampleActivity extends AppCompatActivity {
 
     @Override
     protected void onCreate(final Bundle savedInstanceState) {
diff --git a/android/arch/persistence/db/SimpleSQLiteQuery.java b/android/arch/persistence/db/SimpleSQLiteQuery.java
index b821176..e2a3829 100644
--- a/android/arch/persistence/db/SimpleSQLiteQuery.java
+++ b/android/arch/persistence/db/SimpleSQLiteQuery.java
@@ -20,7 +20,7 @@
  * A basic implemtation of {@link SupportSQLiteQuery} which receives a query and its args and binds
  * args based on the passed in Object type.
  */
-public class SimpleSQLiteQuery implements SupportSQLiteQuery {
+public final class SimpleSQLiteQuery implements SupportSQLiteQuery {
     private final String mQuery;
     private final Object[] mBindArgs;
 
diff --git a/android/arch/persistence/db/SupportSQLiteQueryBuilder.java b/android/arch/persistence/db/SupportSQLiteQueryBuilder.java
index 183fb0a..52957a3 100644
--- a/android/arch/persistence/db/SupportSQLiteQueryBuilder.java
+++ b/android/arch/persistence/db/SupportSQLiteQueryBuilder.java
@@ -22,7 +22,7 @@
  * A simple query builder to create SQL SELECT queries.
  */
 @SuppressWarnings("unused")
-public class SupportSQLiteQueryBuilder {
+public final class SupportSQLiteQueryBuilder {
     private static final Pattern sLimitPattern =
             Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
 
diff --git a/android/arch/persistence/db/framework/FrameworkSQLiteOpenHelperFactory.java b/android/arch/persistence/db/framework/FrameworkSQLiteOpenHelperFactory.java
index 7b4245b..2268f45 100644
--- a/android/arch/persistence/db/framework/FrameworkSQLiteOpenHelperFactory.java
+++ b/android/arch/persistence/db/framework/FrameworkSQLiteOpenHelperFactory.java
@@ -23,7 +23,7 @@
  * framework.
  */
 @SuppressWarnings("unused")
-public class FrameworkSQLiteOpenHelperFactory implements SupportSQLiteOpenHelper.Factory {
+public final class FrameworkSQLiteOpenHelperFactory implements SupportSQLiteOpenHelper.Factory {
     @Override
     public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {
         return new FrameworkSQLiteOpenHelper(
diff --git a/android/arch/persistence/room/ColumnInfo.java b/android/arch/persistence/room/ColumnInfo.java
index 84f5844..65da379 100644
--- a/android/arch/persistence/room/ColumnInfo.java
+++ b/android/arch/persistence/room/ColumnInfo.java
@@ -33,6 +33,7 @@
 public @interface ColumnInfo {
     /**
      * Name of the column in the database. Defaults to the field name if not set.
+     *
      * @return Name of the column in the database.
      */
     String name() default INHERIT_FIELD_NAME;
@@ -40,13 +41,14 @@
     /**
      * The type affinity for the column, which will be used when constructing the database.
      * <p>
-     * If it is not specified, Room resolves it based on the field's type and available
-     * TypeConverters.
+     * If it is not specified, the value defaults to {@link #UNDEFINED} and Room resolves it based
+     * on the field's type and available TypeConverters.
      * <p>
      * See <a href="https://www.sqlite.org/datatype3.html">SQLite types documentation</a> for
      * details.
      *
-     * @return The type affinity of the column.
+     * @return The type affinity of the column. This is either {@link #UNDEFINED}, {@link #TEXT},
+     * {@link #INTEGER}, {@link #REAL}, or {@link #BLOB}.
      */
     @SuppressWarnings("unused") @SQLiteTypeAffinity int typeAffinity() default UNDEFINED;
 
@@ -60,6 +62,17 @@
     boolean index() default false;
 
     /**
+     * The collation sequence for the column, which will be used when constructing the database.
+     * <p>
+     * The default value is {@link #UNSPECIFIED}. In that case, Room does not add any
+     * collation sequence to the column, and SQLite treats it like {@link #BINARY}.
+     *
+     * @return The collation sequence of the column. This is either {@link #UNSPECIFIED},
+     * {@link #BINARY}, {@link #NOCASE}, or {@link #RTRIM}.
+     */
+    @Collate int collate() default UNSPECIFIED;
+
+    /**
      * Constant to let Room inherit the field name as the column name. If used, Room will use the
      * field name as the column name.
      */
@@ -67,22 +80,32 @@
 
     /**
      * Undefined type affinity. Will be resolved based on the type.
+     *
+     * @see #typeAffinity()
      */
     int UNDEFINED = 1;
     /**
      * Column affinity constant for strings.
+     *
+     * @see #typeAffinity()
      */
     int TEXT = 2;
     /**
      * Column affinity constant for integers or booleans.
+     *
+     * @see #typeAffinity()
      */
     int INTEGER = 3;
     /**
      * Column affinity constant for floats or doubles.
+     *
+     * @see #typeAffinity()
      */
     int REAL = 4;
     /**
      * Column affinity constant for binary data.
+     *
+     * @see #typeAffinity()
      */
     int BLOB = 5;
 
@@ -92,4 +115,34 @@
     @IntDef({UNDEFINED, TEXT, INTEGER, REAL, BLOB})
     @interface SQLiteTypeAffinity {
     }
+
+    /**
+     * Collation sequence is not specified. The match will behave like {@link #BINARY}.
+     *
+     * @see #collate()
+     */
+    int UNSPECIFIED = 1;
+    /**
+     * Collation sequence for case-sensitive match.
+     *
+     * @see #collate()
+     */
+    int BINARY = 2;
+    /**
+     * Collation sequence for case-insensitive match.
+     *
+     * @see #collate()
+     */
+    int NOCASE = 3;
+    /**
+     * Collation sequence for case-sensitive match except that trailing space characters are
+     * ignored.
+     *
+     * @see #collate()
+     */
+    int RTRIM = 4;
+
+    @IntDef({UNSPECIFIED, BINARY, NOCASE, RTRIM})
+    @interface Collate {
+    }
 }
diff --git a/android/arch/persistence/room/Relation.java b/android/arch/persistence/room/Relation.java
index 0d2f152..7206699 100644
--- a/android/arch/persistence/room/Relation.java
+++ b/android/arch/persistence/room/Relation.java
@@ -41,7 +41,7 @@
  *
  * {@literal @}Dao
  * public interface UserPetDao {
- *     {@literal @}Query("SELECT id, name from User WHERE age &gte; ?")
+ *     {@literal @}Query("SELECT id, name from User WHERE age &gt; :minAge")
  *     public List&lt;UserNameAndAllPets&gt; loadUserAndPets(int minAge);
  * }
  * </pre>
@@ -67,7 +67,7 @@
  * }
  * {@literal @}Dao
  * public interface UserPetDao {
- *     {@literal @}Query("SELECT * from User WHERE age &gte; ?")
+ *     {@literal @}Query("SELECT * from User WHERE age &gt; :minAge")
  *     public List&lt;UserAllPets&gt; loadUserAndPets(int minAge);
  * }
  * </pre>
diff --git a/android/arch/persistence/room/integration/testapp/RoomPagedListActivity.java b/android/arch/persistence/room/integration/testapp/RoomPagedListActivity.java
index 5650829..818c46b 100644
--- a/android/arch/persistence/room/integration/testapp/RoomPagedListActivity.java
+++ b/android/arch/persistence/room/integration/testapp/RoomPagedListActivity.java
@@ -17,7 +17,6 @@
 package android.arch.persistence.room.integration.testapp;
 
 import android.arch.lifecycle.LifecycleRegistry;
-import android.arch.lifecycle.LifecycleRegistryOwner;
 import android.arch.lifecycle.LiveData;
 import android.arch.lifecycle.Observer;
 import android.arch.lifecycle.ViewModelProviders;
@@ -35,7 +34,7 @@
 /**
  * Sample PagedList activity which uses Room.
  */
-public class RoomPagedListActivity extends AppCompatActivity implements LifecycleRegistryOwner {
+public class RoomPagedListActivity extends AppCompatActivity {
 
     private RecyclerView mRecyclerView;
     private PagedListCustomerAdapter mAdapter;
diff --git a/android/arch/persistence/room/integration/testapp/dao/UserDao.java b/android/arch/persistence/room/integration/testapp/dao/UserDao.java
index 428d87c..337c233 100644
--- a/android/arch/persistence/room/integration/testapp/dao/UserDao.java
+++ b/android/arch/persistence/room/integration/testapp/dao/UserDao.java
@@ -59,6 +59,9 @@
     @Query("select * from user where mId IN(:ids)")
     public abstract User[] loadByIds(int... ids);
 
+    @Query("select * from user where custommm = :customField")
+    public abstract List<User> findByCustomField(String customField);
+
     @Insert
     public abstract void insert(User user);
 
diff --git a/android/arch/persistence/room/integration/testapp/migration/MigrationDb.java b/android/arch/persistence/room/integration/testapp/migration/MigrationDb.java
index 4a95ad8..ef207cf 100644
--- a/android/arch/persistence/room/integration/testapp/migration/MigrationDb.java
+++ b/android/arch/persistence/room/integration/testapp/migration/MigrationDb.java
@@ -17,6 +17,7 @@
 package android.arch.persistence.room.integration.testapp.migration;
 
 import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.room.ColumnInfo;
 import android.arch.persistence.room.Dao;
 import android.arch.persistence.room.Database;
 import android.arch.persistence.room.Entity;
@@ -75,6 +76,7 @@
         public static final String TABLE_NAME = "Entity4";
         @PrimaryKey
         public int id;
+        @ColumnInfo(collate = ColumnInfo.NOCASE)
         public String name;
     }
 
diff --git a/android/arch/persistence/room/integration/testapp/migration/MigrationTest.java b/android/arch/persistence/room/integration/testapp/migration/MigrationTest.java
index aa297ed..725d53f 100644
--- a/android/arch/persistence/room/integration/testapp/migration/MigrationTest.java
+++ b/android/arch/persistence/room/integration/testapp/migration/MigrationTest.java
@@ -315,7 +315,7 @@
         @Override
         public void migrate(SupportSQLiteDatabase database) {
             database.execSQL("CREATE TABLE IF NOT EXISTS " + MigrationDb.Entity4.TABLE_NAME
-                    + " (`id` INTEGER NOT NULL, `name` TEXT, PRIMARY KEY(`id`),"
+                    + " (`id` INTEGER NOT NULL, `name` TEXT COLLATE NOCASE, PRIMARY KEY(`id`),"
                     + " FOREIGN KEY(`name`) REFERENCES `Entity1`(`name`)"
                     + " ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED)");
         }
diff --git a/android/arch/persistence/room/integration/testapp/test/SimpleEntityReadWriteTest.java b/android/arch/persistence/room/integration/testapp/test/SimpleEntityReadWriteTest.java
index 2b4a0e9..8861adb 100644
--- a/android/arch/persistence/room/integration/testapp/test/SimpleEntityReadWriteTest.java
+++ b/android/arch/persistence/room/integration/testapp/test/SimpleEntityReadWriteTest.java
@@ -265,6 +265,16 @@
     }
 
     @Test
+    public void findByCollateNoCase() {
+        User user = TestUtil.createUser(3);
+        user.setCustomField("abc");
+        mUserDao.insert(user);
+        List<User> users = mUserDao.findByCustomField("ABC");
+        assertThat(users, hasSize(1));
+        assertThat(users.get(0).getId(), is(3));
+    }
+
+    @Test
     public void deleteByAge() {
         User user1 = TestUtil.createUser(3);
         user1.setAge(30);
diff --git a/android/arch/persistence/room/integration/testapp/vo/User.java b/android/arch/persistence/room/integration/testapp/vo/User.java
index 57cf585..a5b8839 100644
--- a/android/arch/persistence/room/integration/testapp/vo/User.java
+++ b/android/arch/persistence/room/integration/testapp/vo/User.java
@@ -43,7 +43,7 @@
 
     private Date mBirthday;
 
-    @ColumnInfo(name = "custommm")
+    @ColumnInfo(name = "custommm", collate = ColumnInfo.NOCASE)
     private String mCustomField;
 
     public int getId() {
diff --git a/android/bluetooth/BluetoothGatt.java b/android/bluetooth/BluetoothGatt.java
index 759d772..a2af342 100644
--- a/android/bluetooth/BluetoothGatt.java
+++ b/android/bluetooth/BluetoothGatt.java
@@ -42,7 +42,7 @@
     private static final boolean VDBG = false;
 
     private IBluetoothGatt mService;
-    private BluetoothGattCallback mCallback;
+    private volatile BluetoothGattCallback mCallback;
     private Handler mHandler;
     private int mClientIf;
     private BluetoothDevice mDevice;
@@ -164,8 +164,9 @@
                         runOrQueueCallback(new Runnable() {
                             @Override
                             public void run() {
-                                if (mCallback != null) {
-                                    mCallback.onConnectionStateChange(BluetoothGatt.this,
+                                final BluetoothGattCallback callback = mCallback;
+                                if (callback != null) {
+                                    callback.onConnectionStateChange(BluetoothGatt.this,
                                             GATT_FAILURE,
                                             BluetoothProfile.STATE_DISCONNECTED);
                                 }
@@ -203,8 +204,9 @@
                     runOrQueueCallback(new Runnable() {
                         @Override
                         public void run() {
-                            if (mCallback != null) {
-                                mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status);
+                            final BluetoothGattCallback callback = mCallback;
+                            if (callback != null) {
+                                callback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status);
                             }
                         }
                     });
@@ -227,8 +229,9 @@
                     runOrQueueCallback(new Runnable() {
                         @Override
                         public void run() {
-                            if (mCallback != null) {
-                                mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status);
+                            final BluetoothGattCallback callback = mCallback;
+                            if (callback != null) {
+                                callback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status);
                             }
                         }
                     });
@@ -254,8 +257,9 @@
                     runOrQueueCallback(new Runnable() {
                         @Override
                         public void run() {
-                            if (mCallback != null) {
-                                mCallback.onConnectionStateChange(BluetoothGatt.this, status,
+                            final BluetoothGattCallback callback = mCallback;
+                            if (callback != null) {
+                                callback.onConnectionStateChange(BluetoothGatt.this, status,
                                         profileState);
                             }
                         }
@@ -307,8 +311,7 @@
 
                         for (BluetoothGattService brokenRef : includedServices) {
                             BluetoothGattService includedService = getService(mDevice,
-                                    brokenRef.getUuid(), brokenRef.getInstanceId(),
-                                    brokenRef.getType());
+                                    brokenRef.getUuid(), brokenRef.getInstanceId());
                             if (includedService != null) {
                                 fixedService.addIncludedService(includedService);
                             } else {
@@ -320,8 +323,9 @@
                     runOrQueueCallback(new Runnable() {
                         @Override
                         public void run() {
-                            if (mCallback != null) {
-                                mCallback.onServicesDiscovered(BluetoothGatt.this, status);
+                            final BluetoothGattCallback callback = mCallback;
+                            if (callback != null) {
+                                callback.onServicesDiscovered(BluetoothGatt.this, status);
                             }
                         }
                     });
@@ -371,13 +375,13 @@
                         return;
                     }
 
-                    if (status == 0) characteristic.setValue(value);
-
                     runOrQueueCallback(new Runnable() {
                         @Override
                         public void run() {
-                            if (mCallback != null) {
-                                mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic,
+                            final BluetoothGattCallback callback = mCallback;
+                            if (callback != null) {
+                                if (status == 0) characteristic.setValue(value);
+                                callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
                                         status);
                             }
                         }
@@ -429,8 +433,9 @@
                     runOrQueueCallback(new Runnable() {
                         @Override
                         public void run() {
-                            if (mCallback != null) {
-                                mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic,
+                            final BluetoothGattCallback callback = mCallback;
+                            if (callback != null) {
+                                callback.onCharacteristicWrite(BluetoothGatt.this, characteristic,
                                         status);
                             }
                         }
@@ -454,13 +459,13 @@
                             handle);
                     if (characteristic == null) return;
 
-                    characteristic.setValue(value);
-
                     runOrQueueCallback(new Runnable() {
                         @Override
                         public void run() {
-                            if (mCallback != null) {
-                                mCallback.onCharacteristicChanged(BluetoothGatt.this,
+                            final BluetoothGattCallback callback = mCallback;
+                            if (callback != null) {
+                                characteristic.setValue(value);
+                                callback.onCharacteristicChanged(BluetoothGatt.this,
                                         characteristic);
                             }
                         }
@@ -489,7 +494,6 @@
                     BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
                     if (descriptor == null) return;
 
-                    if (status == 0) descriptor.setValue(value);
 
                     if ((status == GATT_INSUFFICIENT_AUTHENTICATION
                             || status == GATT_INSUFFICIENT_ENCRYPTION)
@@ -510,8 +514,10 @@
                     runOrQueueCallback(new Runnable() {
                         @Override
                         public void run() {
-                            if (mCallback != null) {
-                                mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
+                            final BluetoothGattCallback callback = mCallback;
+                            if (callback != null) {
+                                if (status == 0) descriptor.setValue(value);
+                                callback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
                             }
                         }
                     });
@@ -559,8 +565,9 @@
                     runOrQueueCallback(new Runnable() {
                         @Override
                         public void run() {
-                            if (mCallback != null) {
-                                mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
+                            final BluetoothGattCallback callback = mCallback;
+                            if (callback != null) {
+                                callback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
                             }
                         }
                     });
@@ -587,8 +594,9 @@
                     runOrQueueCallback(new Runnable() {
                         @Override
                         public void run() {
-                            if (mCallback != null) {
-                                mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
+                            final BluetoothGattCallback callback = mCallback;
+                            if (callback != null) {
+                                callback.onReliableWriteCompleted(BluetoothGatt.this, status);
                             }
                         }
                     });
@@ -610,8 +618,9 @@
                     runOrQueueCallback(new Runnable() {
                         @Override
                         public void run() {
-                            if (mCallback != null) {
-                                mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
+                            final BluetoothGattCallback callback = mCallback;
+                            if (callback != null) {
+                                callback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
                             }
                         }
                     });
@@ -634,8 +643,9 @@
                     runOrQueueCallback(new Runnable() {
                         @Override
                         public void run() {
-                            if (mCallback != null) {
-                                mCallback.onMtuChanged(BluetoothGatt.this, mtu, status);
+                            final BluetoothGattCallback callback = mCallback;
+                            if (callback != null) {
+                                callback.onMtuChanged(BluetoothGatt.this, mtu, status);
                             }
                         }
                     });
@@ -660,8 +670,9 @@
                     runOrQueueCallback(new Runnable() {
                         @Override
                         public void run() {
-                            if (mCallback != null) {
-                                mCallback.onConnectionUpdated(BluetoothGatt.this, interval, latency,
+                            final BluetoothGattCallback callback = mCallback;
+                            if (callback != null) {
+                                callback.onConnectionUpdated(BluetoothGatt.this, interval, latency,
                                         timeout, status);
                             }
                         }
@@ -702,10 +713,9 @@
      * @hide
      */
     /*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
-            int instanceId, int type) {
+            int instanceId) {
         for (BluetoothGattService svc : mServices) {
             if (svc.getDevice().equals(device)
-                    && svc.getType() == type
                     && svc.getInstanceId() == instanceId
                     && svc.getUuid().equals(uuid)) {
                 return svc;
@@ -901,7 +911,7 @@
 
     /**
      * Set the preferred connection PHY for this app. Please note that this is just a
-     * recommendation, whether the PHY change will happen depends on other applications peferences,
+     * recommendation, whether the PHY change will happen depends on other applications preferences,
      * local and remote controller capabilities. Controller can override these settings.
      * <p>
      * {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even
diff --git a/android/bluetooth/BluetoothGattServerCallback.java b/android/bluetooth/BluetoothGattServerCallback.java
index 22eba35..2c8114b 100644
--- a/android/bluetooth/BluetoothGattServerCallback.java
+++ b/android/bluetooth/BluetoothGattServerCallback.java
@@ -184,7 +184,7 @@
     /**
      * Callback indicating the connection parameters were updated.
      *
-     * @param gatt The remote device involved
+     * @param device The remote device involved
      * @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from
      * 6 (7.5ms) to 3200 (4000ms).
      * @param latency Slave latency for the connection in number of connection events. Valid range
@@ -195,7 +195,7 @@
      * successfully
      * @hide
      */
-    public void onConnectionUpdated(BluetoothDevice gatt, int interval, int latency, int timeout,
+    public void onConnectionUpdated(BluetoothDevice device, int interval, int latency, int timeout,
             int status) {
     }
 
diff --git a/android/content/pm/ApplicationInfo.java b/android/content/pm/ApplicationInfo.java
index 664bcbc..d73f852 100644
--- a/android/content/pm/ApplicationInfo.java
+++ b/android/content/pm/ApplicationInfo.java
@@ -586,24 +586,32 @@
      */
     public static final int PRIVATE_FLAG_VIRTUAL_PRELOAD = 1 << 16;
 
+    /**
+     * Value for {@linl #privateFlags}: whether this app is pre-installed on the
+     * OEM partition of the system image.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_OEM = 1 << 17;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
-            PRIVATE_FLAG_HIDDEN,
+            PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
+            PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION,
+            PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE,
+            PRIVATE_FLAG_BACKUP_IN_FOREGROUND,
             PRIVATE_FLAG_CANT_SAVE_STATE,
-            PRIVATE_FLAG_FORWARD_LOCK,
-            PRIVATE_FLAG_PRIVILEGED,
-            PRIVATE_FLAG_HAS_DOMAIN_URLS,
             PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE,
             PRIVATE_FLAG_DIRECT_BOOT_AWARE,
+            PRIVATE_FLAG_FORWARD_LOCK,
+            PRIVATE_FLAG_HAS_DOMAIN_URLS,
+            PRIVATE_FLAG_HIDDEN,
             PRIVATE_FLAG_INSTANT,
-            PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE,
-            PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER,
-            PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
-            PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE,
-            PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION,
-            PRIVATE_FLAG_BACKUP_IN_FOREGROUND,
-            PRIVATE_FLAG_STATIC_SHARED_LIBRARY,
             PRIVATE_FLAG_ISOLATED_SPLIT_LOADING,
+            PRIVATE_FLAG_OEM,
+            PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE,
+            PRIVATE_FLAG_PRIVILEGED,
+            PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER,
+            PRIVATE_FLAG_STATIC_SHARED_LIBRARY,
             PRIVATE_FLAG_VIRTUAL_PRELOAD,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -1557,6 +1565,13 @@
     /**
      * @hide
      */
+    public boolean isOem() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
+    }
+
+    /**
+     * @hide
+     */
     @Override protected ApplicationInfo getApplicationInfo() {
         return this;
     }
diff --git a/android/content/pm/PackageParser.java b/android/content/pm/PackageParser.java
index 8b54b0d..6c7c8a0 100644
--- a/android/content/pm/PackageParser.java
+++ b/android/content/pm/PackageParser.java
@@ -96,8 +96,8 @@
 import com.android.internal.util.XmlUtils;
 
 import libcore.io.IoUtils;
-
 import libcore.util.EmptyArray;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -815,21 +815,22 @@
         }
     }
 
-    public final static int PARSE_IS_SYSTEM = 1<<0;
-    public final static int PARSE_CHATTY = 1<<1;
-    public final static int PARSE_MUST_BE_APK = 1<<2;
-    public final static int PARSE_IGNORE_PROCESSES = 1<<3;
-    public final static int PARSE_FORWARD_LOCK = 1<<4;
-    public final static int PARSE_EXTERNAL_STORAGE = 1<<5;
-    public final static int PARSE_IS_SYSTEM_DIR = 1<<6;
-    public final static int PARSE_IS_PRIVILEGED = 1<<7;
-    public final static int PARSE_COLLECT_CERTIFICATES = 1<<8;
-    public final static int PARSE_TRUSTED_OVERLAY = 1<<9;
-    public final static int PARSE_ENFORCE_CODE = 1<<10;
+    public static final int PARSE_IS_SYSTEM = 1 << 0;
+    public static final int PARSE_CHATTY = 1 << 1;
+    public static final int PARSE_MUST_BE_APK = 1 << 2;
+    public static final int PARSE_IGNORE_PROCESSES = 1 << 3;
+    public static final int PARSE_FORWARD_LOCK = 1 << 4;
+    public static final int PARSE_EXTERNAL_STORAGE = 1 << 5;
+    public static final int PARSE_IS_SYSTEM_DIR = 1 << 6;
+    public static final int PARSE_IS_PRIVILEGED = 1 << 7;
+    public static final int PARSE_COLLECT_CERTIFICATES = 1 << 8;
+    public static final int PARSE_TRUSTED_OVERLAY = 1 << 9;
+    public static final int PARSE_ENFORCE_CODE = 1 << 10;
     /** @deprecated remove when fixing b/34761192 */
     @Deprecated
-    public final static int PARSE_IS_EPHEMERAL = 1<<11;
-    public final static int PARSE_FORCE_SDK = 1<<12;
+    public static final int PARSE_IS_EPHEMERAL = 1 << 11;
+    public static final int PARSE_FORCE_SDK = 1 << 12;
+    public static final int PARSE_IS_OEM = 1 << 13;
 
     private static final Comparator<String> sSplitNameComparator = new SplitNameComparator();
 
diff --git a/android/content/pm/PermissionInfo.java b/android/content/pm/PermissionInfo.java
index b84c1b9..17b4f87 100644
--- a/android/content/pm/PermissionInfo.java
+++ b/android/content/pm/PermissionInfo.java
@@ -17,7 +17,6 @@
 package android.content.pm;
 
 import android.annotation.SystemApi;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -135,6 +134,16 @@
     public static final int PROTECTION_FLAG_RUNTIME_ONLY = 0x2000;
 
     /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>oem</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int PROTECTION_FLAG_OEM = 0x4000;
+
+    /**
      * Mask for {@link #protectionLevel}: the basic protection type.
      */
     public static final int PROTECTION_MASK_BASE = 0xf;
@@ -222,7 +231,7 @@
     /** @hide */
     public static String protectionToString(int level) {
         String protLevel = "????";
-        switch (level&PROTECTION_MASK_BASE) {
+        switch (level & PROTECTION_MASK_BASE) {
             case PermissionInfo.PROTECTION_DANGEROUS:
                 protLevel = "dangerous";
                 break;
@@ -236,36 +245,39 @@
                 protLevel = "signatureOrSystem";
                 break;
         }
-        if ((level&PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0) {
+        if ((level & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0) {
             protLevel += "|privileged";
         }
-        if ((level&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
+        if ((level & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
             protLevel += "|development";
         }
-        if ((level&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
+        if ((level & PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
             protLevel += "|appop";
         }
-        if ((level&PermissionInfo.PROTECTION_FLAG_PRE23) != 0) {
+        if ((level & PermissionInfo.PROTECTION_FLAG_PRE23) != 0) {
             protLevel += "|pre23";
         }
-        if ((level&PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0) {
+        if ((level & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0) {
             protLevel += "|installer";
         }
-        if ((level&PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0) {
+        if ((level & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0) {
             protLevel += "|verifier";
         }
-        if ((level&PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0) {
+        if ((level & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0) {
             protLevel += "|preinstalled";
         }
-        if ((level&PermissionInfo.PROTECTION_FLAG_SETUP) != 0) {
+        if ((level & PermissionInfo.PROTECTION_FLAG_SETUP) != 0) {
             protLevel += "|setup";
         }
-        if ((level&PermissionInfo.PROTECTION_FLAG_INSTANT) != 0) {
+        if ((level & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0) {
             protLevel += "|instant";
         }
-        if ((level&PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0) {
+        if ((level & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0) {
             protLevel += "|runtime";
         }
+        if ((level & PermissionInfo.PROTECTION_FLAG_OEM) != 0) {
+            protLevel += "|oem";
+        }
         return protLevel;
     }
 
diff --git a/android/graphics/BitmapFactory.java b/android/graphics/BitmapFactory.java
index 3b272c8..ffb39e3 100644
--- a/android/graphics/BitmapFactory.java
+++ b/android/graphics/BitmapFactory.java
@@ -433,10 +433,15 @@
         static void validate(Options opts) {
             if (opts == null) return;
 
-            if (opts.inMutable && opts.inPreferredConfig == Bitmap.Config.HARDWARE) {
+            if (opts.inBitmap != null && opts.inBitmap.getConfig() == Bitmap.Config.HARDWARE) {
                 throw new IllegalArgumentException("Bitmaps with Config.HARWARE are always immutable");
             }
 
+            if (opts.inMutable && opts.inPreferredConfig == Bitmap.Config.HARDWARE) {
+                throw new IllegalArgumentException("Bitmaps with Config.HARDWARE cannot be " +
+                        "decoded into - they are immutable");
+            }
+
             if (opts.inPreferredColorSpace != null) {
                 if (!(opts.inPreferredColorSpace instanceof ColorSpace.Rgb)) {
                     throw new IllegalArgumentException("The destination color space must use the " +
diff --git a/android/graphics/drawable/VectorDrawable.java b/android/graphics/drawable/VectorDrawable.java
index c3ef450..ceac325 100644
--- a/android/graphics/drawable/VectorDrawable.java
+++ b/android/graphics/drawable/VectorDrawable.java
@@ -31,6 +31,7 @@
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.Rect;
 import android.graphics.Shader;
+import android.os.Trace;
 import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
@@ -605,38 +606,44 @@
     public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
             @NonNull AttributeSet attrs, @Nullable Theme theme)
             throws XmlPullParserException, IOException {
-        if (mVectorState.mRootGroup != null || mVectorState.mNativeTree != null) {
-            // This VD has been used to display other VD resource content, clean up.
-            if (mVectorState.mRootGroup != null) {
-                // Subtract the native allocation for all the nodes.
-                VMRuntime.getRuntime().registerNativeFree(mVectorState.mRootGroup.getNativeSize());
-                // Remove child nodes' reference to tree
-                mVectorState.mRootGroup.setTree(null);
+        try {
+            Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "VectorDrawable#inflate");
+            if (mVectorState.mRootGroup != null || mVectorState.mNativeTree != null) {
+                // This VD has been used to display other VD resource content, clean up.
+                if (mVectorState.mRootGroup != null) {
+                    // Subtract the native allocation for all the nodes.
+                    VMRuntime.getRuntime().registerNativeFree(
+                            mVectorState.mRootGroup.getNativeSize());
+                    // Remove child nodes' reference to tree
+                    mVectorState.mRootGroup.setTree(null);
+                }
+                mVectorState.mRootGroup = new VGroup();
+                if (mVectorState.mNativeTree != null) {
+                    // Subtract the native allocation for the tree wrapper, which contains root node
+                    // as well as rendering related data.
+                    VMRuntime.getRuntime().registerNativeFree(mVectorState.NATIVE_ALLOCATION_SIZE);
+                    mVectorState.mNativeTree.release();
+                }
+                mVectorState.createNativeTree(mVectorState.mRootGroup);
             }
-            mVectorState.mRootGroup = new VGroup();
-            if (mVectorState.mNativeTree != null) {
-                // Subtract the native allocation for the tree wrapper, which contains root node
-                // as well as rendering related data.
-                VMRuntime.getRuntime().registerNativeFree(mVectorState.NATIVE_ALLOCATION_SIZE);
-                mVectorState.mNativeTree.release();
-            }
-            mVectorState.createNativeTree(mVectorState.mRootGroup);
+            final VectorDrawableState state = mVectorState;
+            state.setDensity(Drawable.resolveDensity(r, 0));
+
+            final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.VectorDrawable);
+            updateStateFromTypedArray(a);
+            a.recycle();
+
+            mDpiScaledDirty = true;
+
+            state.mCacheDirty = true;
+            inflateChildElements(r, parser, attrs, theme);
+
+            state.onTreeConstructionFinished();
+            // Update local properties.
+            updateLocalState(r);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
-        final VectorDrawableState state = mVectorState;
-        state.setDensity(Drawable.resolveDensity(r, 0));
-
-        final TypedArray a = obtainAttributes(r, theme, attrs, R.styleable.VectorDrawable);
-        updateStateFromTypedArray(a);
-        a.recycle();
-
-        mDpiScaledDirty = true;
-
-        state.mCacheDirty = true;
-        inflateChildElements(r, parser, attrs, theme);
-
-        state.onTreeConstructionFinished();
-        // Update local properties.
-        updateLocalState(r);
     }
 
     private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException {
diff --git a/android/media/AudioManager.java b/android/media/AudioManager.java
index 1575457..186b265 100644
--- a/android/media/AudioManager.java
+++ b/android/media/AudioManager.java
@@ -3058,7 +3058,11 @@
 
     private final IPlaybackConfigDispatcher mPlayCb = new IPlaybackConfigDispatcher.Stub() {
         @Override
-        public void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs) {
+        public void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs,
+                boolean flush) {
+            if (flush) {
+                Binder.flushPendingCommands();
+            }
             synchronized(mPlaybackCallbackLock) {
                 if (mPlaybackCallbackList != null) {
                     for (int i=0 ; i < mPlaybackCallbackList.size() ; i++) {
diff --git a/android/media/AudioPlaybackConfiguration.java b/android/media/AudioPlaybackConfiguration.java
index 14bc555..8a36f91 100644
--- a/android/media/AudioPlaybackConfiguration.java
+++ b/android/media/AudioPlaybackConfiguration.java
@@ -212,8 +212,10 @@
      * @hide
      */
     public void init() {
-        if (mIPlayerShell != null) {
-            mIPlayerShell.monitorDeath();
+        synchronized (this) {
+            if (mIPlayerShell != null) {
+                mIPlayerShell.monitorDeath();
+            }
         }
     }
 
@@ -322,7 +324,11 @@
      */
     @SystemApi
     public PlayerProxy getPlayerProxy() {
-        return mIPlayerShell == null ? null : new PlayerProxy(this);
+        final IPlayerShell ips;
+        synchronized (this) {
+            ips = mIPlayerShell;
+        }
+        return ips == null ? null : new PlayerProxy(this);
     }
 
     /**
@@ -330,7 +336,11 @@
      * @return the IPlayer interface for the associated player
      */
     IPlayer getIPlayer() {
-        return mIPlayerShell == null ? null : mIPlayerShell.getIPlayer();
+        final IPlayerShell ips;
+        synchronized (this) {
+            ips = mIPlayerShell;
+        }
+        return ips == null ? null : ips.getIPlayer();
     }
 
     /**
@@ -351,10 +361,14 @@
      * @return true if the state changed, false otherwise
      */
     public boolean handleStateEvent(int event) {
-        final boolean changed = (mPlayerState != event);
-        mPlayerState = event;
-        if ((event == PLAYER_STATE_RELEASED) && (mIPlayerShell != null)) {
-            mIPlayerShell.release();
+        final boolean changed;
+        synchronized (this) {
+            changed = (mPlayerState != event);
+            mPlayerState = event;
+            if (changed && (event == PLAYER_STATE_RELEASED) && (mIPlayerShell != null)) {
+                mIPlayerShell.release();
+                mIPlayerShell = null;
+            }
         }
         return changed;
     }
@@ -447,7 +461,11 @@
         dest.writeInt(mClientPid);
         dest.writeInt(mPlayerState);
         mPlayerAttr.writeToParcel(dest, 0);
-        dest.writeStrongInterface(mIPlayerShell == null ? null : mIPlayerShell.getIPlayer());
+        final IPlayerShell ips;
+        synchronized (this) {
+            ips = mIPlayerShell;
+        }
+        dest.writeStrongInterface(ips == null ? null : ips.getIPlayer());
     }
 
     private AudioPlaybackConfiguration(Parcel in) {
@@ -479,14 +497,17 @@
     static final class IPlayerShell implements IBinder.DeathRecipient {
 
         final AudioPlaybackConfiguration mMonitor; // never null
-        private IPlayer mIPlayer;
+        private volatile IPlayer mIPlayer;
 
         IPlayerShell(@NonNull AudioPlaybackConfiguration monitor, @NonNull IPlayer iplayer) {
             mMonitor = monitor;
             mIPlayer = iplayer;
         }
 
-        void monitorDeath() {
+        synchronized void monitorDeath() {
+            if (mIPlayer == null) {
+                return;
+            }
             try {
                 mIPlayer.asBinder().linkToDeath(this, 0);
             } catch (RemoteException e) {
@@ -509,8 +530,13 @@
             } else if (DEBUG) { Log.i(TAG, "IPlayerShell binderDied"); }
         }
 
-        void release() {
+        synchronized void release() {
+            if (mIPlayer == null) {
+                return;
+            }
             mIPlayer.asBinder().unlinkToDeath(this, 0);
+            mIPlayer = null;
+            Binder.flushPendingCommands();
         }
     }
 
@@ -532,7 +558,7 @@
             case PLAYER_TYPE_HW_SOURCE: return "hardware source";
             case PLAYER_TYPE_EXTERNAL_PROXY: return "external proxy";
             default:
-                return "unknown player type - FIXME";
+                return "unknown player type " + type + " - FIXME";
         }
     }
 
diff --git a/android/media/MediaMuxer.java b/android/media/MediaMuxer.java
index 832b297..91e57ee 100644
--- a/android/media/MediaMuxer.java
+++ b/android/media/MediaMuxer.java
@@ -70,7 +70,7 @@
  <p>
   Per-frame metadata is useful in carrying extra information that correlated with video or audio to
   facilitate offline processing, e.g. gyro signals from the sensor could help video stabilization when
-  doing offline processing. Metaadata track is only supported in MP4 container. When adding a new
+  doing offline processing. Metadata track is only supported in MP4 container. When adding a new
   metadata track, track's mime format must start with prefix "application/", e.g. "applicaton/gyro".
   Metadata's format/layout will be defined by the application. Writing metadata is nearly the same as
   writing video/audio data except that the data will not be from mediacodec. Application just needs
diff --git a/android/media/MediaPlayer.java b/android/media/MediaPlayer.java
index 0d99473..7787d4b 100644
--- a/android/media/MediaPlayer.java
+++ b/android/media/MediaPlayer.java
@@ -2072,6 +2072,20 @@
     private native void _reset();
 
     /**
+     * Set up a timer for {@link #TimeProvider}. {@link #TimeProvider} will be
+     * notified when the presentation time reaches (becomes greater than or equal to)
+     * the value specified.
+     *
+     * @param mediaTimeUs presentation time to get timed event callback at
+     * @hide
+     */
+    public void notifyAt(long mediaTimeUs) {
+        _notifyAt(mediaTimeUs);
+    }
+
+    private native void _notifyAt(long mediaTimeUs);
+
+    /**
      * Sets the audio stream type for this MediaPlayer. See {@link AudioManager}
      * for a list of stream types. Must call this method before prepare() or
      * prepareAsync() in order for the target stream type to become effective
@@ -3155,6 +3169,7 @@
     private static final int MEDIA_PAUSED = 7;
     private static final int MEDIA_STOPPED = 8;
     private static final int MEDIA_SKIPPED = 9;
+    private static final int MEDIA_NOTIFY_TIME = 98;
     private static final int MEDIA_TIMED_TEXT = 99;
     private static final int MEDIA_ERROR = 100;
     private static final int MEDIA_INFO = 200;
@@ -3345,6 +3360,14 @@
                 }
                 // No real default action so far.
                 return;
+
+            case MEDIA_NOTIFY_TIME:
+                    TimeProvider timeProvider = mTimeProvider;
+                    if (timeProvider != null) {
+                        timeProvider.onNotifyTime();
+                    }
+                return;
+
             case MEDIA_TIMED_TEXT:
                 OnTimedTextListener onTimedTextListener = mOnTimedTextListener;
                 if (onTimedTextListener == null)
@@ -5144,19 +5167,16 @@
         private boolean mStopped = true;
         private boolean mBuffering;
         private long mLastReportedTime;
-        private long mTimeAdjustment;
         // since we are expecting only a handful listeners per stream, there is
         // no need for log(N) search performance
         private MediaTimeProvider.OnMediaTimeListener mListeners[];
         private long mTimes[];
-        private long mLastNanoTime;
         private Handler mEventHandler;
         private boolean mRefresh = false;
         private boolean mPausing = false;
         private boolean mSeeking = false;
         private static final int NOTIFY = 1;
         private static final int NOTIFY_TIME = 0;
-        private static final int REFRESH_AND_NOTIFY_TIME = 1;
         private static final int NOTIFY_STOP = 2;
         private static final int NOTIFY_SEEK = 3;
         private static final int NOTIFY_TRACK_DATA = 4;
@@ -5188,13 +5208,11 @@
             mListeners = new MediaTimeProvider.OnMediaTimeListener[0];
             mTimes = new long[0];
             mLastTimeUs = 0;
-            mTimeAdjustment = 0;
         }
 
         private void scheduleNotification(int type, long delayUs) {
             // ignore time notifications until seek is handled
-            if (mSeeking &&
-                    (type == NOTIFY_TIME || type == REFRESH_AND_NOTIFY_TIME)) {
+            if (mSeeking && type == NOTIFY_TIME) {
                 return;
             }
 
@@ -5221,6 +5239,14 @@
         }
 
         /** @hide */
+        public void onNotifyTime() {
+            synchronized (this) {
+                if (DEBUG) Log.d(TAG, "onNotifyTime: ");
+                scheduleNotification(NOTIFY_TIME, 0 /* delay */);
+            }
+        }
+
+        /** @hide */
         public void onPaused(boolean paused) {
             synchronized(this) {
                 if (DEBUG) Log.d(TAG, "onPaused: " + paused);
@@ -5231,7 +5257,7 @@
                 } else {
                     mPausing = paused;  // special handling if player disappeared
                     mSeeking = false;
-                    scheduleNotification(REFRESH_AND_NOTIFY_TIME, 0 /* delay */);
+                    scheduleNotification(NOTIFY_TIME, 0 /* delay */);
                 }
             }
         }
@@ -5241,7 +5267,7 @@
             synchronized (this) {
                 if (DEBUG) Log.d(TAG, "onBuffering: " + buffering);
                 mBuffering = buffering;
-                scheduleNotification(REFRESH_AND_NOTIFY_TIME, 0 /* delay */);
+                scheduleNotification(NOTIFY_TIME, 0 /* delay */);
             }
         }
 
@@ -5438,7 +5464,7 @@
             if (nextTimeUs > nowUs && !mPaused) {
                 // schedule callback at nextTimeUs
                 if (DEBUG) Log.d(TAG, "scheduling for " + nextTimeUs + " and " + nowUs);
-                scheduleNotification(NOTIFY_TIME, nextTimeUs - nowUs);
+                mPlayer.notifyAt(nextTimeUs);
             } else {
                 mEventHandler.removeMessages(NOTIFY);
                 // no more callbacks
@@ -5449,25 +5475,6 @@
             }
         }
 
-        private long getEstimatedTime(long nanoTime, boolean monotonic) {
-            if (mPaused) {
-                mLastReportedTime = mLastTimeUs + mTimeAdjustment;
-            } else {
-                long timeSinceRead = (nanoTime - mLastNanoTime) / 1000;
-                mLastReportedTime = mLastTimeUs + timeSinceRead;
-                if (mTimeAdjustment > 0) {
-                    long adjustment =
-                        mTimeAdjustment - timeSinceRead / TIME_ADJUSTMENT_RATE;
-                    if (adjustment <= 0) {
-                        mTimeAdjustment = 0;
-                    } else {
-                        mLastReportedTime += adjustment;
-                    }
-                }
-            }
-            return mLastReportedTime;
-        }
-
         public long getCurrentTimeUs(boolean refreshTime, boolean monotonic)
                 throws IllegalStateException {
             synchronized (this) {
@@ -5477,42 +5484,38 @@
                     return mLastReportedTime;
                 }
 
-                long nanoTime = System.nanoTime();
-                if (refreshTime ||
-                        nanoTime >= mLastNanoTime + MAX_NS_WITHOUT_POSITION_CHECK) {
-                    try {
-                        mLastTimeUs = mPlayer.getCurrentPosition() * 1000L;
-                        mPaused = !mPlayer.isPlaying() || mBuffering;
-                        if (DEBUG) Log.v(TAG, (mPaused ? "paused" : "playing") + " at " + mLastTimeUs);
-                    } catch (IllegalStateException e) {
-                        if (mPausing) {
-                            // if we were pausing, get last estimated timestamp
-                            mPausing = false;
-                            getEstimatedTime(nanoTime, monotonic);
-                            mPaused = true;
-                            if (DEBUG) Log.d(TAG, "illegal state, but pausing: estimating at " + mLastReportedTime);
-                            return mLastReportedTime;
+                try {
+                    mLastTimeUs = mPlayer.getCurrentPosition() * 1000L;
+                    mPaused = !mPlayer.isPlaying() || mBuffering;
+                    if (DEBUG) Log.v(TAG, (mPaused ? "paused" : "playing") + " at " + mLastTimeUs);
+                } catch (IllegalStateException e) {
+                    if (mPausing) {
+                        // if we were pausing, get last estimated timestamp
+                        mPausing = false;
+                        if (!monotonic || mLastReportedTime < mLastTimeUs) {
+                            mLastReportedTime = mLastTimeUs;
                         }
-                        // TODO get time when prepared
-                        throw e;
+                        mPaused = true;
+                        if (DEBUG) Log.d(TAG, "illegal state, but pausing: estimating at " + mLastReportedTime);
+                        return mLastReportedTime;
                     }
-                    mLastNanoTime = nanoTime;
-                    if (monotonic && mLastTimeUs < mLastReportedTime) {
-                        /* have to adjust time */
-                        mTimeAdjustment = mLastReportedTime - mLastTimeUs;
-                        if (mTimeAdjustment > 1000000) {
-                            // schedule seeked event if time jumped significantly
-                            // TODO: do this properly by introducing an exception
-                            mStopped = false;
-                            mSeeking = true;
-                            scheduleNotification(NOTIFY_SEEK, 0 /* delay */);
-                        }
-                    } else {
-                        mTimeAdjustment = 0;
+                    // TODO get time when prepared
+                    throw e;
+                }
+                if (monotonic && mLastTimeUs < mLastReportedTime) {
+                    /* have to adjust time */
+                    if (mLastReportedTime - mLastTimeUs > 1000000) {
+                        // schedule seeked event if time jumped significantly
+                        // TODO: do this properly by introducing an exception
+                        mStopped = false;
+                        mSeeking = true;
+                        scheduleNotification(NOTIFY_SEEK, 0 /* delay */);
                     }
+                } else {
+                    mLastReportedTime = mLastTimeUs;
                 }
 
-                return getEstimatedTime(nanoTime, monotonic);
+                return mLastReportedTime;
             }
         }
 
@@ -5526,9 +5529,6 @@
                 if (msg.what == NOTIFY) {
                     switch (msg.arg1) {
                     case NOTIFY_TIME:
-                        notifyTimedEvent(false /* refreshTime */);
-                        break;
-                    case REFRESH_AND_NOTIFY_TIME:
                         notifyTimedEvent(true /* refreshTime */);
                         break;
                     case NOTIFY_STOP:
diff --git a/android/media/MediaRouter.java b/android/media/MediaRouter.java
index 2894e89..fe427a7 100644
--- a/android/media/MediaRouter.java
+++ b/android/media/MediaRouter.java
@@ -193,7 +193,9 @@
                 } else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
                     name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
                 } else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_HDMI) != 0) {
-                    name = com.android.internal.R.string.default_media_route_name_hdmi;
+                    name = com.android.internal.R.string.default_audio_route_name_hdmi;
+                } else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_USB) != 0) {
+                    name = com.android.internal.R.string.default_audio_route_name_usb;
                 } else {
                     name = com.android.internal.R.string.default_audio_route_name;
                 }
diff --git a/android/net/LinkProperties.java b/android/net/LinkProperties.java
index f527f77..2c9fb23 100644
--- a/android/net/LinkProperties.java
+++ b/android/net/LinkProperties.java
@@ -70,8 +70,23 @@
      * @hide
      */
     public static class CompareResult<T> {
-        public List<T> removed = new ArrayList<T>();
-        public List<T> added = new ArrayList<T>();
+        public final List<T> removed = new ArrayList<T>();
+        public final List<T> added = new ArrayList<T>();
+
+        public CompareResult() {}
+
+        public CompareResult(Collection<T> oldItems, Collection<T> newItems) {
+            if (oldItems != null) {
+                removed.addAll(oldItems);
+            }
+            if (newItems != null) {
+                for (T newItem : newItems) {
+                    if (!removed.remove(newItem)) {
+                        added.add(newItem);
+                    }
+                }
+            }
+        }
 
         @Override
         public String toString() {
@@ -1000,17 +1015,8 @@
          * are in target but not in mLinkAddresses are placed in the
          * addedAddresses.
          */
-        CompareResult<LinkAddress> result = new CompareResult<LinkAddress>();
-        result.removed = new ArrayList<LinkAddress>(mLinkAddresses);
-        result.added.clear();
-        if (target != null) {
-            for (LinkAddress newAddress : target.getLinkAddresses()) {
-                if (! result.removed.remove(newAddress)) {
-                    result.added.add(newAddress);
-                }
-            }
-        }
-        return result;
+        return new CompareResult<>(mLinkAddresses,
+                target != null ? target.getLinkAddresses() : null);
     }
 
     /**
@@ -1029,18 +1035,7 @@
          * are in target but not in mDnses are placed in the
          * addedAddresses.
          */
-        CompareResult<InetAddress> result = new CompareResult<InetAddress>();
-
-        result.removed = new ArrayList<InetAddress>(mDnses);
-        result.added.clear();
-        if (target != null) {
-            for (InetAddress newAddress : target.getDnsServers()) {
-                if (! result.removed.remove(newAddress)) {
-                    result.added.add(newAddress);
-                }
-            }
-        }
-        return result;
+        return new CompareResult<>(mDnses, target != null ? target.getDnsServers() : null);
     }
 
     /**
@@ -1058,18 +1053,7 @@
          * leaving the routes that are different. And route address which
          * are in target but not in mRoutes are placed in added.
          */
-        CompareResult<RouteInfo> result = new CompareResult<RouteInfo>();
-
-        result.removed = getAllRoutes();
-        result.added.clear();
-        if (target != null) {
-            for (RouteInfo r : target.getAllRoutes()) {
-                if (! result.removed.remove(r)) {
-                    result.added.add(r);
-                }
-            }
-        }
-        return result;
+        return new CompareResult<>(getAllRoutes(), target != null ? target.getAllRoutes() : null);
     }
 
     /**
@@ -1087,18 +1071,8 @@
          * leaving the interface names that are different. And interface names which
          * are in target but not in this are placed in added.
          */
-        CompareResult<String> result = new CompareResult<String>();
-
-        result.removed = getAllInterfaceNames();
-        result.added.clear();
-        if (target != null) {
-            for (String r : target.getAllInterfaceNames()) {
-                if (! result.removed.remove(r)) {
-                    result.added.add(r);
-                }
-            }
-        }
-        return result;
+        return new CompareResult<>(getAllInterfaceNames(),
+                target != null ? target.getAllInterfaceNames() : null);
     }
 
 
diff --git a/android/net/metrics/WakeupEvent.java b/android/net/metrics/WakeupEvent.java
new file mode 100644
index 0000000..cbf3fc8
--- /dev/null
+++ b/android/net/metrics/WakeupEvent.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 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 android.net.metrics;
+
+/**
+ * An event logged when NFLOG notifies userspace of a wakeup packet for
+ * watched interfaces.
+ * {@hide}
+ */
+public class WakeupEvent {
+    public String iface;
+    public long timestampMs;
+    public int uid;
+
+    @Override
+    public String toString() {
+        return String.format("WakeupEvent(%tT.%tL, %s, uid: %d)",
+                timestampMs, timestampMs, iface, uid);
+    }
+}
diff --git a/android/net/metrics/WakeupStats.java b/android/net/metrics/WakeupStats.java
new file mode 100644
index 0000000..d520b97
--- /dev/null
+++ b/android/net/metrics/WakeupStats.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 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 android.net.metrics;
+
+import android.os.Process;
+import android.os.SystemClock;
+
+/**
+ * An event logged per interface and that aggregates WakeupEvents for that interface.
+ * {@hide}
+ */
+public class WakeupStats {
+
+    private static final int NO_UID = -1;
+
+    public final long creationTimeMs = SystemClock.elapsedRealtime();
+    public final String iface;
+
+    public long totalWakeups = 0;
+    public long rootWakeups = 0;
+    public long systemWakeups = 0;
+    public long nonApplicationWakeups = 0;
+    public long applicationWakeups = 0;
+    public long unroutedWakeups = 0;
+    public long durationSec = 0;
+
+    public WakeupStats(String iface) {
+        this.iface = iface;
+    }
+
+    /** Update durationSec with current time. */
+    public void updateDuration() {
+        durationSec = (SystemClock.elapsedRealtime() - creationTimeMs) / 1000;
+    }
+
+    /** Update wakeup counters for the given WakeupEvent. */
+    public void countEvent(WakeupEvent ev) {
+        totalWakeups++;
+        switch (ev.uid) {
+            case Process.ROOT_UID:
+                rootWakeups++;
+                break;
+            case Process.SYSTEM_UID:
+                systemWakeups++;
+                break;
+            case NO_UID:
+                unroutedWakeups++;
+                break;
+            default:
+                if (ev.uid >= Process.FIRST_APPLICATION_UID) {
+                    applicationWakeups++;
+                } else {
+                    nonApplicationWakeups++;
+                }
+                break;
+        }
+    }
+
+    @Override
+    public String toString() {
+        updateDuration();
+        return new StringBuilder()
+                .append("WakeupStats(").append(iface)
+                .append(", total: ").append(totalWakeups)
+                .append(", root: ").append(rootWakeups)
+                .append(", system: ").append(systemWakeups)
+                .append(", apps: ").append(applicationWakeups)
+                .append(", non-apps: ").append(nonApplicationWakeups)
+                .append(", unrouted: ").append(unroutedWakeups)
+                .append(", ").append(durationSec).append("s)")
+                .toString();
+    }
+}
diff --git a/android/os/BatteryStats.java b/android/os/BatteryStats.java
index cea5715..66b6b47 100644
--- a/android/os/BatteryStats.java
+++ b/android/os/BatteryStats.java
@@ -53,6 +53,8 @@
     private static final String TAG = "BatteryStats";
 
     private static final boolean LOCAL_LOGV = false;
+    /** Fetching RPM stats is too slow to do each time screen changes, so disable it. */
+    protected static final boolean SCREEN_OFF_RPM_STATS_ENABLED = false;
 
     /** @hide */
     public static final String SERVICE_NAME = "batterystats";
@@ -213,8 +215,10 @@
      *   - Fixed bugs in background timers and BLE scan time
      * New in version 25:
      *   - Package wakeup alarms are now on screen-off timebase
+     * New in version 26:
+     *   - Resource power manager (rpm) states [but screenOffRpm is disabled from working properly]
      */
-    static final String CHECKIN_VERSION = "25";
+    static final String CHECKIN_VERSION = "26";
 
     /**
      * Old version, we hit 9 and ran out of room, need to remove.
@@ -233,6 +237,10 @@
     private static final String CPU_DATA = "cpu";
     private static final String GLOBAL_CPU_FREQ_DATA = "gcf";
     private static final String CPU_TIMES_AT_FREQ_DATA = "ctf";
+    // rpm line is:
+    // BATTERY_STATS_CHECKIN_VERSION, uid, which, "rpm", state/voter name, total time, total count,
+    // screen-off time, screen-off count
+    private static final String RESOURCE_POWER_MANAGER_DATA = "rpm";
     private static final String SENSOR_DATA = "sr";
     private static final String VIBRATOR_DATA = "vib";
     private static final String FOREGROUND_ACTIVITY_DATA = "fg";
@@ -2648,6 +2656,16 @@
 
     public abstract Map<String, ? extends Timer> getKernelWakelockStats();
 
+    /**
+     * Returns Timers tracking the total time of each Resource Power Manager state and voter.
+     */
+    public abstract Map<String, ? extends Timer> getRpmStats();
+    /**
+     * Returns Timers tracking the screen-off time of each Resource Power Manager state and voter.
+     */
+    public abstract Map<String, ? extends Timer> getScreenOffRpmStats();
+
+
     public abstract LongSparseArray<? extends Timer> getKernelMemoryStats();
 
     public abstract void writeToParcelWithoutUids(Parcel out, int flags);
@@ -3309,6 +3327,30 @@
             }
         }
 
+        final Map<String, ? extends Timer> rpmStats = getRpmStats();
+        final Map<String, ? extends Timer> screenOffRpmStats = getScreenOffRpmStats();
+        if (rpmStats.size() > 0) {
+            for (Map.Entry<String, ? extends Timer> ent : rpmStats.entrySet()) {
+                sb.setLength(0);
+                Timer totalTimer = ent.getValue();
+                long timeMs = (totalTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000;
+                int count = totalTimer.getCountLocked(which);
+                Timer screenOffTimer = screenOffRpmStats.get(ent.getKey());
+                long screenOffTimeMs = screenOffTimer != null
+                        ? (screenOffTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000 : 0;
+                int screenOffCount = screenOffTimer != null
+                        ? screenOffTimer.getCountLocked(which) : 0;
+                if (SCREEN_OFF_RPM_STATS_ENABLED) {
+                    dumpLine(pw, 0 /* uid */, category, RESOURCE_POWER_MANAGER_DATA,
+                            "\"" + ent.getKey() + "\"", timeMs, count, screenOffTimeMs,
+                            screenOffCount);
+                } else {
+                    dumpLine(pw, 0 /* uid */, category, RESOURCE_POWER_MANAGER_DATA,
+                            "\"" + ent.getKey() + "\"", timeMs, count);
+                }
+            }
+        }
+
         final BatteryStatsHelper helper = new BatteryStatsHelper(context, false, wifiOnly);
         helper.create(this);
         helper.refreshStats(which, UserHandle.USER_ALL);
@@ -4570,24 +4612,56 @@
         }
 
         final LongSparseArray<? extends Timer> mMemoryStats = getKernelMemoryStats();
-        pw.println("Memory Stats");
-        for (int i = 0; i < mMemoryStats.size(); i++) {
-            sb.setLength(0);
-            sb.append("Bandwidth ");
-            sb.append(mMemoryStats.keyAt(i));
-            sb.append(" Time ");
-            sb.append(mMemoryStats.valueAt(i).getTotalTimeLocked(rawRealtime, which));
-            pw.println(sb.toString());
+        if (mMemoryStats.size() > 0) {
+            pw.println("  Memory Stats");
+            for (int i = 0; i < mMemoryStats.size(); i++) {
+                sb.setLength(0);
+                sb.append("  Bandwidth ");
+                sb.append(mMemoryStats.keyAt(i));
+                sb.append(" Time ");
+                sb.append(mMemoryStats.valueAt(i).getTotalTimeLocked(rawRealtime, which));
+                pw.println(sb.toString());
+            }
+            pw.println();
+        }
+
+        final Map<String, ? extends Timer> rpmStats = getRpmStats();
+        if (rpmStats.size() > 0) {
+            pw.print(prefix); pw.println("  Resource Power Manager Stats");
+            if (rpmStats.size() > 0) {
+                for (Map.Entry<String, ? extends Timer> ent : rpmStats.entrySet()) {
+                    final String timerName = ent.getKey();
+                    final Timer timer = ent.getValue();
+                    printTimer(pw, sb, timer, rawRealtime, which, prefix, timerName);
+                }
+            }
+            pw.println();
+        }
+        if (SCREEN_OFF_RPM_STATS_ENABLED) {
+            final Map<String, ? extends Timer> screenOffRpmStats = getScreenOffRpmStats();
+            if (screenOffRpmStats.size() > 0) {
+                pw.print(prefix);
+                pw.println("  Resource Power Manager Stats for when screen was off");
+                if (screenOffRpmStats.size() > 0) {
+                    for (Map.Entry<String, ? extends Timer> ent : screenOffRpmStats.entrySet()) {
+                        final String timerName = ent.getKey();
+                        final Timer timer = ent.getValue();
+                        printTimer(pw, sb, timer, rawRealtime, which, prefix, timerName);
+                    }
+                }
+                pw.println();
+            }
         }
 
         final long[] cpuFreqs = getCpuFreqs();
         if (cpuFreqs != null) {
             sb.setLength(0);
-            sb.append("CPU freqs:");
+            sb.append("  CPU freqs:");
             for (int i = 0; i < cpuFreqs.length; ++i) {
                 sb.append(" " + cpuFreqs[i]);
             }
             pw.println(sb.toString());
+            pw.println();
         }
 
         for (int iu=0; iu<NU; iu++) {
diff --git a/android/os/Process.java b/android/os/Process.java
index 9351661..b5d62e5 100644
--- a/android/os/Process.java
+++ b/android/os/Process.java
@@ -19,8 +19,8 @@
 import android.annotation.TestApi;
 import android.system.Os;
 import android.system.OsConstants;
-import android.util.Log;
 import android.webkit.WebViewZygote;
+
 import dalvik.system.VMRuntime;
 
 /**
@@ -423,7 +423,7 @@
      * 
      * When invokeWith is not null, the process will be started as a fresh app
      * and not a zygote fork. Note that this is only allowed for uid 0 or when
-     * debugFlags contains DEBUG_ENABLE_DEBUGGER.
+     * runtimeFlags contains DEBUG_ENABLE_DEBUGGER.
      *
      * @param processClass The class to use as the process's main entry
      *                     point.
@@ -431,7 +431,7 @@
      * @param uid The user-id under which the process will run.
      * @param gid The group-id under which the process will run.
      * @param gids Additional group-ids associated with the process.
-     * @param debugFlags Additional flags.
+     * @param runtimeFlags Additional flags for the runtime.
      * @param targetSdkVersion The target SDK version for the app.
      * @param seInfo null-ok SELinux information for the new process.
      * @param abi non-null the ABI this app should be started with.
@@ -448,7 +448,7 @@
     public static final ProcessStartResult start(final String processClass,
                                   final String niceName,
                                   int uid, int gid, int[] gids,
-                                  int debugFlags, int mountExternal,
+                                  int runtimeFlags, int mountExternal,
                                   int targetSdkVersion,
                                   String seInfo,
                                   String abi,
@@ -457,7 +457,7 @@
                                   String invokeWith,
                                   String[] zygoteArgs) {
         return zygoteProcess.start(processClass, niceName, uid, gid, gids,
-                    debugFlags, mountExternal, targetSdkVersion, seInfo,
+                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
     }
 
@@ -465,7 +465,7 @@
     public static final ProcessStartResult startWebView(final String processClass,
                                   final String niceName,
                                   int uid, int gid, int[] gids,
-                                  int debugFlags, int mountExternal,
+                                  int runtimeFlags, int mountExternal,
                                   int targetSdkVersion,
                                   String seInfo,
                                   String abi,
@@ -474,7 +474,7 @@
                                   String invokeWith,
                                   String[] zygoteArgs) {
         return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
-                    debugFlags, mountExternal, targetSdkVersion, seInfo,
+                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
     }
 
diff --git a/android/os/ServiceManager.java b/android/os/ServiceManager.java
index e11494d..34c7845 100644
--- a/android/os/ServiceManager.java
+++ b/android/os/ServiceManager.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2009 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.
@@ -16,114 +16,44 @@
 
 package android.os;
 
-import android.util.Log;
-
-import com.android.internal.os.BinderInternal;
-
-import java.util.HashMap;
 import java.util.Map;
 
-/** @hide */
 public final class ServiceManager {
-    private static final String TAG = "ServiceManager";
-
-    private static IServiceManager sServiceManager;
-    private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
-
-    private static IServiceManager getIServiceManager() {
-        if (sServiceManager != null) {
-            return sServiceManager;
-        }
-
-        // Find the service manager
-        sServiceManager = ServiceManagerNative
-                .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
-        return sServiceManager;
-    }
 
     /**
      * Returns a reference to a service with the given name.
-     * 
+     *
      * @param name the name of the service to get
      * @return a reference to the service, or <code>null</code> if the service doesn't exist
      */
     public static IBinder getService(String name) {
-        try {
-            IBinder service = sCache.get(name);
-            if (service != null) {
-                return service;
-            } else {
-                return Binder.allowBlocking(getIServiceManager().getService(name));
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "error in getService", e);
-        }
         return null;
     }
 
     /**
-     * Returns a reference to a service with the given name, or throws
-     * {@link NullPointerException} if none is found.
-     *
-     * @hide
+     * Is not supposed to return null, but that is fine for layoutlib.
      */
     public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException {
-        final IBinder binder = getService(name);
-        if (binder != null) {
-            return binder;
-        } else {
-            throw new ServiceNotFoundException(name);
-        }
+        throw new ServiceNotFoundException(name);
     }
 
     /**
      * Place a new @a service called @a name into the service
      * manager.
-     * 
+     *
      * @param name the name of the new service
      * @param service the service object
      */
     public static void addService(String name, IBinder service) {
-        try {
-            getIServiceManager().addService(name, service, false);
-        } catch (RemoteException e) {
-            Log.e(TAG, "error in addService", e);
-        }
+        // pass
     }
 
     /**
-     * Place a new @a service called @a name into the service
-     * manager.
-     * 
-     * @param name the name of the new service
-     * @param service the service object
-     * @param allowIsolated set to true to allow isolated sandboxed processes
-     * to access this service
-     */
-    public static void addService(String name, IBinder service, boolean allowIsolated) {
-        try {
-            getIServiceManager().addService(name, service, allowIsolated);
-        } catch (RemoteException e) {
-            Log.e(TAG, "error in addService", e);
-        }
-    }
-    
-    /**
      * Retrieve an existing service called @a name from the
      * service manager.  Non-blocking.
      */
     public static IBinder checkService(String name) {
-        try {
-            IBinder service = sCache.get(name);
-            if (service != null) {
-                return service;
-            } else {
-                return Binder.allowBlocking(getIServiceManager().checkService(name));
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "error in checkService", e);
-            return null;
-        }
+        return null;
     }
 
     /**
@@ -132,27 +62,21 @@
      * case of an exception
      */
     public static String[] listServices() {
-        try {
-            return getIServiceManager().listServices();
-        } catch (RemoteException e) {
-            Log.e(TAG, "error in listServices", e);
-            return null;
-        }
+        // actual implementation returns null sometimes, so it's ok
+        // to return null instead of an empty list.
+        return null;
     }
 
     /**
      * This is only intended to be called when the process is first being brought
      * up and bound by the activity manager. There is only one thread in the process
      * at that time, so no locking is done.
-     * 
+     *
      * @param cache the cache of service references
      * @hide
      */
     public static void initServiceCache(Map<String, IBinder> cache) {
-        if (sCache.size() != 0) {
-            throw new IllegalStateException("setServiceCache may only be called once");
-        }
-        sCache.putAll(cache);
+        // pass
     }
 
     /**
@@ -163,6 +87,7 @@
      * @hide
      */
     public static class ServiceNotFoundException extends Exception {
+        // identical to the original implementation
         public ServiceNotFoundException(String name) {
             super("No service published for: " + name);
         }
diff --git a/android/os/StrictMode.java b/android/os/StrictMode.java
index 615d3c4..f02631c 100644
--- a/android/os/StrictMode.java
+++ b/android/os/StrictMode.java
@@ -722,7 +722,7 @@
             }
 
             /**
-             * Detect when an {@link java.io.Closeable} or other object with a explict termination
+             * Detect when an {@link java.io.Closeable} or other object with an explicit termination
              * method is finalized without having been closed.
              *
              * <p>You always want to explicitly close such objects to avoid unnecessary resources
diff --git a/android/os/ZygoteProcess.java b/android/os/ZygoteProcess.java
index 7a13ee8..670f794 100644
--- a/android/os/ZygoteProcess.java
+++ b/android/os/ZygoteProcess.java
@@ -20,9 +20,11 @@
 import android.net.LocalSocketAddress;
 import android.util.Log;
 import android.util.Slog;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.Zygote;
 import com.android.internal.util.Preconditions;
+
 import java.io.BufferedWriter;
 import java.io.DataInputStream;
 import java.io.IOException;
@@ -173,7 +175,7 @@
      *
      * When invokeWith is not null, the process will be started as a fresh app
      * and not a zygote fork. Note that this is only allowed for uid 0 or when
-     * debugFlags contains DEBUG_ENABLE_DEBUGGER.
+     * runtimeFlags contains DEBUG_ENABLE_DEBUGGER.
      *
      * @param processClass The class to use as the process's main entry
      *                     point.
@@ -181,7 +183,7 @@
      * @param uid The user-id under which the process will run.
      * @param gid The group-id under which the process will run.
      * @param gids Additional group-ids associated with the process.
-     * @param debugFlags Additional flags.
+     * @param runtimeFlags Additional flags.
      * @param targetSdkVersion The target SDK version for the app.
      * @param seInfo null-ok SELinux information for the new process.
      * @param abi non-null the ABI this app should be started with.
@@ -196,7 +198,7 @@
     public final Process.ProcessStartResult start(final String processClass,
                                                   final String niceName,
                                                   int uid, int gid, int[] gids,
-                                                  int debugFlags, int mountExternal,
+                                                  int runtimeFlags, int mountExternal,
                                                   int targetSdkVersion,
                                                   String seInfo,
                                                   String abi,
@@ -206,7 +208,7 @@
                                                   String[] zygoteArgs) {
         try {
             return startViaZygote(processClass, niceName, uid, gid, gids,
-                    debugFlags, mountExternal, targetSdkVersion, seInfo,
+                    runtimeFlags, mountExternal, targetSdkVersion, seInfo,
                     abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
         } catch (ZygoteStartFailedEx ex) {
             Log.e(LOG_TAG,
@@ -317,7 +319,7 @@
      * @param gid a POSIX gid that the new process shuold setgid() to
      * @param gids null-ok; a list of supplementary group IDs that the
      * new process should setgroup() to.
-     * @param debugFlags Additional flags.
+     * @param runtimeFlags Additional flags for the runtime.
      * @param targetSdkVersion The target SDK version for the app.
      * @param seInfo null-ok SELinux information for the new process.
      * @param abi the ABI the process should use.
@@ -331,7 +333,7 @@
                                                       final String niceName,
                                                       final int uid, final int gid,
                                                       final int[] gids,
-                                                      int debugFlags, int mountExternal,
+                                                      int runtimeFlags, int mountExternal,
                                                       int targetSdkVersion,
                                                       String seInfo,
                                                       String abi,
@@ -347,33 +349,7 @@
         argsForZygote.add("--runtime-args");
         argsForZygote.add("--setuid=" + uid);
         argsForZygote.add("--setgid=" + gid);
-        if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {
-            argsForZygote.add("--enable-jni-logging");
-        }
-        if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
-            argsForZygote.add("--enable-safemode");
-        }
-        if ((debugFlags & Zygote.DEBUG_ENABLE_JDWP) != 0) {
-            argsForZygote.add("--enable-jdwp");
-        }
-        if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
-            argsForZygote.add("--enable-checkjni");
-        }
-        if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) {
-            argsForZygote.add("--generate-debug-info");
-        }
-        if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) {
-            argsForZygote.add("--always-jit");
-        }
-        if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) {
-            argsForZygote.add("--native-debuggable");
-        }
-        if ((debugFlags & Zygote.DEBUG_JAVA_DEBUGGABLE) != 0) {
-            argsForZygote.add("--java-debuggable");
-        }
-        if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
-            argsForZygote.add("--enable-assert");
-        }
+        argsForZygote.add("--runtime-flags=" + runtimeFlags);
         if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
             argsForZygote.add("--mount-external-default");
         } else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
diff --git a/android/os/storage/StorageManager.java b/android/os/storage/StorageManager.java
index 7036346..6594cd0 100644
--- a/android/os/storage/StorageManager.java
+++ b/android/os/storage/StorageManager.java
@@ -221,8 +221,6 @@
 
     /** {@hide} */
     public static final int FSTRIM_FLAG_DEEP = IVold.FSTRIM_FLAG_DEEP_TRIM;
-    /** {@hide} */
-    public static final int FSTRIM_FLAG_BENCHMARK = IVold.FSTRIM_FLAG_BENCHMARK_AFTER;
 
     /** @hide The volume is not encrypted. */
     public static final int ENCRYPTION_STATE_NONE =
diff --git a/android/os/storage/VolumeInfo.java b/android/os/storage/VolumeInfo.java
index b8353d7..76f79f1 100644
--- a/android/os/storage/VolumeInfo.java
+++ b/android/os/storage/VolumeInfo.java
@@ -76,21 +76,21 @@
     /** Real volume representing internal emulated storage */
     public static final String ID_EMULATED_INTERNAL = "emulated";
 
-    public static final int TYPE_PUBLIC = IVold.TYPE_PUBLIC;
-    public static final int TYPE_PRIVATE = IVold.TYPE_PRIVATE;
-    public static final int TYPE_EMULATED = IVold.TYPE_EMULATED;
-    public static final int TYPE_ASEC = IVold.TYPE_ASEC;
-    public static final int TYPE_OBB = IVold.TYPE_OBB;
+    public static final int TYPE_PUBLIC = IVold.VOLUME_TYPE_PUBLIC;
+    public static final int TYPE_PRIVATE = IVold.VOLUME_TYPE_PRIVATE;
+    public static final int TYPE_EMULATED = IVold.VOLUME_TYPE_EMULATED;
+    public static final int TYPE_ASEC = IVold.VOLUME_TYPE_ASEC;
+    public static final int TYPE_OBB = IVold.VOLUME_TYPE_OBB;
 
-    public static final int STATE_UNMOUNTED = IVold.STATE_UNMOUNTED;
-    public static final int STATE_CHECKING = IVold.STATE_CHECKING;
-    public static final int STATE_MOUNTED = IVold.STATE_MOUNTED;
-    public static final int STATE_MOUNTED_READ_ONLY = IVold.STATE_MOUNTED_READ_ONLY;
-    public static final int STATE_FORMATTING = IVold.STATE_FORMATTING;
-    public static final int STATE_EJECTING = IVold.STATE_EJECTING;
-    public static final int STATE_UNMOUNTABLE = IVold.STATE_UNMOUNTABLE;
-    public static final int STATE_REMOVED = IVold.STATE_REMOVED;
-    public static final int STATE_BAD_REMOVAL = IVold.STATE_BAD_REMOVAL;
+    public static final int STATE_UNMOUNTED = IVold.VOLUME_STATE_UNMOUNTED;
+    public static final int STATE_CHECKING = IVold.VOLUME_STATE_CHECKING;
+    public static final int STATE_MOUNTED = IVold.VOLUME_STATE_MOUNTED;
+    public static final int STATE_MOUNTED_READ_ONLY = IVold.VOLUME_STATE_MOUNTED_READ_ONLY;
+    public static final int STATE_FORMATTING = IVold.VOLUME_STATE_FORMATTING;
+    public static final int STATE_EJECTING = IVold.VOLUME_STATE_EJECTING;
+    public static final int STATE_UNMOUNTABLE = IVold.VOLUME_STATE_UNMOUNTABLE;
+    public static final int STATE_REMOVED = IVold.VOLUME_STATE_REMOVED;
+    public static final int STATE_BAD_REMOVAL = IVold.VOLUME_STATE_BAD_REMOVAL;
 
     public static final int MOUNT_FLAG_PRIMARY = IVold.MOUNT_FLAG_PRIMARY;
     public static final int MOUNT_FLAG_VISIBLE = IVold.MOUNT_FLAG_VISIBLE;
diff --git a/android/provider/Settings.java b/android/provider/Settings.java
index 2cb3864..40ced6c 100644
--- a/android/provider/Settings.java
+++ b/android/provider/Settings.java
@@ -5194,17 +5194,39 @@
         public static final String ALLOW_MOCK_LOCATION = "mock_location";
 
         /**
-         * A 64-bit number (as a hex string) that is randomly
-         * generated when the user first sets up the device and should remain
-         * constant for the lifetime of the user's device. The value may
-         * change if a factory reset is performed on the device.
-         * <p class="note"><strong>Note:</strong> When a device has <a
-         * href="{@docRoot}about/versions/android-4.2.html#MultipleUsers">multiple users</a>
-         * (available on certain devices running Android 4.2 or higher), each user appears as a
-         * completely separate device, so the {@code ANDROID_ID} value is unique to each
-         * user.</p>
+         * On Android 8.0 (API level 26) and higher versions of the platform,
+         * a 64-bit number (expressed as a hexadecimal string), unique to
+         * each combination of app-signing key, user, and device.
+         * Values of {@code ANDROID_ID} are scoped by signing key and user.
+         * The value may change if a factory reset is performed on the
+         * device or if an APK signing key changes.
          *
-         * <p class="note"><strong>Note:</strong> If the caller is an Instant App the id is scoped
+         * For more information about how the platform handles {@code ANDROID_ID}
+         * in Android 8.0 (API level 26) and higher, see <a
+         * href="{@docRoot}preview/behavior-changes.html#privacy-all">
+         * Android 8.0 Behavior Changes</a>.
+         *
+         * <p class="note"><strong>Note:</strong> For apps that were installed
+         * prior to updating the device to a version of Android 8.0
+         * (API level 26) or higher, the value of {@code ANDROID_ID} changes
+         * if the app is uninstalled and then reinstalled after the OTA.
+         * To preserve values across uninstalls after an OTA to Android 8.0
+         * or higher, developers can use
+         * <a href="{@docRoot}guide/topics/data/keyvaluebackup.html">
+         * Key/Value Backup</a>.</p>
+         *
+         * <p>In versions of the platform lower than Android 8.0 (API level 26),
+         * a 64-bit number (expressed as a hexadecimal string) that is randomly
+         * generated when the user first sets up the device and should remain
+         * constant for the lifetime of the user's device.
+         *
+         * On devices that have
+         * <a href="{@docRoot}about/versions/android-4.2.html#MultipleUsers">
+         * multiple users</a>, each user appears as a
+         * completely separate device, so the {@code ANDROID_ID} value is
+         * unique to each user.</p>
+         *
+         * <p class="note"><strong>Note:</strong> If the caller is an Instant App the ID is scoped
          * to the Instant App, it is generated when the Instant App is first installed and reset if
          * the user clears the Instant App.
          */
@@ -7124,6 +7146,31 @@
          * @hide
          */
         public static final String LOCKDOWN_IN_POWER_MENU = "lockdown_in_power_menu";
+
+        /**
+         * Backup manager behavioral parameters.
+         * This is encoded as a key=value list, separated by commas. Ex:
+         *
+         * "key_value_backup_interval_milliseconds=14400000,key_value_backup_require_charging=true"
+         *
+         * The following keys are supported:
+         *
+         * <pre>
+         * key_value_backup_interval_milliseconds  (long)
+         * key_value_backup_fuzz_milliseconds      (long)
+         * key_value_backup_require_charging       (boolean)
+         * key_value_backup_required_network_type  (int)
+         * full_backup_interval_milliseconds       (long)
+         * full_backup_require_charging            (boolean)
+         * full_backup_required_network_type       (int)
+         * </pre>
+         *
+         * <p>
+         * Type: string
+         * @hide
+         */
+        public static final String BACKUP_MANAGER_CONSTANTS = "backup_manager_constants";
+
         /**
          * This are the settings to be backed up.
          *
diff --git a/android/service/settings/suggestions/Suggestion.java b/android/service/settings/suggestions/Suggestion.java
new file mode 100644
index 0000000..f27cc2e
--- /dev/null
+++ b/android/service/settings/suggestions/Suggestion.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2017 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 android.service.settings.suggestions;
+
+import android.annotation.SystemApi;
+import android.app.PendingIntent;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * Data object that has information about a device suggestion.
+ *
+ * @hide
+ */
+@SystemApi
+public final class Suggestion implements Parcelable {
+
+    private final String mId;
+    private final CharSequence mTitle;
+    private final CharSequence mSummary;
+    private final PendingIntent mPendingIntent;
+
+    /**
+     * Gets the id for the suggestion object.
+     */
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * Title of the suggestion that is shown to the user.
+     */
+    public CharSequence getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Optional summary describing what this suggestion controls.
+     */
+    public CharSequence getSummary() {
+        return mSummary;
+    }
+
+    /**
+     * The Intent to launch when the suggestion is activated.
+     */
+    public PendingIntent getPendingIntent() {
+        return mPendingIntent;
+    }
+
+    private Suggestion(Builder builder) {
+        mId = builder.mId;
+        mTitle = builder.mTitle;
+        mSummary = builder.mSummary;
+        mPendingIntent = builder.mPendingIntent;
+    }
+
+    private Suggestion(Parcel in) {
+        mId = in.readString();
+        mTitle = in.readCharSequence();
+        mSummary = in.readCharSequence();
+        mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader());
+    }
+
+    public static final Creator<Suggestion> CREATOR = new Creator<Suggestion>() {
+        @Override
+        public Suggestion createFromParcel(Parcel in) {
+            return new Suggestion(in);
+        }
+
+        @Override
+        public Suggestion[] newArray(int size) {
+            return new Suggestion[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mId);
+        dest.writeCharSequence(mTitle);
+        dest.writeCharSequence(mSummary);
+        dest.writeParcelable(mPendingIntent, flags);
+    }
+
+    /**
+     * Builder class for {@link Suggestion}.
+     */
+    public static class Builder {
+        private final String mId;
+        private CharSequence mTitle;
+        private CharSequence mSummary;
+        private PendingIntent mPendingIntent;
+
+        public Builder(String id) {
+            if (TextUtils.isEmpty(id)) {
+                throw new IllegalArgumentException("Suggestion id cannot be empty");
+            }
+            mId = id;
+        }
+
+        /**
+         * Sets suggestion title
+         */
+        public Builder setTitle(CharSequence title) {
+            mTitle = title;
+            return this;
+        }
+
+        /**
+         * Sets suggestion summary
+         */
+        public Builder setSummary(CharSequence summary) {
+            mSummary = summary;
+            return this;
+        }
+
+        /**
+         * Sets suggestion intent
+         */
+        public Builder setPendingIntent(PendingIntent pendingIntent) {
+            mPendingIntent = pendingIntent;
+            return this;
+        }
+
+        /**
+         * Builds an immutable {@link Suggestion} object.
+         */
+        public Suggestion build() {
+            return new Suggestion(this /* builder */);
+        }
+    }
+}
diff --git a/android/service/settings/suggestions/SuggestionService.java b/android/service/settings/suggestions/SuggestionService.java
new file mode 100644
index 0000000..2a4c84c
--- /dev/null
+++ b/android/service/settings/suggestions/SuggestionService.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 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 android.service.settings.suggestions;
+
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * This is the base class for implementing suggestion service. A suggestion service is responsible
+ * to provide a collection of {@link Suggestion}s for the current user when queried.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class SuggestionService extends Service {
+
+    private static final String TAG = "SuggestionService";
+    private static final boolean DEBUG = false;
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return new ISuggestionService.Stub() {
+            @Override
+            public List<Suggestion> getSuggestions() {
+                if (DEBUG) {
+                    Log.d(TAG, "getSuggestions() " + getPackageName());
+                }
+                return onGetSuggestions();
+            }
+
+            @Override
+            public  void dismissSuggestion(Suggestion suggestion) {
+                if (DEBUG) {
+                    Log.d(TAG, "dismissSuggestion() " + getPackageName());
+                }
+                onSuggestionDismissed(suggestion);
+            }
+        };
+    }
+
+    /**
+     * Return all available suggestions.
+     */
+    public abstract List<Suggestion> onGetSuggestions();
+
+    /**
+     * Dismiss a suggestion. The suggestion will not be included in future
+     * {@link #onGetSuggestions()} calls.
+     * @param suggestion
+     */
+    public abstract void onSuggestionDismissed(Suggestion suggestion);
+}
diff --git a/android/support/v17/leanback/media/PlaybackTransportControlGlue.java b/android/support/v17/leanback/media/PlaybackTransportControlGlue.java
index d0496e4..61ea52b 100644
--- a/android/support/v17/leanback/media/PlaybackTransportControlGlue.java
+++ b/android/support/v17/leanback/media/PlaybackTransportControlGlue.java
@@ -254,9 +254,7 @@
             // playing    paused                  paused
             // paused     playing       playing
             // ff/rw      playing       playing   paused
-            if (canPause
-                    && (canPlay ? mIsPlaying :
-                    !mIsPlaying)) {
+            if (canPause && mIsPlaying) {
                 mIsPlaying = false;
                 pause();
             } else if (canPlay && !mIsPlaying) {
diff --git a/android/support/v4/app/FragmentManager.java b/android/support/v4/app/FragmentManager.java
index 20e8d9e..6e6caa0 100644
--- a/android/support/v4/app/FragmentManager.java
+++ b/android/support/v4/app/FragmentManager.java
@@ -1594,6 +1594,8 @@
     private void animateRemoveFragment(@NonNull final Fragment fragment,
             @NonNull AnimationOrAnimator anim, final int newState) {
         final View viewToAnimate = fragment.mView;
+        final ViewGroup container = fragment.mContainer;
+        container.startViewTransition(viewToAnimate);
         fragment.setStateAfterAnimating(newState);
         if (anim.animation != null) {
             Animation animation = anim.animation;
@@ -1603,6 +1605,8 @@
                 @Override
                 public void onAnimationEnd(Animation animation) {
                     super.onAnimationEnd(animation);
+                    container.endViewTransition(viewToAnimate);
+
                     if (fragment.getAnimatingAway() != null) {
                         fragment.setAnimatingAway(null);
                         moveToState(fragment, fragment.getStateAfterAnimating(), 0, 0, false);
@@ -1612,25 +1616,18 @@
             setHWLayerAnimListenerIfAlpha(viewToAnimate, anim);
             fragment.mView.startAnimation(animation);
         } else {
-            final Animator animator = anim.animator;
+            Animator animator = anim.animator;
             fragment.setAnimator(anim.animator);
-            final ViewGroup container = fragment.mContainer;
-            if (container != null) {
-                container.startViewTransition(viewToAnimate);
-            }
             animator.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator anim) {
-                    // AnimatorSet in API 26 (only) can end() during start(), so delay by posting
-                    if (container == null || container.indexOfChild(viewToAnimate) < 0) {
-                        finishAnimatedFragmentRemoval(fragment, container, viewToAnimate);
-                    } else {
-                        viewToAnimate.post(new Runnable() {
-                            @Override
-                            public void run() {
-                                finishAnimatedFragmentRemoval(fragment, container, viewToAnimate);
-                            }
-                        });
+                    container.endViewTransition(viewToAnimate);
+                    // If an animator ends immediately, we can just pretend there is no animation.
+                    // When that happens the the fragment's view won't have been removed yet.
+                    Animator animator = fragment.getAnimator();
+                    fragment.setAnimator(null);
+                    if (animator != null && container.indexOfChild(viewToAnimate) < 0) {
+                        moveToState(fragment, fragment.getStateAfterAnimating(), 0, 0, false);
                     }
                 }
             });
@@ -1640,17 +1637,6 @@
         }
     }
 
-    void finishAnimatedFragmentRemoval(Fragment fragment, ViewGroup container, View view) {
-        if (container != null) {
-            container.endViewTransition(view);
-        }
-        if (fragment.getAnimator() != null) {
-            fragment.setAnimator(null);
-            moveToState(fragment, fragment.getStateAfterAnimating(), 0, 0,
-                    false);
-        }
-    }
-
     void moveToState(Fragment f) {
         moveToState(f, mCurState, 0, 0, false);
     }
@@ -2646,7 +2632,6 @@
                     // Give up waiting for the animation and just end it.
                     final int stateAfterAnimating = fragment.getStateAfterAnimating();
                     final View animatingAway = fragment.getAnimatingAway();
-                    fragment.setAnimatingAway(null);
                     Animation animation = animatingAway.getAnimation();
                     if (animation != null) {
                         animation.cancel();
@@ -2654,6 +2639,7 @@
                         // and will instead cause the animation to infinitely loop
                         animatingAway.clearAnimation();
                     }
+                    fragment.setAnimatingAway(null);
                     moveToState(fragment, stateAfterAnimating, 0, 0, false);
                 } else if (fragment.getAnimator() != null) {
                     fragment.getAnimator().end();
diff --git a/android/support/v4/media/MediaBrowserServiceCompat.java b/android/support/v4/media/MediaBrowserServiceCompat.java
index 8175aae..53b111a 100644
--- a/android/support/v4/media/MediaBrowserServiceCompat.java
+++ b/android/support/v4/media/MediaBrowserServiceCompat.java
@@ -550,7 +550,7 @@
     /**
      * All the info about a connection.
      */
-    private static class ConnectionRecord {
+    private class ConnectionRecord implements IBinder.DeathRecipient {
         String pkg;
         Bundle rootHints;
         ServiceCallbacks callbacks;
@@ -559,6 +559,16 @@
 
         ConnectionRecord() {
         }
+
+        @Override
+        public void binderDied() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mConnections.remove(callbacks.asBinder());
+                }
+            });
+        }
     }
 
     /**
@@ -747,6 +757,7 @@
                     } else {
                         try {
                             mConnections.put(b, connection);
+                            b.linkToDeath(connection, 0);
                             if (mSession != null) {
                                 callbacks.onConnect(connection.root.getRootId(),
                                         mSession, connection.root.getExtras());
@@ -771,6 +782,7 @@
                     final ConnectionRecord old = mConnections.remove(b);
                     if (old != null) {
                         // TODO
+                        old.callbacks.asBinder().unlinkToDeath(old, 0);
                     }
                 }
             });
@@ -852,6 +864,11 @@
                     connection.callbacks = callbacks;
                     connection.rootHints = rootHints;
                     mConnections.put(b, connection);
+                    try {
+                        b.linkToDeath(connection, 0);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "IBinder is already dead.");
+                    }
                 }
             });
         }
@@ -862,7 +879,10 @@
                 @Override
                 public void run() {
                     final IBinder b = callbacks.asBinder();
-                    mConnections.remove(b);
+                    ConnectionRecord old = mConnections.remove(b);
+                    if (old != null) {
+                        b.unlinkToDeath(old, 0);
+                    }
                 }
             });
         }
diff --git a/android/support/v4/os/BuildCompat.java b/android/support/v4/os/BuildCompat.java
index 9a48c5f..586557d 100644
--- a/android/support/v4/os/BuildCompat.java
+++ b/android/support/v4/os/BuildCompat.java
@@ -60,6 +60,7 @@
      *             be removed in a future release of the Support Library. Instead use
      *             {@code Build.SDK_INT >= Build.VERSION_CODES#O}.
      */
+    @Deprecated
     public static boolean isAtLeastO() {
         return VERSION.SDK_INT >= 26;
     }
@@ -68,7 +69,11 @@
      * Checks if the device is running on a pre-release version of Android O MR1 or newer.
      * <p>
      * @return {@code true} if O MR1 APIs are available for use, {@code false} otherwise
+     * @deprecated Android O MR1 is a finalized release and this method is no longer necessary. It
+     *             will be removed in a future release of the Support Library. Instead, use
+     *             {@code Build.SDK_INT >= Build.VERSION_CODES#O_MR1}.
      */
+    @Deprecated
     public static boolean isAtLeastOMR1() {
         return VERSION.SDK_INT >= 27;
     }
diff --git a/android/support/v7/preference/CollapsiblePreferenceGroupController.java b/android/support/v7/preference/CollapsiblePreferenceGroupController.java
new file mode 100644
index 0000000..e15ca18
--- /dev/null
+++ b/android/support/v7/preference/CollapsiblePreferenceGroupController.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2017 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 android.support.v7.preference;
+
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A controller to handle advanced children display logic with collapsible functionality.
+ */
+final class CollapsiblePreferenceGroupController
+        implements PreferenceGroup.PreferenceInstanceStateCallback {
+
+    private final PreferenceGroupAdapter mPreferenceGroupAdapter;
+    private int mMaxPreferenceToShow;
+    private final Context mContext;
+
+    CollapsiblePreferenceGroupController(PreferenceGroup preferenceGroup,
+            PreferenceGroupAdapter preferenceGroupAdapter) {
+        mPreferenceGroupAdapter = preferenceGroupAdapter;
+        mMaxPreferenceToShow = preferenceGroup.getInitialExpandedChildrenCount();
+        mContext = preferenceGroup.getContext();
+        preferenceGroup.setPreferenceInstanceStateCallback(this);
+    }
+
+    /**
+     * Creates the visible portion of the flattened preferences.
+     *
+     * @param flattenedPreferenceList the flattened children of the preference group
+     * @return the visible portion of the flattened preferences
+     */
+    public List<Preference> createVisiblePreferencesList(List<Preference> flattenedPreferenceList) {
+        int visiblePreferenceCount = 0;
+        final List<Preference> visiblePreferenceList =
+                new ArrayList<>(flattenedPreferenceList.size());
+        // Copy only the visible preferences to the active list up to the maximum specified
+        for (final Preference preference : flattenedPreferenceList) {
+            if (preference.isVisible()) {
+                if (visiblePreferenceCount < mMaxPreferenceToShow) {
+                    visiblePreferenceList.add(preference);
+                }
+                // Do no count PreferenceGroup as expanded preference because the list of its child
+                // is already contained in the flattenedPreferenceList
+                if (!(preference instanceof PreferenceGroup)) {
+                    visiblePreferenceCount++;
+                }
+            }
+        }
+        // If there are any visible preferences being hidden, add an expand button to show the rest
+        // of the preferences. Clicking the expand button will show all the visible preferences and
+        // reset mMaxPreferenceToShow
+        if (showLimitedChildren() && visiblePreferenceCount > mMaxPreferenceToShow) {
+            final ExpandButton expandButton  = createExpandButton(visiblePreferenceList,
+                    flattenedPreferenceList);
+            visiblePreferenceList.add(expandButton);
+        }
+        return visiblePreferenceList;
+    }
+
+    /**
+     * Called when a preference has changed its visibility.
+     *
+     * @param preference The preference whose visibility has changed.
+     * @return {@code true} if view update has been handled by this controller.
+     */
+    public boolean onPreferenceVisibilityChange(Preference preference) {
+        if (showLimitedChildren()) {
+            // We only want to show up to the max number of preferences. Preference visibility
+            // change can result in the expand button being added/removed, as well as expand button
+            // summary change. Rebulid the data to ensure the correct data is shown.
+            mPreferenceGroupAdapter.onPreferenceHierarchyChange(preference);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public Parcelable saveInstanceState(Parcelable state) {
+        final SavedState myState = new SavedState(state);
+        myState.mMaxPreferenceToShow = mMaxPreferenceToShow;
+        return myState;
+    }
+
+    @Override
+    public Parcelable restoreInstanceState(Parcelable state) {
+        if (state == null || !state.getClass().equals(SavedState.class)) {
+            // Didn't save state for us in saveInstanceState
+            return state;
+        }
+        SavedState myState = (SavedState) state;
+        final int restoredMaxToShow = myState.mMaxPreferenceToShow;
+        if (mMaxPreferenceToShow != restoredMaxToShow) {
+            mMaxPreferenceToShow = restoredMaxToShow;
+            mPreferenceGroupAdapter.onPreferenceHierarchyChange(null);
+        }
+        return myState.getSuperState();
+    }
+
+    private ExpandButton createExpandButton(List<Preference> visiblePreferenceList,
+            List<Preference> flattenedPreferenceList) {
+        final ExpandButton preference = new ExpandButton(mContext, visiblePreferenceList,
+                flattenedPreferenceList);
+        preference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+            @Override
+            public boolean onPreferenceClick(Preference preference) {
+                mMaxPreferenceToShow = Integer.MAX_VALUE;
+                mPreferenceGroupAdapter.onPreferenceHierarchyChange(preference);
+                return true;
+            }
+        });
+        return preference;
+    }
+
+    private boolean showLimitedChildren() {
+        return mMaxPreferenceToShow != Integer.MAX_VALUE;
+    }
+
+    /**
+     * A {@link Preference} that provides capability to expand the collapsed items in the
+     * {@link PreferenceGroup}.
+     */
+    static class ExpandButton extends Preference {
+        ExpandButton(Context context, List<Preference> visiblePreferenceList,
+                List<Preference> flattenedPreferenceList) {
+            super(context);
+            initLayout();
+            setSummary(visiblePreferenceList, flattenedPreferenceList);
+        }
+
+        private void initLayout() {
+            setLayoutResource(R.layout.expand_button);
+            setIcon(R.drawable.ic_arrow_down_24dp);
+            setTitle(R.string.expand_button_title);
+            // Sets a high order so that the expand button will be placed at the bottom of the group
+            setOrder(999);
+        }
+
+        /*
+         * The summary of this will be the list of title for collapsed preferences. Iterate through
+         * the preferences not in the visible list and add its title to the summary text.
+         */
+        private void setSummary(List<Preference> visiblePreferenceList,
+                List<Preference> flattenedPreferenceList) {
+            final Preference lastVisiblePreference =
+                    visiblePreferenceList.get(visiblePreferenceList.size() - 1);
+            final int collapsedIndex = flattenedPreferenceList.indexOf(lastVisiblePreference) + 1;
+            CharSequence summary = null;
+            for (int i = collapsedIndex; i < flattenedPreferenceList.size(); i++) {
+                final Preference preference = flattenedPreferenceList.get(i);
+                if (preference instanceof PreferenceGroup) {
+                    continue;
+                }
+                final CharSequence title = preference.getTitle();
+                if (!TextUtils.isEmpty(title)) {
+                    if (summary == null) {
+                        summary = title;
+                    } else {
+                        summary = getContext().getString(
+                                R.string.summary_collapsed_preference_list, summary, title);
+                    }
+                }
+            }
+            setSummary(summary);
+        }
+
+        @Override
+        public void onBindViewHolder(PreferenceViewHolder holder) {
+            super.onBindViewHolder(holder);
+            holder.setDividerAllowedAbove(false);
+        }
+    }
+
+    /**
+     * A class for managing the instance state of a {@link PreferenceGroup}.
+     */
+    static class SavedState extends Preference.BaseSavedState {
+        int mMaxPreferenceToShow;
+
+        SavedState(Parcel source) {
+            super(source);
+            mMaxPreferenceToShow = source.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeInt(mMaxPreferenceToShow);
+        }
+
+        SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new Parcelable.Creator<SavedState>() {
+                    @Override
+                    public SavedState createFromParcel(Parcel in) {
+                        return new SavedState(in);
+                    }
+
+                    @Override
+                    public SavedState[] newArray(int size) {
+                        return new SavedState[size];
+                    }
+                };
+    }
+}
diff --git a/android/support/v7/preference/PreferenceGroup.java b/android/support/v7/preference/PreferenceGroup.java
index d285ee6..a951e70 100644
--- a/android/support/v7/preference/PreferenceGroup.java
+++ b/android/support/v7/preference/PreferenceGroup.java
@@ -22,7 +22,9 @@
 import android.content.res.TypedArray;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Parcelable;
 import android.support.annotation.RestrictTo;
+import android.support.annotation.VisibleForTesting;
 import android.support.v4.content.res.TypedArrayUtils;
 import android.support.v4.util.SimpleArrayMap;
 import android.text.TextUtils;
@@ -45,6 +47,7 @@
  * </div>
  *
  * @attr name android:orderingFromXml
+ * @attr name initialExpandedChildrenCount
  */
 public abstract class PreferenceGroup extends Preference {
     /**
@@ -60,6 +63,9 @@
 
     private boolean mAttachedToHierarchy = false;
 
+    private int mInitialExpandedChildrenCount = Integer.MAX_VALUE;
+    private PreferenceInstanceStateCallback mPreferenceInstanceStateCallback;
+
     private final SimpleArrayMap<String, Long> mIdRecycleCache = new SimpleArrayMap<>();
     private final Handler mHandler = new Handler();
     private final Runnable mClearRecycleCacheRunnable = new Runnable() {
@@ -83,6 +89,11 @@
                 TypedArrayUtils.getBoolean(a, R.styleable.PreferenceGroup_orderingFromXml,
                         R.styleable.PreferenceGroup_orderingFromXml, true);
 
+        if (a.hasValue(R.styleable.PreferenceGroup_initialExpandedChildrenCount)) {
+            mInitialExpandedChildrenCount = TypedArrayUtils.getInt(
+                    a, R.styleable.PreferenceGroup_initialExpandedChildrenCount,
+                            R.styleable.PreferenceGroup_initialExpandedChildrenCount, -1);
+        }
         a.recycle();
     }
 
@@ -120,6 +131,35 @@
     }
 
     /**
+     * Sets the maximal number of children that are shown when the preference group is launched
+     * where the rest of the children will be hidden.
+     * If some children are hidden an expand button will be provided to show all the hidden
+     * children. Any child in any level of the hierarchy that is also a preference group (e.g.
+     * preference category) will not be counted towards the limit. But instead the children of such
+     * group will be counted.
+     * By default, all children will be shown, so the default value of this attribute is equal to
+     * Integer.MAX_VALUE.
+     *
+     * @param expandedCount the number of children that is initially shown.
+     *
+     * @attr ref R.styleable#PreferenceGroup_initialExpandedChildrenCount
+     */
+    public void setInitialExpandedChildrenCount(int expandedCount) {
+        mInitialExpandedChildrenCount = expandedCount;
+    }
+
+    /**
+     * Gets the maximal number of children that is initially shown.
+     *
+     * @return the maximal number of children that is initially shown.
+     *
+     * @attr ref R.styleable#PreferenceGroup_initialExpandedChildrenCount
+     */
+    public int getInitialExpandedChildrenCount() {
+        return mInitialExpandedChildrenCount;
+    }
+
+    /**
      * Called by the inflater to add an item to this group.
      */
     public void addItemFromInflater(Preference preference) {
@@ -400,6 +440,44 @@
         }
     }
 
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        final Parcelable superState = super.onSaveInstanceState();
+        if (mPreferenceInstanceStateCallback != null) {
+            return mPreferenceInstanceStateCallback.saveInstanceState(superState);
+        }
+        return superState;
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Parcelable state) {
+        if (mPreferenceInstanceStateCallback != null) {
+            state = mPreferenceInstanceStateCallback.restoreInstanceState(state);
+        }
+        super.onRestoreInstanceState(state);
+    }
+
+    /**
+     * Sets the instance state callback.
+     *
+     * @param callback The callback.
+     * @see #onSaveInstanceState()
+     * @see #onRestoreInstanceState()
+     */
+    final void setPreferenceInstanceStateCallback(PreferenceInstanceStateCallback callback) {
+        mPreferenceInstanceStateCallback = callback;
+    }
+
+    /**
+     * Gets the instance state callback.
+     *
+     * @return the instance state callback.
+     */
+    @VisibleForTesting
+    final PreferenceInstanceStateCallback getPreferenceInstanceStateCallback() {
+        return mPreferenceInstanceStateCallback;
+    }
+
     /**
      * Interface for PreferenceGroup Adapters to implement so that
      * {@link android.support.v14.preference.PreferenceFragment#scrollToPreference(String)} and
@@ -426,4 +504,29 @@
          */
         int getPreferenceAdapterPosition(Preference preference);
     }
+
+    /**
+     * Interface for callback to implement so that they can save and restore the preference group's
+     * instance state.
+     */
+    interface PreferenceInstanceStateCallback {
+
+        /**
+         * Save the internal state that can later be used to create a new instance with that
+         * same state.
+         *
+         * @param state The Parcelable to save the current dynamic state.
+         */
+        Parcelable saveInstanceState(Parcelable state);
+
+        /**
+         * Restore the previously saved state from the given parcelable.
+         *
+         * @param state The Parcelable that holds the previously saved state.
+         * @return the super state if data has been saved in the state in {@link saveInstanceState}
+         *         or state otherwise
+         */
+        Parcelable restoreInstanceState(Parcelable state);
+    }
+
 }
diff --git a/android/support/v7/preference/PreferenceGroupAdapter.java b/android/support/v7/preference/PreferenceGroupAdapter.java
index d1c630f..00a0c5b 100644
--- a/android/support/v7/preference/PreferenceGroupAdapter.java
+++ b/android/support/v7/preference/PreferenceGroupAdapter.java
@@ -22,6 +22,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.support.annotation.RestrictTo;
+import android.support.annotation.VisibleForTesting;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.util.DiffUtil;
@@ -73,7 +74,9 @@
 
     private PreferenceLayout mTempPreferenceLayout = new PreferenceLayout();
 
-    private Handler mHandler = new Handler();
+    private Handler mHandler;
+
+    private CollapsiblePreferenceGroupController mPreferenceGroupController;
 
     private Runnable mSyncRunnable = new Runnable() {
         @Override
@@ -117,7 +120,14 @@
     }
 
     public PreferenceGroupAdapter(PreferenceGroup preferenceGroup) {
+        this(preferenceGroup, new Handler());
+    }
+
+    private PreferenceGroupAdapter(PreferenceGroup preferenceGroup, Handler handler) {
         mPreferenceGroup = preferenceGroup;
+        mHandler = handler;
+        mPreferenceGroupController =
+                new CollapsiblePreferenceGroupController(preferenceGroup, this);
         // If this group gets or loses any children, let us know
         mPreferenceGroup.setOnPreferenceChangeInternalListener(this);
 
@@ -134,6 +144,12 @@
         syncMyPreferences();
     }
 
+    @VisibleForTesting
+    static PreferenceGroupAdapter createInstanceWithCustomHandler(PreferenceGroup preferenceGroup,
+            Handler handler) {
+        return new PreferenceGroupAdapter(preferenceGroup, handler);
+    }
+
     private void syncMyPreferences() {
         for (final Preference preference : mPreferenceListInternal) {
             // Clear out the listeners in anticipation of some items being removed. This listener
@@ -143,13 +159,8 @@
         final List<Preference> fullPreferenceList = new ArrayList<>(mPreferenceListInternal.size());
         flattenPreferenceGroup(fullPreferenceList, mPreferenceGroup);
 
-        final List<Preference> visiblePreferenceList = new ArrayList<>(fullPreferenceList.size());
-        // Copy only the visible preferences to the active list
-        for (final Preference preference : fullPreferenceList) {
-            if (preference.isVisible()) {
-                visiblePreferenceList.add(preference);
-            }
-        }
+        final List<Preference> visiblePreferenceList =
+                mPreferenceGroupController.createVisiblePreferencesList(fullPreferenceList);
 
         final List<Preference> oldVisibleList = mPreferenceList;
         mPreferenceList = visiblePreferenceList;
@@ -277,6 +288,9 @@
         if (!mPreferenceListInternal.contains(preference)) {
             return;
         }
+        if (mPreferenceGroupController.onPreferenceVisibilityChange(preference)) {
+            return;
+        }
         if (preference.isVisible()) {
             // The preference has become visible, we need to add it in the correct location.
 
diff --git a/android/support/v7/recyclerview/extensions/ListAdapter.java b/android/support/v7/recyclerview/extensions/ListAdapter.java
index 8035a27..e08cb53 100644
--- a/android/support/v7/recyclerview/extensions/ListAdapter.java
+++ b/android/support/v7/recyclerview/extensions/ListAdapter.java
@@ -46,7 +46,7 @@
  *     }
  * }
  *
- * class MyActivity extends Activity implements LifecycleRegistryOwner {
+ * class MyActivity extends AppCompatActivity {
  *     {@literal @}Override
  *     public void onCreate(Bundle savedState) {
  *         super.onCreate(savedState);
diff --git a/android/support/v7/recyclerview/extensions/ListAdapterHelper.java b/android/support/v7/recyclerview/extensions/ListAdapterHelper.java
index e4a53fd..b47b833 100644
--- a/android/support/v7/recyclerview/extensions/ListAdapterHelper.java
+++ b/android/support/v7/recyclerview/extensions/ListAdapterHelper.java
@@ -55,7 +55,7 @@
  *     }
  * }
  *
- * class MyActivity extends Activity implements LifecycleRegistryOwner {
+ * class MyActivity extends AppCompatActivity {
  *     {@literal @}Override
  *     public void onCreate(Bundle savedState) {
  *         super.onCreate(savedState);
diff --git a/android/support/v7/view/menu/CascadingMenuPopup.java b/android/support/v7/view/menu/CascadingMenuPopup.java
index 73499cf..564bbfc 100644
--- a/android/support/v7/view/menu/CascadingMenuPopup.java
+++ b/android/support/v7/view/menu/CascadingMenuPopup.java
@@ -404,14 +404,14 @@
             final boolean showOnRight = nextMenuPosition == HORIZ_POSITION_RIGHT;
             mLastPosition = nextMenuPosition;
 
-            final int parentOffsetLeft;
-            final int parentOffsetTop;
+            final int parentOffsetX;
+            final int parentOffsetY;
             if (Build.VERSION.SDK_INT >= 26) {
                 // Anchor the submenu directly to the parent menu item view. This allows for
                 // accurate submenu positioning when the parent menu is being moved.
                 popupWindow.setAnchorView(parentView);
-                parentOffsetLeft = 0;
-                parentOffsetTop = 0;
+                parentOffsetX = 0;
+                parentOffsetY = 0;
             } else {
                 // Framework does not allow anchoring to a view in another popup window. Use the
                 // same top-level anchor as the parent menu is using, with appropriate offsets.
@@ -428,10 +428,19 @@
                 final int[] parentViewScreenLocation = new int[2];
                 parentView.getLocationOnScreen(parentViewScreenLocation);
 
+                // For Gravity.LEFT case, the baseline is just the left border of the view. So we
+                // can use the X of the location directly. But for Gravity.RIGHT case, the baseline
+                // is the right border. So we need add view's width with the location to make the
+                // baseline as the right border correctly.
+                if ((mDropDownGravity & (Gravity.RIGHT | Gravity.LEFT)) == Gravity.RIGHT) {
+                    anchorScreenLocation[0] += mAnchorView.getWidth();
+                    parentViewScreenLocation[0] += parentView.getWidth();
+                }
+
                 // If used as horizontal/vertical offsets, these values would position the submenu
                 // at the exact same position as the parent item.
-                parentOffsetLeft = parentViewScreenLocation[0] - anchorScreenLocation[0];
-                parentOffsetTop = parentViewScreenLocation[1] - anchorScreenLocation[1];
+                parentOffsetX = parentViewScreenLocation[0] - anchorScreenLocation[0];
+                parentOffsetY = parentViewScreenLocation[1] - anchorScreenLocation[1];
             }
 
             // Adjust the horizontal offset to display the submenu to the right or to the left
@@ -441,22 +450,22 @@
             final int x;
             if ((mDropDownGravity & Gravity.RIGHT) == Gravity.RIGHT) {
                 if (showOnRight) {
-                    x = parentOffsetLeft + menuWidth;
+                    x = parentOffsetX + menuWidth;
                 } else {
-                    x = parentOffsetLeft - parentView.getWidth();
+                    x = parentOffsetX - parentView.getWidth();
                 }
             } else {
                 if (showOnRight) {
-                    x = parentOffsetLeft + parentView.getWidth();
+                    x = parentOffsetX + parentView.getWidth();
                 } else {
-                    x = parentOffsetLeft - menuWidth;
+                    x = parentOffsetX - menuWidth;
                 }
             }
             popupWindow.setHorizontalOffset(x);
 
             // Vertically align with the parent item.
             popupWindow.setOverlapAnchor(true);
-            popupWindow.setVerticalOffset(parentOffsetTop);
+            popupWindow.setVerticalOffset(parentOffsetY);
         } else {
             if (mHasXOffset) {
                 popupWindow.setHorizontalOffset(mXOffset);
diff --git a/android/support/v7/widget/AppCompatImageButton.java b/android/support/v7/widget/AppCompatImageButton.java
index 90e6aa9..b2b1f10 100644
--- a/android/support/v7/widget/AppCompatImageButton.java
+++ b/android/support/v7/widget/AppCompatImageButton.java
@@ -23,7 +23,6 @@
 import android.graphics.Bitmap;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.support.annotation.DrawableRes;
 import android.support.annotation.Nullable;
@@ -102,14 +101,6 @@
     }
 
     @Override
-    public void setImageIcon(@Nullable Icon icon) {
-        super.setImageIcon(icon);
-        if (mImageHelper != null) {
-            mImageHelper.applySupportImageTint();
-        }
-    }
-
-    @Override
     public void setImageURI(@Nullable Uri uri) {
         super.setImageURI(uri);
         if (mImageHelper != null) {
diff --git a/android/support/v7/widget/AppCompatImageView.java b/android/support/v7/widget/AppCompatImageView.java
index 0844f9a..f50799e 100644
--- a/android/support/v7/widget/AppCompatImageView.java
+++ b/android/support/v7/widget/AppCompatImageView.java
@@ -23,7 +23,6 @@
 import android.graphics.Bitmap;
 import android.graphics.PorterDuff;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.support.annotation.DrawableRes;
 import android.support.annotation.Nullable;
@@ -112,14 +111,6 @@
     }
 
     @Override
-    public void setImageIcon(@Nullable Icon icon) {
-        super.setImageIcon(icon);
-        if (mImageHelper != null) {
-            mImageHelper.applySupportImageTint();
-        }
-    }
-
-    @Override
     public void setImageURI(@Nullable Uri uri) {
         super.setImageURI(uri);
         if (mImageHelper != null) {
diff --git a/android/support/v7/widget/RecyclerView.java b/android/support/v7/widget/RecyclerView.java
index dea8546..7009733 100644
--- a/android/support/v7/widget/RecyclerView.java
+++ b/android/support/v7/widget/RecyclerView.java
@@ -44,6 +44,7 @@
 import android.support.annotation.RestrictTo;
 import android.support.annotation.VisibleForTesting;
 import android.support.v4.os.TraceCompat;
+import android.support.v4.util.Preconditions;
 import android.support.v4.view.AbsSavedState;
 import android.support.v4.view.InputDeviceCompat;
 import android.support.v4.view.MotionEventCompat;
@@ -417,6 +418,8 @@
      */
     private int mDispatchScrollCounter = 0;
 
+    @NonNull
+    private EdgeEffectFactory mEdgeEffectFactory = new EdgeEffectFactory();
     private EdgeEffect mLeftGlow, mTopGlow, mRightGlow, mBottomGlow;
 
     ItemAnimator mItemAnimator = new DefaultItemAnimator();
@@ -2306,7 +2309,7 @@
         if (mLeftGlow != null) {
             return;
         }
-        mLeftGlow = new EdgeEffect(getContext());
+        mLeftGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_LEFT);
         if (mClipToPadding) {
             mLeftGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
@@ -2319,7 +2322,7 @@
         if (mRightGlow != null) {
             return;
         }
-        mRightGlow = new EdgeEffect(getContext());
+        mRightGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_RIGHT);
         if (mClipToPadding) {
             mRightGlow.setSize(getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
                     getMeasuredWidth() - getPaddingLeft() - getPaddingRight());
@@ -2332,7 +2335,7 @@
         if (mTopGlow != null) {
             return;
         }
-        mTopGlow = new EdgeEffect(getContext());
+        mTopGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_TOP);
         if (mClipToPadding) {
             mTopGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
@@ -2346,7 +2349,7 @@
         if (mBottomGlow != null) {
             return;
         }
-        mBottomGlow = new EdgeEffect(getContext());
+        mBottomGlow = mEdgeEffectFactory.createEdgeEffect(this, EdgeEffectFactory.DIRECTION_BOTTOM);
         if (mClipToPadding) {
             mBottomGlow.setSize(getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
                     getMeasuredHeight() - getPaddingTop() - getPaddingBottom());
@@ -2360,6 +2363,32 @@
     }
 
     /**
+     * Set a {@link EdgeEffectFactory} for this {@link RecyclerView}.
+     * <p>
+     * When a new {@link EdgeEffectFactory} is set, any existing over-scroll effects are cleared
+     * and new effects are created as needed using
+     * {@link EdgeEffectFactory#createEdgeEffect(RecyclerView, int)}
+     *
+     * @param edgeEffectFactory The {@link EdgeEffectFactory} instance.
+     */
+    public void setEdgeEffectFactory(@NonNull EdgeEffectFactory edgeEffectFactory) {
+        Preconditions.checkNotNull(edgeEffectFactory);
+        mEdgeEffectFactory = edgeEffectFactory;
+        invalidateGlows();
+    }
+
+    /**
+     * Retrieves the previously set {@link EdgeEffectFactory} or the default factory if nothing
+     * was set.
+     *
+     * @return The previously set {@link EdgeEffectFactory}
+     * @see #setEdgeEffectFactory(EdgeEffectFactory)
+     */
+    public EdgeEffectFactory getEdgeEffectFactory() {
+        return mEdgeEffectFactory;
+    }
+
+    /**
      * Since RecyclerView is a collection ViewGroup that includes virtual children (items that are
      * in the Adapter but not visible in the UI), it employs a more involved focus search strategy
      * that differs from other ViewGroups.
@@ -5130,6 +5159,46 @@
     }
 
     /**
+     * EdgeEffectFactory lets you customize the over-scroll edge effect for RecyclerViews.
+     *
+     * @see RecyclerView#setEdgeEffectFactory(EdgeEffectFactory)
+     */
+    public static class EdgeEffectFactory {
+
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef({DIRECTION_LEFT, DIRECTION_TOP, DIRECTION_RIGHT, DIRECTION_BOTTOM})
+        public @interface EdgeDirection {}
+
+        /**
+         * Direction constant for the left edge
+         */
+        public static final int DIRECTION_LEFT = 0;
+
+        /**
+         * Direction constant for the top edge
+         */
+        public static final int DIRECTION_TOP = 1;
+
+        /**
+         * Direction constant for the right edge
+         */
+        public static final int DIRECTION_RIGHT = 2;
+
+        /**
+         * Direction constant for the bottom edge
+         */
+        public static final int DIRECTION_BOTTOM = 3;
+
+        /**
+         * Create a new EdgeEffect for the provided direction.
+         */
+        protected @NonNull EdgeEffect createEdgeEffect(RecyclerView view,
+                @EdgeDirection int direction) {
+            return new EdgeEffect(view.getContext());
+        }
+    }
+
+    /**
      * RecycledViewPool lets you share Views between multiple RecyclerViews.
      * <p>
      * If you want to recycle views across RecyclerViews, create an instance of RecycledViewPool
diff --git a/android/telecom/DefaultDialerManager.java b/android/telecom/DefaultDialerManager.java
index 2a707c9..1806aee 100644
--- a/android/telecom/DefaultDialerManager.java
+++ b/android/telecom/DefaultDialerManager.java
@@ -22,6 +22,7 @@
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.Process;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
 
@@ -163,7 +164,10 @@
 
         for (ResolveInfo resolveInfo : resolveInfoList) {
             final ActivityInfo activityInfo = resolveInfo.activityInfo;
-            if (activityInfo != null && !packageNames.contains(activityInfo.packageName)) {
+            if (activityInfo != null
+                    && !packageNames.contains(activityInfo.packageName)
+                    // ignore cross profile intent handler
+                    && resolveInfo.targetUserId == UserHandle.USER_CURRENT) {
                 packageNames.add(activityInfo.packageName);
             }
         }
diff --git a/android/telephony/MbmsDownloadManager.java b/android/telephony/MbmsDownloadManager.java
deleted file mode 100644
index 1e8cf18..0000000
--- a/android/telephony/MbmsDownloadManager.java
+++ /dev/null
@@ -1,586 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.telephony;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SdkConstant;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.SharedPreferences;
-import android.net.Uri;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.telephony.mbms.DownloadProgressListener;
-import android.telephony.mbms.FileInfo;
-import android.telephony.mbms.DownloadRequest;
-import android.telephony.mbms.MbmsDownloadManagerCallback;
-import android.telephony.mbms.MbmsDownloadReceiver;
-import android.telephony.mbms.MbmsException;
-import android.telephony.mbms.MbmsTempFileProvider;
-import android.telephony.mbms.MbmsUtils;
-import android.telephony.mbms.vendor.IMbmsDownloadService;
-import android.util.Log;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
-/** @hide */
-public class MbmsDownloadManager {
-    private static final String LOG_TAG = MbmsDownloadManager.class.getSimpleName();
-
-    /** @hide */
-    // TODO: systemapi
-    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
-    public static final String MBMS_DOWNLOAD_SERVICE_ACTION =
-            "android.telephony.action.EmbmsDownload";
-
-    /**
-     * Integer extra indicating the result code of the download. One of
-     * {@link #RESULT_SUCCESSFUL}, {@link #RESULT_EXPIRED}, or {@link #RESULT_CANCELLED}.
-     */
-    public static final String EXTRA_RESULT = "android.telephony.mbms.extra.RESULT";
-
-    /**
-     * Extra containing the {@link android.telephony.mbms.FileInfo} for which the download result
-     * is for. Must not be null.
-     */
-    public static final String EXTRA_FILE_INFO = "android.telephony.mbms.extra.FILE_INFO";
-
-    /**
-     * Extra containing a single {@link Uri} indicating the location of the successfully
-     * downloaded file. Set on the intent provided via
-     * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}.
-     * Will always be set to a non-null value if {@link #EXTRA_RESULT} is set to
-     * {@link #RESULT_SUCCESSFUL}.
-     */
-    public static final String EXTRA_COMPLETED_FILE_URI =
-            "android.telephony.mbms.extra.COMPLETED_FILE_URI";
-
-    public static final int RESULT_SUCCESSFUL = 1;
-    public static final int RESULT_CANCELLED = 2;
-    public static final int RESULT_EXPIRED = 3;
-    public static final int RESULT_IO_ERROR = 4;
-    // TODO - more results!
-
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({STATUS_UNKNOWN, STATUS_ACTIVELY_DOWNLOADING, STATUS_PENDING_DOWNLOAD,
-            STATUS_PENDING_REPAIR, STATUS_PENDING_DOWNLOAD_WINDOW})
-    public @interface DownloadStatus {}
-
-    public static final int STATUS_UNKNOWN = 0;
-    public static final int STATUS_ACTIVELY_DOWNLOADING = 1;
-    public static final int STATUS_PENDING_DOWNLOAD = 2;
-    public static final int STATUS_PENDING_REPAIR = 3;
-    public static final int STATUS_PENDING_DOWNLOAD_WINDOW = 4;
-
-    private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
-
-    private final Context mContext;
-    private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
-    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
-        @Override
-        public void binderDied() {
-            sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, "Received death notification");
-        }
-    };
-
-    private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null);
-    private final MbmsDownloadManagerCallback mCallback;
-
-    private MbmsDownloadManager(Context context, MbmsDownloadManagerCallback callback, int subId) {
-        mContext = context;
-        mCallback = callback;
-        mSubscriptionId = subId;
-    }
-
-    /**
-     * Create a new MbmsDownloadManager using the system default data subscription ID.
-     * See {@link #create(Context, MbmsDownloadManagerCallback, int)}
-     *
-     * @hide
-     */
-    public static MbmsDownloadManager create(Context context,
-            MbmsDownloadManagerCallback listener)
-            throws MbmsException {
-        return create(context, listener, SubscriptionManager.getDefaultSubscriptionId());
-    }
-
-    /**
-     * Create a new MbmsDownloadManager using the given subscription ID.
-     *
-     * Note that this call will bind a remote service and that may take a bit. The instance of
-     * {@link MbmsDownloadManager} that is returned will not be ready for use until
-     * {@link MbmsDownloadManagerCallback#middlewareReady()} is called on the provided callback.
-     * If you attempt to use the manager before it is ready, a {@link MbmsException} will be thrown.
-     *
-     * This also may throw an {@link IllegalArgumentException} or an {@link IllegalStateException}.
-     *
-     * You may only have one instance of {@link MbmsDownloadManager} per UID. If you call this
-     * method while there is an active instance of {@link MbmsDownloadManager} in your process
-     * (in other words, one that has not had {@link #dispose()} called on it), this method will
-     * throw an {@link MbmsException}. If you call this method in a different process
-     * running under the same UID, an error will be indicated via
-     * {@link MbmsDownloadManagerCallback#error(int, String)}.
-     *
-     * Note that initialization may fail asynchronously. If you wish to try again after you
-     * receive such an asynchronous error, you must call dispose() on the instance of
-     * {@link MbmsDownloadManager} that you received before calling this method again.
-     *
-     * @param context The instance of {@link Context} to use
-     * @param listener A callback to get asynchronous error messages and file service updates.
-     * @param subscriptionId The data subscription ID to use
-     * @hide
-     */
-    public static MbmsDownloadManager create(Context context,
-            MbmsDownloadManagerCallback listener, int subscriptionId)
-            throws MbmsException {
-        if (!sIsInitialized.compareAndSet(false, true)) {
-            throw new MbmsException(MbmsException.InitializationErrors.ERROR_DUPLICATE_INITIALIZE);
-        }
-        MbmsDownloadManager mdm = new MbmsDownloadManager(context, listener, subscriptionId);
-        try {
-            mdm.bindAndInitialize();
-        } catch (MbmsException e) {
-            sIsInitialized.set(false);
-            throw e;
-        }
-        return mdm;
-    }
-
-    private void bindAndInitialize() throws MbmsException {
-        MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION,
-                new ServiceConnection() {
-                    @Override
-                    public void onServiceConnected(ComponentName name, IBinder service) {
-                        IMbmsDownloadService downloadService =
-                                IMbmsDownloadService.Stub.asInterface(service);
-                        int result;
-                        try {
-                            result = downloadService.initialize(mSubscriptionId, mCallback);
-                        } catch (RemoteException e) {
-                            Log.e(LOG_TAG, "Service died before initialization");
-                            sIsInitialized.set(false);
-                            return;
-                        } catch (RuntimeException e) {
-                            Log.e(LOG_TAG, "Runtime exception during initialization");
-                            sendErrorToApp(
-                                    MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
-                                    e.toString());
-                            sIsInitialized.set(false);
-                            return;
-                        }
-                        if (result != MbmsException.SUCCESS) {
-                            sendErrorToApp(result, "Error returned during initialization");
-                            sIsInitialized.set(false);
-                            return;
-                        }
-                        try {
-                            downloadService.asBinder().linkToDeath(mDeathRecipient, 0);
-                        } catch (RemoteException e) {
-                            sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST,
-                                    "Middleware lost during initialization");
-                            sIsInitialized.set(false);
-                            return;
-                        }
-                        mService.set(downloadService);
-                    }
-
-                    @Override
-                    public void onServiceDisconnected(ComponentName name) {
-                        sIsInitialized.set(false);
-                        mService.set(null);
-                    }
-                });
-    }
-
-    /**
-     * An inspection API to retrieve the list of available
-     * {@link android.telephony.mbms.FileServiceInfo}s currently being advertised.
-     * The results are returned asynchronously via a call to
-     * {@link MbmsDownloadManagerCallback#fileServicesUpdated(List)}
-     *
-     * The serviceClasses argument lets the app filter on types of programming and is opaque data
-     * negotiated beforehand between the app and the carrier.
-     *
-     * This may throw an {@link MbmsException} containing one of the following errors:
-     * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
-     * {@link MbmsException#ERROR_MIDDLEWARE_LOST}
-     *
-     * Asynchronous error codes via the {@link MbmsDownloadManagerCallback#error(int, String)}
-     * callback can include any of the errors except:
-     * {@link MbmsException.StreamingErrors#ERROR_UNABLE_TO_START_SERVICE}
-     *
-     * @param classList A list of service classes which the app wishes to receive
-     *                  {@link MbmsDownloadManagerCallback#fileServicesUpdated(List)} callbacks
-     *                  about. Subsequent calls to this method will replace this list of service
-     *                  classes (i.e. the middleware will no longer send updates for services
-     *                  matching classes only in the old list).
-     */
-    public void getFileServices(List<String> classList) throws MbmsException {
-        IMbmsDownloadService downloadService = mService.get();
-        if (downloadService == null) {
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
-        }
-        try {
-            int returnCode = downloadService.getFileServices(mSubscriptionId, classList);
-            if (returnCode != MbmsException.SUCCESS) {
-                throw new MbmsException(returnCode);
-            }
-        } catch (RemoteException e) {
-            Log.w(LOG_TAG, "Remote process died");
-            mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
-        }
-    }
-
-    /**
-     * Sets the temp file root for downloads.
-     * All temp files created for the middleware to write to will be contained in the specified
-     * directory. Applications that wish to specify a location only need to call this method once
-     * as long their data is persisted in storage -- the argument will be stored both in a
-     * local instance of {@link android.content.SharedPreferences} and by the middleware.
-     *
-     * If this method is not called at least once before calling
-     * {@link #download(DownloadRequest, DownloadProgressListener)}, the framework
-     * will default to a directory formed by the concatenation of the app's files directory and
-     * {@link android.telephony.mbms.MbmsTempFileProvider#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY}.
-     *
-     * Before calling this method, the app must cancel all of its pending
-     * {@link DownloadRequest}s via {@link #cancelDownload(DownloadRequest)}. If this is not done,
-     * an {@link MbmsException} will be thrown with code
-     * {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} unless the
-     * provided directory is the same as what has been previously configured.
-     *
-     * The {@link File} supplied as a root temp file directory must already exist. If not, an
-     * {@link IllegalArgumentException} will be thrown.
-     * @param tempFileRootDirectory A directory to place temp files in.
-     */
-    public void setTempFileRootDirectory(@NonNull File tempFileRootDirectory)
-            throws MbmsException {
-        IMbmsDownloadService downloadService = mService.get();
-        if (downloadService == null) {
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
-        }
-        if (!tempFileRootDirectory.exists()) {
-            throw new IllegalArgumentException("Provided directory does not exist");
-        }
-        if (!tempFileRootDirectory.isDirectory()) {
-            throw new IllegalArgumentException("Provided File is not a directory");
-        }
-        String filePath;
-        try {
-            filePath = tempFileRootDirectory.getCanonicalPath();
-        } catch (IOException e) {
-            throw new IllegalArgumentException("Unable to canonicalize the provided path: " + e);
-        }
-
-        try {
-            int result = downloadService.setTempFileRootDirectory(mSubscriptionId, filePath);
-            if (result != MbmsException.SUCCESS) {
-                throw new MbmsException(result);
-            }
-        } catch (RemoteException e) {
-            mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
-        }
-
-        SharedPreferences prefs = mContext.getSharedPreferences(
-                MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
-        prefs.edit().putString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, filePath).apply();
-    }
-
-    /**
-     * Retrieves the currently configured temp file root directory. Returns the file that was
-     * configured via {@link #setTempFileRootDirectory(File)} or the default directory
-     * {@link #download(DownloadRequest, DownloadProgressListener)} was called without ever setting
-     * the temp file root. If neither method has been called since the last time the app's shared
-     * preferences were reset, returns null.
-     *
-     * @return A {@link File} pointing to the configured temp file directory, or null if not yet
-     *         configured.
-     */
-    public @Nullable File getTempFileRootDirectory() {
-        SharedPreferences prefs = mContext.getSharedPreferences(
-                MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
-        String path = prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null);
-        if (path != null) {
-            return new File(path);
-        }
-        return null;
-    }
-
-    /**
-     * Requests a download of a file that is available via multicast.
-     *
-     * downloadListener is an optional callback object which can be used to get progress reports
-     *     of a currently occuring download.  Note this can only run while the calling app
-     *     is running, so future downloads will simply result in resultIntents being sent
-     *     for completed or errored-out downloads.  A NULL indicates no callbacks are needed.
-     *
-     * May throw an {@link IllegalArgumentException}
-     *
-     * If {@link #setTempFileRootDirectory(File)} has not called after the app has been installed,
-     * this method will create a directory at the default location defined at
-     * {@link MbmsTempFileProvider#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY} and store that as the temp
-     * file root directory.
-     *
-     * Asynchronous errors through the listener include any of the errors
-     *
-     * @param request The request that specifies what should be downloaded
-     * @param progressListener Optional listener that will be provided progress updates
-     *                         if the app is running.
-     */
-    public void download(DownloadRequest request, DownloadProgressListener progressListener)
-            throws MbmsException {
-        IMbmsDownloadService downloadService = mService.get();
-        if (downloadService == null) {
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
-        }
-
-        // Check to see whether the app's set a temp root dir yet, and set it if not.
-        SharedPreferences prefs = mContext.getSharedPreferences(
-                MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
-        if (prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null) == null) {
-            File tempRootDirectory = new File(mContext.getFilesDir(),
-                    MbmsTempFileProvider.DEFAULT_TOP_LEVEL_TEMP_DIRECTORY);
-            tempRootDirectory.mkdirs();
-            setTempFileRootDirectory(tempRootDirectory);
-        }
-
-        checkValidDownloadDestination(request);
-        writeDownloadRequestToken(request);
-        try {
-            downloadService.download(request, progressListener);
-        } catch (RemoteException e) {
-            mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
-        }
-    }
-
-    /**
-     * Returns a list of pending {@link DownloadRequest}s that originated from this application.
-     * A pending request is one that was issued via
-     * {@link #download(DownloadRequest, DownloadProgressListener)} but not cancelled through
-     * {@link #cancelDownload(DownloadRequest)}.
-     * @return A list, possibly empty, of {@link DownloadRequest}s
-     */
-    public @NonNull List<DownloadRequest> listPendingDownloads() throws MbmsException {
-        IMbmsDownloadService downloadService = mService.get();
-        if (downloadService == null) {
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
-        }
-
-        try {
-            return downloadService.listPendingDownloads(mSubscriptionId);
-        } catch (RemoteException e) {
-            mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
-        }
-    }
-
-    /**
-     * Attempts to cancel the specified {@link DownloadRequest}.
-     *
-     * If the middleware is not aware of the specified download request, an MbmsException will be
-     * thrown with error code {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
-     *
-     * If this method returns without throwing an exception, you may assume that cancellation
-     * was successful.
-     * @param downloadRequest The download request that you wish to cancel.
-     */
-    public void cancelDownload(DownloadRequest downloadRequest) throws MbmsException {
-        IMbmsDownloadService downloadService = mService.get();
-        if (downloadService == null) {
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
-        }
-
-        try {
-            int result = downloadService.cancelDownload(downloadRequest);
-            if (result != MbmsException.SUCCESS) {
-                throw new MbmsException(result);
-            }
-        } catch (RemoteException e) {
-            mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
-        }
-        deleteDownloadRequestToken(downloadRequest);
-    }
-
-    /**
-     * Gets information about the status of a file pending download.
-     *
-     * If the middleware has not yet been properly initialized or if it has no records of the
-     * file indicated by {@code fileInfo} being associated with {@code downloadRequest},
-     * {@link #STATUS_UNKNOWN} will be returned.
-     *
-     * @param downloadRequest The download request to query.
-     * @param fileInfo The particular file within the request to get information on.
-     * @return The status of the download.
-     */
-    @DownloadStatus
-    public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo)
-            throws MbmsException {
-        IMbmsDownloadService downloadService = mService.get();
-        if (downloadService == null) {
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
-        }
-
-        try {
-            return downloadService.getDownloadStatus(downloadRequest, fileInfo);
-        } catch (RemoteException e) {
-            mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
-        }
-    }
-
-    /**
-     * Resets the middleware's knowledge of previously-downloaded files in this download request.
-     *
-     * Normally, the middleware keeps track of the hashes of downloaded files and won't re-download
-     * files whose server-reported hash matches one of the already-downloaded files. This means
-     * that if the file is accidentally deleted by the user or by the app, the middleware will
-     * not try to download it again.
-     * This method will reset the middleware's cache of hashes for the provided
-     * {@link DownloadRequest}, so that previously downloaded content will be downloaded again
-     * when available.
-     * This will not interrupt in-progress downloads.
-     *
-     * If the middleware is not aware of the specified download request, an MbmsException will be
-     * thrown with error code {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
-     *
-     * May throw a {@link MbmsException} with error code
-     * @param downloadRequest The request to re-download files for.
-     */
-    public void resetDownloadKnowledge(DownloadRequest downloadRequest) throws MbmsException {
-        IMbmsDownloadService downloadService = mService.get();
-        if (downloadService == null) {
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
-        }
-
-        try {
-            int result = downloadService.resetDownloadKnowledge(downloadRequest);
-            if (result != MbmsException.SUCCESS) {
-                throw new MbmsException(result);
-            }
-        } catch (RemoteException e) {
-            mService.set(null);
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
-        }
-    }
-
-    public void dispose() {
-        try {
-            IMbmsDownloadService downloadService = mService.get();
-            if (downloadService == null) {
-                Log.i(LOG_TAG, "Service already dead");
-                return;
-            }
-            downloadService.dispose(mSubscriptionId);
-        } catch (RemoteException e) {
-            // Ignore
-            Log.i(LOG_TAG, "Remote exception while disposing of service");
-        } finally {
-            mService.set(null);
-            sIsInitialized.set(false);
-        }
-    }
-
-    private void writeDownloadRequestToken(DownloadRequest request) {
-        File token = getDownloadRequestTokenPath(request);
-        if (!token.getParentFile().exists()) {
-            token.getParentFile().mkdirs();
-        }
-        if (token.exists()) {
-            Log.w(LOG_TAG, "Download token " + token.getName() + " already exists");
-            return;
-        }
-        try {
-            if (!token.createNewFile()) {
-                throw new RuntimeException("Failed to create download token for request "
-                        + request);
-            }
-        } catch (IOException e) {
-            throw new RuntimeException("Failed to create download token for request " + request
-                    + " due to IOException " + e);
-        }
-    }
-
-    private void deleteDownloadRequestToken(DownloadRequest request) {
-        File token = getDownloadRequestTokenPath(request);
-        if (!token.isFile()) {
-            Log.w(LOG_TAG, "Attempting to delete non-existent download token at " + token);
-            return;
-        }
-        if (!token.delete()) {
-            Log.w(LOG_TAG, "Couldn't delete download token at " + token);
-        }
-    }
-
-    private File getDownloadRequestTokenPath(DownloadRequest request) {
-        File tempFileLocation = MbmsUtils.getEmbmsTempFileDirForService(mContext,
-                request.getFileServiceId());
-        String downloadTokenFileName = request.getHash()
-                + MbmsDownloadReceiver.DOWNLOAD_TOKEN_SUFFIX;
-        return new File(tempFileLocation, downloadTokenFileName);
-    }
-
-    /**
-     * Verifies the following:
-     * If a request is multi-part,
-     *     1. Destination Uri must exist and be a directory
-     *     2. Directory specified must contain no files.
-     * Otherwise
-     *     1. The file specified by the destination Uri must not exist.
-     */
-    private void checkValidDownloadDestination(DownloadRequest request) {
-        File toFile = new File(request.getDestinationUri().getSchemeSpecificPart());
-        if (request.isMultipartDownload()) {
-            if (!toFile.isDirectory()) {
-                throw new IllegalArgumentException("Multipart download must specify valid " +
-                        "destination directory.");
-            }
-            if (toFile.listFiles().length > 0) {
-                throw new IllegalArgumentException("Destination directory must be clear of all " +
-                        "files.");
-            }
-        } else {
-            if (toFile.exists()) {
-                throw new IllegalArgumentException("Destination file must not exist.");
-            }
-        }
-    }
-
-    private void sendErrorToApp(int errorCode, String message) {
-        try {
-            mCallback.error(errorCode, message);
-        } catch (RemoteException e) {
-            // Ignore, should not happen locally.
-        }
-    }
-}
diff --git a/android/telephony/MbmsDownloadSession.java b/android/telephony/MbmsDownloadSession.java
new file mode 100644
index 0000000..ebac041
--- /dev/null
+++ b/android/telephony/MbmsDownloadSession.java
@@ -0,0 +1,773 @@
+/*
+ * Copyright (C) 2016 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 android.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.telephony.mbms.DownloadStateCallback;
+import android.telephony.mbms.FileInfo;
+import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.InternalDownloadSessionCallback;
+import android.telephony.mbms.InternalDownloadStateCallback;
+import android.telephony.mbms.MbmsDownloadSessionCallback;
+import android.telephony.mbms.MbmsDownloadReceiver;
+import android.telephony.mbms.MbmsErrors;
+import android.telephony.mbms.MbmsTempFileProvider;
+import android.telephony.mbms.MbmsUtils;
+import android.telephony.mbms.vendor.IMbmsDownloadService;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+/**
+ * This class provides functionality for file download over MBMS.
+ */
+public class MbmsDownloadSession implements AutoCloseable {
+    private static final String LOG_TAG = MbmsDownloadSession.class.getSimpleName();
+
+    /**
+     * Service action which must be handled by the middleware implementing the MBMS file download
+     * interface.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String MBMS_DOWNLOAD_SERVICE_ACTION =
+            "android.telephony.action.EmbmsDownload";
+
+    /**
+     * Integer extra that Android will attach to the intent supplied via
+     * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}
+     * Indicates the result code of the download. One of
+     * {@link #RESULT_SUCCESSFUL}, {@link #RESULT_EXPIRED}, {@link #RESULT_CANCELLED}, or
+     * {@link #RESULT_IO_ERROR}.
+     *
+     * This extra may also be used by the middleware when it is sending intents to the app.
+     */
+    public static final String EXTRA_MBMS_DOWNLOAD_RESULT =
+            "android.telephony.extra.MBMS_DOWNLOAD_RESULT";
+
+    /**
+     * {@link FileInfo} extra that Android will attach to the intent supplied via
+     * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}
+     * Indicates the file for which the download result is for. Never null.
+     *
+     * This extra may also be used by the middleware when it is sending intents to the app.
+     */
+    public static final String EXTRA_MBMS_FILE_INFO = "android.telephony.extra.MBMS_FILE_INFO";
+
+    /**
+     * {@link Uri} extra that Android will attach to the intent supplied via
+     * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}
+     * Indicates the location of the successfully downloaded file within the temp file root set
+     * via {@link #setTempFileRootDirectory(File)}.
+     * While you may use this file in-place, it is highly encouraged that you move
+     * this file to a different location after receiving the download completion intent, as this
+     * file resides within the temp file directory.
+     *
+     * Will always be set to a non-null value if
+     * {@link #EXTRA_MBMS_DOWNLOAD_RESULT} is set to {@link #RESULT_SUCCESSFUL}.
+     */
+    public static final String EXTRA_MBMS_COMPLETED_FILE_URI =
+            "android.telephony.extra.MBMS_COMPLETED_FILE_URI";
+
+    /**
+     * Extra containing the {@link DownloadRequest} for which the download result or file
+     * descriptor request is for. Must not be null.
+     */
+    public static final String EXTRA_MBMS_DOWNLOAD_REQUEST =
+            "android.telephony.extra.MBMS_DOWNLOAD_REQUEST";
+
+    /**
+     * The default directory name for all MBMS temp files. If you call
+     * {@link #download(DownloadRequest)} without first calling
+     * {@link #setTempFileRootDirectory(File)}, this directory will be created for you under the
+     * path returned by {@link Context#getFilesDir()}.
+     */
+    public static final String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot";
+
+    /**
+     * Indicates that the download was successful.
+     */
+    public static final int RESULT_SUCCESSFUL = 1;
+
+    /**
+     * Indicates that the download was cancelled via {@link #cancelDownload(DownloadRequest)}.
+     */
+    public static final int RESULT_CANCELLED = 2;
+
+    /**
+     * Indicates that the download will not be completed due to the expiration of its download
+     * window on the carrier's network.
+     */
+    public static final int RESULT_EXPIRED = 3;
+
+    /**
+     * Indicates that the download will not be completed due to an I/O error incurred while
+     * writing to temp files. This commonly indicates that the device is out of storage space,
+     * but may indicate other conditions as well (such as an SD card being removed).
+     */
+    public static final int RESULT_IO_ERROR = 4;
+    // TODO - more results!
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({STATUS_UNKNOWN, STATUS_ACTIVELY_DOWNLOADING, STATUS_PENDING_DOWNLOAD,
+            STATUS_PENDING_REPAIR, STATUS_PENDING_DOWNLOAD_WINDOW})
+    public @interface DownloadStatus {}
+
+    /**
+     * Indicates that the middleware has no information on the file.
+     */
+    public static final int STATUS_UNKNOWN = 0;
+
+    /**
+     * Indicates that the file is actively downloading.
+     */
+    public static final int STATUS_ACTIVELY_DOWNLOADING = 1;
+
+    /**
+     * TODO: I don't know...
+     */
+    public static final int STATUS_PENDING_DOWNLOAD = 2;
+
+    /**
+     * Indicates that the file is being repaired after the download being interrupted.
+     */
+    public static final int STATUS_PENDING_REPAIR = 3;
+
+    /**
+     * Indicates that the file is waiting to download because its download window has not yet
+     * started.
+     */
+    public static final int STATUS_PENDING_DOWNLOAD_WINDOW = 4;
+
+    private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
+
+    private final Context mContext;
+    private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
+    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, "Received death notification");
+        }
+    };
+
+    private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null);
+    private final InternalDownloadSessionCallback mInternalCallback;
+    private final Map<DownloadStateCallback, InternalDownloadStateCallback>
+            mInternalDownloadCallbacks = new HashMap<>();
+
+    private MbmsDownloadSession(Context context, MbmsDownloadSessionCallback callback,
+            int subscriptionId, Handler handler) {
+        mContext = context;
+        mSubscriptionId = subscriptionId;
+        if (handler == null) {
+            handler = new Handler(Looper.getMainLooper());
+        }
+        mInternalCallback = new InternalDownloadSessionCallback(callback, handler);
+    }
+
+    /**
+     * Create a new {@link MbmsDownloadSession} using the system default data subscription ID.
+     * See {@link #create(Context, MbmsDownloadSessionCallback, int, Handler)}
+     */
+    public static MbmsDownloadSession create(@NonNull Context context,
+            @NonNull MbmsDownloadSessionCallback callback, @NonNull Handler handler) {
+        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
+    }
+
+    /**
+     * Create a new MbmsDownloadManager using the given subscription ID.
+     *
+     * Note that this call will bind a remote service and that may take a bit. The instance of
+     * {@link MbmsDownloadSession} that is returned will not be ready for use until
+     * {@link MbmsDownloadSessionCallback#onMiddlewareReady()} is called on the provided callback.
+     * If you attempt to use the instance before it is ready, an {@link IllegalStateException}
+     * will be thrown or an error will be delivered through
+     * {@link MbmsDownloadSessionCallback#onError(int, String)}.
+     *
+     * This also may throw an {@link IllegalArgumentException}.
+     *
+     * You may only have one instance of {@link MbmsDownloadSession} per UID. If you call this
+     * method while there is an active instance of {@link MbmsDownloadSession} in your process
+     * (in other words, one that has not had {@link #close()} called on it), this method will
+     * throw an {@link IllegalStateException}. If you call this method in a different process
+     * running under the same UID, an error will be indicated via
+     * {@link MbmsDownloadSessionCallback#onError(int, String)}.
+     *
+     * Note that initialization may fail asynchronously. If you wish to try again after you
+     * receive such an asynchronous error, you must call {@link #close()} on the instance of
+     * {@link MbmsDownloadSession} that you received before calling this method again.
+     *
+     * @param context The instance of {@link Context} to use
+     * @param callback A callback to get asynchronous error messages and file service updates.
+     * @param subscriptionId The data subscription ID to use
+     * @param handler The {@link Handler} on which callbacks should be enqueued.
+     * @return A new instance of {@link MbmsDownloadSession}, or null if an error occurred during
+     * setup.
+     */
+    public static @Nullable MbmsDownloadSession create(@NonNull Context context,
+            final @NonNull MbmsDownloadSessionCallback callback,
+            int subscriptionId, @NonNull Handler handler) {
+        if (!sIsInitialized.compareAndSet(false, true)) {
+            throw new IllegalStateException("Cannot have two active instances");
+        }
+        MbmsDownloadSession session =
+                new MbmsDownloadSession(context, callback, subscriptionId, handler);
+        final int result = session.bindAndInitialize();
+        if (result != MbmsErrors.SUCCESS) {
+            sIsInitialized.set(false);
+            handler.post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onError(result, null);
+                }
+            });
+            return null;
+        }
+        return session;
+    }
+
+    private int bindAndInitialize() {
+        return MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION,
+                new ServiceConnection() {
+                    @Override
+                    public void onServiceConnected(ComponentName name, IBinder service) {
+                        IMbmsDownloadService downloadService =
+                                IMbmsDownloadService.Stub.asInterface(service);
+                        int result;
+                        try {
+                            result = downloadService.initialize(mSubscriptionId, mInternalCallback);
+                        } catch (RemoteException e) {
+                            Log.e(LOG_TAG, "Service died before initialization");
+                            sIsInitialized.set(false);
+                            return;
+                        } catch (RuntimeException e) {
+                            Log.e(LOG_TAG, "Runtime exception during initialization");
+                            sendErrorToApp(
+                                    MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+                                    e.toString());
+                            sIsInitialized.set(false);
+                            return;
+                        }
+                        if (result != MbmsErrors.SUCCESS) {
+                            sendErrorToApp(result, "Error returned during initialization");
+                            sIsInitialized.set(false);
+                            return;
+                        }
+                        try {
+                            downloadService.asBinder().linkToDeath(mDeathRecipient, 0);
+                        } catch (RemoteException e) {
+                            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+                                    "Middleware lost during initialization");
+                            sIsInitialized.set(false);
+                            return;
+                        }
+                        mService.set(downloadService);
+                    }
+
+                    @Override
+                    public void onServiceDisconnected(ComponentName name) {
+                        sIsInitialized.set(false);
+                        mService.set(null);
+                    }
+                });
+    }
+
+    /**
+     * An inspection API to retrieve the list of available
+     * {@link android.telephony.mbms.FileServiceInfo}s currently being advertised.
+     * The results are returned asynchronously via a call to
+     * {@link MbmsDownloadSessionCallback#onFileServicesUpdated(List)}
+     *
+     * Asynchronous error codes via the {@link MbmsDownloadSessionCallback#onError(int, String)}
+     * callback may include any of the errors that are not specific to the streaming use-case.
+     *
+     * May throw an {@link IllegalStateException} or {@link IllegalArgumentException}.
+     *
+     * @param classList A list of service classes which the app wishes to receive
+     *                  {@link MbmsDownloadSessionCallback#onFileServicesUpdated(List)} callbacks
+     *                  about. Subsequent calls to this method will replace this list of service
+     *                  classes (i.e. the middleware will no longer send updates for services
+     *                  matching classes only in the old list).
+     *                  Values in this list should be negotiated with the wireless carrier prior
+     *                  to using this API.
+     */
+    public void requestUpdateFileServices(@NonNull List<String> classList) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+        try {
+            int returnCode = downloadService.requestUpdateFileServices(mSubscriptionId, classList);
+            if (returnCode != MbmsErrors.SUCCESS) {
+                sendErrorToApp(returnCode, null);
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Remote process died");
+            mService.set(null);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+        }
+    }
+
+    /**
+     * Sets the temp file root for downloads.
+     * All temp files created for the middleware to write to will be contained in the specified
+     * directory. Applications that wish to specify a location only need to call this method once
+     * as long their data is persisted in storage -- the argument will be stored both in a
+     * local instance of {@link android.content.SharedPreferences} and by the middleware.
+     *
+     * If this method is not called at least once before calling
+     * {@link #download(DownloadRequest)}, the framework
+     * will default to a directory formed by the concatenation of the app's files directory and
+     * {@link MbmsDownloadSession#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY}.
+     *
+     * Before calling this method, the app must cancel all of its pending
+     * {@link DownloadRequest}s via {@link #cancelDownload(DownloadRequest)}. If this is not done,
+     * you will receive an asynchronous error with code
+     * {@link MbmsErrors.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} unless the
+     * provided directory is the same as what has been previously configured.
+     *
+     * The {@link File} supplied as a root temp file directory must already exist. If not, an
+     * {@link IllegalArgumentException} will be thrown. In addition, as an additional sanity
+     * check, an {@link IllegalArgumentException} will be thrown if you attempt to set the temp
+     * file root directory to one of your data roots (the value of {@link Context#getDataDir()},
+     * {@link Context#getFilesDir()}, or {@link Context#getCacheDir()}).
+     * @param tempFileRootDirectory A directory to place temp files in.
+     */
+    public void setTempFileRootDirectory(@NonNull File tempFileRootDirectory) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+        try {
+            validateTempFileRootSanity(tempFileRootDirectory);
+        } catch (IOException e) {
+            throw new IllegalStateException("Got IOException checking directory sanity");
+        }
+        String filePath;
+        try {
+            filePath = tempFileRootDirectory.getCanonicalPath();
+        } catch (IOException e) {
+            throw new IllegalArgumentException("Unable to canonicalize the provided path: " + e);
+        }
+
+        try {
+            int result = downloadService.setTempFileRootDirectory(mSubscriptionId, filePath);
+            if (result != MbmsErrors.SUCCESS) {
+                sendErrorToApp(result, null);
+            }
+        } catch (RemoteException e) {
+            mService.set(null);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return;
+        }
+
+        SharedPreferences prefs = mContext.getSharedPreferences(
+                MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+        prefs.edit().putString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, filePath).apply();
+    }
+
+    private void validateTempFileRootSanity(File tempFileRootDirectory) throws IOException {
+        if (!tempFileRootDirectory.exists()) {
+            throw new IllegalArgumentException("Provided directory does not exist");
+        }
+        if (!tempFileRootDirectory.isDirectory()) {
+            throw new IllegalArgumentException("Provided File is not a directory");
+        }
+        String canonicalTempFilePath = tempFileRootDirectory.getCanonicalPath();
+        if (mContext.getDataDir().getCanonicalPath().equals(canonicalTempFilePath)) {
+            throw new IllegalArgumentException("Temp file root cannot be your data dir");
+        }
+        if (mContext.getCacheDir().getCanonicalPath().equals(canonicalTempFilePath)) {
+            throw new IllegalArgumentException("Temp file root cannot be your cache dir");
+        }
+        if (mContext.getFilesDir().getCanonicalPath().equals(canonicalTempFilePath)) {
+            throw new IllegalArgumentException("Temp file root cannot be your files dir");
+        }
+    }
+    /**
+     * Retrieves the currently configured temp file root directory. Returns the file that was
+     * configured via {@link #setTempFileRootDirectory(File)} or the default directory
+     * {@link #download(DownloadRequest)} was called without ever
+     * setting the temp file root. If neither method has been called since the last time the app's
+     * shared preferences were reset, returns {@code null}.
+     *
+     * @return A {@link File} pointing to the configured temp file directory, or null if not yet
+     *         configured.
+     */
+    public @Nullable File getTempFileRootDirectory() {
+        SharedPreferences prefs = mContext.getSharedPreferences(
+                MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+        String path = prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null);
+        if (path != null) {
+            return new File(path);
+        }
+        return null;
+    }
+
+    /**
+     * Requests the download of a file or set of files that the carrier has indicated to be
+     * available.
+     *
+     * May throw an {@link IllegalArgumentException}
+     *
+     * If {@link #setTempFileRootDirectory(File)} has not called after the app has been installed,
+     * this method will create a directory at the default location defined at
+     * {@link MbmsDownloadSession#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY} and store that as the temp
+     * file root directory.
+     *
+     * Asynchronous errors through the callback may include any error not specific to the
+     * streaming use-case.
+     * @param request The request that specifies what should be downloaded.
+     */
+    public void download(@NonNull DownloadRequest request) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        // Check to see whether the app's set a temp root dir yet, and set it if not.
+        SharedPreferences prefs = mContext.getSharedPreferences(
+                MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+        if (prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null) == null) {
+            File tempRootDirectory = new File(mContext.getFilesDir(),
+                    DEFAULT_TOP_LEVEL_TEMP_DIRECTORY);
+            tempRootDirectory.mkdirs();
+            setTempFileRootDirectory(tempRootDirectory);
+        }
+
+        writeDownloadRequestToken(request);
+        try {
+            downloadService.download(request);
+        } catch (RemoteException e) {
+            mService.set(null);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+        }
+    }
+
+    /**
+     * Returns a list of pending {@link DownloadRequest}s that originated from this application.
+     * A pending request is one that was issued via
+     * {@link #download(DownloadRequest)} but not cancelled through
+     * {@link #cancelDownload(DownloadRequest)}.
+     * @return A list, possibly empty, of {@link DownloadRequest}s
+     */
+    public @NonNull List<DownloadRequest> listPendingDownloads() {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        try {
+            return downloadService.listPendingDownloads(mSubscriptionId);
+        } catch (RemoteException e) {
+            mService.set(null);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * Registers a callback for a {@link DownloadRequest} previously requested via
+     * {@link #download(DownloadRequest)}. This callback will only be called as long as both this
+     * app and the middleware are both running -- if either one stops, no further calls on the
+     * provided {@link DownloadStateCallback} will be enqueued.
+     *
+     * If the middleware is not aware of the specified download request,
+     * this method will throw an {@link IllegalArgumentException}.
+     *
+     * @param request The {@link DownloadRequest} that you want updates on.
+     * @param callback The callback that should be called when the middleware has information to
+     *                 share on the download.
+     * @param handler The {@link Handler} on which calls to {@code callback} should be enqueued on.
+     */
+    public void registerStateCallback(@NonNull DownloadRequest request,
+            @NonNull DownloadStateCallback callback,
+            @NonNull Handler handler) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        InternalDownloadStateCallback internalCallback =
+                new InternalDownloadStateCallback(callback, handler);
+
+        try {
+            int result = downloadService.registerStateCallback(request, internalCallback);
+            if (result != MbmsErrors.SUCCESS) {
+                if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                    throw new IllegalArgumentException("Unknown download request.");
+                }
+                sendErrorToApp(result, null);
+                return;
+            }
+        } catch (RemoteException e) {
+            mService.set(null);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return;
+        }
+        mInternalDownloadCallbacks.put(callback, internalCallback);
+    }
+
+    /**
+     * Un-register a callback previously registered via
+     * {@link #registerStateCallback(DownloadRequest, DownloadStateCallback, Handler)}. After
+     * this method is called, no further callbacks will be enqueued on the {@link Handler}
+     * provided upon registration, even if this method throws an exception.
+     *
+     * If the middleware is not aware of the specified download request,
+     * this method will throw an {@link IllegalArgumentException}.
+     *
+     * @param request The {@link DownloadRequest} provided during registration
+     * @param callback The callback provided during registration.
+     */
+    public void unregisterStateCallback(@NonNull DownloadRequest request,
+            @NonNull DownloadStateCallback callback) {
+        try {
+            IMbmsDownloadService downloadService = mService.get();
+            if (downloadService == null) {
+                throw new IllegalStateException("Middleware not yet bound");
+            }
+
+            InternalDownloadStateCallback internalCallback =
+                    mInternalDownloadCallbacks.get(callback);
+
+            try {
+                int result = downloadService.unregisterStateCallback(request, internalCallback);
+                if (result != MbmsErrors.SUCCESS) {
+                    if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                        throw new IllegalArgumentException("Unknown download request.");
+                    }
+                    sendErrorToApp(result, null);
+                }
+            } catch (RemoteException e) {
+                mService.set(null);
+                sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            }
+        } finally {
+            InternalDownloadStateCallback internalCallback =
+                    mInternalDownloadCallbacks.remove(callback);
+            if (internalCallback != null) {
+                internalCallback.stop();
+            }
+        }
+    }
+
+    /**
+     * Attempts to cancel the specified {@link DownloadRequest}.
+     *
+     * If the middleware is not aware of the specified download request,
+     * this method will throw an {@link IllegalArgumentException}.
+     *
+     * @param downloadRequest The download request that you wish to cancel.
+     */
+    public void cancelDownload(@NonNull DownloadRequest downloadRequest) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        try {
+            int result = downloadService.cancelDownload(downloadRequest);
+            if (result != MbmsErrors.SUCCESS) {
+                if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                    throw new IllegalArgumentException("Unknown download request.");
+                }
+                sendErrorToApp(result, null);
+                return;
+            }
+        } catch (RemoteException e) {
+            mService.set(null);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return;
+        }
+        deleteDownloadRequestToken(downloadRequest);
+    }
+
+    /**
+     * Gets information about the status of a file pending download.
+     *
+     * If there was a problem communicating with the middleware or if it has no records of the
+     * file indicated by {@code fileInfo} being associated with {@code downloadRequest},
+     * {@link #STATUS_UNKNOWN} will be returned.
+     *
+     * @param downloadRequest The download request to query.
+     * @param fileInfo The particular file within the request to get information on.
+     * @return The status of the download.
+     */
+    @DownloadStatus
+    public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        try {
+            return downloadService.getDownloadStatus(downloadRequest, fileInfo);
+        } catch (RemoteException e) {
+            mService.set(null);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return STATUS_UNKNOWN;
+        }
+    }
+
+    /**
+     * Resets the middleware's knowledge of previously-downloaded files in this download request.
+     *
+     * Normally, the middleware keeps track of the hashes of downloaded files and won't re-download
+     * files whose server-reported hash matches one of the already-downloaded files. This means
+     * that if the file is accidentally deleted by the user or by the app, the middleware will
+     * not try to download it again.
+     * This method will reset the middleware's cache of hashes for the provided
+     * {@link DownloadRequest}, so that previously downloaded content will be downloaded again
+     * when available.
+     * This will not interrupt in-progress downloads.
+     *
+     * This is distinct from cancelling and re-issuing the download request -- if you cancel and
+     * re-issue, the middleware will not clear its cache of download state information.
+     *
+     * If the middleware is not aware of the specified download request, an
+     * {@link IllegalArgumentException} will be thrown.
+     *
+     * @param downloadRequest The request to re-download files for.
+     */
+    public void resetDownloadKnowledge(DownloadRequest downloadRequest) {
+        IMbmsDownloadService downloadService = mService.get();
+        if (downloadService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        try {
+            int result = downloadService.resetDownloadKnowledge(downloadRequest);
+            if (result != MbmsErrors.SUCCESS) {
+                if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+                    throw new IllegalArgumentException("Unknown download request.");
+                }
+                sendErrorToApp(result, null);
+            }
+        } catch (RemoteException e) {
+            mService.set(null);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+        }
+    }
+
+    /**
+     * Terminates this instance.
+     *
+     * After this method returns,
+     * no further callbacks originating from the middleware will be enqueued on the provided
+     * instance of {@link MbmsDownloadSessionCallback}, but callbacks that have already been
+     * enqueued will still be delivered.
+     *
+     * It is safe to call {@link #create(Context, MbmsDownloadSessionCallback, int, Handler)} to
+     * obtain another instance of {@link MbmsDownloadSession} immediately after this method
+     * returns.
+     *
+     * May throw an {@link IllegalStateException}
+     */
+    @Override
+    public void close() {
+        try {
+            IMbmsDownloadService downloadService = mService.get();
+            if (downloadService == null) {
+                Log.i(LOG_TAG, "Service already dead");
+                return;
+            }
+            downloadService.dispose(mSubscriptionId);
+        } catch (RemoteException e) {
+            // Ignore
+            Log.i(LOG_TAG, "Remote exception while disposing of service");
+        } finally {
+            mService.set(null);
+            sIsInitialized.set(false);
+            mInternalCallback.stop();
+        }
+    }
+
+    private void writeDownloadRequestToken(DownloadRequest request) {
+        File token = getDownloadRequestTokenPath(request);
+        if (!token.getParentFile().exists()) {
+            token.getParentFile().mkdirs();
+        }
+        if (token.exists()) {
+            Log.w(LOG_TAG, "Download token " + token.getName() + " already exists");
+            return;
+        }
+        try {
+            if (!token.createNewFile()) {
+                throw new RuntimeException("Failed to create download token for request "
+                        + request);
+            }
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to create download token for request " + request
+                    + " due to IOException " + e);
+        }
+    }
+
+    private void deleteDownloadRequestToken(DownloadRequest request) {
+        File token = getDownloadRequestTokenPath(request);
+        if (!token.isFile()) {
+            Log.w(LOG_TAG, "Attempting to delete non-existent download token at " + token);
+            return;
+        }
+        if (!token.delete()) {
+            Log.w(LOG_TAG, "Couldn't delete download token at " + token);
+        }
+    }
+
+    private File getDownloadRequestTokenPath(DownloadRequest request) {
+        File tempFileLocation = MbmsUtils.getEmbmsTempFileDirForService(mContext,
+                request.getFileServiceId());
+        String downloadTokenFileName = request.getHash()
+                + MbmsDownloadReceiver.DOWNLOAD_TOKEN_SUFFIX;
+        return new File(tempFileLocation, downloadTokenFileName);
+    }
+
+    private void sendErrorToApp(int errorCode, String message) {
+        try {
+            mInternalCallback.onError(errorCode, message);
+        } catch (RemoteException e) {
+            // Ignore, should not happen locally.
+        }
+    }
+}
diff --git a/android/telephony/MbmsStreamingManager.java b/android/telephony/MbmsStreamingManager.java
deleted file mode 100644
index b6b253e..0000000
--- a/android/telephony/MbmsStreamingManager.java
+++ /dev/null
@@ -1,323 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.telephony;
-
-import android.annotation.SdkConstant;
-import android.annotation.SystemApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.ServiceConnection;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.telephony.mbms.InternalStreamingManagerCallback;
-import android.telephony.mbms.InternalStreamingServiceCallback;
-import android.telephony.mbms.MbmsException;
-import android.telephony.mbms.MbmsStreamingManagerCallback;
-import android.telephony.mbms.MbmsUtils;
-import android.telephony.mbms.StreamingService;
-import android.telephony.mbms.StreamingServiceCallback;
-import android.telephony.mbms.StreamingServiceInfo;
-import android.telephony.mbms.vendor.IMbmsStreamingService;
-import android.util.Log;
-
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
-/**
- * This class provides functionality for streaming media over MBMS.
- */
-public class MbmsStreamingManager {
-    private static final String LOG_TAG = "MbmsStreamingManager";
-
-    /**
-     * Service action which must be handled by the middleware implementing the MBMS streaming
-     * interface.
-     * @hide
-     */
-    @SystemApi
-    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
-    public static final String MBMS_STREAMING_SERVICE_ACTION =
-            "android.telephony.action.EmbmsStreaming";
-
-    private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
-
-    private AtomicReference<IMbmsStreamingService> mService = new AtomicReference<>(null);
-    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
-        @Override
-        public void binderDied() {
-            sIsInitialized.set(false);
-            sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, "Received death notification");
-        }
-    };
-
-    private InternalStreamingManagerCallback mInternalCallback;
-
-    private final Context mContext;
-    private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
-
-    /** @hide */
-    private MbmsStreamingManager(Context context, MbmsStreamingManagerCallback callback,
-                    int subscriptionId, Handler handler) {
-        mContext = context;
-        mSubscriptionId = subscriptionId;
-        if (handler == null) {
-            handler = new Handler(Looper.getMainLooper());
-        }
-        mInternalCallback = new InternalStreamingManagerCallback(callback, handler);
-    }
-
-    /**
-     * Create a new MbmsStreamingManager using the given subscription ID.
-     *
-     * Note that this call will bind a remote service. You may not call this method on your app's
-     * main thread. This may throw an {@link MbmsException}, indicating errors that may happen
-     * during the initialization or binding process.
-     *
-     *
-     * You may only have one instance of {@link MbmsStreamingManager} per UID. If you call this
-     * method while there is an active instance of {@link MbmsStreamingManager} in your process
-     * (in other words, one that has not had {@link #dispose()} called on it), this method will
-     * throw an {@link MbmsException}. If you call this method in a different process
-     * running under the same UID, an error will be indicated via
-     * {@link MbmsStreamingManagerCallback#onError(int, String)}.
-     *
-     * Note that initialization may fail asynchronously. If you wish to try again after you
-     * receive such an asynchronous error, you must call dispose() on the instance of
-     * {@link MbmsStreamingManager} that you received before calling this method again.
-     *
-     * @param context The {@link Context} to use.
-     * @param callback A callback object on which you wish to receive results of asynchronous
-     *                 operations.
-     * @param subscriptionId The subscription ID to use.
-     * @param handler The handler you wish to receive callbacks on. If null, callbacks will be
-     *                processed on the main looper (in other words, the looper returned from
-     *                {@link Looper#getMainLooper()}).
-     */
-    public static MbmsStreamingManager create(Context context,
-            MbmsStreamingManagerCallback callback, int subscriptionId, Handler handler)
-            throws MbmsException {
-        if (!sIsInitialized.compareAndSet(false, true)) {
-            throw new MbmsException(MbmsException.InitializationErrors.ERROR_DUPLICATE_INITIALIZE);
-        }
-        MbmsStreamingManager manager = new MbmsStreamingManager(context, callback,
-                subscriptionId, handler);
-        try {
-            manager.bindAndInitialize();
-        } catch (MbmsException e) {
-            sIsInitialized.set(false);
-            throw e;
-        }
-        return manager;
-    }
-
-    /**
-     * Create a new MbmsStreamingManager using the system default data subscription ID.
-     * See {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}.
-     */
-    public static MbmsStreamingManager create(Context context,
-            MbmsStreamingManagerCallback callback, Handler handler)
-            throws MbmsException {
-        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
-    }
-
-    /**
-     * Create a new MbmsStreamingManager using the system default data subscription ID and
-     * default {@link Handler}.
-     * See {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}.
-     */
-    public static MbmsStreamingManager create(Context context,
-            MbmsStreamingManagerCallback callback)
-            throws MbmsException {
-        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), null);
-    }
-
-    /**
-     * Terminates this instance, ending calls to the registered listener.  Also terminates
-     * any streaming services spawned from this instance.
-     *
-     * May throw an {@link IllegalStateException}
-     */
-    public void dispose() {
-        try {
-            IMbmsStreamingService streamingService = mService.get();
-            if (streamingService == null) {
-                // Ignore and return, assume already disposed.
-                return;
-            }
-            streamingService.dispose(mSubscriptionId);
-        } catch (RemoteException e) {
-            // Ignore for now
-        } finally {
-            mService.set(null);
-            sIsInitialized.set(false);
-        }
-    }
-
-    /**
-     * An inspection API to retrieve the list of streaming media currently be advertised.
-     * The results are returned asynchronously through the previously registered callback.
-     * serviceClasses lets the app filter on types of programming and is opaque data between
-     * the app and the carrier.
-     *
-     * Multiple calls replace the list of serviceClasses of interest.
-     *
-     * This may throw an {@link MbmsException} containing any error in
-     * {@link android.telephony.mbms.MbmsException.GeneralErrors},
-     * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}, or
-     * {@link MbmsException#ERROR_MIDDLEWARE_LOST}.
-     *
-     * May also throw an unchecked {@link IllegalArgumentException} or an
-     * {@link IllegalStateException}
-     *
-     * @param classList A list of streaming service classes that the app would like updates on.
-     */
-    public void getStreamingServices(List<String> classList) throws MbmsException {
-        IMbmsStreamingService streamingService = mService.get();
-        if (streamingService == null) {
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
-        }
-        try {
-            int returnCode = streamingService.getStreamingServices(mSubscriptionId, classList);
-            if (returnCode != MbmsException.SUCCESS) {
-                throw new MbmsException(returnCode);
-            }
-        } catch (RemoteException e) {
-            Log.w(LOG_TAG, "Remote process died");
-            mService.set(null);
-            sIsInitialized.set(false);
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
-        }
-    }
-
-    /**
-     * Starts streaming a requested service, reporting status to the indicated callback.
-     * Returns an object used to control that stream. The stream may not be ready for consumption
-     * immediately upon return from this method -- wait until the streaming state has been
-     * reported via
-     * {@link android.telephony.mbms.StreamingServiceCallback#onStreamStateUpdated(int, int)}
-     *
-     * May throw an
-     * {@link MbmsException} containing any of the error codes in
-     * {@link android.telephony.mbms.MbmsException.GeneralErrors},
-     * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}, or
-     * {@link MbmsException#ERROR_MIDDLEWARE_LOST}.
-     *
-     * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
-     *
-     * Asynchronous errors through the callback include any of the errors in
-     * {@link android.telephony.mbms.MbmsException.GeneralErrors} or
-     * {@link android.telephony.mbms.MbmsException.StreamingErrors}.
-     *
-     * @param serviceInfo The information about the service to stream.
-     * @param callback A callback that'll be called when something about the stream changes.
-     * @param handler A handler that calls to {@code callback} should be called on. If null,
-     *                defaults to the handler provided via
-     *                {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}.
-     * @return An instance of {@link StreamingService} through which the stream can be controlled.
-     */
-    public StreamingService startStreaming(StreamingServiceInfo serviceInfo,
-            StreamingServiceCallback callback, Handler handler) throws MbmsException {
-        IMbmsStreamingService streamingService = mService.get();
-        if (streamingService == null) {
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
-        }
-
-        InternalStreamingServiceCallback serviceCallback = new InternalStreamingServiceCallback(
-                callback, handler == null ? mInternalCallback.getHandler() : handler);
-
-        StreamingService serviceForApp = new StreamingService(
-                mSubscriptionId, streamingService, serviceInfo, serviceCallback);
-
-        try {
-            int returnCode = streamingService.startStreaming(
-                    mSubscriptionId, serviceInfo.getServiceId(), serviceCallback);
-            if (returnCode != MbmsException.SUCCESS) {
-                throw new MbmsException(returnCode);
-            }
-        } catch (RemoteException e) {
-            Log.w(LOG_TAG, "Remote process died");
-            mService.set(null);
-            sIsInitialized.set(false);
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
-        }
-
-        return serviceForApp;
-    }
-
-    private void bindAndInitialize() throws MbmsException {
-        MbmsUtils.startBinding(mContext, MBMS_STREAMING_SERVICE_ACTION,
-                new ServiceConnection() {
-                    @Override
-                    public void onServiceConnected(ComponentName name, IBinder service) {
-                        IMbmsStreamingService streamingService =
-                                IMbmsStreamingService.Stub.asInterface(service);
-                        int result;
-                        try {
-                            result = streamingService.initialize(mInternalCallback,
-                                    mSubscriptionId);
-                        } catch (RemoteException e) {
-                            Log.e(LOG_TAG, "Service died before initialization");
-                            sendErrorToApp(
-                                    MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
-                                    e.toString());
-                            sIsInitialized.set(false);
-                            return;
-                        } catch (RuntimeException e) {
-                            Log.e(LOG_TAG, "Runtime exception during initialization");
-                            sendErrorToApp(
-                                    MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
-                                    e.toString());
-                            sIsInitialized.set(false);
-                            return;
-                        }
-                        if (result != MbmsException.SUCCESS) {
-                            sendErrorToApp(result, "Error returned during initialization");
-                            sIsInitialized.set(false);
-                            return;
-                        }
-                        try {
-                            streamingService.asBinder().linkToDeath(mDeathRecipient, 0);
-                        } catch (RemoteException e) {
-                            sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST,
-                                    "Middleware lost during initialization");
-                            sIsInitialized.set(false);
-                            return;
-                        }
-                        mService.set(streamingService);
-                    }
-
-                    @Override
-                    public void onServiceDisconnected(ComponentName name) {
-                        sIsInitialized.set(false);
-                        mService.set(null);
-                    }
-                });
-    }
-
-    private void sendErrorToApp(int errorCode, String message) {
-        try {
-            mInternalCallback.error(errorCode, message);
-        } catch (RemoteException e) {
-            // Ignore, should not happen locally.
-        }
-    }
-}
diff --git a/android/telephony/MbmsStreamingSession.java b/android/telephony/MbmsStreamingSession.java
new file mode 100644
index 0000000..a8c4607
--- /dev/null
+++ b/android/telephony/MbmsStreamingSession.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2016 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 android.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.telephony.mbms.InternalStreamingSessionCallback;
+import android.telephony.mbms.InternalStreamingServiceCallback;
+import android.telephony.mbms.MbmsErrors;
+import android.telephony.mbms.MbmsStreamingSessionCallback;
+import android.telephony.mbms.MbmsUtils;
+import android.telephony.mbms.StreamingService;
+import android.telephony.mbms.StreamingServiceCallback;
+import android.telephony.mbms.StreamingServiceInfo;
+import android.telephony.mbms.vendor.IMbmsStreamingService;
+import android.util.ArraySet;
+import android.util.Log;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+/**
+ * This class provides functionality for streaming media over MBMS.
+ */
+public class MbmsStreamingSession implements AutoCloseable {
+    private static final String LOG_TAG = "MbmsStreamingSession";
+
+    /**
+     * Service action which must be handled by the middleware implementing the MBMS streaming
+     * interface.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String MBMS_STREAMING_SERVICE_ACTION =
+            "android.telephony.action.EmbmsStreaming";
+
+    private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
+
+    private AtomicReference<IMbmsStreamingService> mService = new AtomicReference<>(null);
+    private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, "Received death notification");
+        }
+    };
+
+    private InternalStreamingSessionCallback mInternalCallback;
+    private Set<StreamingService> mKnownActiveStreamingServices = new ArraySet<>();
+
+    private final Context mContext;
+    private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
+
+    /** @hide */
+    private MbmsStreamingSession(Context context, MbmsStreamingSessionCallback callback,
+                    int subscriptionId, Handler handler) {
+        mContext = context;
+        mSubscriptionId = subscriptionId;
+        if (handler == null) {
+            handler = new Handler(Looper.getMainLooper());
+        }
+        mInternalCallback = new InternalStreamingSessionCallback(callback, handler);
+    }
+
+    /**
+     * Create a new {@link MbmsStreamingSession} using the given subscription ID.
+     *
+     * Note that this call will bind a remote service. You may not call this method on your app's
+     * main thread.
+     *
+     * You may only have one instance of {@link MbmsStreamingSession} per UID. If you call this
+     * method while there is an active instance of {@link MbmsStreamingSession} in your process
+     * (in other words, one that has not had {@link #close()} called on it), this method will
+     * throw an {@link IllegalStateException}. If you call this method in a different process
+     * running under the same UID, an error will be indicated via
+     * {@link MbmsStreamingSessionCallback#onError(int, String)}.
+     *
+     * Note that initialization may fail asynchronously. If you wish to try again after you
+     * receive such an asynchronous error, you must call {@link #close()} on the instance of
+     * {@link MbmsStreamingSession} that you received before calling this method again.
+     *
+     * @param context The {@link Context} to use.
+     * @param callback A callback object on which you wish to receive results of asynchronous
+     *                 operations.
+     * @param subscriptionId The subscription ID to use.
+     * @param handler The handler you wish to receive callbacks on.
+     * @return An instance of {@link MbmsStreamingSession}, or null if an error occurred.
+     */
+    public static @Nullable MbmsStreamingSession create(@NonNull Context context,
+            final @NonNull MbmsStreamingSessionCallback callback, int subscriptionId,
+            @NonNull Handler handler) {
+        if (!sIsInitialized.compareAndSet(false, true)) {
+            throw new IllegalStateException("Cannot create two instances of MbmsStreamingSession");
+        }
+        MbmsStreamingSession session = new MbmsStreamingSession(context, callback,
+                subscriptionId, handler);
+
+        final int result = session.bindAndInitialize();
+        if (result != MbmsErrors.SUCCESS) {
+            sIsInitialized.set(false);
+            handler.post(new Runnable() {
+                @Override
+                public void run() {
+                    callback.onError(result, null);
+                }
+            });
+            return null;
+        }
+        return session;
+    }
+
+    /**
+     * Create a new {@link MbmsStreamingSession} using the system default data subscription ID.
+     * See {@link #create(Context, MbmsStreamingSessionCallback, int, Handler)}.
+     */
+    public static MbmsStreamingSession create(@NonNull Context context,
+            @NonNull MbmsStreamingSessionCallback callback, @NonNull Handler handler) {
+        return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
+    }
+
+    /**
+     * Terminates this instance. Also terminates
+     * any streaming services spawned from this instance as if
+     * {@link StreamingService#stopStreaming()} had been called on them. After this method returns,
+     * no further callbacks originating from the middleware will be enqueued on the provided
+     * instance of {@link MbmsStreamingSessionCallback}, but callbacks that have already been
+     * enqueued will still be delivered.
+     *
+     * It is safe to call {@link #create(Context, MbmsStreamingSessionCallback, int, Handler)} to
+     * obtain another instance of {@link MbmsStreamingSession} immediately after this method
+     * returns.
+     *
+     * May throw an {@link IllegalStateException}
+     */
+    public void close() {
+        try {
+            IMbmsStreamingService streamingService = mService.get();
+            if (streamingService == null) {
+                // Ignore and return, assume already disposed.
+                return;
+            }
+            streamingService.dispose(mSubscriptionId);
+            for (StreamingService s : mKnownActiveStreamingServices) {
+                s.getCallback().stop();
+            }
+            mKnownActiveStreamingServices.clear();
+        } catch (RemoteException e) {
+            // Ignore for now
+        } finally {
+            mService.set(null);
+            sIsInitialized.set(false);
+            mInternalCallback.stop();
+        }
+    }
+
+    /**
+     * An inspection API to retrieve the list of streaming media currently be advertised.
+     * The results are returned asynchronously via
+     * {@link MbmsStreamingSessionCallback#onStreamingServicesUpdated(List)} on the callback
+     * provided upon creation.
+     *
+     * Multiple calls replace the list of service classes of interest.
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}.
+     *
+     * @param serviceClassList A list of streaming service classes that the app would like updates
+     *                         on. The exact names of these classes should be negotiated with the
+     *                         wireless carrier separately.
+     */
+    public void requestUpdateStreamingServices(List<String> serviceClassList) {
+        IMbmsStreamingService streamingService = mService.get();
+        if (streamingService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+        try {
+            int returnCode = streamingService.requestUpdateStreamingServices(
+                    mSubscriptionId, serviceClassList);
+            if (returnCode != MbmsErrors.SUCCESS) {
+                sendErrorToApp(returnCode, null);
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Remote process died");
+            mService.set(null);
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+        }
+    }
+
+    /**
+     * Starts streaming a requested service, reporting status to the indicated callback.
+     * Returns an object used to control that stream. The stream may not be ready for consumption
+     * immediately upon return from this method -- wait until the streaming state has been
+     * reported via
+     * {@link android.telephony.mbms.StreamingServiceCallback#onStreamStateUpdated(int, int)}
+     *
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     *
+     * Asynchronous errors through the callback include any of the errors in
+     * {@link MbmsErrors.GeneralErrors} or
+     * {@link MbmsErrors.StreamingErrors}.
+     *
+     * @param serviceInfo The information about the service to stream.
+     * @param callback A callback that'll be called when something about the stream changes.
+     * @param handler A handler that calls to {@code callback} should be called on.
+     * @return An instance of {@link StreamingService} through which the stream can be controlled.
+     *         May be {@code null} if an error occurred.
+     */
+    public @Nullable StreamingService startStreaming(StreamingServiceInfo serviceInfo,
+            StreamingServiceCallback callback, @NonNull Handler handler) {
+        IMbmsStreamingService streamingService = mService.get();
+        if (streamingService == null) {
+            throw new IllegalStateException("Middleware not yet bound");
+        }
+
+        InternalStreamingServiceCallback serviceCallback = new InternalStreamingServiceCallback(
+                callback, handler);
+
+        StreamingService serviceForApp = new StreamingService(
+                mSubscriptionId, streamingService, this, serviceInfo, serviceCallback);
+        mKnownActiveStreamingServices.add(serviceForApp);
+
+        try {
+            int returnCode = streamingService.startStreaming(
+                    mSubscriptionId, serviceInfo.getServiceId(), serviceCallback);
+            if (returnCode != MbmsErrors.SUCCESS) {
+                sendErrorToApp(returnCode, null);
+                return null;
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Remote process died");
+            mService.set(null);
+            sIsInitialized.set(false);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return null;
+        }
+
+        return serviceForApp;
+    }
+
+    /** @hide */
+    public void onStreamingServiceStopped(StreamingService service) {
+        mKnownActiveStreamingServices.remove(service);
+    }
+
+    private int bindAndInitialize() {
+        return MbmsUtils.startBinding(mContext, MBMS_STREAMING_SERVICE_ACTION,
+                new ServiceConnection() {
+                    @Override
+                    public void onServiceConnected(ComponentName name, IBinder service) {
+                        IMbmsStreamingService streamingService =
+                                IMbmsStreamingService.Stub.asInterface(service);
+                        int result;
+                        try {
+                            result = streamingService.initialize(mInternalCallback,
+                                    mSubscriptionId);
+                        } catch (RemoteException e) {
+                            Log.e(LOG_TAG, "Service died before initialization");
+                            sendErrorToApp(
+                                    MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+                                    e.toString());
+                            sIsInitialized.set(false);
+                            return;
+                        } catch (RuntimeException e) {
+                            Log.e(LOG_TAG, "Runtime exception during initialization");
+                            sendErrorToApp(
+                                    MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+                                    e.toString());
+                            sIsInitialized.set(false);
+                            return;
+                        }
+                        if (result != MbmsErrors.SUCCESS) {
+                            sendErrorToApp(result, "Error returned during initialization");
+                            sIsInitialized.set(false);
+                            return;
+                        }
+                        try {
+                            streamingService.asBinder().linkToDeath(mDeathRecipient, 0);
+                        } catch (RemoteException e) {
+                            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+                                    "Middleware lost during initialization");
+                            sIsInitialized.set(false);
+                            return;
+                        }
+                        mService.set(streamingService);
+                    }
+
+                    @Override
+                    public void onServiceDisconnected(ComponentName name) {
+                        sIsInitialized.set(false);
+                        mService.set(null);
+                    }
+                });
+    }
+
+    private void sendErrorToApp(int errorCode, String message) {
+        try {
+            mInternalCallback.onError(errorCode, message);
+        } catch (RemoteException e) {
+            // Ignore, should not happen locally.
+        }
+    }
+}
diff --git a/android/telephony/mbms/DownloadProgressListener.java b/android/telephony/mbms/DownloadProgressListener.java
deleted file mode 100644
index d91e9ad..0000000
--- a/android/telephony/mbms/DownloadProgressListener.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.telephony.mbms;
-
-import android.os.RemoteException;
-
-/**
- * A optional listener class used by download clients to track progress.
- * @hide
- */
-public class DownloadProgressListener extends IDownloadProgressListener.Stub {
-    /**
-     * Gives process callbacks for a given DownloadRequest.
-     * This is optionally specified when requesting a download and
-     * only lives while the app is running - it's unlikely to be useful for
-     * downloads far in the future.
-     *
-     * @param request a {@link DownloadRequest}, indicating which download is being referenced.
-     * @param fileInfo a {@link FileInfo} specifying the file to report progress on.  Note that
-     *   the request may result in many files being downloaded and the client
-     *   may not have been able to get a list of them in advance.
-     * @param currentDownloadSize is the current amount downloaded.
-     * @param fullDownloadSize is the total number of bytes that make up the downloaded content.
-     *   This may be different from the decoded final size, but is useful in gauging download
-     *   progress.
-     * @param currentDecodedSize is the number of bytes that have been decoded.
-     * @param fullDecodedSize is the total number of bytes that make up the final decoded content.
-     */
-    @Override
-    public void progress(DownloadRequest request, FileInfo fileInfo,
-            int currentDownloadSize, int fullDownloadSize,
-            int currentDecodedSize, int fullDecodedSize) throws RemoteException {
-    }
-}
diff --git a/android/telephony/mbms/DownloadRequest.java b/android/telephony/mbms/DownloadRequest.java
index eae9011..5a57f32 100644
--- a/android/telephony/mbms/DownloadRequest.java
+++ b/android/telephony/mbms/DownloadRequest.java
@@ -16,6 +16,7 @@
 
 package android.telephony.mbms;
 
+import android.annotation.SystemApi;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Parcel;
@@ -37,34 +38,27 @@
 import java.util.Objects;
 
 /**
- * A Parcelable class describing a pending Cell-Broadcast download request
- * @hide
+ * Describes a request to download files over cell-broadcast. Instances of this class should be
+ * created by the app when requesting a download, and instances of this class will be passed back
+ * to the app when the middleware updates the status of the download.
  */
-public class DownloadRequest implements Parcelable {
+public final class DownloadRequest implements Parcelable {
     // Version code used to keep token calculation consistent.
     private static final int CURRENT_VERSION = 1;
     private static final String LOG_TAG = "MbmsDownloadRequest";
 
-    /**
-     * Maximum permissible length for the app's download-completion intent, when serialized via
-     * {@link Intent#toUri(int)}.
-     */
+    /** @hide */
     public static final int MAX_APP_INTENT_SIZE = 50000;
 
-    /**
-     * Maximum permissible length for the app's destination path, when serialized via
-     * {@link Uri#toString()}.
-     */
+    /** @hide */
     public static final int MAX_DESTINATION_URI_SIZE = 50000;
 
     /** @hide */
     private static class OpaqueDataContainer implements Serializable {
-        private final String destinationUri;
         private final String appIntent;
         private final int version;
 
-        public OpaqueDataContainer(String destinationUri, String appIntent, int version) {
-            this.destinationUri = destinationUri;
+        public OpaqueDataContainer(String appIntent, int version) {
             this.appIntent = appIntent;
             this.version = version;
         }
@@ -73,7 +67,6 @@
     public static class Builder {
         private String fileServiceId;
         private Uri source;
-        private Uri dest;
         private int subscriptionId;
         private String appIntent;
         private int version = CURRENT_VERSION;
@@ -91,8 +84,8 @@
         /**
          * Set the service ID for the download request. For use by the middleware only.
          * @hide
-         * TODO: systemapi
          */
+        @SystemApi
         public Builder setServiceId(String serviceId) {
             fileServiceId = serviceId;
             return this;
@@ -101,7 +94,6 @@
         /**
          * Sets the source URI for the download request to be built.
          * @param source
-         * @return
          */
         public Builder setSource(Uri source) {
             this.source = source;
@@ -109,25 +101,8 @@
         }
 
         /**
-         * Sets the destination URI for the download request to be built. The middleware should
-         * not set this directly.
-         * @param dest A URI obtained from {@link Uri#fromFile(File)}, denoting the requested
-         *             final destination of the download.
-         * @return
-         */
-        public Builder setDest(Uri dest) {
-            if (dest.toString().length() > MAX_DESTINATION_URI_SIZE) {
-                throw new IllegalArgumentException("Destination uri must not exceed length " +
-                        MAX_DESTINATION_URI_SIZE);
-            }
-            this.dest = dest;
-            return this;
-        }
-
-        /**
          * Set the subscription ID on which the file(s) should be downloaded.
          * @param subscriptionId
-         * @return
          */
         public Builder setSubscriptionId(int subscriptionId) {
             this.subscriptionId = subscriptionId;
@@ -141,7 +116,6 @@
          *
          * The middleware should not use this method.
          * @param intent
-         * @return
          */
         public Builder setAppIntent(Intent intent) {
             this.appIntent = intent.toUri(0);
@@ -158,17 +132,15 @@
          * manager code, but is irrelevant to the middleware.
          * @param data A byte array, the contents of which should have been originally obtained
          *             from {@link DownloadRequest#getOpaqueData()}.
-         * @return
-         * TODO: systemapi
          * @hide
          */
+        @SystemApi
         public Builder setOpaqueData(byte[] data) {
             try {
                 ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(data));
                 OpaqueDataContainer dataContainer = (OpaqueDataContainer) stream.readObject();
                 version = dataContainer.version;
                 appIntent = dataContainer.appIntent;
-                dest = Uri.parse(dataContainer.destinationUri);
             } catch (IOException e) {
                 // Really should never happen
                 Log.e(LOG_TAG, "Got IOException trying to parse opaque data");
@@ -181,24 +153,21 @@
         }
 
         public DownloadRequest build() {
-            return new DownloadRequest(fileServiceId, source, dest,
-                    subscriptionId, appIntent, version);
+            return new DownloadRequest(fileServiceId, source, subscriptionId, appIntent, version);
         }
     }
 
     private final String fileServiceId;
     private final Uri sourceUri;
-    private final Uri destinationUri;
     private final int subscriptionId;
     private final String serializedResultIntentForApp;
     private final int version;
 
     private DownloadRequest(String fileServiceId,
-            Uri source, Uri dest,
-            int sub, String appIntent, int version) {
+            Uri source, int sub,
+            String appIntent, int version) {
         this.fileServiceId = fileServiceId;
         sourceUri = source;
-        destinationUri = dest;
         subscriptionId = sub;
         serializedResultIntentForApp = appIntent;
         this.version = version;
@@ -211,7 +180,6 @@
     private DownloadRequest(DownloadRequest dr) {
         fileServiceId = dr.fileServiceId;
         sourceUri = dr.sourceUri;
-        destinationUri = dr.destinationUri;
         subscriptionId = dr.subscriptionId;
         serializedResultIntentForApp = dr.serializedResultIntentForApp;
         version = dr.version;
@@ -220,7 +188,6 @@
     private DownloadRequest(Parcel in) {
         fileServiceId = in.readString();
         sourceUri = in.readParcelable(getClass().getClassLoader());
-        destinationUri = in.readParcelable(getClass().getClassLoader());
         subscriptionId = in.readInt();
         serializedResultIntentForApp = in.readString();
         version = in.readInt();
@@ -233,7 +200,6 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(fileServiceId);
         out.writeParcelable(sourceUri, flags);
-        out.writeParcelable(destinationUri, flags);
         out.writeInt(subscriptionId);
         out.writeString(serializedResultIntentForApp);
         out.writeInt(version);
@@ -254,14 +220,6 @@
     }
 
     /**
-     * For use by the client app only.
-     * @return The URI of the final destination of the download.
-     */
-    public Uri getDestinationUri() {
-        return destinationUri;
-    }
-
-    /**
      * @return The subscription ID on which to perform MBMS operations.
      */
     public int getSubscriptionId() {
@@ -287,14 +245,14 @@
      * {@link Builder#setOpaqueData(byte[])}.
      * @return A byte array of opaque data to persist.
      * @hide
-     * TODO: systemapi
      */
+    @SystemApi
     public byte[] getOpaqueData() {
         try {
             ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             ObjectOutputStream stream = new ObjectOutputStream(byteArrayOutputStream);
             OpaqueDataContainer container = new OpaqueDataContainer(
-                    destinationUri.toString(), serializedResultIntentForApp, version);
+                    serializedResultIntentForApp, version);
             stream.writeObject(container);
             stream.flush();
             return byteArrayOutputStream.toByteArray();
@@ -321,6 +279,22 @@
     };
 
     /**
+     * Maximum permissible length for the app's destination path, when serialized via
+     * {@link Uri#toString()}.
+     */
+    public static int getMaxAppIntentSize() {
+        return MAX_APP_INTENT_SIZE;
+    }
+
+    /**
+     * Maximum permissible length for the app's download-completion intent, when serialized via
+     * {@link Intent#toUri(int)}.
+     */
+    public static int getMaxDestinationUriSize() {
+        return MAX_DESTINATION_URI_SIZE;
+    }
+
+    /**
      * @hide
      */
     public boolean isMultipartDownload() {
@@ -344,7 +318,6 @@
         if (version >= 1) {
             // Hash the source URI, destination URI, and the app intent
             digest.update(sourceUri.toString().getBytes(StandardCharsets.UTF_8));
-            digest.update(destinationUri.toString().getBytes(StandardCharsets.UTF_8));
             digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8));
         }
         // Add updates for future versions here
@@ -365,13 +338,12 @@
                 version == request.version &&
                 Objects.equals(fileServiceId, request.fileServiceId) &&
                 Objects.equals(sourceUri, request.sourceUri) &&
-                Objects.equals(destinationUri, request.destinationUri) &&
                 Objects.equals(serializedResultIntentForApp, request.serializedResultIntentForApp);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(fileServiceId, sourceUri, destinationUri,
+        return Objects.hash(fileServiceId, sourceUri,
                 subscriptionId, serializedResultIntentForApp, version);
     }
 }
diff --git a/android/telephony/mbms/DownloadStateCallback.java b/android/telephony/mbms/DownloadStateCallback.java
new file mode 100644
index 0000000..86920bd
--- /dev/null
+++ b/android/telephony/mbms/DownloadStateCallback.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 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 android.telephony.mbms;
+
+import android.telephony.MbmsDownloadSession;
+
+/**
+ * A optional listener class used by download clients to track progress. Apps should extend this
+ * class and pass an instance into
+ * {@link MbmsDownloadSession#download(DownloadRequest)}
+ *
+ * This is optionally specified when requesting a download and will only be called while the app
+ * is running.
+ */
+public class DownloadStateCallback {
+
+    /**
+     * Called when the middleware wants to report progress for a file in a {@link DownloadRequest}.
+     *
+     * @param request a {@link DownloadRequest}, indicating which download is being referenced.
+     * @param fileInfo a {@link FileInfo} specifying the file to report progress on.  Note that
+     *   the request may result in many files being downloaded and the client
+     *   may not have been able to get a list of them in advance.
+     * @param currentDownloadSize is the current amount downloaded.
+     * @param fullDownloadSize is the total number of bytes that make up the downloaded content.
+     *   This may be different from the decoded final size, but is useful in gauging download
+     *   progress.
+     * @param currentDecodedSize is the number of bytes that have been decoded.
+     * @param fullDecodedSize is the total number of bytes that make up the final decoded content.
+     */
+    public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
+            int currentDownloadSize, int fullDownloadSize,
+            int currentDecodedSize, int fullDecodedSize) {
+    }
+
+    /**
+     * Gives download state callbacks for a file in a {@link DownloadRequest}.
+     *
+     * @param request a {@link DownloadRequest}, indicating which download is being referenced.
+     * @param fileInfo a {@link FileInfo} specifying the file to report progress on.  Note that
+     *   the request may result in many files being downloaded and the client
+     *   may not have been able to get a list of them in advance.
+     * @param state The current state of the download.
+     */
+    public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
+            @MbmsDownloadSession.DownloadStatus int state) {
+    }
+}
diff --git a/android/telephony/mbms/FileInfo.java b/android/telephony/mbms/FileInfo.java
index f97131d..0d737b5 100644
--- a/android/telephony/mbms/FileInfo.java
+++ b/android/telephony/mbms/FileInfo.java
@@ -16,26 +16,18 @@
 
 package android.telephony.mbms;
 
+import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 /**
- * A Parcelable class Cell-Broadcast downloadable file information.
- * @hide
+ * Describes a single file that is available over MBMS.
  */
-public class FileInfo implements Parcelable {
+public final class FileInfo implements Parcelable {
 
-    /**
-     * The URI into the carriers infrastructure which points to this file.
-     * This is used internally but is also one of the few pieces of data about the content that is
-     * exposed and may be needed for disambiguation by the application.
-     */
     private final Uri uri;
 
-    /**
-     * The mime type of the content.
-     */
     private final String mimeType;
 
     public static final Parcelable.Creator<FileInfo> CREATOR =
@@ -53,8 +45,8 @@
 
     /**
      * @hide
-     * TODO: systemapi
      */
+    @SystemApi
     public FileInfo(Uri uri, String mimeType) {
         this.uri = uri;
         this.mimeType = mimeType;
@@ -76,10 +68,17 @@
         return 0;
     }
 
+    /**
+     * @return The URI in the carrier's infrastructure which points to this file. Apps should
+     * negotiate the contents of this URI separately with the carrier.
+     */
     public Uri getUri() {
         return uri;
     }
 
+    /**
+     * @return The MIME type of the file.
+     */
     public String getMimeType() {
         return mimeType;
     }
diff --git a/android/telephony/mbms/FileServiceInfo.java b/android/telephony/mbms/FileServiceInfo.java
index 8afe4d3..d8d7f48 100644
--- a/android/telephony/mbms/FileServiceInfo.java
+++ b/android/telephony/mbms/FileServiceInfo.java
@@ -16,6 +16,7 @@
 
 package android.telephony.mbms;
 
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -26,13 +27,14 @@
 import java.util.Map;
 
 /**
- * A Parcelable class Cell-Broadcast downloadable file information.
- * @hide
+ * Describes a file service available from the carrier from which files can be downloaded via
+ * cell-broadcast.
  */
-public class FileServiceInfo extends ServiceInfo implements Parcelable {
+public final class FileServiceInfo extends ServiceInfo implements Parcelable {
     private final List<FileInfo> files;
 
-    /** @hide TODO: systemapi */
+    /** @hide */
+    @SystemApi
     public FileServiceInfo(Map<Locale, String> newNames, String newClassName,
             List<Locale> newLocales, String newServiceId, Date start, Date end,
             List<FileInfo> newFiles) {
@@ -56,7 +58,7 @@
     FileServiceInfo(Parcel in) {
         super(in);
         files = new ArrayList<FileInfo>();
-        in.readList(files, null);
+        in.readList(files, FileInfo.class.getClassLoader());
     }
 
     @Override
@@ -70,8 +72,12 @@
         return 0;
     }
 
+    /**
+     * @return A list of files available from this service. Note that this list may not be
+     * exhaustive -- the middleware may not have information on all files that are available.
+     * Consult the carrier for an authoritative and exhaustive list.
+     */
     public List<FileInfo> getFiles() {
         return files;
     }
-
 }
diff --git a/android/telephony/mbms/InternalStreamingManagerCallback.java b/android/telephony/mbms/InternalDownloadSessionCallback.java
similarity index 64%
copy from android/telephony/mbms/InternalStreamingManagerCallback.java
copy to android/telephony/mbms/InternalDownloadSessionCallback.java
index b52df8c..a7a5958 100644
--- a/android/telephony/mbms/InternalStreamingManagerCallback.java
+++ b/android/telephony/mbms/InternalDownloadSessionCallback.java
@@ -18,25 +18,28 @@
 
 import android.os.Handler;
 import android.os.RemoteException;
-import android.telephony.mbms.IMbmsStreamingManagerCallback;
-import android.telephony.mbms.MbmsStreamingManagerCallback;
-import android.telephony.mbms.StreamingServiceInfo;
 
 import java.util.List;
 
 /** @hide */
-public class InternalStreamingManagerCallback extends IMbmsStreamingManagerCallback.Stub {
-    private final Handler mHandler;
-    private final MbmsStreamingManagerCallback mAppCallback;
+public class InternalDownloadSessionCallback extends IMbmsDownloadSessionCallback.Stub {
 
-    public InternalStreamingManagerCallback(MbmsStreamingManagerCallback appCallback,
+    private final Handler mHandler;
+    private final MbmsDownloadSessionCallback mAppCallback;
+    private volatile boolean mIsStopped = false;
+
+    public InternalDownloadSessionCallback(MbmsDownloadSessionCallback appCallback,
             Handler handler) {
         mAppCallback = appCallback;
         mHandler = handler;
     }
 
     @Override
-    public void error(int errorCode, String message) throws RemoteException {
+    public void onError(final int errorCode, final String message) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -46,18 +49,25 @@
     }
 
     @Override
-    public void streamingServicesUpdated(List<StreamingServiceInfo> services)
-            throws RemoteException {
+    public void onFileServicesUpdated(final List<FileServiceInfo> services) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
         mHandler.post(new Runnable() {
             @Override
             public void run() {
-                mAppCallback.onStreamingServicesUpdated(services);
+                mAppCallback.onFileServicesUpdated(services);
             }
         });
     }
 
     @Override
-    public void middlewareReady() throws RemoteException {
+    public void onMiddlewareReady() throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -69,4 +79,8 @@
     public Handler getHandler() {
         return mHandler;
     }
+
+    public void stop() {
+        mIsStopped = true;
+    }
 }
diff --git a/android/telephony/mbms/InternalDownloadStateCallback.java b/android/telephony/mbms/InternalDownloadStateCallback.java
new file mode 100644
index 0000000..8702952
--- /dev/null
+++ b/android/telephony/mbms/InternalDownloadStateCallback.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 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 android.telephony.mbms;
+
+import android.os.Handler;
+import android.os.RemoteException;
+
+/**
+ * @hide
+ */
+public class InternalDownloadStateCallback extends IDownloadStateCallback.Stub {
+    private final Handler mHandler;
+    private final DownloadStateCallback mAppCallback;
+    private volatile boolean mIsStopped = false;
+
+    public InternalDownloadStateCallback(DownloadStateCallback appCallback, Handler handler) {
+        mAppCallback = appCallback;
+        mHandler = handler;
+    }
+
+    @Override
+    public void onProgressUpdated(final DownloadRequest request, final FileInfo fileInfo,
+            final int currentDownloadSize, final int fullDownloadSize, final int currentDecodedSize,
+            final int fullDecodedSize) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mAppCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
+                        fullDownloadSize, currentDecodedSize, fullDecodedSize);
+            }
+        });
+    }
+
+    @Override
+    public void onStateUpdated(final DownloadRequest request, final FileInfo fileInfo,
+            final int state) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mAppCallback.onStateUpdated(request, fileInfo, state);
+            }
+        });
+    }
+
+    public void stop() {
+        mIsStopped = true;
+    }
+}
diff --git a/android/telephony/mbms/InternalStreamingServiceCallback.java b/android/telephony/mbms/InternalStreamingServiceCallback.java
index bb337b2..eb6579c 100644
--- a/android/telephony/mbms/InternalStreamingServiceCallback.java
+++ b/android/telephony/mbms/InternalStreamingServiceCallback.java
@@ -23,6 +23,7 @@
 public class InternalStreamingServiceCallback extends IStreamingServiceCallback.Stub {
     private final StreamingServiceCallback mAppCallback;
     private final Handler mHandler;
+    private volatile boolean mIsStopped = false;
 
     public InternalStreamingServiceCallback(StreamingServiceCallback appCallback, Handler handler) {
         mAppCallback = appCallback;
@@ -30,7 +31,11 @@
     }
 
     @Override
-    public void error(int errorCode, String message) throws RemoteException {
+    public void onError(final int errorCode, final String message) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -40,7 +45,11 @@
     }
 
     @Override
-    public void streamStateUpdated(int state, int reason) throws RemoteException {
+    public void onStreamStateUpdated(final int state, final int reason) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -50,7 +59,11 @@
     }
 
     @Override
-    public void mediaDescriptionUpdated() throws RemoteException {
+    public void onMediaDescriptionUpdated() throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -60,7 +73,11 @@
     }
 
     @Override
-    public void broadcastSignalStrengthUpdated(int signalStrength) throws RemoteException {
+    public void onBroadcastSignalStrengthUpdated(final int signalStrength) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -70,7 +87,11 @@
     }
 
     @Override
-    public void streamMethodUpdated(int methodType) throws RemoteException {
+    public void onStreamMethodUpdated(final int methodType) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -78,4 +99,8 @@
             }
         });
     }
+
+    public void stop() {
+        mIsStopped = true;
+    }
 }
diff --git a/android/telephony/mbms/InternalStreamingManagerCallback.java b/android/telephony/mbms/InternalStreamingSessionCallback.java
similarity index 68%
rename from android/telephony/mbms/InternalStreamingManagerCallback.java
rename to android/telephony/mbms/InternalStreamingSessionCallback.java
index b52df8c..d782d12 100644
--- a/android/telephony/mbms/InternalStreamingManagerCallback.java
+++ b/android/telephony/mbms/InternalStreamingSessionCallback.java
@@ -18,25 +18,27 @@
 
 import android.os.Handler;
 import android.os.RemoteException;
-import android.telephony.mbms.IMbmsStreamingManagerCallback;
-import android.telephony.mbms.MbmsStreamingManagerCallback;
-import android.telephony.mbms.StreamingServiceInfo;
 
 import java.util.List;
 
 /** @hide */
-public class InternalStreamingManagerCallback extends IMbmsStreamingManagerCallback.Stub {
+public class InternalStreamingSessionCallback extends IMbmsStreamingSessionCallback.Stub {
     private final Handler mHandler;
-    private final MbmsStreamingManagerCallback mAppCallback;
+    private final MbmsStreamingSessionCallback mAppCallback;
+    private volatile boolean mIsStopped = false;
 
-    public InternalStreamingManagerCallback(MbmsStreamingManagerCallback appCallback,
+    public InternalStreamingSessionCallback(MbmsStreamingSessionCallback appCallback,
             Handler handler) {
         mAppCallback = appCallback;
         mHandler = handler;
     }
 
     @Override
-    public void error(int errorCode, String message) throws RemoteException {
+    public void onError(final int errorCode, final String message) throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -46,8 +48,12 @@
     }
 
     @Override
-    public void streamingServicesUpdated(List<StreamingServiceInfo> services)
+    public void onStreamingServicesUpdated(final List<StreamingServiceInfo> services)
             throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -57,7 +63,11 @@
     }
 
     @Override
-    public void middlewareReady() throws RemoteException {
+    public void onMiddlewareReady() throws RemoteException {
+        if (mIsStopped) {
+            return;
+        }
+
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -69,4 +79,8 @@
     public Handler getHandler() {
         return mHandler;
     }
+
+    public void stop() {
+        mIsStopped = true;
+    }
 }
diff --git a/android/telephony/mbms/MbmsDownloadManagerCallback.java b/android/telephony/mbms/MbmsDownloadManagerCallback.java
deleted file mode 100644
index 17291d0..0000000
--- a/android/telephony/mbms/MbmsDownloadManagerCallback.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2016 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 android.telephony.mbms;
-
-import android.os.RemoteException;
-import android.telephony.MbmsDownloadManager;
-
-import java.util.List;
-
-/**
- * A Parcelable class with Cell-Broadcast service information.
- * @hide
- */
-public class MbmsDownloadManagerCallback extends IMbmsDownloadManagerCallback.Stub {
-
-    @Override
-    public void error(int errorCode, String message) throws RemoteException {
-        // default implementation empty
-    }
-
-    /**
-     * Called to indicate published File Services have changed.
-     *
-     * This will only be called after the application has requested
-     * a list of file services and specified a service class list
-     * of interest AND the results of a subsequent getFileServices
-     * call with the same service class list would return different
-     * results.
-     *
-     * @param services a List of FileServiceInfos
-     *
-     */
-    @Override
-    public void fileServicesUpdated(List<FileServiceInfo> services) throws RemoteException {
-        // default implementation empty
-    }
-
-    /**
-     * Called to indicate that the middleware has been initialized and is ready.
-     *
-     * Before this method is called, calling any method on an instance of
-     * {@link android.telephony.MbmsDownloadManager} will result in an {@link MbmsException}
-     * being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
-     * or {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
-     */
-    @Override
-    public void middlewareReady() throws RemoteException {
-        // default implementation empty
-    }
-}
diff --git a/android/telephony/mbms/MbmsDownloadReceiver.java b/android/telephony/mbms/MbmsDownloadReceiver.java
index ba7d120..61415b5 100644
--- a/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -16,6 +16,7 @@
 
 package android.telephony.mbms;
 
+import android.annotation.SystemApi;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -24,8 +25,8 @@
 import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Bundle;
-import android.telephony.MbmsDownloadManager;
-import android.telephony.mbms.vendor.VendorIntents;
+import android.telephony.MbmsDownloadSession;
+import android.telephony.mbms.vendor.VendorUtils;
 import android.util.Log;
 
 import java.io.File;
@@ -35,52 +36,74 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Objects;
 import java.util.UUID;
 
 /**
- * @hide
+ * The {@link BroadcastReceiver} responsible for handling intents sent from the middleware. Apps
+ * that wish to download using MBMS APIs should declare this class in their AndroidManifest.xml as
+ * follows:
+<pre>{@code
+<receiver
+    android:name="android.telephony.mbms.MbmsDownloadReceiver"
+    android:permission="android.permission.SEND_EMBMS_INTENTS"
+    android:enabled="true"
+    android:exported="true">
+</receiver>}</pre>
  */
 public class MbmsDownloadReceiver extends BroadcastReceiver {
+    /** @hide */
     public static final String DOWNLOAD_TOKEN_SUFFIX = ".download_token";
+    /** @hide */
     public static final String MBMS_FILE_PROVIDER_META_DATA_KEY = "mbms-file-provider-authority";
 
     /**
-     * TODO: @SystemApi all these result codes
      * Indicates that the requested operation completed without error.
+     * @hide
      */
+    @SystemApi
     public static final int RESULT_OK = 0;
 
     /**
      * Indicates that the intent sent had an invalid action. This will be the result if
      * {@link Intent#getAction()} returns anything other than
-     * {@link VendorIntents#ACTION_DOWNLOAD_RESULT_INTERNAL},
-     * {@link VendorIntents#ACTION_FILE_DESCRIPTOR_REQUEST}, or
-     * {@link VendorIntents#ACTION_CLEANUP}.
+     * {@link VendorUtils#ACTION_DOWNLOAD_RESULT_INTERNAL},
+     * {@link VendorUtils#ACTION_FILE_DESCRIPTOR_REQUEST}, or
+     * {@link VendorUtils#ACTION_CLEANUP}.
      * This is a fatal result code and no result extras should be expected.
+     * @hide
      */
+    @SystemApi
     public static final int RESULT_INVALID_ACTION = 1;
 
     /**
      * Indicates that the intent was missing some required extras.
      * This is a fatal result code and no result extras should be expected.
+     * @hide
      */
+    @SystemApi
     public static final int RESULT_MALFORMED_INTENT = 2;
 
     /**
-     * Indicates that the supplied value for {@link VendorIntents#EXTRA_TEMP_FILE_ROOT}
+     * Indicates that the supplied value for {@link VendorUtils#EXTRA_TEMP_FILE_ROOT}
      * does not match what the app has stored.
      * This is a fatal result code and no result extras should be expected.
+     * @hide
      */
+    @SystemApi
     public static final int RESULT_BAD_TEMP_FILE_ROOT = 3;
 
     /**
      * Indicates that the manager was unable to move the completed download to its final location.
      * This is a fatal result code and no result extras should be expected.
+     * @hide
      */
+    @SystemApi
     public static final int RESULT_DOWNLOAD_FINALIZATION_ERROR = 4;
 
     /**
@@ -88,35 +111,48 @@
      * descriptors.
      * This is a non-fatal result code -- some file descriptors may still be generated, but there
      * is no guarantee that they will be the same number as requested.
+     * @hide
      */
+    @SystemApi
     public static final int RESULT_TEMP_FILE_GENERATION_ERROR = 5;
 
+    /**
+     * Indicates that the manager was unable to notify the app of the completed download.
+     * This is a fatal result code and no result extras should be expected.
+     * @hide
+     */
+    @SystemApi
+    public static final int RESULT_APP_NOTIFICATION_ERROR = 6;
+
+
     private static final String LOG_TAG = "MbmsDownloadReceiver";
     private static final String TEMP_FILE_SUFFIX = ".embms.temp";
-    private static final int MAX_TEMP_FILE_RETRIES = 5;
+    private static final String TEMP_FILE_STAGING_LOCATION = "staged_completed_files";
 
+    private static final int MAX_TEMP_FILE_RETRIES = 5;
 
     private String mFileProviderAuthorityCache = null;
     private String mMiddlewarePackageNameCache = null;
 
+    /** @hide */
     @Override
     public void onReceive(Context context, Intent intent) {
         if (!verifyIntentContents(context, intent)) {
             setResultCode(RESULT_MALFORMED_INTENT);
             return;
         }
-        if (!Objects.equals(intent.getStringExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT),
+        if (!Objects.equals(intent.getStringExtra(VendorUtils.EXTRA_TEMP_FILE_ROOT),
                 MbmsTempFileProvider.getEmbmsTempFileDir(context).getPath())) {
             setResultCode(RESULT_BAD_TEMP_FILE_ROOT);
             return;
         }
 
-        if (VendorIntents.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
+        if (VendorUtils.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
             moveDownloadedFile(context, intent);
             cleanupPostMove(context, intent);
-        } else if (VendorIntents.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
+        } else if (VendorUtils.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
             generateTempFiles(context, intent);
-        } else if (VendorIntents.ACTION_CLEANUP.equals(intent.getAction())) {
+        } else if (VendorUtils.ACTION_CLEANUP.equals(intent.getAction())) {
             cleanupTempFiles(context, intent);
         } else {
             setResultCode(RESULT_INVALID_ACTION);
@@ -124,30 +160,31 @@
     }
 
     private boolean verifyIntentContents(Context context, Intent intent) {
-        if (VendorIntents.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
-            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_RESULT)) {
+        if (VendorUtils.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
+            if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT)) {
                 Log.w(LOG_TAG, "Download result did not include a result code. Ignoring.");
                 return false;
             }
-            if (!intent.hasExtra(VendorIntents.EXTRA_REQUEST)) {
+            if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST)) {
                 Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring.");
                 return false;
             }
-            if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT)) {
+            if (!intent.hasExtra(VendorUtils.EXTRA_TEMP_FILE_ROOT)) {
                 Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
                 return false;
             }
-            if (!intent.hasExtra(MbmsDownloadManager.EXTRA_FILE_INFO)) {
+            if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO)) {
                 Log.w(LOG_TAG, "Download result did not include the associated file info. " +
                         "Ignoring.");
                 return false;
             }
-            if (!intent.hasExtra(VendorIntents.EXTRA_FINAL_URI)) {
+            if (!intent.hasExtra(VendorUtils.EXTRA_FINAL_URI)) {
                 Log.w(LOG_TAG, "Download result did not include the path to the final " +
                         "temp file. Ignoring.");
                 return false;
             }
-            DownloadRequest request = intent.getParcelableExtra(VendorIntents.EXTRA_REQUEST);
+            DownloadRequest request = intent.getParcelableExtra(
+                    MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST);
             String expectedTokenFileName = request.getHash() + DOWNLOAD_TOKEN_SUFFIX;
             File expectedTokenFile = new File(
                     MbmsUtils.getEmbmsTempFileDirForService(context, request.getFileServiceId()),
@@ -157,27 +194,27 @@
                         "Expected " + expectedTokenFile);
                 return false;
             }
-        } else if (VendorIntents.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
-            if (!intent.hasExtra(VendorIntents.EXTRA_SERVICE_INFO)) {
-                Log.w(LOG_TAG, "Temp file request did not include the associated service info." +
+        } else if (VendorUtils.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
+            if (!intent.hasExtra(VendorUtils.EXTRA_SERVICE_ID)) {
+                Log.w(LOG_TAG, "Temp file request did not include the associated service id." +
                         " Ignoring.");
                 return false;
             }
-            if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT)) {
+            if (!intent.hasExtra(VendorUtils.EXTRA_TEMP_FILE_ROOT)) {
                 Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
                 return false;
             }
-        } else if (VendorIntents.ACTION_CLEANUP.equals(intent.getAction())) {
-            if (!intent.hasExtra(VendorIntents.EXTRA_SERVICE_INFO)) {
-                Log.w(LOG_TAG, "Cleanup request did not include the associated service info." +
+        } else if (VendorUtils.ACTION_CLEANUP.equals(intent.getAction())) {
+            if (!intent.hasExtra(VendorUtils.EXTRA_SERVICE_ID)) {
+                Log.w(LOG_TAG, "Cleanup request did not include the associated service id." +
                         " Ignoring.");
                 return false;
             }
-            if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT)) {
+            if (!intent.hasExtra(VendorUtils.EXTRA_TEMP_FILE_ROOT)) {
                 Log.w(LOG_TAG, "Cleanup request did not include the temp file root. Ignoring.");
                 return false;
             }
-            if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILES_IN_USE)) {
+            if (!intent.hasExtra(VendorUtils.EXTRA_TEMP_FILES_IN_USE)) {
                 Log.w(LOG_TAG, "Cleanup request did not include the list of temp files in use. " +
                         "Ignoring.");
                 return false;
@@ -187,21 +224,26 @@
     }
 
     private void moveDownloadedFile(Context context, Intent intent) {
-        DownloadRequest request = intent.getParcelableExtra(VendorIntents.EXTRA_REQUEST);
+        DownloadRequest request = intent.getParcelableExtra(
+                MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST);
         Intent intentForApp = request.getIntentForApp();
+        if (intentForApp == null) {
+            Log.i(LOG_TAG, "Malformed app notification intent");
+            setResultCode(RESULT_APP_NOTIFICATION_ERROR);
+            return;
+        }
 
-        int result = intent.getIntExtra(MbmsDownloadManager.EXTRA_RESULT,
-                MbmsDownloadManager.RESULT_CANCELLED);
-        intentForApp.putExtra(MbmsDownloadManager.EXTRA_RESULT, result);
+        int result = intent.getIntExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT,
+                MbmsDownloadSession.RESULT_CANCELLED);
+        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT, result);
 
-        if (result != MbmsDownloadManager.RESULT_SUCCESSFUL) {
+        if (result != MbmsDownloadSession.RESULT_SUCCESSFUL) {
             Log.i(LOG_TAG, "Download request indicated a failed download. Aborting.");
             context.sendBroadcast(intentForApp);
             return;
         }
 
-        Uri destinationUri = request.getDestinationUri();
-        Uri finalTempFile = intent.getParcelableExtra(VendorIntents.EXTRA_FINAL_URI);
+        Uri finalTempFile = intent.getParcelableExtra(VendorUtils.EXTRA_FINAL_URI);
         if (!verifyTempFilePath(context, request.getFileServiceId(), finalTempFile)) {
             Log.w(LOG_TAG, "Download result specified an invalid temp file " + finalTempFile);
             setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
@@ -209,30 +251,37 @@
         }
 
         FileInfo completedFileInfo =
-                (FileInfo) intent.getParcelableExtra(MbmsDownloadManager.EXTRA_FILE_INFO);
-        String relativePath = calculateDestinationFileRelativePath(request, completedFileInfo);
+                (FileInfo) intent.getParcelableExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO);
+        Path stagingDirectory = FileSystems.getDefault().getPath(
+                MbmsTempFileProvider.getEmbmsTempFileDir(context).getPath(),
+                TEMP_FILE_STAGING_LOCATION);
 
-        Uri finalFileLocation = moveTempFile(finalTempFile, destinationUri, relativePath);
-        if (finalFileLocation == null) {
+        Uri stagedFileLocation;
+        try {
+            stagedFileLocation = stageTempFile(finalTempFile, stagingDirectory);
+        } catch (IOException e) {
             Log.w(LOG_TAG, "Failed to move temp file to final destination");
             setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
             return;
         }
-        intentForApp.putExtra(MbmsDownloadManager.EXTRA_COMPLETED_FILE_URI, finalFileLocation);
-        intentForApp.putExtra(MbmsDownloadManager.EXTRA_FILE_INFO, completedFileInfo);
+        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_COMPLETED_FILE_URI,
+                stagedFileLocation);
+        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO, completedFileInfo);
+        intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST, request);
 
         context.sendBroadcast(intentForApp);
         setResultCode(RESULT_OK);
     }
 
     private void cleanupPostMove(Context context, Intent intent) {
-        DownloadRequest request = intent.getParcelableExtra(VendorIntents.EXTRA_REQUEST);
+        DownloadRequest request = intent.getParcelableExtra(
+                MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST);
         if (request == null) {
             Log.w(LOG_TAG, "Intent does not include a DownloadRequest. Ignoring.");
             return;
         }
 
-        List<Uri> tempFiles = intent.getParcelableExtra(VendorIntents.EXTRA_TEMP_LIST);
+        List<Uri> tempFiles = intent.getParcelableExtra(VendorUtils.EXTRA_TEMP_LIST);
         if (tempFiles == null) {
             return;
         }
@@ -246,16 +295,15 @@
     }
 
     private void generateTempFiles(Context context, Intent intent) {
-        FileServiceInfo serviceInfo =
-                intent.getParcelableExtra(VendorIntents.EXTRA_SERVICE_INFO);
-        if (serviceInfo == null) {
-            Log.w(LOG_TAG, "Temp file request did not include the associated service info. " +
+        String serviceId = intent.getStringExtra(VendorUtils.EXTRA_SERVICE_ID);
+        if (serviceId == null) {
+            Log.w(LOG_TAG, "Temp file request did not include the associated service id. " +
                     "Ignoring.");
             setResultCode(RESULT_MALFORMED_INTENT);
             return;
         }
-        int fdCount = intent.getIntExtra(VendorIntents.EXTRA_FD_COUNT, 0);
-        List<Uri> pausedList = intent.getParcelableExtra(VendorIntents.EXTRA_PAUSED_LIST);
+        int fdCount = intent.getIntExtra(VendorUtils.EXTRA_FD_COUNT, 0);
+        List<Uri> pausedList = intent.getParcelableExtra(VendorUtils.EXTRA_PAUSED_LIST);
 
         if (fdCount == 0 && (pausedList == null || pausedList.size() == 0)) {
             Log.i(LOG_TAG, "No temp files actually requested. Ending.");
@@ -265,22 +313,20 @@
         }
 
         ArrayList<UriPathPair> freshTempFiles =
-                generateFreshTempFiles(context, serviceInfo, fdCount);
+                generateFreshTempFiles(context, serviceId, fdCount);
         ArrayList<UriPathPair> pausedFiles =
-                generateUrisForPausedFiles(context, serviceInfo, pausedList);
+                generateUrisForPausedFiles(context, serviceId, pausedList);
 
         Bundle result = new Bundle();
-        result.putParcelableArrayList(VendorIntents.EXTRA_FREE_URI_LIST, freshTempFiles);
-        result.putParcelableArrayList(VendorIntents.EXTRA_PAUSED_URI_LIST, pausedFiles);
+        result.putParcelableArrayList(VendorUtils.EXTRA_FREE_URI_LIST, freshTempFiles);
+        result.putParcelableArrayList(VendorUtils.EXTRA_PAUSED_URI_LIST, pausedFiles);
         setResultCode(RESULT_OK);
         setResultExtras(result);
     }
 
-    private ArrayList<UriPathPair> generateFreshTempFiles(Context context,
-            FileServiceInfo serviceInfo,
+    private ArrayList<UriPathPair> generateFreshTempFiles(Context context, String serviceId,
             int freshFdCount) {
-        File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context,
-                serviceInfo.getServiceId());
+        File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context, serviceId);
         if (!tempFileDir.exists()) {
             tempFileDir.mkdirs();
         }
@@ -324,14 +370,14 @@
     }
 
     private ArrayList<UriPathPair> generateUrisForPausedFiles(Context context,
-            FileServiceInfo serviceInfo, List<Uri> pausedFiles) {
+            String serviceId, List<Uri> pausedFiles) {
         if (pausedFiles == null) {
             return new ArrayList<>(0);
         }
         ArrayList<UriPathPair> result = new ArrayList<>(pausedFiles.size());
 
         for (Uri fileUri : pausedFiles) {
-            if (!verifyTempFilePath(context, serviceInfo.getServiceId(), fileUri)) {
+            if (!verifyTempFilePath(context, serviceId, fileUri)) {
                 Log.w(LOG_TAG, "Supplied file " + fileUri + " is not a valid temp file to resume");
                 setResultCode(RESULT_TEMP_FILE_GENERATION_ERROR);
                 continue;
@@ -353,12 +399,10 @@
     }
 
     private void cleanupTempFiles(Context context, Intent intent) {
-        FileServiceInfo serviceInfo =
-                intent.getParcelableExtra(VendorIntents.EXTRA_SERVICE_INFO);
-        File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context,
-                serviceInfo.getServiceId());
+        String serviceId = intent.getStringExtra(VendorUtils.EXTRA_SERVICE_ID);
+        File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context, serviceId);
         final List<Uri> filesInUse =
-                intent.getParcelableArrayListExtra(VendorIntents.EXTRA_TEMP_FILES_IN_USE);
+                intent.getParcelableArrayListExtra(VendorUtils.EXTRA_TEMP_FILES_IN_USE);
         File[] filesToDelete = tempFileDir.listFiles(new FileFilter() {
             @Override
             public boolean accept(File file) {
@@ -385,63 +429,22 @@
         }
     }
 
-    private static String calculateDestinationFileRelativePath(DownloadRequest request,
-            FileInfo info) {
-        List<String> filePathComponents = info.getUri().getPathSegments();
-        List<String> requestPathComponents = request.getSourceUri().getPathSegments();
-        Iterator<String> filePathIter = filePathComponents.iterator();
-        Iterator<String> requestPathIter = requestPathComponents.iterator();
-
-        StringBuilder pathBuilder = new StringBuilder();
-        // Iterate through the segments of the carrier's URI to the file, along with the segments
-        // of the source URI specified in the download request. The relative path is calculated
-        // as the tail of the file's URI that does not match the path segments in the source URI.
-        while (filePathIter.hasNext()) {
-            String currFilePathComponent = filePathIter.next();
-            if (requestPathIter.hasNext()) {
-                String requestFilePathComponent = requestPathIter.next();
-                if (requestFilePathComponent.equals(currFilePathComponent)) {
-                    continue;
-                }
-            }
-            pathBuilder.append(currFilePathComponent);
-            pathBuilder.append('/');
-        }
-        // remove the trailing slash
-        if (pathBuilder.length() > 0) {
-            pathBuilder.deleteCharAt(pathBuilder.length() - 1);
-        }
-        return pathBuilder.toString();
-    }
-
     /*
-     * Moves a tempfile located at fromPath to a new location at toPath. If
-     * toPath is a directory, the destination file will be located at  relativePath
-     * underneath toPath.
+     * Moves a tempfile located at fromPath to a new location in the staging directory.
      */
-    private static Uri moveTempFile(Uri fromPath, Uri toPath, String relativePath) {
+    private static Uri stageTempFile(Uri fromPath, Path stagingDirectory) throws IOException {
         if (!ContentResolver.SCHEME_FILE.equals(fromPath.getScheme())) {
             Log.w(LOG_TAG, "Moving source uri " + fromPath+ " does not have a file scheme");
             return null;
         }
-        if (!ContentResolver.SCHEME_FILE.equals(toPath.getScheme())) {
-            Log.w(LOG_TAG, "Moving destination uri " + toPath + " does not have a file scheme");
-            return null;
-        }
 
-        File fromFile = new File(fromPath.getSchemeSpecificPart());
-        File toFile = new File(toPath.getSchemeSpecificPart());
-        if (toFile.isDirectory()) {
-            toFile = new File(toFile, relativePath);
+        Path fromFile = FileSystems.getDefault().getPath(fromPath.getPath());
+        if (!Files.isDirectory(stagingDirectory)) {
+            Files.createDirectory(stagingDirectory);
         }
-        toFile.getParentFile().mkdirs();
+        Path result = Files.move(fromFile, stagingDirectory.resolve(fromFile.getFileName()));
 
-        if (fromFile.renameTo(toFile)) {
-            return Uri.fromFile(toFile);
-        } else if (manualMove(fromFile, toFile)) {
-            return Uri.fromFile(toFile);
-        }
-        return null;
+        return Uri.fromFile(result.toFile());
     }
 
     private static boolean verifyTempFilePath(Context context, String serviceId,
@@ -493,7 +496,7 @@
     private String getMiddlewarePackageCached(Context context) {
         if (mMiddlewarePackageNameCache == null) {
             mMiddlewarePackageNameCache = MbmsUtils.getMiddlewareServiceInfo(context,
-                    MbmsDownloadManager.MBMS_DOWNLOAD_SERVICE_ACTION).packageName;
+                    MbmsDownloadSession.MBMS_DOWNLOAD_SERVICE_ACTION).packageName;
         }
         return mMiddlewarePackageNameCache;
     }
diff --git a/android/telephony/mbms/MbmsDownloadSessionCallback.java b/android/telephony/mbms/MbmsDownloadSessionCallback.java
new file mode 100644
index 0000000..77dea6f
--- /dev/null
+++ b/android/telephony/mbms/MbmsDownloadSessionCallback.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 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 android.telephony.mbms;
+
+import android.telephony.MbmsDownloadSession;
+
+import java.util.List;
+
+/**
+ * A callback class that apps should use to receive information on file downloads over
+ * cell-broadcast.
+ */
+public class MbmsDownloadSessionCallback {
+
+    /**
+     * Indicates that the middleware has encountered an asynchronous error.
+     * @param errorCode Any error code listed in {@link MbmsErrors}
+     * @param message A message, intended for debugging purposes, describing the error in further
+     *                detail.
+     */
+    public void onError(int errorCode, String message) {
+        // default implementation empty
+    }
+
+    /**
+     * Called to indicate published File Services have changed.
+     *
+     * This will only be called after the application has requested a list of file services and
+     * specified a service class list of interest via
+     * {@link MbmsDownloadSession#requestUpdateFileServices(List)}. If there are subsequent calls to
+     * {@link MbmsDownloadSession#requestUpdateFileServices(List)},
+     * this method may not be called again if
+     * the list of service classes would remain the same.
+     *
+     * @param services The most recently updated list of available file services.
+     */
+    public void onFileServicesUpdated(List<FileServiceInfo> services) {
+        // default implementation empty
+    }
+
+    /**
+     * Called to indicate that the middleware has been initialized and is ready.
+     *
+     * Before this method is called, calling any method on an instance of
+     * {@link MbmsDownloadSession} will result in an {@link IllegalStateException}
+     * being thrown or {@link #onError(int, String)} being called with error code
+     * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
+     */
+    public void onMiddlewareReady() {
+        // default implementation empty
+    }
+}
diff --git a/android/telephony/mbms/MbmsException.java b/android/telephony/mbms/MbmsErrors.java
similarity index 87%
rename from android/telephony/mbms/MbmsException.java
rename to android/telephony/mbms/MbmsErrors.java
index 6de5a18..af0af24 100644
--- a/android/telephony/mbms/MbmsException.java
+++ b/android/telephony/mbms/MbmsErrors.java
@@ -16,7 +16,9 @@
 
 package android.telephony.mbms;
 
-public class MbmsException extends Exception {
+import android.telephony.MbmsStreamingSession;
+
+public class MbmsErrors {
     /** Indicates that the operation was successful. */
     public static final int SUCCESS = 0;
 
@@ -30,8 +32,8 @@
 
     /**
      * Indicates that the app attempted to perform an operation on an instance of
-     * TODO: link android.telephony.MbmsDownloadManager or
-     * {@link android.telephony.MbmsStreamingManager} without being bound to the middleware.
+     * {@link android.telephony.MbmsDownloadSession} or
+     * {@link MbmsStreamingSession} without being bound to the middleware.
      */
     public static final int ERROR_MIDDLEWARE_NOT_BOUND = 2;
 
@@ -46,8 +48,7 @@
         private InitializationErrors() {}
         /**
          * Indicates that the app tried to create more than one instance each of
-         * {@link android.telephony.MbmsStreamingManager} or
-         * TODO: link android.telephony.MbmsDownloadManager
+         * {@link MbmsStreamingSession} or {@link android.telephony.MbmsDownloadSession}.
          */
         public static final int ERROR_DUPLICATE_INITIALIZE = 101;
         /** Indicates that the app is not authorized to access media via MBMS.*/
@@ -64,8 +65,8 @@
         private GeneralErrors() {}
         /**
          * Indicates that the app attempted to perform an operation before receiving notification
-         * that the middleware is ready via {@link MbmsStreamingManagerCallback#onMiddlewareReady()}
-         * or TODO: link MbmsDownloadManagerCallback#middlewareReady
+         * that the middleware is ready via {@link MbmsStreamingSessionCallback#onMiddlewareReady()}
+         * or {@link MbmsDownloadSessionCallback#onMiddlewareReady()}.
          */
         public static final int ERROR_MIDDLEWARE_NOT_YET_READY = 201;
         /**
@@ -107,7 +108,7 @@
 
         /**
          * Indicates that the app called
-         * {@link android.telephony.MbmsStreamingManager#startStreaming(
+         * {@link MbmsStreamingSession#startStreaming(
          * StreamingServiceInfo, StreamingServiceCallback, android.os.Handler)}
          * more than once for the same {@link StreamingServiceInfo}.
          */
@@ -116,10 +117,9 @@
 
     /**
      * Indicates the errors that are applicable only to the file-download use-case
-     * TODO: unhide
-     * @hide
      */
     public static class DownloadErrors {
+        private DownloadErrors() { }
         /**
          * Indicates that the app is not allowed to change the temp file root at this time due to
          * outstanding download requests.
@@ -130,15 +130,5 @@
         public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 402;
     }
 
-    private final int mErrorCode;
-
-    /** @hide */
-    public MbmsException(int errorCode) {
-        super();
-        mErrorCode = errorCode;
-    }
-
-    public int getErrorCode() {
-        return mErrorCode;
-    }
+    private MbmsErrors() {}
 }
diff --git a/android/telephony/mbms/MbmsStreamingManagerCallback.java b/android/telephony/mbms/MbmsStreamingSessionCallback.java
similarity index 75%
rename from android/telephony/mbms/MbmsStreamingManagerCallback.java
rename to android/telephony/mbms/MbmsStreamingSessionCallback.java
index b31ffa7..5c130a0 100644
--- a/android/telephony/mbms/MbmsStreamingManagerCallback.java
+++ b/android/telephony/mbms/MbmsStreamingSessionCallback.java
@@ -16,25 +16,26 @@
 
 package android.telephony.mbms;
 
+import android.annotation.Nullable;
 import android.content.Context;
-import android.os.RemoteException;
-import android.telephony.MbmsStreamingManager;
+import android.os.Handler;
+import android.telephony.MbmsStreamingSession;
 
 import java.util.List;
 
 /**
  * A callback class that is used to receive information from the middleware on MBMS streaming
  * services. An instance of this object should be passed into
- * {@link android.telephony.MbmsStreamingManager#create(Context, MbmsStreamingManagerCallback)}.
+ * {@link MbmsStreamingSession#create(Context, MbmsStreamingSessionCallback, int, Handler)}.
  */
-public class MbmsStreamingManagerCallback {
+public class MbmsStreamingSessionCallback {
     /**
      * Called by the middleware when it has detected an error condition. The possible error codes
-     * are listed in {@link MbmsException}.
+     * are listed in {@link MbmsErrors}.
      * @param errorCode The error code.
      * @param message A human-readable message generated by the middleware for debugging purposes.
      */
-    public void onError(int errorCode, String message) {
+    public void onError(int errorCode, @Nullable String message) {
         // default implementation empty
     }
 
@@ -47,8 +48,7 @@
      * call with the same service class list would return different
      * results.
      *
-     * @param services a List of StreamingServiceInfos
-     *
+     * @param services The list of available services.
      */
     public void onStreamingServicesUpdated(List<StreamingServiceInfo> services) {
         // default implementation empty
@@ -58,9 +58,9 @@
      * Called to indicate that the middleware has been initialized and is ready.
      *
      * Before this method is called, calling any method on an instance of
-     * {@link android.telephony.MbmsStreamingManager} will result in an {@link MbmsException}
-     * being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
-     * or {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
+     * {@link MbmsStreamingSession} will result in an {@link IllegalStateException} or an error
+     * delivered via {@link #onError(int, String)} with error code
+     * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}.
      */
     public void onMiddlewareReady() {
         // default implementation empty
diff --git a/android/telephony/mbms/MbmsTempFileProvider.java b/android/telephony/mbms/MbmsTempFileProvider.java
index c4d033b..689becd 100644
--- a/android/telephony/mbms/MbmsTempFileProvider.java
+++ b/android/telephony/mbms/MbmsTempFileProvider.java
@@ -23,12 +23,11 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
 import android.content.pm.ProviderInfo;
 import android.database.Cursor;
 import android.net.Uri;
-import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
+import android.telephony.MbmsDownloadSession;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -39,7 +38,6 @@
  * @hide
  */
 public class MbmsTempFileProvider extends ContentProvider {
-    public static final String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot";
     public static final String TEMP_FILE_ROOT_PREF_FILE_NAME = "MbmsTempFileRootPrefs";
     public static final String TEMP_FILE_ROOT_PREF_NAME = "mbms_temp_file_root";
 
@@ -182,8 +180,8 @@
             if (storedTempFileRoot != null) {
                 return new File(storedTempFileRoot).getCanonicalFile();
             } else {
-                return new File(context.getFilesDir(), DEFAULT_TOP_LEVEL_TEMP_DIRECTORY)
-                        .getCanonicalFile();
+                return new File(context.getFilesDir(),
+                        MbmsDownloadSession.DEFAULT_TOP_LEVEL_TEMP_DIRECTORY).getCanonicalFile();
             }
         } catch (IOException e) {
             throw new RuntimeException("Unable to canonicalize temp file root path " + e);
diff --git a/android/telephony/mbms/MbmsUtils.java b/android/telephony/mbms/MbmsUtils.java
index 4b913f8..d38d8a7 100644
--- a/android/telephony/mbms/MbmsUtils.java
+++ b/android/telephony/mbms/MbmsUtils.java
@@ -68,19 +68,20 @@
         return downloadServices.get(0).serviceInfo;
     }
 
-    public static void startBinding(Context context, String serviceAction,
-            ServiceConnection serviceConnection) throws MbmsException {
+    public static int startBinding(Context context, String serviceAction,
+            ServiceConnection serviceConnection) {
         Intent bindIntent = new Intent();
         ServiceInfo mbmsServiceInfo =
                 MbmsUtils.getMiddlewareServiceInfo(context, serviceAction);
 
         if (mbmsServiceInfo == null) {
-            throw new MbmsException(MbmsException.ERROR_NO_UNIQUE_MIDDLEWARE);
+            return MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE;
         }
 
         bindIntent.setComponent(MbmsUtils.toComponentName(mbmsServiceInfo));
 
         context.bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
+        return MbmsErrors.SUCCESS;
     }
 
     /**
diff --git a/android/telephony/mbms/ServiceInfo.java b/android/telephony/mbms/ServiceInfo.java
index c01604b..9a01ed0 100644
--- a/android/telephony/mbms/ServiceInfo.java
+++ b/android/telephony/mbms/ServiceInfo.java
@@ -16,6 +16,8 @@
 
 package android.telephony.mbms;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -26,12 +28,13 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Set;
 
 /**
  * Describes a cell-broadcast service. This class should not be instantiated directly -- use
- * {@link StreamingServiceInfo} or FileServiceInfo TODO: add link once that's unhidden
+ * {@link StreamingServiceInfo} or {@link FileServiceInfo}
  */
 public class ServiceInfo {
     // arbitrary limit on the number of locale -> name pairs we support
@@ -58,6 +61,13 @@
         if (newLocales.size() > MAP_LIMIT) {
             throw new RuntimeException("bad locales length " + newLocales.size());
         }
+
+        for (Locale l : newLocales) {
+            if (!newNames.containsKey(l)) {
+                throw new IllegalArgumentException("A name must be provided for each locale");
+            }
+        }
+
         names = new HashMap(newNames.size());
         names.putAll(newNames);
         className = newClassName;
@@ -114,16 +124,25 @@
     }
 
     /**
-     * User displayable names listed by language. Do not modify the map returned from this method.
+     * Get the user-displayable name for this cell-broadcast service corresponding to the
+     * provided {@link Locale}.
+     * @param locale The {@link Locale} in which you want the name of the service. This must be a
+     *               value from the list returned by {@link #getLocales()} -- an
+     *               {@link java.util.NoSuchElementException} may be thrown otherwise.
+     * @return The {@link CharSequence} providing the name of the service in the given
+     *         {@link Locale}
      */
-    public Map<Locale, String> getNames() {
-        return names;
+    public @NonNull CharSequence getNameForLocale(@NonNull Locale locale) {
+        if (!names.containsKey(locale)) {
+            throw new NoSuchElementException("Locale not supported");
+        }
+        return names.get(locale);
     }
 
     /**
      * The class name for this service - used to categorize and filter
      */
-    public String getClassName() {
+    public String getServiceClassName() {
         return className;
     }
 
diff --git a/android/telephony/mbms/StreamingService.java b/android/telephony/mbms/StreamingService.java
index 1d66bac..ec9134a 100644
--- a/android/telephony/mbms/StreamingService.java
+++ b/android/telephony/mbms/StreamingService.java
@@ -17,8 +17,10 @@
 package android.telephony.mbms;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.net.Uri;
 import android.os.RemoteException;
+import android.telephony.MbmsStreamingSession;
 import android.telephony.mbms.vendor.IMbmsStreamingService;
 import android.util.Log;
 
@@ -27,7 +29,7 @@
 
 /**
  * Class used to represent a single MBMS stream. After a stream has been started with
- * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo,
+ * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
  * StreamingServiceCallback, android.os.Handler)},
  * this class is used to hold information about the stream and control it.
  */
@@ -63,7 +65,7 @@
 
     /**
      * State changed due to a call to {@link #stopStreaming()} or
-     * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo,
+     * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
      * StreamingServiceCallback, android.os.Handler)}
      */
     public static final int REASON_BY_USER_REQUEST = 1;
@@ -101,6 +103,7 @@
     public final static int UNICAST_METHOD   = 2;
 
     private final int mSubscriptionId;
+    private final MbmsStreamingSession mParentSession;
     private final StreamingServiceInfo mServiceInfo;
     private final InternalStreamingServiceCallback mCallback;
 
@@ -111,25 +114,25 @@
      */
     public StreamingService(int subscriptionId,
             IMbmsStreamingService service,
+            MbmsStreamingSession session,
             StreamingServiceInfo streamingServiceInfo,
             InternalStreamingServiceCallback callback) {
         mSubscriptionId = subscriptionId;
+        mParentSession = session;
         mService = service;
         mServiceInfo = streamingServiceInfo;
         mCallback = callback;
     }
 
     /**
-     * Retreive the Uri used to play this stream.
+     * Retrieve the Uri used to play this stream.
      *
-     * This may throw a {@link MbmsException} with the error code
-     * {@link MbmsException#ERROR_MIDDLEWARE_LOST}
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}.
      *
-     * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
-     *
-     * @return The {@link Uri} to pass to the streaming client.
+     * @return The {@link Uri} to pass to the streaming client, or {@code null} if an error
+     *         occurred.
      */
-    public Uri getPlaybackUri() throws MbmsException {
+    public @Nullable Uri getPlaybackUri() {
         if (mService == null) {
             throw new IllegalStateException("No streaming service attached");
         }
@@ -139,25 +142,26 @@
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService = null;
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+            mParentSession.onStreamingServiceStopped(this);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+            return null;
         }
     }
 
     /**
-     * Retreive the info for this StreamingService.
+     * Retrieve the {@link StreamingServiceInfo} corresponding to this stream.
      */
     public StreamingServiceInfo getInfo() {
         return mServiceInfo;
     }
 
     /**
-     * Stop streaming this service.
-     * This may throw a {@link MbmsException} with the error code
-     * {@link MbmsException#ERROR_MIDDLEWARE_LOST}
+     * Stop streaming this service. Further operations on this object will fail with an
+     * {@link IllegalStateException}.
      *
-     * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
      */
-    public void stopStreaming() throws MbmsException {
+    public void stopStreaming() {
         if (mService == null) {
             throw new IllegalStateException("No streaming service attached");
         }
@@ -167,32 +171,22 @@
         } catch (RemoteException e) {
             Log.w(LOG_TAG, "Remote process died");
             mService = null;
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+            sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+        } finally {
+            mParentSession.onStreamingServiceStopped(this);
         }
     }
 
-    /**
-     * Disposes of this stream. Further operations on this object will fail with an
-     * {@link IllegalStateException}.
-     *
-     * This may throw a {@link MbmsException} with the error code
-     * {@link MbmsException#ERROR_MIDDLEWARE_LOST}
-     * May also throw an {@link IllegalStateException}
-     */
-    public void dispose() throws MbmsException {
-        if (mService == null) {
-            throw new IllegalStateException("No streaming service attached");
-        }
+    /** @hide */
+    public InternalStreamingServiceCallback getCallback() {
+        return mCallback;
+    }
 
+    private void sendErrorToApp(int errorCode, String message) {
         try {
-            mService.disposeStream(mSubscriptionId, mServiceInfo.getServiceId());
+            mCallback.onError(errorCode, message);
         } catch (RemoteException e) {
-            Log.w(LOG_TAG, "Remote process died");
-            throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
-        } catch (IllegalArgumentException e) {
-            throw new IllegalStateException("StreamingService state inconsistent with middleware");
-        } finally {
-            mService = null;
+            // Ignore, should not happen locally.
         }
     }
 }
diff --git a/android/telephony/mbms/StreamingServiceCallback.java b/android/telephony/mbms/StreamingServiceCallback.java
index b72c715..0903824 100644
--- a/android/telephony/mbms/StreamingServiceCallback.java
+++ b/android/telephony/mbms/StreamingServiceCallback.java
@@ -16,6 +16,8 @@
 
 package android.telephony.mbms;
 
+import android.annotation.Nullable;
+
 /**
  * A callback class for use when the application is actively streaming content. The middleware
  * will provide updates on the status of the stream via this callback.
@@ -33,11 +35,11 @@
 
     /**
      * Called by the middleware when it has detected an error condition in this stream. The
-     * possible error codes are listed in {@link MbmsException}.
+     * possible error codes are listed in {@link MbmsErrors}.
      * @param errorCode The error code.
      * @param message A human-readable message generated by the middleware for debugging purposes.
      */
-    public void onError(int errorCode, String message) {
+    public void onError(int errorCode, @Nullable String message) {
         // default implementation empty
     }
 
diff --git a/android/telephony/mbms/UriPathPair.java b/android/telephony/mbms/UriPathPair.java
index 7acc270..187e9ee 100644
--- a/android/telephony/mbms/UriPathPair.java
+++ b/android/telephony/mbms/UriPathPair.java
@@ -16,13 +16,20 @@
 
 package android.telephony.mbms;
 
+import android.annotation.SystemApi;
 import android.content.ContentResolver;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.mbms.vendor.VendorUtils;
 
-/** @hide */
-public class UriPathPair implements Parcelable {
+/**
+ * Wrapper for a pair of {@link Uri}s that describe a temp file used by the middleware to
+ * download files via cell-broadcast.
+ * @hide
+ */
+@SystemApi
+public final class UriPathPair implements Parcelable {
     private final Uri mFilePathUri;
     private final Uri mContentUri;
 
@@ -40,7 +47,7 @@
     }
 
     /** @hide */
-    protected UriPathPair(Parcel in) {
+    private UriPathPair(Parcel in) {
         mFilePathUri = in.readParcelable(Uri.class.getClassLoader());
         mContentUri = in.readParcelable(Uri.class.getClassLoader());
     }
@@ -57,12 +64,23 @@
         }
     };
 
-    /** future systemapi */
+    /**
+     * Returns the file-path {@link Uri}. This has scheme {@code file} and points to the actual
+     * location on disk where the temp file resides. Use this when sending {@link Uri}s back to the
+     * app in the intents in {@link VendorUtils}.
+     * @return A {@code file} {@link Uri}.
+     */
     public Uri getFilePathUri() {
         return mFilePathUri;
     }
 
-    /** future systemapi */
+    /**
+     * Returns the content {@link Uri} that may be used with
+     * {@link ContentResolver#openFileDescriptor(Uri, String)} to obtain a
+     * {@link android.os.ParcelFileDescriptor} to a temp file to write to. This {@link Uri} will
+     * expire if the middleware process dies.
+     * @return A {@code content} {@link Uri}
+     */
     public Uri getContentUri() {
         return mContentUri;
     }
diff --git a/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index 71713d0..d845a57 100644
--- a/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -17,40 +17,50 @@
 package android.telephony.mbms.vendor;
 
 import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
 import android.os.RemoteException;
-import android.telephony.mbms.DownloadProgressListener;
+import android.telephony.MbmsDownloadSession;
 import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.DownloadStateCallback;
 import android.telephony.mbms.FileInfo;
 import android.telephony.mbms.FileServiceInfo;
-import android.telephony.mbms.IDownloadProgressListener;
-import android.telephony.mbms.IMbmsDownloadManagerCallback;
-import android.telephony.mbms.MbmsDownloadManagerCallback;
-import android.telephony.mbms.MbmsException;
+import android.telephony.mbms.IDownloadStateCallback;
+import android.telephony.mbms.IMbmsDownloadSessionCallback;
+import android.telephony.mbms.MbmsDownloadSessionCallback;
+import android.telephony.mbms.MbmsErrors;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
- * Base class for MbmsDownloadService. The middleware should extend this base class rather than
- * the aidl stub for compatibility
+ * Base class for MbmsDownloadService. The middleware should return an instance of this object from
+ * its {@link android.app.Service#onBind(Intent)} method.
  * @hide
- * TODO: future systemapi
  */
+@SystemApi
 public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
+    private final Map<IBinder, DownloadStateCallback> mDownloadCallbackBinderMap = new HashMap<>();
+    private final Map<IBinder, DeathRecipient> mDownloadCallbackDeathRecipients = new HashMap<>();
+
     /**
      * Initialize the download service for this app and subId, registering the listener.
      *
      * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}, which
      * will be intercepted and passed to the app as
-     * {@link android.telephony.mbms.MbmsException.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
+     * {@link MbmsErrors.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
      *
-     * May return any value from {@link android.telephony.mbms.MbmsException.InitializationErrors}
-     * or {@link MbmsException#SUCCESS}. Non-successful error codes will be passed to the app via
-     * {@link IMbmsDownloadManagerCallback#error(int, String)}.
+     * May return any value from {@link MbmsErrors.InitializationErrors}
+     * or {@link MbmsErrors#SUCCESS}. Non-successful error codes will be passed to the app via
+     * {@link IMbmsDownloadSessionCallback#onError(int, String)}.
      *
      * @param callback The callback to use to communicate with the app.
      * @param subscriptionId The subscription ID to use.
      */
-    public int initialize(int subscriptionId, MbmsDownloadManagerCallback callback)
+    public int initialize(int subscriptionId, MbmsDownloadSessionCallback callback)
             throws RemoteException {
         return 0;
     }
@@ -60,22 +70,42 @@
      * @hide
      */
     @Override
-    public final int initialize(int subscriptionId,
-            final IMbmsDownloadManagerCallback callback) throws RemoteException {
-        return initialize(subscriptionId, new MbmsDownloadManagerCallback() {
+    public final int initialize(final int subscriptionId,
+            final IMbmsDownloadSessionCallback callback) throws RemoteException {
+        final int uid = Binder.getCallingUid();
+        callback.asBinder().linkToDeath(new DeathRecipient() {
             @Override
-            public void error(int errorCode, String message) throws RemoteException {
-                callback.error(errorCode, message);
+            public void binderDied() {
+                onAppCallbackDied(uid, subscriptionId);
+            }
+        }, 0);
+
+        return initialize(subscriptionId, new MbmsDownloadSessionCallback() {
+            @Override
+            public void onError(int errorCode, String message) {
+                try {
+                    callback.onError(errorCode, message);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
             }
 
             @Override
-            public void fileServicesUpdated(List<FileServiceInfo> services) throws RemoteException {
-                callback.fileServicesUpdated(services);
+            public void onFileServicesUpdated(List<FileServiceInfo> services) {
+                try {
+                    callback.onFileServicesUpdated(services);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
             }
 
             @Override
-            public void middlewareReady() throws RemoteException {
-                callback.middlewareReady();
+            public void onMiddlewareReady() {
+                try {
+                    callback.onMiddlewareReady();
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, subscriptionId);
+                }
             }
         });
     }
@@ -83,7 +113,7 @@
     /**
      * Registers serviceClasses of interest with the appName/subId key.
      * Starts async fetching data on streaming services of matching classes to be reported
-     * later via {@link IMbmsDownloadManagerCallback#fileServicesUpdated(List)}
+     * later via {@link IMbmsDownloadSessionCallback#onFileServicesUpdated(List)}
      *
      * Note that subsequent calls with the same uid and subId will replace
      * the service class list.
@@ -94,11 +124,11 @@
      * @param serviceClasses The service classes that the app wishes to get info on. The strings
      *                       may contain arbitrary data as negotiated between the app and the
      *                       carrier.
-     * @return One of {@link MbmsException#SUCCESS} or
-     *         {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY},
+     * @return One of {@link MbmsErrors#SUCCESS} or
+     *         {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY},
      */
     @Override
-    public int getFileServices(int subscriptionId, List<String> serviceClasses)
+    public int requestUpdateFileServices(int subscriptionId, List<String> serviceClasses)
             throws RemoteException {
         return 0;
     }
@@ -110,13 +140,13 @@
      *
      * If the calling app (as identified by the calling UID) currently has any pending download
      * requests that have not been canceled, the middleware must return
-     * {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} here.
+     * {@link MbmsErrors.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} here.
      *
      * @param subscriptionId The subscription id the download is operating under.
      * @param rootDirectoryPath The path to the app's temp file root directory.
-     * @return {@link MbmsException#SUCCESS},
-     *         {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} or
-     *         {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT}
+     * @return {@link MbmsErrors#SUCCESS},
+     *         {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} or
+     *         {@link MbmsErrors.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT}
      */
     @Override
     public int setTempFileRootDirectory(int subscriptionId,
@@ -132,12 +162,32 @@
      * this is not the case, an {@link IllegalStateException} may be thrown.
      *
      * @param downloadRequest An object describing the set of files to be downloaded.
-     * @param listener A listener through which the middleware can provide progress updates to
-     *                 the app while both are still running.
-     * @return Any error from {@link android.telephony.mbms.MbmsException.GeneralErrors}
-     *         or {@link MbmsException#SUCCESS}
+     * @return Any error from {@link MbmsErrors.GeneralErrors}
+     *         or {@link MbmsErrors#SUCCESS}
      */
-    public int download(DownloadRequest downloadRequest, DownloadProgressListener listener) {
+    @Override
+    public int download(DownloadRequest downloadRequest) throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Registers a download state callbacks for the provided {@link DownloadRequest}.
+     *
+     * This method is called by the app when it wants to request updates on the progress or
+     * status of the download.
+     *
+     * If the middleware is not aware of a download having been requested with the provided
+     *
+     * {@link DownloadRequest} in the past,
+     * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}
+     * must be returned.
+     *
+     * @param downloadRequest The {@link DownloadRequest} that was used to initiate the download
+     *                        for which progress updates are being requested.
+     * @param callback The callback object to use.
+     */
+    public int registerStateCallback(DownloadRequest downloadRequest,
+            DownloadStateCallback callback) throws RemoteException {
         return 0;
     }
 
@@ -146,24 +196,101 @@
      * @hide
      */
     @Override
-    public final int download(DownloadRequest downloadRequest, IDownloadProgressListener listener)
+    public final int registerStateCallback(
+            final DownloadRequest downloadRequest, final IDownloadStateCallback callback)
             throws RemoteException {
-        return download(downloadRequest, new DownloadProgressListener() {
+        final int uid = Binder.getCallingUid();
+        DeathRecipient deathRecipient = new DeathRecipient() {
             @Override
-            public void progress(DownloadRequest request, FileInfo fileInfo, int
-                    currentDownloadSize, int fullDownloadSize, int currentDecodedSize, int
-                    fullDecodedSize) throws RemoteException {
-                listener.progress(request, fileInfo, currentDownloadSize, fullDownloadSize,
-                        currentDecodedSize, fullDecodedSize);
+            public void binderDied() {
+                onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+                mDownloadCallbackBinderMap.remove(callback.asBinder());
+                mDownloadCallbackDeathRecipients.remove(callback.asBinder());
             }
-        });
+        };
+        mDownloadCallbackDeathRecipients.put(callback.asBinder(), deathRecipient);
+        callback.asBinder().linkToDeath(deathRecipient, 0);
+
+        DownloadStateCallback exposedCallback = new DownloadStateCallback() {
+            @Override
+            public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo, int
+                    currentDownloadSize, int fullDownloadSize, int currentDecodedSize, int
+                    fullDecodedSize) {
+                try {
+                    callback.onProgressUpdated(request, fileInfo, currentDownloadSize,
+                            fullDownloadSize,
+                            currentDecodedSize, fullDecodedSize);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+                }
+            }
+
+            @Override
+            public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
+                    @MbmsDownloadSession.DownloadStatus int state) {
+                try {
+                    callback.onStateUpdated(request, fileInfo, state);
+                } catch (RemoteException e) {
+                    onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+                }
+            }
+        };
+
+        mDownloadCallbackBinderMap.put(callback.asBinder(), exposedCallback);
+
+        return registerStateCallback(downloadRequest, exposedCallback);
     }
 
+    /**
+     * Un-registers a download state callbacks for the provided {@link DownloadRequest}.
+     *
+     * This method is called by the app when it no longer wants to request updates on the
+     * download.
+     *
+     * If the middleware is not aware of a download having been requested with the provided
+     * {@link DownloadRequest} in the past,
+     * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}
+     * must be returned.
+     *
+     * @param downloadRequest The {@link DownloadRequest} that was used to register the callback
+     * @param callback The callback object that
+     *                 {@link #registerStateCallback(DownloadRequest, DownloadStateCallback)}
+     *                 was called with.
+     */
+    public int unregisterStateCallback(DownloadRequest downloadRequest,
+            DownloadStateCallback callback) throws RemoteException {
+        return 0;
+    }
+
+    /**
+     * Actual AIDL implementation -- hides the callback AIDL from the API.
+     * @hide
+     */
+    @Override
+    public final int unregisterStateCallback(
+            final DownloadRequest downloadRequest, final IDownloadStateCallback callback)
+            throws RemoteException {
+        DeathRecipient deathRecipient =
+                mDownloadCallbackDeathRecipients.remove(callback.asBinder());
+        if (deathRecipient == null) {
+            throw new IllegalArgumentException("Unknown callback");
+        }
+
+        callback.asBinder().unlinkToDeath(deathRecipient, 0);
+
+        DownloadStateCallback exposedCallback =
+                mDownloadCallbackBinderMap.remove(callback.asBinder());
+        if (exposedCallback == null) {
+            throw new IllegalArgumentException("Unknown callback");
+        }
+
+        return unregisterStateCallback(downloadRequest, exposedCallback);
+    }
 
     /**
      * Returns a list of pending {@link DownloadRequest}s that originated from the calling
      * application, identified by its uid. A pending request is one that was issued via
-     * {@link #download(DownloadRequest, IDownloadProgressListener)} but not cancelled through
+     * {@link #download(DownloadRequest)} but not cancelled through
      * {@link #cancelDownload(DownloadRequest)}.
      * The middleware must return a non-null result synchronously or throw an exception
      * inheriting from {@link RuntimeException}.
@@ -179,13 +306,13 @@
      * Issues a request to cancel the specified download request.
      *
      * If the middleware is unable to cancel the request for whatever reason, it should return
-     * synchronously with an error. If this method returns {@link MbmsException#SUCCESS}, the app
+     * synchronously with an error. If this method returns {@link MbmsErrors#SUCCESS}, the app
      * will no longer be expecting any more file-completed intents from the middleware for this
      * {@link DownloadRequest}.
      * @param downloadRequest The request to cancel
-     * @return {@link MbmsException#SUCCESS},
-     *         {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST},
-     *         {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
+     * @return {@link MbmsErrors#SUCCESS},
+     *         {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST},
+     *         {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
      */
     @Override
     public int cancelDownload(DownloadRequest downloadRequest) throws RemoteException {
@@ -197,7 +324,7 @@
      *
      * If the middleware has not yet been properly initialized or if it has no records of the
      * file indicated by {@code fileInfo} being associated with {@code downloadRequest},
-     * {@link android.telephony.MbmsDownloadManager#STATUS_UNKNOWN} must be returned.
+     * {@link MbmsDownloadSession#STATUS_UNKNOWN} must be returned.
      *
      * @param downloadRequest The download request to query.
      * @param fileInfo The particular file within the request to get information on.
@@ -217,7 +344,7 @@
      * In addition, current in-progress downloads must not be interrupted.
      *
      * If the middleware is not aware of the specified download request, return
-     * {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
+     * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
      *
      * @param downloadRequest The request to re-download files for.
      */
@@ -231,7 +358,7 @@
      * Signals that the app wishes to dispose of the session identified by the
      * {@code subscriptionId} argument and the caller's uid. No notification back to the
      * app is required for this operation, and the corresponding callback provided via
-     * {@link #initialize(int, IMbmsDownloadManagerCallback)} should no longer be used
+     * {@link #initialize(int, IMbmsDownloadSessionCallback)} should no longer be used
      * after this method has been called by the app.
      *
      * Any download requests issued by the app should remain in effect until the app calls
@@ -244,4 +371,12 @@
     @Override
     public void dispose(int subscriptionId) throws RemoteException {
     }
+
+    /**
+     * Indicates that the app identified by the given UID and subscription ID has died.
+     * @param uid the UID of the app, as returned by {@link Binder#getCallingUid()}.
+     * @param subscriptionId The subscription ID the app is using.
+     */
+    public void onAppCallbackDied(int uid, int subscriptionId) {
+    }
 }
diff --git a/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index 843e048..f8f370a 100644
--- a/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -18,13 +18,14 @@
 
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.content.Intent;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.RemoteException;
-import android.telephony.mbms.IMbmsStreamingManagerCallback;
+import android.telephony.mbms.IMbmsStreamingSessionCallback;
 import android.telephony.mbms.IStreamingServiceCallback;
-import android.telephony.mbms.MbmsException;
-import android.telephony.mbms.MbmsStreamingManagerCallback;
+import android.telephony.mbms.MbmsErrors;
+import android.telephony.mbms.MbmsStreamingSessionCallback;
 import android.telephony.mbms.StreamingService;
 import android.telephony.mbms.StreamingServiceCallback;
 import android.telephony.mbms.StreamingServiceInfo;
@@ -32,6 +33,8 @@
 import java.util.List;
 
 /**
+ * Base class for MBMS streaming services. The middleware should return an instance of this
+ * object from its {@link android.app.Service#onBind(Intent)} method.
  * @hide
  */
 @SystemApi
@@ -41,16 +44,16 @@
      *
      * May throw an {@link IllegalArgumentException} or a {@link SecurityException}, which
      * will be intercepted and passed to the app as
-     * {@link android.telephony.mbms.MbmsException.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
+     * {@link MbmsErrors.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
      *
-     * May return any value from {@link android.telephony.mbms.MbmsException.InitializationErrors}
-     * or {@link MbmsException#SUCCESS}. Non-successful error codes will be passed to the app via
-     * {@link IMbmsStreamingManagerCallback#error(int, String)}.
+     * May return any value from {@link MbmsErrors.InitializationErrors}
+     * or {@link MbmsErrors#SUCCESS}. Non-successful error codes will be passed to the app via
+     * {@link IMbmsStreamingSessionCallback#onError(int, String)}.
      *
      * @param callback The callback to use to communicate with the app.
      * @param subscriptionId The subscription ID to use.
      */
-    public int initialize(MbmsStreamingManagerCallback callback, int subscriptionId)
+    public int initialize(MbmsStreamingSessionCallback callback, int subscriptionId)
             throws RemoteException {
         return 0;
     }
@@ -60,7 +63,7 @@
      * @hide
      */
     @Override
-    public final int initialize(final IMbmsStreamingManagerCallback callback,
+    public final int initialize(final IMbmsStreamingSessionCallback callback,
             final int subscriptionId) throws RemoteException {
         final int uid = Binder.getCallingUid();
         callback.asBinder().linkToDeath(new DeathRecipient() {
@@ -70,20 +73,20 @@
             }
         }, 0);
 
-        return initialize(new MbmsStreamingManagerCallback() {
+        return initialize(new MbmsStreamingSessionCallback() {
             @Override
-            public void onError(int errorCode, String message) {
+            public void onError(final int errorCode, final String message) {
                 try {
-                    callback.error(errorCode, message);
+                    callback.onError(errorCode, message);
                 } catch (RemoteException e) {
                     onAppCallbackDied(uid, subscriptionId);
                 }
             }
 
             @Override
-            public void onStreamingServicesUpdated(List<StreamingServiceInfo> services) {
+            public void onStreamingServicesUpdated(final List<StreamingServiceInfo> services) {
                 try {
-                    callback.streamingServicesUpdated(services);
+                    callback.onStreamingServicesUpdated(services);
                 } catch (RemoteException e) {
                     onAppCallbackDied(uid, subscriptionId);
                 }
@@ -92,7 +95,7 @@
             @Override
             public void onMiddlewareReady() {
                 try {
-                    callback.middlewareReady();
+                    callback.onMiddlewareReady();
                 } catch (RemoteException e) {
                     onAppCallbackDied(uid, subscriptionId);
                 }
@@ -104,7 +107,7 @@
     /**
      * Registers serviceClasses of interest with the appName/subId key.
      * Starts async fetching data on streaming services of matching classes to be reported
-     * later via {@link IMbmsStreamingManagerCallback#streamingServicesUpdated(List)}
+     * later via {@link IMbmsStreamingSessionCallback#onStreamingServicesUpdated(List)}
      *
      * Note that subsequent calls with the same uid and subId will replace
      * the service class list.
@@ -115,11 +118,11 @@
      * @param serviceClasses The service classes that the app wishes to get info on. The strings
      *                       may contain arbitrary data as negotiated between the app and the
      *                       carrier.
-     * @return {@link MbmsException#SUCCESS} or any of the errors in
-     * {@link android.telephony.mbms.MbmsException.GeneralErrors}
+     * @return {@link MbmsErrors#SUCCESS} or any of the errors in
+     * {@link MbmsErrors.GeneralErrors}
      */
     @Override
-    public int getStreamingServices(int subscriptionId,
+    public int requestUpdateStreamingServices(int subscriptionId,
             List<String> serviceClasses) throws RemoteException {
         return 0;
     }
@@ -127,14 +130,14 @@
     /**
      * Starts streaming on a particular service. This method may perform asynchronous work. When
      * the middleware is ready to send bits to the frontend, it should inform the app via
-     * {@link IStreamingServiceCallback#streamStateUpdated(int, int)}.
+     * {@link IStreamingServiceCallback#onStreamStateUpdated(int, int)}.
      *
      * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
      *
      * @param subscriptionId The subscription id to use.
      * @param serviceId The ID of the streaming service that the app has requested.
      * @param callback The callback object on which the app wishes to receive updates.
-     * @return Any error in {@link android.telephony.mbms.MbmsException.GeneralErrors}
+     * @return Any error in {@link MbmsErrors.GeneralErrors}
      */
     public int startStreaming(int subscriptionId, String serviceId,
             StreamingServiceCallback callback) throws RemoteException {
@@ -147,8 +150,8 @@
      * @hide
      */
     @Override
-    public int startStreaming(int subscriptionId, String serviceId,
-            IStreamingServiceCallback callback) throws RemoteException {
+    public int startStreaming(final int subscriptionId, String serviceId,
+            final IStreamingServiceCallback callback) throws RemoteException {
         final int uid = Binder.getCallingUid();
         callback.asBinder().linkToDeath(new DeathRecipient() {
             @Override
@@ -159,19 +162,19 @@
 
         return startStreaming(subscriptionId, serviceId, new StreamingServiceCallback() {
             @Override
-            public void onError(int errorCode, String message) {
+            public void onError(final int errorCode, final String message) {
                 try {
-                    callback.error(errorCode, message);
+                    callback.onError(errorCode, message);
                 } catch (RemoteException e) {
                     onAppCallbackDied(uid, subscriptionId);
                 }
             }
 
             @Override
-            public void onStreamStateUpdated(@StreamingService.StreamingState int state,
-                    @StreamingService.StreamingStateChangeReason int reason) {
+            public void onStreamStateUpdated(@StreamingService.StreamingState final int state,
+                    @StreamingService.StreamingStateChangeReason final int reason) {
                 try {
-                    callback.streamStateUpdated(state, reason);
+                    callback.onStreamStateUpdated(state, reason);
                 } catch (RemoteException e) {
                     onAppCallbackDied(uid, subscriptionId);
                 }
@@ -180,25 +183,25 @@
             @Override
             public void onMediaDescriptionUpdated() {
                 try {
-                    callback.mediaDescriptionUpdated();
+                    callback.onMediaDescriptionUpdated();
                 } catch (RemoteException e) {
                     onAppCallbackDied(uid, subscriptionId);
                 }
             }
 
             @Override
-            public void onBroadcastSignalStrengthUpdated(int signalStrength) {
+            public void onBroadcastSignalStrengthUpdated(final int signalStrength) {
                 try {
-                    callback.broadcastSignalStrengthUpdated(signalStrength);
+                    callback.onBroadcastSignalStrengthUpdated(signalStrength);
                 } catch (RemoteException e) {
                     onAppCallbackDied(uid, subscriptionId);
                 }
             }
 
             @Override
-            public void onStreamMethodUpdated(int methodType) {
+            public void onStreamMethodUpdated(final int methodType) {
                 try {
-                    callback.streamMethodUpdated(methodType);
+                    callback.onStreamMethodUpdated(methodType);
                 } catch (RemoteException e) {
                     onAppCallbackDied(uid, subscriptionId);
                 }
@@ -225,7 +228,11 @@
     /**
      * Stop streaming the stream identified by {@code serviceId}. Notification of the resulting
      * stream state change should be reported to the app via
-     * {@link IStreamingServiceCallback#streamStateUpdated(int, int)}.
+     * {@link IStreamingServiceCallback#onStreamStateUpdated(int, int)}.
+     *
+     * In addition, the callback provided via
+     * {@link #startStreaming(int, String, IStreamingServiceCallback)} should no longer be
+     * used after this method has called by the app.
      *
      * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
      *
@@ -238,27 +245,10 @@
     }
 
     /**
-     * Dispose of the stream identified by {@code serviceId} for the app identified by the
-     * {@code appName} and {@code subscriptionId} arguments along with the caller's uid.
-     * No notification back to the app is required for this operation, and the callback provided via
-     * {@link #startStreaming(int, String, IStreamingServiceCallback)} should no longer be
-     * used after this method has called by the app.
-     *
-     * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
-     *
-     * @param subscriptionId The subscription id to use.
-     * @param serviceId The ID of the streaming service that the app wishes to dispose of.
-     */
-    @Override
-    public void disposeStream(int subscriptionId, String serviceId)
-            throws RemoteException {
-    }
-
-    /**
      * Signals that the app wishes to dispose of the session identified by the
      * {@code subscriptionId} argument and the caller's uid. No notification back to the
      * app is required for this operation, and the corresponding callback provided via
-     * {@link #initialize(IMbmsStreamingManagerCallback, int)} should no longer be used
+     * {@link #initialize(IMbmsStreamingSessionCallback, int)} should no longer be used
      * after this method has been called by the app.
      *
      * May throw an {@link IllegalStateException}
diff --git a/android/telephony/mbms/vendor/VendorIntents.java b/android/telephony/mbms/vendor/VendorUtils.java
similarity index 76%
rename from android/telephony/mbms/vendor/VendorIntents.java
rename to android/telephony/mbms/vendor/VendorUtils.java
index 367c995..8fb27b2 100644
--- a/android/telephony/mbms/vendor/VendorIntents.java
+++ b/android/telephony/mbms/vendor/VendorUtils.java
@@ -16,29 +16,32 @@
 
 package android.telephony.mbms.vendor;
 
+import android.annotation.SystemApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
-import android.telephony.mbms.DownloadRequest;
+import android.telephony.MbmsDownloadSession;
 import android.telephony.mbms.MbmsDownloadReceiver;
 
 import java.io.File;
 import java.util.List;
 
 /**
+ * Contains constants and utility methods for MBMS Download middleware apps to communicate with
+ * frontend apps.
  * @hide
- * TODO: future systemapi
  */
-public class VendorIntents {
+@SystemApi
+public class VendorUtils {
 
     /**
      * The MBMS middleware should send this when a download of single file has completed or
      * failed. Mandatory extras are
-     * {@link android.telephony.MbmsDownloadManager#EXTRA_RESULT}
-     * {@link android.telephony.MbmsDownloadManager#EXTRA_FILE_INFO}
-     * {@link #EXTRA_REQUEST}
+     * {@link MbmsDownloadSession#EXTRA_MBMS_DOWNLOAD_RESULT}
+     * {@link MbmsDownloadSession#EXTRA_MBMS_FILE_INFO}
+     * {@link MbmsDownloadSession#EXTRA_MBMS_DOWNLOAD_REQUEST}
      * {@link #EXTRA_TEMP_LIST}
      * {@link #EXTRA_FINAL_URI}
      */
@@ -49,7 +52,7 @@
      * The MBMS middleware should send this when it wishes to request {@code content://} URIs to
      * serve as temp files for downloads or when it wishes to resume paused downloads. Mandatory
      * extras are
-     * {@link #EXTRA_REQUEST}
+     * {@link #EXTRA_SERVICE_ID}
      *
      * Optional extras are
      * {@link #EXTRA_FD_COUNT} (0 if not present)
@@ -118,48 +121,35 @@
             "android.telephony.mbms.extra.TEMP_FILES_IN_USE";
 
     /**
-     * Extra containing the {@link DownloadRequest} for which the download result or file
-     * descriptor request is for. Must not be null.
-     */
-    public static final String EXTRA_REQUEST = "android.telephony.mbms.extra.REQUEST";
-
-    /**
      * Extra containing a single {@link Uri} indicating the path to the temp file in which the
      * decoded downloaded file resides. Must not be null.
      */
     public static final String EXTRA_FINAL_URI = "android.telephony.mbms.extra.FINAL_URI";
 
     /**
-     * Extra containing an instance of {@link android.telephony.mbms.ServiceInfo}, used by
+     * Extra containing a String representing a service ID, used by
      * file-descriptor requests and cleanup requests to specify which service they want to
      * request temp files or clean up temp files for, respectively.
      */
-    public static final String EXTRA_SERVICE_INFO =
-            "android.telephony.mbms.extra.SERVICE_INFO";
+    public static final String EXTRA_SERVICE_ID =
+            "android.telephony.mbms.extra.SERVICE_ID";
 
     /**
      * Retrieves the {@link ComponentName} for the {@link android.content.BroadcastReceiver} that
      * the various intents from the middleware should be targeted towards.
-     * @param uid The uid of the frontend app.
+     * @param packageName The package name of the app.
      * @return The component name of the receiver that the middleware should send its intents to,
      * or null if the app didn't declare it in the manifest.
      */
-    public static ComponentName getAppReceiverFromUid(Context context, int uid) {
-        String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
-        if (packageNames == null) {
-            return null;
-        }
-
-        for (String packageName : packageNames) {
-            ComponentName candidate = new ComponentName(packageName,
-                    MbmsDownloadReceiver.class.getCanonicalName());
-            Intent queryIntent = new Intent();
-            queryIntent.setComponent(candidate);
-            List<ResolveInfo> receivers =
-                    context.getPackageManager().queryBroadcastReceivers(queryIntent, 0);
-            if (receivers != null && receivers.size() > 0) {
-                return candidate;
-            }
+    public static ComponentName getAppReceiverFromPackageName(Context context, String packageName) {
+        ComponentName candidate = new ComponentName(packageName,
+                MbmsDownloadReceiver.class.getCanonicalName());
+        Intent queryIntent = new Intent();
+        queryIntent.setComponent(candidate);
+        List<ResolveInfo> receivers =
+                context.getPackageManager().queryBroadcastReceivers(queryIntent, 0);
+        if (receivers != null && receivers.size() > 0) {
+            return candidate;
         }
         return null;
     }
diff --git a/android/text/InputType.java b/android/text/InputType.java
index 8967b70..f38482e 100644
--- a/android/text/InputType.java
+++ b/android/text/InputType.java
@@ -182,9 +182,9 @@
      * want the IME to correct typos.
      * Note the contrast with {@link #TYPE_TEXT_FLAG_AUTO_CORRECT} and
      * {@link #TYPE_TEXT_FLAG_AUTO_COMPLETE}:
-     * {@code TYPE_TEXT_FLAG_NO_SUGGESTIONS} means the IME should never
+     * {@code TYPE_TEXT_FLAG_NO_SUGGESTIONS} means the IME does not need to
      * show an interface to display suggestions. Most IMEs will also take this to
-     * mean they should not try to auto-correct what the user is typing.
+     * mean they do not need to try to auto-correct what the user is typing.
      */
     public static final int TYPE_TEXT_FLAG_NO_SUGGESTIONS = 0x00080000;
 
diff --git a/android/util/LruCache.java b/android/util/LruCache.java
index 4015488..5208606 100644
--- a/android/util/LruCache.java
+++ b/android/util/LruCache.java
@@ -20,6 +20,10 @@
 import java.util.Map;
 
 /**
+ * BEGIN LAYOUTLIB CHANGE
+ * This is a custom version that doesn't use the non standard LinkedHashMap#eldest.
+ * END LAYOUTLIB CHANGE
+ *
  * A cache that holds strong references to a limited number of values. Each time
  * a value is accessed, it is moved to the head of a queue. When a value is
  * added to a full cache, the value at the end of that queue is evicted and may
@@ -87,8 +91,9 @@
 
     /**
      * Sets the size of the cache.
-     *
      * @param maxSize The new maximum size.
+     *
+     * @hide
      */
     public void resize(int maxSize) {
         if (maxSize <= 0) {
@@ -185,13 +190,10 @@
     }
 
     /**
-     * Remove the eldest entries until the total of remaining entries is at or
-     * below the requested size.
-     *
      * @param maxSize the maximum size of the cache before returning. May be -1
-     *            to evict even 0-sized elements.
+     *     to evict even 0-sized elements.
      */
-    public void trimToSize(int maxSize) {
+    private void trimToSize(int maxSize) {
         while (true) {
             K key;
             V value;
@@ -205,7 +207,16 @@
                     break;
                 }
 
-                Map.Entry<K, V> toEvict = map.eldest();
+                // BEGIN LAYOUTLIB CHANGE
+                // get the last item in the linked list.
+                // This is not efficient, the goal here is to minimize the changes
+                // compared to the platform version.
+                Map.Entry<K, V> toEvict = null;
+                for (Map.Entry<K, V> entry : map.entrySet()) {
+                    toEvict = entry;
+                }
+                // END LAYOUTLIB CHANGE
+
                 if (toEvict == null) {
                     break;
                 }
diff --git a/android/view/SurfaceView.java b/android/view/SurfaceView.java
index cac27af..ebb2af4 100644
--- a/android/view/SurfaceView.java
+++ b/android/view/SurfaceView.java
@@ -16,1209 +16,115 @@
 
 package android.view;
 
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
-import static android.view.WindowManagerPolicy.APPLICATION_MEDIA_OVERLAY_SUBLAYER;
-import static android.view.WindowManagerPolicy.APPLICATION_MEDIA_SUBLAYER;
-import static android.view.WindowManagerPolicy.APPLICATION_PANEL_SUBLAYER;
+import com.android.layoutlib.bridge.MockView;
 
 import android.content.Context;
-import android.content.res.CompatibilityInfo.Translator;
-import android.content.res.Configuration;
 import android.graphics.Canvas;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.os.Build;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.SystemClock;
 import android.util.AttributeSet;
-import android.util.Log;
-
-import com.android.internal.view.SurfaceCallbackHelper;
-
-import java.util.ArrayList;
-import java.util.concurrent.locks.ReentrantLock;
 
 /**
- * Provides a dedicated drawing surface embedded inside of a view hierarchy.
- * You can control the format of this surface and, if you like, its size; the
- * SurfaceView takes care of placing the surface at the correct location on the
- * screen
+ * Mock version of the SurfaceView.
+ * Only non override public methods from the real SurfaceView have been added in there.
+ * Methods that take an unknown class as parameter or as return object, have been removed for now.
  *
- * <p>The surface is Z ordered so that it is behind the window holding its
- * SurfaceView; the SurfaceView punches a hole in its window to allow its
- * surface to be displayed. The view hierarchy will take care of correctly
- * compositing with the Surface any siblings of the SurfaceView that would
- * normally appear on top of it. This can be used to place overlays such as
- * buttons on top of the Surface, though note however that it can have an
- * impact on performance since a full alpha-blended composite will be performed
- * each time the Surface changes.
+ * TODO: generate automatically.
  *
- * <p> The transparent region that makes the surface visible is based on the
- * layout positions in the view hierarchy. If the post-layout transform
- * properties are used to draw a sibling view on top of the SurfaceView, the
- * view may not be properly composited with the surface.
- *
- * <p>Access to the underlying surface is provided via the SurfaceHolder interface,
- * which can be retrieved by calling {@link #getHolder}.
- *
- * <p>The Surface will be created for you while the SurfaceView's window is
- * visible; you should implement {@link SurfaceHolder.Callback#surfaceCreated}
- * and {@link SurfaceHolder.Callback#surfaceDestroyed} to discover when the
- * Surface is created and destroyed as the window is shown and hidden.
- *
- * <p>One of the purposes of this class is to provide a surface in which a
- * secondary thread can render into the screen. If you are going to use it
- * this way, you need to be aware of some threading semantics:
- *
- * <ul>
- * <li> All SurfaceView and
- * {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called
- * from the thread running the SurfaceView's window (typically the main thread
- * of the application). They thus need to correctly synchronize with any
- * state that is also touched by the drawing thread.
- * <li> You must ensure that the drawing thread only touches the underlying
- * Surface while it is valid -- between
- * {@link SurfaceHolder.Callback#surfaceCreated SurfaceHolder.Callback.surfaceCreated()}
- * and
- * {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}.
- * </ul>
- *
- * <p class="note"><strong>Note:</strong> Starting in platform version
- * {@link android.os.Build.VERSION_CODES#N}, SurfaceView's window position is
- * updated synchronously with other View rendering. This means that translating
- * and scaling a SurfaceView on screen will not cause rendering artifacts. Such
- * artifacts may occur on previous versions of the platform when its window is
- * positioned asynchronously.</p>
  */
-public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallback {
-    private static final String TAG = "SurfaceView";
-    private static final boolean DEBUG = false;
-
-    final ArrayList<SurfaceHolder.Callback> mCallbacks
-            = new ArrayList<SurfaceHolder.Callback>();
-
-    final int[] mLocation = new int[2];
-
-    final ReentrantLock mSurfaceLock = new ReentrantLock();
-    final Surface mSurface = new Surface();       // Current surface in use
-    boolean mDrawingStopped = true;
-    // We use this to track if the application has produced a frame
-    // in to the Surface. Up until that point, we should be careful not to punch
-    // holes.
-    boolean mDrawFinished = false;
-
-    final Rect mScreenRect = new Rect();
-    SurfaceSession mSurfaceSession;
-
-    SurfaceControl mSurfaceControl;
-    // In the case of format changes we switch out the surface in-place
-    // we need to preserve the old one until the new one has drawn.
-    SurfaceControl mDeferredDestroySurfaceControl;
-    final Rect mTmpRect = new Rect();
-    final Configuration mConfiguration = new Configuration();
-
-    int mSubLayer = APPLICATION_MEDIA_SUBLAYER;
-
-    boolean mIsCreating = false;
-    private volatile boolean mRtHandlingPositionUpdates = false;
-
-    private final ViewTreeObserver.OnScrollChangedListener mScrollChangedListener
-            = new ViewTreeObserver.OnScrollChangedListener() {
-                    @Override
-                    public void onScrollChanged() {
-                        updateSurface();
-                    }
-            };
-
-    private final ViewTreeObserver.OnPreDrawListener mDrawListener =
-            new ViewTreeObserver.OnPreDrawListener() {
-                @Override
-                public boolean onPreDraw() {
-                    // reposition ourselves where the surface is
-                    mHaveFrame = getWidth() > 0 && getHeight() > 0;
-                    updateSurface();
-                    return true;
-                }
-            };
-
-    boolean mRequestedVisible = false;
-    boolean mWindowVisibility = false;
-    boolean mLastWindowVisibility = false;
-    boolean mViewVisibility = false;
-    boolean mWindowStopped = false;
-
-    int mRequestedWidth = -1;
-    int mRequestedHeight = -1;
-    /* Set SurfaceView's format to 565 by default to maintain backward
-     * compatibility with applications assuming this format.
-     */
-    int mRequestedFormat = PixelFormat.RGB_565;
-
-    boolean mHaveFrame = false;
-    boolean mSurfaceCreated = false;
-    long mLastLockTime = 0;
-
-    boolean mVisible = false;
-    int mWindowSpaceLeft = -1;
-    int mWindowSpaceTop = -1;
-    int mSurfaceWidth = -1;
-    int mSurfaceHeight = -1;
-    int mFormat = -1;
-    final Rect mSurfaceFrame = new Rect();
-    int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
-    private Translator mTranslator;
-
-    private boolean mGlobalListenersAdded;
-    private boolean mAttachedToWindow;
-
-    private int mSurfaceFlags = SurfaceControl.HIDDEN;
-
-    private int mPendingReportDraws;
+public class SurfaceView extends MockView {
 
     public SurfaceView(Context context) {
         this(context, null);
     }
 
     public SurfaceView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
+        this(context, attrs , 0);
     }
 
-    public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
+    public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
     }
 
     public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        mRenderNode.requestPositionUpdates(this);
-
-        setWillNotDraw(true);
     }
 
-    /**
-     * Return the SurfaceHolder providing access and control over this
-     * SurfaceView's underlying surface.
-     *
-     * @return SurfaceHolder The holder of the surface.
-     */
+    public boolean gatherTransparentRegion(Region region) {
+      return false;
+    }
+
+    public void setZOrderMediaOverlay(boolean isMediaOverlay) {
+    }
+
+    public void setZOrderOnTop(boolean onTop) {
+    }
+
+    public void setSecure(boolean isSecure) {
+    }
+
     public SurfaceHolder getHolder() {
         return mSurfaceHolder;
     }
 
-    private void updateRequestedVisibility() {
-        mRequestedVisible = mViewVisibility && mWindowVisibility && !mWindowStopped;
-    }
-
-    /** @hide */
-    @Override
-    public void windowStopped(boolean stopped) {
-        mWindowStopped = stopped;
-        updateRequestedVisibility();
-        updateSurface();
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        getViewRootImpl().addWindowStoppedCallback(this);
-        mWindowStopped = false;
-
-        mViewVisibility = getVisibility() == VISIBLE;
-        updateRequestedVisibility();
-
-        mAttachedToWindow = true;
-        if (!mGlobalListenersAdded) {
-            ViewTreeObserver observer = getViewTreeObserver();
-            observer.addOnScrollChangedListener(mScrollChangedListener);
-            observer.addOnPreDrawListener(mDrawListener);
-            mGlobalListenersAdded = true;
-        }
-    }
-
-    @Override
-    protected void onWindowVisibilityChanged(int visibility) {
-        super.onWindowVisibilityChanged(visibility);
-        mWindowVisibility = visibility == VISIBLE;
-        updateRequestedVisibility();
-        updateSurface();
-    }
-
-    @Override
-    public void setVisibility(int visibility) {
-        super.setVisibility(visibility);
-        mViewVisibility = visibility == VISIBLE;
-        boolean newRequestedVisible = mWindowVisibility && mViewVisibility && !mWindowStopped;
-        if (newRequestedVisible != mRequestedVisible) {
-            // our base class (View) invalidates the layout only when
-            // we go from/to the GONE state. However, SurfaceView needs
-            // to request a re-layout when the visibility changes at all.
-            // This is needed because the transparent region is computed
-            // as part of the layout phase, and it changes (obviously) when
-            // the visibility changes.
-            requestLayout();
-        }
-        mRequestedVisible = newRequestedVisible;
-        updateSurface();
-    }
-
-    private void performDrawFinished() {
-        if (mPendingReportDraws > 0) {
-            mDrawFinished = true;
-            if (mAttachedToWindow) {
-                mParent.requestTransparentRegion(SurfaceView.this);
-                
-                notifyDrawFinished();
-                invalidate();
-            }
-        } else {
-            Log.e(TAG, System.identityHashCode(this) + "finished drawing"
-                    + " but no pending report draw (extra call"
-                    + " to draw completion runnable?)");
-        }
-    }
-
-    void notifyDrawFinished() {
-        ViewRootImpl viewRoot = getViewRootImpl();
-        if (viewRoot != null) {
-            viewRoot.pendingDrawFinished();
-        }
-        mPendingReportDraws--;
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        ViewRootImpl viewRoot = getViewRootImpl();
-        // It's possible to create a SurfaceView using the default constructor and never
-        // attach it to a view hierarchy, this is a common use case when dealing with
-        // OpenGL. A developer will probably create a new GLSurfaceView, and let it manage
-        // the lifecycle. Instead of attaching it to a view, he/she can just pass
-        // the SurfaceHolder forward, most live wallpapers do it.
-        if (viewRoot != null) {
-            viewRoot.removeWindowStoppedCallback(this);
-        }
-
-        mAttachedToWindow = false;
-        if (mGlobalListenersAdded) {
-            ViewTreeObserver observer = getViewTreeObserver();
-            observer.removeOnScrollChangedListener(mScrollChangedListener);
-            observer.removeOnPreDrawListener(mDrawListener);
-            mGlobalListenersAdded = false;
-        }
-
-        while (mPendingReportDraws > 0) {
-            notifyDrawFinished();
-        }
-
-        mRequestedVisible = false;
-
-        updateSurface();
-        if (mSurfaceControl != null) {
-            mSurfaceControl.destroy();
-        }
-        mSurfaceControl = null;
-
-        mHaveFrame = false;
-
-        super.onDetachedFromWindow();
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int width = mRequestedWidth >= 0
-                ? resolveSizeAndState(mRequestedWidth, widthMeasureSpec, 0)
-                : getDefaultSize(0, widthMeasureSpec);
-        int height = mRequestedHeight >= 0
-                ? resolveSizeAndState(mRequestedHeight, heightMeasureSpec, 0)
-                : getDefaultSize(0, heightMeasureSpec);
-        setMeasuredDimension(width, height);
-    }
-
-    /** @hide */
-    @Override
-    protected boolean setFrame(int left, int top, int right, int bottom) {
-        boolean result = super.setFrame(left, top, right, bottom);
-        updateSurface();
-        return result;
-    }
-
-    @Override
-    public boolean gatherTransparentRegion(Region region) {
-        if (isAboveParent() || !mDrawFinished) {
-            return super.gatherTransparentRegion(region);
-        }
-
-        boolean opaque = true;
-        if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
-            // this view draws, remove it from the transparent region
-            opaque = super.gatherTransparentRegion(region);
-        } else if (region != null) {
-            int w = getWidth();
-            int h = getHeight();
-            if (w>0 && h>0) {
-                getLocationInWindow(mLocation);
-                // otherwise, punch a hole in the whole hierarchy
-                int l = mLocation[0];
-                int t = mLocation[1];
-                region.op(l, t, l+w, t+h, Region.Op.UNION);
-            }
-        }
-        if (PixelFormat.formatHasAlpha(mRequestedFormat)) {
-            opaque = false;
-        }
-        return opaque;
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        if (mDrawFinished && !isAboveParent()) {
-            // draw() is not called when SKIP_DRAW is set
-            if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {
-                // punch a whole in the view-hierarchy below us
-                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
-            }
-        }
-        super.draw(canvas);
-    }
-
-    @Override
-    protected void dispatchDraw(Canvas canvas) {
-        if (mDrawFinished && !isAboveParent()) {
-            // draw() is not called when SKIP_DRAW is set
-            if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
-                // punch a whole in the view-hierarchy below us
-                canvas.drawColor(0, PorterDuff.Mode.CLEAR);
-            }
-        }
-        super.dispatchDraw(canvas);
-    }
-
-    /**
-     * Control whether the surface view's surface is placed on top of another
-     * regular surface view in the window (but still behind the window itself).
-     * This is typically used to place overlays on top of an underlying media
-     * surface view.
-     *
-     * <p>Note that this must be set before the surface view's containing
-     * window is attached to the window manager.
-     *
-     * <p>Calling this overrides any previous call to {@link #setZOrderOnTop}.
-     */
-    public void setZOrderMediaOverlay(boolean isMediaOverlay) {
-        mSubLayer = isMediaOverlay
-            ? APPLICATION_MEDIA_OVERLAY_SUBLAYER : APPLICATION_MEDIA_SUBLAYER;
-    }
-
-    /**
-     * Control whether the surface view's surface is placed on top of its
-     * window.  Normally it is placed behind the window, to allow it to
-     * (for the most part) appear to composite with the views in the
-     * hierarchy.  By setting this, you cause it to be placed above the
-     * window.  This means that none of the contents of the window this
-     * SurfaceView is in will be visible on top of its surface.
-     *
-     * <p>Note that this must be set before the surface view's containing
-     * window is attached to the window manager.
-     *
-     * <p>Calling this overrides any previous call to {@link #setZOrderMediaOverlay}.
-     */
-    public void setZOrderOnTop(boolean onTop) {
-        if (onTop) {
-            mSubLayer = APPLICATION_PANEL_SUBLAYER;
-        } else {
-            mSubLayer = APPLICATION_MEDIA_SUBLAYER;
-        }
-    }
-
-    /**
-     * Control whether the surface view's content should be treated as secure,
-     * preventing it from appearing in screenshots or from being viewed on
-     * non-secure displays.
-     *
-     * <p>Note that this must be set before the surface view's containing
-     * window is attached to the window manager.
-     *
-     * <p>See {@link android.view.Display#FLAG_SECURE} for details.
-     *
-     * @param isSecure True if the surface view is secure.
-     */
-    public void setSecure(boolean isSecure) {
-        if (isSecure) {
-            mSurfaceFlags |= SurfaceControl.SECURE;
-        } else {
-            mSurfaceFlags &= ~SurfaceControl.SECURE;
-        }
-    }
-
-    private void updateOpaqueFlag() {
-        if (!PixelFormat.formatHasAlpha(mRequestedFormat)) {
-            mSurfaceFlags |= SurfaceControl.OPAQUE;
-        } else {
-            mSurfaceFlags &= ~SurfaceControl.OPAQUE;
-        }
-    }
-
-    private Rect getParentSurfaceInsets() {
-        final ViewRootImpl root = getViewRootImpl();
-        if (root == null) {
-            return null;
-        } else {
-            return root.mWindowAttributes.surfaceInsets;
-        }
-    }
-
-    /** @hide */
-    protected void updateSurface() {
-        if (!mHaveFrame) {
-            return;
-        }
-        ViewRootImpl viewRoot = getViewRootImpl();
-        if (viewRoot == null || viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
-            return;
-        }
-
-        mTranslator = viewRoot.mTranslator;
-        if (mTranslator != null) {
-            mSurface.setCompatibilityTranslator(mTranslator);
-        }
-
-        int myWidth = mRequestedWidth;
-        if (myWidth <= 0) myWidth = getWidth();
-        int myHeight = mRequestedHeight;
-        if (myHeight <= 0) myHeight = getHeight();
-
-        final boolean formatChanged = mFormat != mRequestedFormat;
-        final boolean visibleChanged = mVisible != mRequestedVisible;
-        final boolean creating = (mSurfaceControl == null || formatChanged || visibleChanged)
-                && mRequestedVisible;
-        final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight;
-        final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility;
-        boolean redrawNeeded = false;
-
-        if (creating || formatChanged || sizeChanged || visibleChanged || windowVisibleChanged) {
-            getLocationInWindow(mLocation);
-
-            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
-                    + "Changes: creating=" + creating
-                    + " format=" + formatChanged + " size=" + sizeChanged
-                    + " visible=" + visibleChanged
-                    + " left=" + (mWindowSpaceLeft != mLocation[0])
-                    + " top=" + (mWindowSpaceTop != mLocation[1]));
-
-            try {
-                final boolean visible = mVisible = mRequestedVisible;
-                mWindowSpaceLeft = mLocation[0];
-                mWindowSpaceTop = mLocation[1];
-                mSurfaceWidth = myWidth;
-                mSurfaceHeight = myHeight;
-                mFormat = mRequestedFormat;
-                mLastWindowVisibility = mWindowVisibility;
-
-                mScreenRect.left = mWindowSpaceLeft;
-                mScreenRect.top = mWindowSpaceTop;
-                mScreenRect.right = mWindowSpaceLeft + getWidth();
-                mScreenRect.bottom = mWindowSpaceTop + getHeight();
-                if (mTranslator != null) {
-                    mTranslator.translateRectInAppWindowToScreen(mScreenRect);
-                }
-
-                final Rect surfaceInsets = getParentSurfaceInsets();
-                mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);
-
-                if (creating) {
-                    mSurfaceSession = new SurfaceSession(viewRoot.mSurface);
-                    mDeferredDestroySurfaceControl = mSurfaceControl;
-
-                    updateOpaqueFlag();
-                    mSurfaceControl = new SurfaceControlWithBackground(mSurfaceSession,
-                            "SurfaceView - " + viewRoot.getTitle().toString(),
-                            mSurfaceWidth, mSurfaceHeight, mFormat,
-                            mSurfaceFlags);
-                } else if (mSurfaceControl == null) {
-                    return;
-                }
-
-                boolean realSizeChanged = false;
-
-                mSurfaceLock.lock();
-                try {
-                    mDrawingStopped = !visible;
-
-                    if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
-                            + "Cur surface: " + mSurface);
-
-                    SurfaceControl.openTransaction();
-                    try {
-                        mSurfaceControl.setLayer(mSubLayer);
-                        if (mViewVisibility) {
-                            mSurfaceControl.show();
-                        } else {
-                            mSurfaceControl.hide();
-                        }
-
-                        // While creating the surface, we will set it's initial
-                        // geometry. Outside of that though, we should generally
-                        // leave it to the RenderThread.
-                        //
-                        // There is one more case when the buffer size changes we aren't yet
-                        // prepared to sync (as even following the transaction applying
-                        // we still need to latch a buffer).
-                        // b/28866173
-                        if (sizeChanged || creating || !mRtHandlingPositionUpdates) {
-                            mSurfaceControl.setPosition(mScreenRect.left, mScreenRect.top);
-                            mSurfaceControl.setMatrix(mScreenRect.width() / (float) mSurfaceWidth,
-                                    0.0f, 0.0f,
-                                    mScreenRect.height() / (float) mSurfaceHeight);
-                        }
-                        if (sizeChanged) {
-                            mSurfaceControl.setSize(mSurfaceWidth, mSurfaceHeight);
-                        }
-                    } finally {
-                        SurfaceControl.closeTransaction();
-                    }
-
-                    if (sizeChanged || creating) {
-                        redrawNeeded = true;
-                    }
-
-                    mSurfaceFrame.left = 0;
-                    mSurfaceFrame.top = 0;
-                    if (mTranslator == null) {
-                        mSurfaceFrame.right = mSurfaceWidth;
-                        mSurfaceFrame.bottom = mSurfaceHeight;
-                    } else {
-                        float appInvertedScale = mTranslator.applicationInvertedScale;
-                        mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f);
-                        mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f);
-                    }
-
-                    final int surfaceWidth = mSurfaceFrame.right;
-                    final int surfaceHeight = mSurfaceFrame.bottom;
-                    realSizeChanged = mLastSurfaceWidth != surfaceWidth
-                            || mLastSurfaceHeight != surfaceHeight;
-                    mLastSurfaceWidth = surfaceWidth;
-                    mLastSurfaceHeight = surfaceHeight;
-                } finally {
-                    mSurfaceLock.unlock();
-                }
-
-                try {
-                    redrawNeeded |= visible && !mDrawFinished;
-
-                    SurfaceHolder.Callback callbacks[] = null;
-
-                    final boolean surfaceChanged = creating;
-                    if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
-                        mSurfaceCreated = false;
-                        if (mSurface.isValid()) {
-                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
-                                    + "visibleChanged -- surfaceDestroyed");
-                            callbacks = getSurfaceCallbacks();
-                            for (SurfaceHolder.Callback c : callbacks) {
-                                c.surfaceDestroyed(mSurfaceHolder);
-                            }
-                            // Since Android N the same surface may be reused and given to us
-                            // again by the system server at a later point. However
-                            // as we didn't do this in previous releases, clients weren't
-                            // necessarily required to clean up properly in
-                            // surfaceDestroyed. This leads to problems for example when
-                            // clients don't destroy their EGL context, and try
-                            // and create a new one on the same surface following reuse.
-                            // Since there is no valid use of the surface in-between
-                            // surfaceDestroyed and surfaceCreated, we force a disconnect,
-                            // so the next connect will always work if we end up reusing
-                            // the surface.
-                            if (mSurface.isValid()) {
-                                mSurface.forceScopedDisconnect();
-                            }
-                        }
-                    }
-
-                    if (creating) {
-                        mSurface.copyFrom(mSurfaceControl);
-                    }
-
-                    if (sizeChanged && getContext().getApplicationInfo().targetSdkVersion
-                            < Build.VERSION_CODES.O) {
-                        // Some legacy applications use the underlying native {@link Surface} object
-                        // as a key to whether anything has changed. In these cases, updates to the
-                        // existing {@link Surface} will be ignored when the size changes.
-                        // Therefore, we must explicitly recreate the {@link Surface} in these
-                        // cases.
-                        mSurface.createFrom(mSurfaceControl);
-                    }
-
-                    if (visible && mSurface.isValid()) {
-                        if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
-                            mSurfaceCreated = true;
-                            mIsCreating = true;
-                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
-                                    + "visibleChanged -- surfaceCreated");
-                            if (callbacks == null) {
-                                callbacks = getSurfaceCallbacks();
-                            }
-                            for (SurfaceHolder.Callback c : callbacks) {
-                                c.surfaceCreated(mSurfaceHolder);
-                            }
-                        }
-                        if (creating || formatChanged || sizeChanged
-                                || visibleChanged || realSizeChanged) {
-                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
-                                    + "surfaceChanged -- format=" + mFormat
-                                    + " w=" + myWidth + " h=" + myHeight);
-                            if (callbacks == null) {
-                                callbacks = getSurfaceCallbacks();
-                            }
-                            for (SurfaceHolder.Callback c : callbacks) {
-                                c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
-                            }
-                        }
-                        if (redrawNeeded) {
-                            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
-                                    + "surfaceRedrawNeeded");
-                            if (callbacks == null) {
-                                callbacks = getSurfaceCallbacks();
-                            }
-
-                            mPendingReportDraws++;
-                            viewRoot.drawPending();
-                            SurfaceCallbackHelper sch =
-                                    new SurfaceCallbackHelper(this::onDrawFinished);
-                            sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
-                        }
-                    }
-                } finally {
-                    mIsCreating = false;
-                    if (mSurfaceControl != null && !mSurfaceCreated) {
-                        mSurface.release();
-                        // If we are not in the stopped state, then the destruction of the Surface
-                        // represents a visual change we need to display, and we should go ahead
-                        // and destroy the SurfaceControl. However if we are in the stopped state,
-                        // we can just leave the Surface around so it can be a part of animations,
-                        // and we let the life-time be tied to the parent surface.
-                        if (!mWindowStopped) {
-                            mSurfaceControl.destroy();
-                            mSurfaceControl = null;
-                        }
-                    }
-                }
-            } catch (Exception ex) {
-                Log.e(TAG, "Exception configuring surface", ex);
-            }
-            if (DEBUG) Log.v(
-                TAG, "Layout: x=" + mScreenRect.left + " y=" + mScreenRect.top
-                + " w=" + mScreenRect.width() + " h=" + mScreenRect.height()
-                + ", frame=" + mSurfaceFrame);
-        } else {
-            // Calculate the window position in case RT loses the window
-            // and we need to fallback to a UI-thread driven position update
-            getLocationInSurface(mLocation);
-            final boolean positionChanged = mWindowSpaceLeft != mLocation[0]
-                    || mWindowSpaceTop != mLocation[1];
-            final boolean layoutSizeChanged = getWidth() != mScreenRect.width()
-                    || getHeight() != mScreenRect.height();
-            if (positionChanged || layoutSizeChanged) { // Only the position has changed
-                mWindowSpaceLeft = mLocation[0];
-                mWindowSpaceTop = mLocation[1];
-                // For our size changed check, we keep mScreenRect.width() and mScreenRect.height()
-                // in view local space.
-                mLocation[0] = getWidth();
-                mLocation[1] = getHeight();
-
-                mScreenRect.set(mWindowSpaceLeft, mWindowSpaceTop,
-                        mWindowSpaceLeft + mLocation[0], mWindowSpaceTop + mLocation[1]);
-
-                if (mTranslator != null) {
-                    mTranslator.translateRectInAppWindowToScreen(mScreenRect);
-                }
-
-                if (mSurfaceControl == null) {
-                    return;
-                }
-
-                if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) {
-                    try {
-                        if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition UI, " +
-                                "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
-                                mScreenRect.left, mScreenRect.top,
-                                mScreenRect.right, mScreenRect.bottom));
-                        setParentSpaceRectangle(mScreenRect, -1);
-                    } catch (Exception ex) {
-                        Log.e(TAG, "Exception configuring surface", ex);
-                    }
-                }
-            }
-        }
-    }
-
-    private void onDrawFinished() {
-        if (DEBUG) {
-            Log.i(TAG, System.identityHashCode(this) + " "
-                    + "finishedDrawing");
-        }
-
-        if (mDeferredDestroySurfaceControl != null) {
-            mDeferredDestroySurfaceControl.destroy();
-            mDeferredDestroySurfaceControl = null;
-        }
-
-        runOnUiThread(() -> {
-            performDrawFinished();
-        });
-    }
-
-    private void setParentSpaceRectangle(Rect position, long frameNumber) {
-        ViewRootImpl viewRoot = getViewRootImpl();
-
-        SurfaceControl.openTransaction();
-        try {
-            if (frameNumber > 0) {
-                mSurfaceControl.deferTransactionUntil(viewRoot.mSurface, frameNumber);
-            }
-            mSurfaceControl.setPosition(position.left, position.top);
-            mSurfaceControl.setMatrix(position.width() / (float) mSurfaceWidth,
-                    0.0f, 0.0f,
-                    position.height() / (float) mSurfaceHeight);
-        } finally {
-            SurfaceControl.closeTransaction();
-        }
-    }
-
-    private Rect mRTLastReportedPosition = new Rect();
-
-    /**
-     * Called by native by a Rendering Worker thread to update the window position
-     * @hide
-     */
-    public final void updateSurfacePosition_renderWorker(long frameNumber,
-            int left, int top, int right, int bottom) {
-        if (mSurfaceControl == null) {
-            return;
-        }
-
-        // TODO: This is teensy bit racey in that a brand new SurfaceView moving on
-        // its 2nd frame if RenderThread is running slowly could potentially see
-        // this as false, enter the branch, get pre-empted, then this comes along
-        // and reports a new position, then the UI thread resumes and reports
-        // its position. This could therefore be de-sync'd in that interval, but
-        // the synchronization would violate the rule that RT must never block
-        // on the UI thread which would open up potential deadlocks. The risk of
-        // a single-frame desync is therefore preferable for now.
-        mRtHandlingPositionUpdates = true;
-        if (mRTLastReportedPosition.left == left
-                && mRTLastReportedPosition.top == top
-                && mRTLastReportedPosition.right == right
-                && mRTLastReportedPosition.bottom == bottom) {
-            return;
-        }
-        try {
-            if (DEBUG) {
-                Log.d(TAG, String.format("%d updateSurfacePosition RenderWorker, frameNr = %d, " +
-                        "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
-                        frameNumber, left, top, right, bottom));
-            }
-            mRTLastReportedPosition.set(left, top, right, bottom);
-            setParentSpaceRectangle(mRTLastReportedPosition, frameNumber);
-            // Now overwrite mRTLastReportedPosition with our values
-        } catch (Exception ex) {
-            Log.e(TAG, "Exception from repositionChild", ex);
-        }
-    }
-
-    /**
-     * Called by native on RenderThread to notify that the view is no longer in the
-     * draw tree. UI thread is blocked at this point.
-     * @hide
-     */
-    public final void surfacePositionLost_uiRtSync(long frameNumber) {
-        if (DEBUG) {
-            Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
-                    System.identityHashCode(this), frameNumber));
-        }
-        mRTLastReportedPosition.setEmpty();
-
-        if (mSurfaceControl == null) {
-            return;
-        }
-        if (mRtHandlingPositionUpdates) {
-            mRtHandlingPositionUpdates = false;
-            // This callback will happen while the UI thread is blocked, so we can
-            // safely access other member variables at this time.
-            // So do what the UI thread would have done if RT wasn't handling position
-            // updates.
-            if (!mScreenRect.isEmpty() && !mScreenRect.equals(mRTLastReportedPosition)) {
-                try {
-                    if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition, " +
-                            "postion = [%d, %d, %d, %d]", System.identityHashCode(this),
-                            mScreenRect.left, mScreenRect.top,
-                            mScreenRect.right, mScreenRect.bottom));
-                    setParentSpaceRectangle(mScreenRect, frameNumber);
-                } catch (Exception ex) {
-                    Log.e(TAG, "Exception configuring surface", ex);
-                }
-            }
-        }
-    }
-
-    private SurfaceHolder.Callback[] getSurfaceCallbacks() {
-        SurfaceHolder.Callback callbacks[];
-        synchronized (mCallbacks) {
-            callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
-            mCallbacks.toArray(callbacks);
-        }
-        return callbacks;
-    }
-
-    /**
-     * This method still exists only for compatibility reasons because some applications have relied
-     * on this method via reflection. See Issue 36345857 for details.
-     *
-     * @deprecated No platform code is using this method anymore.
-     * @hide
-     */
-    @Deprecated
-    public void setWindowType(int type) {
-        if (getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.O) {
-            throw new UnsupportedOperationException(
-                    "SurfaceView#setWindowType() has never been a public API.");
-        }
-
-        if (type == TYPE_APPLICATION_PANEL) {
-            Log.e(TAG, "If you are calling SurfaceView#setWindowType(TYPE_APPLICATION_PANEL) "
-                    + "just to make the SurfaceView to be placed on top of its window, you must "
-                    + "call setZOrderOnTop(true) instead.", new Throwable());
-            setZOrderOnTop(true);
-            return;
-        }
-        Log.e(TAG, "SurfaceView#setWindowType(int) is deprecated and now does nothing. "
-                + "type=" + type, new Throwable());
-    }
-
-    private void runOnUiThread(Runnable runnable) {
-        Handler handler = getHandler();
-        if (handler != null && handler.getLooper() != Looper.myLooper()) {
-            handler.post(runnable);
-        } else {
-            runnable.run();
-        }
-    }
-
-    /**
-     * Check to see if the surface has fixed size dimensions or if the surface's
-     * dimensions are dimensions are dependent on its current layout.
-     *
-     * @return true if the surface has dimensions that are fixed in size
-     * @hide
-     */
-    public boolean isFixedSize() {
-        return (mRequestedWidth != -1 || mRequestedHeight != -1);
-    }
-
-    private boolean isAboveParent() {
-        return mSubLayer >= 0;
-    }
-
-    private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
-        private static final String LOG_TAG = "SurfaceHolder";
+    private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
 
         @Override
         public boolean isCreating() {
-            return mIsCreating;
+            return false;
         }
 
         @Override
         public void addCallback(Callback callback) {
-            synchronized (mCallbacks) {
-                // This is a linear search, but in practice we'll
-                // have only a couple callbacks, so it doesn't matter.
-                if (mCallbacks.contains(callback) == false) {
-                    mCallbacks.add(callback);
-                }
-            }
         }
 
         @Override
         public void removeCallback(Callback callback) {
-            synchronized (mCallbacks) {
-                mCallbacks.remove(callback);
-            }
         }
 
         @Override
         public void setFixedSize(int width, int height) {
-            if (mRequestedWidth != width || mRequestedHeight != height) {
-                mRequestedWidth = width;
-                mRequestedHeight = height;
-                requestLayout();
-            }
         }
 
         @Override
         public void setSizeFromLayout() {
-            if (mRequestedWidth != -1 || mRequestedHeight != -1) {
-                mRequestedWidth = mRequestedHeight = -1;
-                requestLayout();
-            }
         }
 
         @Override
         public void setFormat(int format) {
-            // for backward compatibility reason, OPAQUE always
-            // means 565 for SurfaceView
-            if (format == PixelFormat.OPAQUE)
-                format = PixelFormat.RGB_565;
-
-            mRequestedFormat = format;
-            if (mSurfaceControl != null) {
-                updateSurface();
-            }
         }
 
-        /**
-         * @deprecated setType is now ignored.
-         */
         @Override
-        @Deprecated
-        public void setType(int type) { }
+        public void setType(int type) {
+        }
 
         @Override
         public void setKeepScreenOn(boolean screenOn) {
-            runOnUiThread(() -> SurfaceView.this.setKeepScreenOn(screenOn));
         }
 
-        /**
-         * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
-         *
-         * After drawing into the provided {@link Canvas}, the caller must
-         * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
-         *
-         * The caller must redraw the entire surface.
-         * @return A canvas for drawing into the surface.
-         */
         @Override
         public Canvas lockCanvas() {
-            return internalLockCanvas(null, false);
-        }
-
-        /**
-         * Gets a {@link Canvas} for drawing into the SurfaceView's Surface
-         *
-         * After drawing into the provided {@link Canvas}, the caller must
-         * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.
-         *
-         * @param inOutDirty A rectangle that represents the dirty region that the caller wants
-         * to redraw.  This function may choose to expand the dirty rectangle if for example
-         * the surface has been resized or if the previous contents of the surface were
-         * not available.  The caller must redraw the entire dirty region as represented
-         * by the contents of the inOutDirty rectangle upon return from this function.
-         * The caller may also pass <code>null</code> instead, in the case where the
-         * entire surface should be redrawn.
-         * @return A canvas for drawing into the surface.
-         */
-        @Override
-        public Canvas lockCanvas(Rect inOutDirty) {
-            return internalLockCanvas(inOutDirty, false);
-        }
-
-        @Override
-        public Canvas lockHardwareCanvas() {
-            return internalLockCanvas(null, true);
-        }
-
-        private Canvas internalLockCanvas(Rect dirty, boolean hardware) {
-            mSurfaceLock.lock();
-
-            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Locking canvas... stopped="
-                    + mDrawingStopped + ", surfaceControl=" + mSurfaceControl);
-
-            Canvas c = null;
-            if (!mDrawingStopped && mSurfaceControl != null) {
-                try {
-                    if (hardware) {
-                        c = mSurface.lockHardwareCanvas();
-                    } else {
-                        c = mSurface.lockCanvas(dirty);
-                    }
-                } catch (Exception e) {
-                    Log.e(LOG_TAG, "Exception locking surface", e);
-                }
-            }
-
-            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + "Returned canvas: " + c);
-            if (c != null) {
-                mLastLockTime = SystemClock.uptimeMillis();
-                return c;
-            }
-
-            // If the Surface is not ready to be drawn, then return null,
-            // but throttle calls to this function so it isn't called more
-            // than every 100ms.
-            long now = SystemClock.uptimeMillis();
-            long nextTime = mLastLockTime + 100;
-            if (nextTime > now) {
-                try {
-                    Thread.sleep(nextTime-now);
-                } catch (InterruptedException e) {
-                }
-                now = SystemClock.uptimeMillis();
-            }
-            mLastLockTime = now;
-            mSurfaceLock.unlock();
-
             return null;
         }
 
-        /**
-         * Posts the new contents of the {@link Canvas} to the surface and
-         * releases the {@link Canvas}.
-         *
-         * @param canvas The canvas previously obtained from {@link #lockCanvas}.
-         */
+        @Override
+        public Canvas lockCanvas(Rect dirty) {
+            return null;
+        }
+
         @Override
         public void unlockCanvasAndPost(Canvas canvas) {
-            mSurface.unlockCanvasAndPost(canvas);
-            mSurfaceLock.unlock();
         }
 
         @Override
         public Surface getSurface() {
-            return mSurface;
+            return null;
         }
 
         @Override
         public Rect getSurfaceFrame() {
-            return mSurfaceFrame;
+            return null;
         }
     };
-
-    class SurfaceControlWithBackground extends SurfaceControl {
-        private SurfaceControl mBackgroundControl;
-        private boolean mOpaque = true;
-        public boolean mVisible = false;
-
-        public SurfaceControlWithBackground(SurfaceSession s,
-                        String name, int w, int h, int format, int flags)
-                       throws Exception {
-            super(s, name, w, h, format, flags);
-            mBackgroundControl = new SurfaceControl(s, "Background for - " + name, w, h,
-                    PixelFormat.OPAQUE, flags | SurfaceControl.FX_SURFACE_DIM);
-            mOpaque = (flags & SurfaceControl.OPAQUE) != 0;
-        }
-
-        @Override
-        public void setAlpha(float alpha) {
-            super.setAlpha(alpha);
-            mBackgroundControl.setAlpha(alpha);
-        }
-
-        @Override
-        public void setLayer(int zorder) {
-            super.setLayer(zorder);
-            // -3 is below all other child layers as SurfaceView never goes below -2
-            mBackgroundControl.setLayer(-3);
-        }
-
-        @Override
-        public void setPosition(float x, float y) {
-            super.setPosition(x, y);
-            mBackgroundControl.setPosition(x, y);
-        }
-
-        @Override
-        public void setSize(int w, int h) {
-            super.setSize(w, h);
-            mBackgroundControl.setSize(w, h);
-        }
-
-        @Override
-        public void setWindowCrop(Rect crop) {
-            super.setWindowCrop(crop);
-            mBackgroundControl.setWindowCrop(crop);
-        }
-
-        @Override
-        public void setFinalCrop(Rect crop) {
-            super.setFinalCrop(crop);
-            mBackgroundControl.setFinalCrop(crop);
-        }
-
-        @Override
-        public void setLayerStack(int layerStack) {
-            super.setLayerStack(layerStack);
-            mBackgroundControl.setLayerStack(layerStack);
-        }
-
-        @Override
-        public void setOpaque(boolean isOpaque) {
-            super.setOpaque(isOpaque);
-            mOpaque = isOpaque;
-            updateBackgroundVisibility();
-        }
-
-        @Override
-        public void setSecure(boolean isSecure) {
-            super.setSecure(isSecure);
-        }
-
-        @Override
-        public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
-            super.setMatrix(dsdx, dtdx, dsdy, dtdy);
-            mBackgroundControl.setMatrix(dsdx, dtdx, dsdy, dtdy);
-        }
-
-        @Override
-        public void hide() {
-            super.hide();
-            mVisible = false;
-            updateBackgroundVisibility();
-        }
-
-        @Override
-        public void show() {
-            super.show();
-            mVisible = true;
-            updateBackgroundVisibility();
-        }
-
-        @Override
-        public void destroy() {
-            super.destroy();
-            mBackgroundControl.destroy();
-         }
-
-        @Override
-        public void release() {
-            super.release();
-            mBackgroundControl.release();
-        }
-
-        @Override
-        public void setTransparentRegionHint(Region region) {
-            super.setTransparentRegionHint(region);
-            mBackgroundControl.setTransparentRegionHint(region);
-        }
-
-        @Override
-        public void deferTransactionUntil(IBinder handle, long frame) {
-            super.deferTransactionUntil(handle, frame);
-            mBackgroundControl.deferTransactionUntil(handle, frame);
-        }
-
-        @Override
-        public void deferTransactionUntil(Surface barrier, long frame) {
-            super.deferTransactionUntil(barrier, frame);
-            mBackgroundControl.deferTransactionUntil(barrier, frame);
-        }
-
-        void updateBackgroundVisibility() {
-            if (mOpaque && mVisible) {
-                mBackgroundControl.show();
-            } else {
-                mBackgroundControl.hide();
-            }
-        }
-    }
 }
+
diff --git a/android/view/accessibility/AccessibilityManager.java b/android/view/accessibility/AccessibilityManager.java
index 0b9bc57..11cb046 100644
--- a/android/view/accessibility/AccessibilityManager.java
+++ b/android/view/accessibility/AccessibilityManager.java
@@ -16,152 +16,46 @@
 
 package android.view.accessibility;
 
-import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME;
-
-import android.Manifest;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.SdkConstant;
-import android.annotation.SystemService;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
-import android.os.Binder;
 import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.SparseArray;
 import android.view.IWindow;
 import android.view.View;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.IntPair;
-
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
 /**
- * System level service that serves as an event dispatch for {@link AccessibilityEvent}s,
- * and provides facilities for querying the accessibility state of the system.
- * Accessibility events are generated when something notable happens in the user interface,
+ * System level service that serves as an event dispatch for {@link AccessibilityEvent}s.
+ * Such events are generated when something notable happens in the user interface,
  * for example an {@link android.app.Activity} starts, the focus or selection of a
  * {@link android.view.View} changes etc. Parties interested in handling accessibility
  * events implement and register an accessibility service which extends
- * {@link android.accessibilityservice.AccessibilityService}.
+ * {@code android.accessibilityservice.AccessibilityService}.
  *
  * @see AccessibilityEvent
- * @see AccessibilityNodeInfo
- * @see android.accessibilityservice.AccessibilityService
- * @see Context#getSystemService
- * @see Context#ACCESSIBILITY_SERVICE
+ * @see android.content.Context#getSystemService
  */
-@SystemService(Context.ACCESSIBILITY_SERVICE)
+@SuppressWarnings("UnusedDeclaration")
 public final class AccessibilityManager {
-    private static final boolean DEBUG = false;
 
-    private static final String LOG_TAG = "AccessibilityManager";
+    private static AccessibilityManager sInstance = new AccessibilityManager(null, null, 0);
 
-    /** @hide */
-    public static final int STATE_FLAG_ACCESSIBILITY_ENABLED = 0x00000001;
-
-    /** @hide */
-    public static final int STATE_FLAG_TOUCH_EXPLORATION_ENABLED = 0x00000002;
-
-    /** @hide */
-    public static final int STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED = 0x00000004;
-
-    /** @hide */
-    public static final int DALTONIZER_DISABLED = -1;
-
-    /** @hide */
-    public static final int DALTONIZER_SIMULATE_MONOCHROMACY = 0;
-
-    /** @hide */
-    public static final int DALTONIZER_CORRECT_DEUTERANOMALY = 12;
-
-    /** @hide */
-    public static final int AUTOCLICK_DELAY_DEFAULT = 600;
 
     /**
-     * Activity action: Launch UI to manage which accessibility service or feature is assigned
-     * to the navigation bar Accessibility button.
-     * <p>
-     * Input: Nothing.
-     * </p>
-     * <p>
-     * Output: Nothing.
-     * </p>
-     *
-     * @hide
-     */
-    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_CHOOSE_ACCESSIBILITY_BUTTON =
-            "com.android.internal.intent.action.CHOOSE_ACCESSIBILITY_BUTTON";
-
-    static final Object sInstanceSync = new Object();
-
-    private static AccessibilityManager sInstance;
-
-    private final Object mLock = new Object();
-
-    private IAccessibilityManager mService;
-
-    final int mUserId;
-
-    final Handler mHandler;
-
-    final Handler.Callback mCallback;
-
-    boolean mIsEnabled;
-
-    int mRelevantEventTypes = AccessibilityEvent.TYPES_ALL_MASK;
-
-    boolean mIsTouchExplorationEnabled;
-
-    boolean mIsHighTextContrastEnabled;
-
-    private final ArrayMap<AccessibilityStateChangeListener, Handler>
-            mAccessibilityStateChangeListeners = new ArrayMap<>();
-
-    private final ArrayMap<TouchExplorationStateChangeListener, Handler>
-            mTouchExplorationStateChangeListeners = new ArrayMap<>();
-
-    private final ArrayMap<HighTextContrastChangeListener, Handler>
-            mHighTextContrastStateChangeListeners = new ArrayMap<>();
-
-    private final ArrayMap<AccessibilityServicesStateChangeListener, Handler>
-            mServicesStateChangeListeners = new ArrayMap<>();
-
-    /**
-     * Map from a view's accessibility id to the list of request preparers set for that view
-     */
-    private SparseArray<List<AccessibilityRequestPreparer>> mRequestPreparerLists;
-
-    /**
-     * Listener for the system accessibility state. To listen for changes to the
-     * accessibility state on the device, implement this interface and register
-     * it with the system by calling {@link #addAccessibilityStateChangeListener}.
+     * Listener for the accessibility state.
      */
     public interface AccessibilityStateChangeListener {
 
         /**
-         * Called when the accessibility enabled state changes.
+         * Called back on change in the accessibility state.
          *
          * @param enabled Whether accessibility is enabled.
          */
-        void onAccessibilityStateChanged(boolean enabled);
+        public void onAccessibilityStateChanged(boolean enabled);
     }
 
     /**
@@ -177,24 +71,7 @@
          *
          * @param enabled Whether touch exploration is enabled.
          */
-        void onTouchExplorationStateChanged(boolean enabled);
-    }
-
-    /**
-     * Listener for changes to the state of accessibility services. Changes include services being
-     * enabled or disabled, or changes to the {@link AccessibilityServiceInfo} of a running service.
-     * {@see #addAccessibilityServicesStateChangeListener}.
-     *
-     * @hide
-     */
-    public interface AccessibilityServicesStateChangeListener {
-
-        /**
-         * Called when the state of accessibility services changes.
-         *
-         * @param manager The manager that is calling back
-         */
-        void onAccessibilityServicesStateChanged(AccessibilityManager manager);
+        public void onTouchExplorationStateChanged(boolean enabled);
     }
 
     /**
@@ -202,8 +79,6 @@
      * the high text contrast state on the device, implement this interface and
      * register it with the system by calling
      * {@link #addHighTextContrastStateChangeListener}.
-     *
-     * @hide
      */
     public interface HighTextContrastChangeListener {
 
@@ -212,72 +87,26 @@
          *
          * @param enabled Whether high text contrast is enabled.
          */
-        void onHighTextContrastStateChanged(boolean enabled);
+        public void onHighTextContrastStateChanged(boolean enabled);
     }
 
     private final IAccessibilityManagerClient.Stub mClient =
             new IAccessibilityManagerClient.Stub() {
-        @Override
-        public void setState(int state) {
-            // We do not want to change this immediately as the application may
-            // have already checked that accessibility is on and fired an event,
-            // that is now propagating up the view tree, Hence, if accessibility
-            // is now off an exception will be thrown. We want to have the exception
-            // enforcement to guard against apps that fire unnecessary accessibility
-            // events when accessibility is off.
-            mHandler.obtainMessage(MyCallback.MSG_SET_STATE, state, 0).sendToTarget();
-        }
-
-        @Override
-        public void notifyServicesStateChanged() {
-            final ArrayMap<AccessibilityServicesStateChangeListener, Handler> listeners;
-            synchronized (mLock) {
-                if (mServicesStateChangeListeners.isEmpty()) {
-                    return;
+                public void setState(int state) {
                 }
-                listeners = new ArrayMap<>(mServicesStateChangeListeners);
-            }
 
-            int numListeners = listeners.size();
-            for (int i = 0; i < numListeners; i++) {
-                final AccessibilityServicesStateChangeListener listener =
-                        mServicesStateChangeListeners.keyAt(i);
-                mServicesStateChangeListeners.valueAt(i).post(() -> listener
-                        .onAccessibilityServicesStateChanged(AccessibilityManager.this));
-            }
-        }
+                public void notifyServicesStateChanged() {
+                }
 
-        @Override
-        public void setRelevantEventTypes(int eventTypes) {
-            mRelevantEventTypes = eventTypes;
-        }
-    };
+                public void setRelevantEventTypes(int eventTypes) {
+                }
+            };
 
     /**
      * Get an AccessibilityManager instance (create one if necessary).
      *
-     * @param context Context in which this manager operates.
-     *
-     * @hide
      */
     public static AccessibilityManager getInstance(Context context) {
-        synchronized (sInstanceSync) {
-            if (sInstance == null) {
-                final int userId;
-                if (Binder.getCallingUid() == Process.SYSTEM_UID
-                        || context.checkCallingOrSelfPermission(
-                                Manifest.permission.INTERACT_ACROSS_USERS)
-                                        == PackageManager.PERMISSION_GRANTED
-                        || context.checkCallingOrSelfPermission(
-                                Manifest.permission.INTERACT_ACROSS_USERS_FULL)
-                                        == PackageManager.PERMISSION_GRANTED) {
-                    userId = UserHandle.USER_CURRENT;
-                } else {
-                    userId = UserHandle.myUserId();
-                }
-                sInstance = new AccessibilityManager(context, null, userId);
-            }
-        }
         return sInstance;
     }
 
@@ -285,68 +114,21 @@
      * Create an instance.
      *
      * @param context A {@link Context}.
-     * @param service An interface to the backing service.
-     * @param userId User id under which to run.
-     *
-     * @hide
      */
     public AccessibilityManager(Context context, IAccessibilityManager service, int userId) {
-        // Constructor can't be chained because we can't create an instance of an inner class
-        // before calling another constructor.
-        mCallback = new MyCallback();
-        mHandler = new Handler(context.getMainLooper(), mCallback);
-        mUserId = userId;
-        synchronized (mLock) {
-            tryConnectToServiceLocked(service);
-        }
     }
 
-    /**
-     * Create an instance.
-     *
-     * @param handler The handler to use
-     * @param service An interface to the backing service.
-     * @param userId User id under which to run.
-     *
-     * @hide
-     */
-    public AccessibilityManager(Handler handler, IAccessibilityManager service, int userId) {
-        mCallback = new MyCallback();
-        mHandler = handler;
-        mUserId = userId;
-        synchronized (mLock) {
-            tryConnectToServiceLocked(service);
-        }
-    }
-
-    /**
-     * @hide
-     */
     public IAccessibilityManagerClient getClient() {
         return mClient;
     }
 
     /**
-     * @hide
-     */
-    @VisibleForTesting
-    public Handler.Callback getCallback() {
-        return mCallback;
-    }
-
-    /**
-     * Returns if the accessibility in the system is enabled.
+     * Returns if the {@link AccessibilityManager} is enabled.
      *
-     * @return True if accessibility is enabled, false otherwise.
+     * @return True if this {@link AccessibilityManager} is enabled, false otherwise.
      */
     public boolean isEnabled() {
-        synchronized (mLock) {
-            IAccessibilityManager service = getServiceLocked();
-            if (service == null) {
-                return false;
-            }
-            return mIsEnabled;
-        }
+        return false;
     }
 
     /**
@@ -355,13 +137,7 @@
      * @return True if touch exploration is enabled, false otherwise.
      */
     public boolean isTouchExplorationEnabled() {
-        synchronized (mLock) {
-            IAccessibilityManager service = getServiceLocked();
-            if (service == null) {
-                return false;
-            }
-            return mIsTouchExplorationEnabled;
-        }
+        return true;
     }
 
     /**
@@ -371,169 +147,35 @@
      * doing its own rendering and does not rely on the platform rendering pipeline.
      * </p>
      *
-     * @return True if high text contrast is enabled, false otherwise.
-     *
-     * @hide
      */
     public boolean isHighTextContrastEnabled() {
-        synchronized (mLock) {
-            IAccessibilityManager service = getServiceLocked();
-            if (service == null) {
-                return false;
-            }
-            return mIsHighTextContrastEnabled;
-        }
+        return false;
     }
 
     /**
      * Sends an {@link AccessibilityEvent}.
-     *
-     * @param event The event to send.
-     *
-     * @throws IllegalStateException if accessibility is not enabled.
-     *
-     * <strong>Note:</strong> The preferred mechanism for sending custom accessibility
-     * events is through calling
-     * {@link android.view.ViewParent#requestSendAccessibilityEvent(View, AccessibilityEvent)}
-     * instead of this method to allow predecessors to augment/filter events sent by
-     * their descendants.
      */
     public void sendAccessibilityEvent(AccessibilityEvent event) {
-        final IAccessibilityManager service;
-        final int userId;
-        synchronized (mLock) {
-            service = getServiceLocked();
-            if (service == null) {
-                return;
-            }
-            if (!mIsEnabled) {
-                Looper myLooper = Looper.myLooper();
-                if (myLooper == Looper.getMainLooper()) {
-                    throw new IllegalStateException(
-                            "Accessibility off. Did you forget to check that?");
-                } else {
-                    // If we're not running on the thread with the main looper, it's possible for
-                    // the state of accessibility to change between checking isEnabled and
-                    // calling this method. So just log the error rather than throwing the
-                    // exception.
-                    Log.e(LOG_TAG, "AccessibilityEvent sent with accessibility disabled");
-                    return;
-                }
-            }
-            if ((event.getEventType() & mRelevantEventTypes) == 0) {
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "Not dispatching irrelevant event: " + event
-                            + " that is not among "
-                            + AccessibilityEvent.eventTypeToString(mRelevantEventTypes));
-                }
-                return;
-            }
-            userId = mUserId;
-        }
-        try {
-            event.setEventTime(SystemClock.uptimeMillis());
-            // it is possible that this manager is in the same process as the service but
-            // client using it is called through Binder from another process. Example: MMS
-            // app adds a SMS notification and the NotificationManagerService calls this method
-            long identityToken = Binder.clearCallingIdentity();
-            service.sendAccessibilityEvent(event, userId);
-            Binder.restoreCallingIdentity(identityToken);
-            if (DEBUG) {
-                Log.i(LOG_TAG, event + " sent");
-            }
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error during sending " + event + " ", re);
-        } finally {
-            event.recycle();
-        }
     }
 
     /**
-     * Requests feedback interruption from all accessibility services.
+     * Requests interruption of the accessibility feedback from all accessibility services.
      */
     public void interrupt() {
-        final IAccessibilityManager service;
-        final int userId;
-        synchronized (mLock) {
-            service = getServiceLocked();
-            if (service == null) {
-                return;
-            }
-            if (!mIsEnabled) {
-                Looper myLooper = Looper.myLooper();
-                if (myLooper == Looper.getMainLooper()) {
-                    throw new IllegalStateException(
-                            "Accessibility off. Did you forget to check that?");
-                } else {
-                    // If we're not running on the thread with the main looper, it's possible for
-                    // the state of accessibility to change between checking isEnabled and
-                    // calling this method. So just log the error rather than throwing the
-                    // exception.
-                    Log.e(LOG_TAG, "Interrupt called with accessibility disabled");
-                    return;
-                }
-            }
-            userId = mUserId;
-        }
-        try {
-            service.interrupt(userId);
-            if (DEBUG) {
-                Log.i(LOG_TAG, "Requested interrupt from all services");
-            }
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error while requesting interrupt from all services. ", re);
-        }
     }
 
     /**
      * Returns the {@link ServiceInfo}s of the installed accessibility services.
      *
      * @return An unmodifiable list with {@link ServiceInfo}s.
-     *
-     * @deprecated Use {@link #getInstalledAccessibilityServiceList()}
      */
     @Deprecated
     public List<ServiceInfo> getAccessibilityServiceList() {
-        List<AccessibilityServiceInfo> infos = getInstalledAccessibilityServiceList();
-        List<ServiceInfo> services = new ArrayList<>();
-        final int infoCount = infos.size();
-        for (int i = 0; i < infoCount; i++) {
-            AccessibilityServiceInfo info = infos.get(i);
-            services.add(info.getResolveInfo().serviceInfo);
-        }
-        return Collections.unmodifiableList(services);
+        return Collections.emptyList();
     }
 
-    /**
-     * Returns the {@link AccessibilityServiceInfo}s of the installed accessibility services.
-     *
-     * @return An unmodifiable list with {@link AccessibilityServiceInfo}s.
-     */
     public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() {
-        final IAccessibilityManager service;
-        final int userId;
-        synchronized (mLock) {
-            service = getServiceLocked();
-            if (service == null) {
-                return Collections.emptyList();
-            }
-            userId = mUserId;
-        }
-
-        List<AccessibilityServiceInfo> services = null;
-        try {
-            services = service.getInstalledAccessibilityServiceList(userId);
-            if (DEBUG) {
-                Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
-            }
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
-        }
-        if (services != null) {
-            return Collections.unmodifiableList(services);
-        } else {
-            return Collections.emptyList();
-        }
+        return Collections.emptyList();
     }
 
     /**
@@ -548,48 +190,21 @@
      * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC
      * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN
      * @see AccessibilityServiceInfo#FEEDBACK_VISUAL
-     * @see AccessibilityServiceInfo#FEEDBACK_BRAILLE
      */
     public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
             int feedbackTypeFlags) {
-        final IAccessibilityManager service;
-        final int userId;
-        synchronized (mLock) {
-            service = getServiceLocked();
-            if (service == null) {
-                return Collections.emptyList();
-            }
-            userId = mUserId;
-        }
-
-        List<AccessibilityServiceInfo> services = null;
-        try {
-            services = service.getEnabledAccessibilityServiceList(feedbackTypeFlags, userId);
-            if (DEBUG) {
-                Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
-            }
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
-        }
-        if (services != null) {
-            return Collections.unmodifiableList(services);
-        } else {
-            return Collections.emptyList();
-        }
+        return Collections.emptyList();
     }
 
     /**
      * Registers an {@link AccessibilityStateChangeListener} for changes in
-     * the global accessibility state of the system. Equivalent to calling
-     * {@link #addAccessibilityStateChangeListener(AccessibilityStateChangeListener, Handler)}
-     * with a null handler.
+     * the global accessibility state of the system.
      *
      * @param listener The listener.
-     * @return Always returns {@code true}.
+     * @return True if successfully registered.
      */
     public boolean addAccessibilityStateChangeListener(
-            @NonNull AccessibilityStateChangeListener listener) {
-        addAccessibilityStateChangeListener(listener, null);
+            AccessibilityStateChangeListener listener) {
         return true;
     }
 
@@ -603,40 +218,22 @@
      *                for a callback on the process's main handler.
      */
     public void addAccessibilityStateChangeListener(
-            @NonNull AccessibilityStateChangeListener listener, @Nullable Handler handler) {
-        synchronized (mLock) {
-            mAccessibilityStateChangeListeners
-                    .put(listener, (handler == null) ? mHandler : handler);
-        }
-    }
+            @NonNull AccessibilityStateChangeListener listener, @Nullable Handler handler) {}
 
-    /**
-     * Unregisters an {@link AccessibilityStateChangeListener}.
-     *
-     * @param listener The listener.
-     * @return True if the listener was previously registered.
-     */
     public boolean removeAccessibilityStateChangeListener(
-            @NonNull AccessibilityStateChangeListener listener) {
-        synchronized (mLock) {
-            int index = mAccessibilityStateChangeListeners.indexOfKey(listener);
-            mAccessibilityStateChangeListeners.remove(listener);
-            return (index >= 0);
-        }
+            AccessibilityStateChangeListener listener) {
+        return true;
     }
 
     /**
      * Registers a {@link TouchExplorationStateChangeListener} for changes in
-     * the global touch exploration state of the system. Equivalent to calling
-     * {@link #addTouchExplorationStateChangeListener(TouchExplorationStateChangeListener, Handler)}
-     * with a null handler.
+     * the global touch exploration state of the system.
      *
      * @param listener The listener.
-     * @return Always returns {@code true}.
+     * @return True if successfully registered.
      */
     public boolean addTouchExplorationStateChangeListener(
             @NonNull TouchExplorationStateChangeListener listener) {
-        addTouchExplorationStateChangeListener(listener, null);
         return true;
     }
 
@@ -650,103 +247,17 @@
      *                for a callback on the process's main handler.
      */
     public void addTouchExplorationStateChangeListener(
-            @NonNull TouchExplorationStateChangeListener listener, @Nullable Handler handler) {
-        synchronized (mLock) {
-            mTouchExplorationStateChangeListeners
-                    .put(listener, (handler == null) ? mHandler : handler);
-        }
-    }
+            @NonNull TouchExplorationStateChangeListener listener, @Nullable Handler handler) {}
 
     /**
      * Unregisters a {@link TouchExplorationStateChangeListener}.
      *
      * @param listener The listener.
-     * @return True if listener was previously registered.
+     * @return True if successfully unregistered.
      */
     public boolean removeTouchExplorationStateChangeListener(
             @NonNull TouchExplorationStateChangeListener listener) {
-        synchronized (mLock) {
-            int index = mTouchExplorationStateChangeListeners.indexOfKey(listener);
-            mTouchExplorationStateChangeListeners.remove(listener);
-            return (index >= 0);
-        }
-    }
-
-    /**
-     * Registers a {@link AccessibilityServicesStateChangeListener}.
-     *
-     * @param listener The listener.
-     * @param handler The handler on which the listener should be called back, or {@code null}
-     *                for a callback on the process's main handler.
-     * @hide
-     */
-    public void addAccessibilityServicesStateChangeListener(
-            @NonNull AccessibilityServicesStateChangeListener listener, @Nullable Handler handler) {
-        synchronized (mLock) {
-            mServicesStateChangeListeners
-                    .put(listener, (handler == null) ? mHandler : handler);
-        }
-    }
-
-    /**
-     * Unregisters a {@link AccessibilityServicesStateChangeListener}.
-     *
-     * @param listener The listener.
-     *
-     * @hide
-     */
-    public void removeAccessibilityServicesStateChangeListener(
-            @NonNull AccessibilityServicesStateChangeListener listener) {
-        // Final CopyOnWriteArrayList - no lock needed.
-        mServicesStateChangeListeners.remove(listener);
-    }
-
-    /**
-     * Registers a {@link AccessibilityRequestPreparer}.
-     */
-    public void addAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer) {
-        if (mRequestPreparerLists == null) {
-            mRequestPreparerLists = new SparseArray<>(1);
-        }
-        int id = preparer.getView().getAccessibilityViewId();
-        List<AccessibilityRequestPreparer> requestPreparerList = mRequestPreparerLists.get(id);
-        if (requestPreparerList == null) {
-            requestPreparerList = new ArrayList<>(1);
-            mRequestPreparerLists.put(id, requestPreparerList);
-        }
-        requestPreparerList.add(preparer);
-    }
-
-    /**
-     * Unregisters a {@link AccessibilityRequestPreparer}.
-     */
-    public void removeAccessibilityRequestPreparer(AccessibilityRequestPreparer preparer) {
-        if (mRequestPreparerLists == null) {
-            return;
-        }
-        int viewId = preparer.getView().getAccessibilityViewId();
-        List<AccessibilityRequestPreparer> requestPreparerList = mRequestPreparerLists.get(viewId);
-        if (requestPreparerList != null) {
-            requestPreparerList.remove(preparer);
-            if (requestPreparerList.isEmpty()) {
-                mRequestPreparerLists.remove(viewId);
-            }
-        }
-    }
-
-    /**
-     * Get the preparers that are registered for an accessibility ID
-     *
-     * @param id The ID of interest
-     * @return The list of preparers, or {@code null} if there are none.
-     *
-     * @hide
-     */
-    public List<AccessibilityRequestPreparer> getRequestPreparersForAccessibilityId(int id) {
-        if (mRequestPreparerLists == null) {
-            return null;
-        }
-        return mRequestPreparerLists.get(id);
+        return true;
     }
 
     /**
@@ -758,12 +269,7 @@
      * @hide
      */
     public void addHighTextContrastStateChangeListener(
-            @NonNull HighTextContrastChangeListener listener, @Nullable Handler handler) {
-        synchronized (mLock) {
-            mHighTextContrastStateChangeListeners
-                    .put(listener, (handler == null) ? mHandler : handler);
-        }
-    }
+            @NonNull HighTextContrastChangeListener listener, @Nullable Handler handler) {}
 
     /**
      * Unregisters a {@link HighTextContrastChangeListener}.
@@ -773,51 +279,7 @@
      * @hide
      */
     public void removeHighTextContrastStateChangeListener(
-            @NonNull HighTextContrastChangeListener listener) {
-        synchronized (mLock) {
-            mHighTextContrastStateChangeListeners.remove(listener);
-        }
-    }
-
-    /**
-     * Check if the accessibility volume stream is active.
-     *
-     * @return True if accessibility volume is active (i.e. some service has requested it). False
-     * otherwise.
-     * @hide
-     */
-    public boolean isAccessibilityVolumeStreamActive() {
-        List<AccessibilityServiceInfo> serviceInfos =
-                getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
-        for (int i = 0; i < serviceInfos.size(); i++) {
-            if ((serviceInfos.get(i).flags & FLAG_ENABLE_ACCESSIBILITY_VOLUME) != 0) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Report a fingerprint gesture to accessibility. Only available for the system process.
-     *
-     * @param keyCode The key code of the gesture
-     * @return {@code true} if accessibility consumes the event. {@code false} if not.
-     * @hide
-     */
-    public boolean sendFingerprintGesture(int keyCode) {
-        final IAccessibilityManager service;
-        synchronized (mLock) {
-            service = getServiceLocked();
-            if (service == null) {
-                return false;
-            }
-        }
-        try {
-            return service.sendFingerprintGesture(keyCode);
-        } catch (RemoteException e) {
-            return false;
-        }
-    }
+            @NonNull HighTextContrastChangeListener listener) {}
 
     /**
      * Sets the current state and notifies listeners, if necessary.
@@ -825,314 +287,14 @@
      * @param stateFlags The state flags.
      */
     private void setStateLocked(int stateFlags) {
-        final boolean enabled = (stateFlags & STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
-        final boolean touchExplorationEnabled =
-                (stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0;
-        final boolean highTextContrastEnabled =
-                (stateFlags & STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED) != 0;
-
-        final boolean wasEnabled = mIsEnabled;
-        final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
-        final boolean wasHighTextContrastEnabled = mIsHighTextContrastEnabled;
-
-        // Ensure listeners get current state from isZzzEnabled() calls.
-        mIsEnabled = enabled;
-        mIsTouchExplorationEnabled = touchExplorationEnabled;
-        mIsHighTextContrastEnabled = highTextContrastEnabled;
-
-        if (wasEnabled != enabled) {
-            notifyAccessibilityStateChanged();
-        }
-
-        if (wasTouchExplorationEnabled != touchExplorationEnabled) {
-            notifyTouchExplorationStateChanged();
-        }
-
-        if (wasHighTextContrastEnabled != highTextContrastEnabled) {
-            notifyHighTextContrastStateChanged();
-        }
     }
 
-    /**
-     * Find an installed service with the specified {@link ComponentName}.
-     *
-     * @param componentName The name to match to the service.
-     *
-     * @return The info corresponding to the installed service, or {@code null} if no such service
-     * is installed.
-     * @hide
-     */
-    public AccessibilityServiceInfo getInstalledServiceInfoWithComponentName(
-            ComponentName componentName) {
-        final List<AccessibilityServiceInfo> installedServiceInfos =
-                getInstalledAccessibilityServiceList();
-        if ((installedServiceInfos == null) || (componentName == null)) {
-            return null;
-        }
-        for (int i = 0; i < installedServiceInfos.size(); i++) {
-            if (componentName.equals(installedServiceInfos.get(i).getComponentName())) {
-                return installedServiceInfos.get(i);
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Adds an accessibility interaction connection interface for a given window.
-     * @param windowToken The window token to which a connection is added.
-     * @param connection The connection.
-     *
-     * @hide
-     */
     public int addAccessibilityInteractionConnection(IWindow windowToken,
             IAccessibilityInteractionConnection connection) {
-        final IAccessibilityManager service;
-        final int userId;
-        synchronized (mLock) {
-            service = getServiceLocked();
-            if (service == null) {
-                return View.NO_ID;
-            }
-            userId = mUserId;
-        }
-        try {
-            return service.addAccessibilityInteractionConnection(windowToken, connection, userId);
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re);
-        }
         return View.NO_ID;
     }
 
-    /**
-     * Removed an accessibility interaction connection interface for a given window.
-     * @param windowToken The window token to which a connection is removed.
-     *
-     * @hide
-     */
     public void removeAccessibilityInteractionConnection(IWindow windowToken) {
-        final IAccessibilityManager service;
-        synchronized (mLock) {
-            service = getServiceLocked();
-            if (service == null) {
-                return;
-            }
-        }
-        try {
-            service.removeAccessibilityInteractionConnection(windowToken);
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error while removing an accessibility interaction connection. ", re);
-        }
     }
 
-    /**
-     * Perform the accessibility shortcut if the caller has permission.
-     *
-     * @hide
-     */
-    public void performAccessibilityShortcut() {
-        final IAccessibilityManager service;
-        synchronized (mLock) {
-            service = getServiceLocked();
-            if (service == null) {
-                return;
-            }
-        }
-        try {
-            service.performAccessibilityShortcut();
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error performing accessibility shortcut. ", re);
-        }
-    }
-
-    /**
-     * Notifies that the accessibility button in the system's navigation area has been clicked
-     *
-     * @hide
-     */
-    public void notifyAccessibilityButtonClicked() {
-        final IAccessibilityManager service;
-        synchronized (mLock) {
-            service = getServiceLocked();
-            if (service == null) {
-                return;
-            }
-        }
-        try {
-            service.notifyAccessibilityButtonClicked();
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error while dispatching accessibility button click", re);
-        }
-    }
-
-    /**
-     * Notifies that the visibility of the accessibility button in the system's navigation area
-     * has changed.
-     *
-     * @param shown {@code true} if the accessibility button is visible within the system
-     *                  navigation area, {@code false} otherwise
-     * @hide
-     */
-    public void notifyAccessibilityButtonVisibilityChanged(boolean shown) {
-        final IAccessibilityManager service;
-        synchronized (mLock) {
-            service = getServiceLocked();
-            if (service == null) {
-                return;
-            }
-        }
-        try {
-            service.notifyAccessibilityButtonVisibilityChanged(shown);
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error while dispatching accessibility button visibility change", re);
-        }
-    }
-
-    /**
-     * Set an IAccessibilityInteractionConnection to replace the actions of a picture-in-picture
-     * window. Intended for use by the System UI only.
-     *
-     * @param connection The connection to handle the actions. Set to {@code null} to avoid
-     * affecting the actions.
-     *
-     * @hide
-     */
-    public void setPictureInPictureActionReplacingConnection(
-            @Nullable IAccessibilityInteractionConnection connection) {
-        final IAccessibilityManager service;
-        synchronized (mLock) {
-            service = getServiceLocked();
-            if (service == null) {
-                return;
-            }
-        }
-        try {
-            service.setPictureInPictureActionReplacingConnection(connection);
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error setting picture in picture action replacement", re);
-        }
-    }
-
-    private IAccessibilityManager getServiceLocked() {
-        if (mService == null) {
-            tryConnectToServiceLocked(null);
-        }
-        return mService;
-    }
-
-    private void tryConnectToServiceLocked(IAccessibilityManager service) {
-        if (service == null) {
-            IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
-            if (iBinder == null) {
-                return;
-            }
-            service = IAccessibilityManager.Stub.asInterface(iBinder);
-        }
-
-        try {
-            final long userStateAndRelevantEvents = service.addClient(mClient, mUserId);
-            setStateLocked(IntPair.first(userStateAndRelevantEvents));
-            mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents);
-            mService = service;
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
-        }
-    }
-
-    /**
-     * Notifies the registered {@link AccessibilityStateChangeListener}s.
-     */
-    private void notifyAccessibilityStateChanged() {
-        final boolean isEnabled;
-        final ArrayMap<AccessibilityStateChangeListener, Handler> listeners;
-        synchronized (mLock) {
-            if (mAccessibilityStateChangeListeners.isEmpty()) {
-                return;
-            }
-            isEnabled = mIsEnabled;
-            listeners = new ArrayMap<>(mAccessibilityStateChangeListeners);
-        }
-
-        int numListeners = listeners.size();
-        for (int i = 0; i < numListeners; i++) {
-            final AccessibilityStateChangeListener listener =
-                    mAccessibilityStateChangeListeners.keyAt(i);
-            mAccessibilityStateChangeListeners.valueAt(i)
-                    .post(() -> listener.onAccessibilityStateChanged(isEnabled));
-        }
-    }
-
-    /**
-     * Notifies the registered {@link TouchExplorationStateChangeListener}s.
-     */
-    private void notifyTouchExplorationStateChanged() {
-        final boolean isTouchExplorationEnabled;
-        final ArrayMap<TouchExplorationStateChangeListener, Handler> listeners;
-        synchronized (mLock) {
-            if (mTouchExplorationStateChangeListeners.isEmpty()) {
-                return;
-            }
-            isTouchExplorationEnabled = mIsTouchExplorationEnabled;
-            listeners = new ArrayMap<>(mTouchExplorationStateChangeListeners);
-        }
-
-        int numListeners = listeners.size();
-        for (int i = 0; i < numListeners; i++) {
-            final TouchExplorationStateChangeListener listener =
-                    mTouchExplorationStateChangeListeners.keyAt(i);
-            mTouchExplorationStateChangeListeners.valueAt(i)
-                    .post(() -> listener.onTouchExplorationStateChanged(isTouchExplorationEnabled));
-        }
-    }
-
-    /**
-     * Notifies the registered {@link HighTextContrastChangeListener}s.
-     */
-    private void notifyHighTextContrastStateChanged() {
-        final boolean isHighTextContrastEnabled;
-        final ArrayMap<HighTextContrastChangeListener, Handler> listeners;
-        synchronized (mLock) {
-            if (mHighTextContrastStateChangeListeners.isEmpty()) {
-                return;
-            }
-            isHighTextContrastEnabled = mIsHighTextContrastEnabled;
-            listeners = new ArrayMap<>(mHighTextContrastStateChangeListeners);
-        }
-
-        int numListeners = listeners.size();
-        for (int i = 0; i < numListeners; i++) {
-            final HighTextContrastChangeListener listener =
-                    mHighTextContrastStateChangeListeners.keyAt(i);
-            mHighTextContrastStateChangeListeners.valueAt(i)
-                    .post(() -> listener.onHighTextContrastStateChanged(isHighTextContrastEnabled));
-        }
-    }
-
-    /**
-     * Determines if the accessibility button within the system navigation area is supported.
-     *
-     * @return {@code true} if the accessibility button is supported on this device,
-     * {@code false} otherwise
-     */
-    public static boolean isAccessibilityButtonSupported() {
-        final Resources res = Resources.getSystem();
-        return res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);
-    }
-
-    private final class MyCallback implements Handler.Callback {
-        public static final int MSG_SET_STATE = 1;
-
-        @Override
-        public boolean handleMessage(Message message) {
-            switch (message.what) {
-                case MSG_SET_STATE: {
-                    // See comment at mClient
-                    final int state = message.arg1;
-                    synchronized (mLock) {
-                        setStateLocked(state);
-                    }
-                } break;
-            }
-            return true;
-        }
-    }
 }
diff --git a/android/view/inputmethod/ExtractedText.java b/android/view/inputmethod/ExtractedText.java
index 0c5d9e9..003f221 100644
--- a/android/view/inputmethod/ExtractedText.java
+++ b/android/view/inputmethod/ExtractedText.java
@@ -87,6 +87,11 @@
     public int flags;
 
     /**
+     * The hint that has been extracted.
+     */
+    public CharSequence hint;
+
+    /**
      * Used to package this object into a {@link Parcel}.
      *
      * @param dest The {@link Parcel} to be written.
@@ -100,6 +105,7 @@
         dest.writeInt(selectionStart);
         dest.writeInt(selectionEnd);
         dest.writeInt(this.flags);
+        TextUtils.writeToParcel(hint, dest, flags);
     }
 
     /**
@@ -107,17 +113,18 @@
      */
     public static final Parcelable.Creator<ExtractedText> CREATOR
             = new Parcelable.Creator<ExtractedText>() {
-        public ExtractedText createFromParcel(Parcel source) {
-            ExtractedText res = new ExtractedText();
-            res.text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
-            res.startOffset = source.readInt();
-            res.partialStartOffset = source.readInt();
-            res.partialEndOffset = source.readInt();
-            res.selectionStart = source.readInt();
-            res.selectionEnd = source.readInt();
-            res.flags = source.readInt();
-            return res;
-        }
+                public ExtractedText createFromParcel(Parcel source) {
+                    ExtractedText res = new ExtractedText();
+                    res.text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+                    res.startOffset = source.readInt();
+                    res.partialStartOffset = source.readInt();
+                    res.partialEndOffset = source.readInt();
+                    res.selectionStart = source.readInt();
+                    res.selectionEnd = source.readInt();
+                    res.flags = source.readInt();
+                    res.hint = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+                    return res;
+                }
 
         public ExtractedText[] newArray(int size) {
             return new ExtractedText[size];
diff --git a/android/view/textclassifier/logging/SmartSelectionEventTracker.java b/android/view/textclassifier/logging/SmartSelectionEventTracker.java
index 77aea23..8d88ba6 100644
--- a/android/view/textclassifier/logging/SmartSelectionEventTracker.java
+++ b/android/view/textclassifier/logging/SmartSelectionEventTracker.java
@@ -42,17 +42,16 @@
 //TODO: Do not allow any crashes from this class.
 public final class SmartSelectionEventTracker {
 
-    private static final String LOG_TAG = "SmartSelectionEventTracker";
+    private static final String LOG_TAG = "SmartSelectEventTracker";
     private static final boolean DEBUG_LOG_ENABLED = true;
 
-    private static final int START_EVENT_DELTA = MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS;
-    private static final int PREV_EVENT_DELTA = MetricsEvent.NOTIFICATION_SINCE_UPDATE_MILLIS;
-    private static final int ENTITY_TYPE = MetricsEvent.NOTIFICATION_TAG;
-    private static final int INDEX = MetricsEvent.NOTIFICATION_SHADE_INDEX;
-    private static final int TAG = MetricsEvent.FIELD_CLASS_NAME;
-    private static final int SMART_INDICES = MetricsEvent.FIELD_GESTURE_LENGTH;
-    private static final int EVENT_INDICES = MetricsEvent.FIELD_CONTEXT;
-    private static final int SESSION_ID = MetricsEvent.FIELD_INSTANT_APP_LAUNCH_TOKEN;
+    private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START;
+    private static final int PREV_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_PREVIOUS;
+    private static final int INDEX = MetricsEvent.FIELD_SELECTION_SESSION_INDEX;
+    private static final int VERSION_TAG = MetricsEvent.FIELD_SELECTION_VERSION_TAG;
+    private static final int SMART_INDICES = MetricsEvent.FIELD_SELECTION_SMART_RANGE;
+    private static final int EVENT_INDICES = MetricsEvent.FIELD_SELECTION_RANGE;
+    private static final int SESSION_ID = MetricsEvent.FIELD_SELECTION_SESSION_ID;
 
     private static final String ZERO = "0";
     private static final String TEXTVIEW = "textview";
@@ -84,6 +83,7 @@
     private long mSessionStartTime;
     private long mLastEventTime;
     private boolean mSmartSelectionTriggered;
+    private String mVersionTag;
 
     public SmartSelectionEventTracker(@NonNull Context context, @WidgetType int widgetType) {
         mWidgetType = widgetType;
@@ -98,7 +98,8 @@
     public void logEvent(@NonNull SelectionEvent event) {
         Preconditions.checkNotNull(event);
 
-        if (event.mEventType != SelectionEvent.EventType.SELECTION_STARTED && mSessionId == null) {
+        if (event.mEventType != SelectionEvent.EventType.SELECTION_STARTED && mSessionId == null
+                && DEBUG_LOG_ENABLED) {
             Log.d(LOG_TAG, "Selection session not yet started. Ignoring event");
             return;
         }
@@ -114,6 +115,7 @@
             case SelectionEvent.EventType.SMART_SELECTION_SINGLE:  // fall through
             case SelectionEvent.EventType.SMART_SELECTION_MULTI:
                 mSmartSelectionTriggered = true;
+                mVersionTag = getVersionTag(event);
                 mSmartIndices[0] = event.mStart;
                 mSmartIndices[1] = event.mEnd;
                 break;
@@ -132,16 +134,15 @@
     }
 
     private void writeEvent(SelectionEvent event, long now) {
-        final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST)
+        final long prevEventDelta = mLastEventTime == 0 ? 0 : now - mLastEventTime;
+        final LogMaker log = new LogMaker(MetricsEvent.TEXT_SELECTION_SESSION)
                 .setType(getLogType(event))
-                .setSubtype(event.mEventType)
+                .setSubtype(getLogSubType(event))
                 .setPackageName(mContext.getPackageName())
-                .setTimestamp(now)
                 .addTaggedData(START_EVENT_DELTA, now - mSessionStartTime)
-                .addTaggedData(PREV_EVENT_DELTA, now - mLastEventTime)
-                .addTaggedData(ENTITY_TYPE, event.mEntityType)
+                .addTaggedData(PREV_EVENT_DELTA, prevEventDelta)
                 .addTaggedData(INDEX, mIndex)
-                .addTaggedData(TAG, getTag(event))
+                .addTaggedData(VERSION_TAG, mVersionTag)
                 .addTaggedData(SMART_INDICES, getSmartDelta())
                 .addTaggedData(EVENT_INDICES, getEventDelta(event))
                 .addTaggedData(SESSION_ID, mSessionId);
@@ -168,42 +169,120 @@
         mSessionStartTime = 0;
         mLastEventTime = 0;
         mSmartSelectionTriggered = false;
+        mVersionTag = getVersionTag(null);
         mSessionId = null;
     }
 
-    private int getLogType(SelectionEvent event) {
+    private static int getLogType(SelectionEvent event) {
         switch (event.mEventType) {
-            case SelectionEvent.EventType.SELECTION_STARTED:  // fall through
-            case SelectionEvent.EventType.SMART_SELECTION_SINGLE:  // fall through
-            case SelectionEvent.EventType.SMART_SELECTION_MULTI:  // fall through
-            case SelectionEvent.EventType.AUTO_SELECTION:
-                return MetricsEvent.TYPE_OPEN;
+            case SelectionEvent.ActionType.OVERTYPE:
+                return MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE;
+            case SelectionEvent.ActionType.COPY:
+                return MetricsEvent.ACTION_TEXT_SELECTION_COPY;
+            case SelectionEvent.ActionType.PASTE:
+                return MetricsEvent.ACTION_TEXT_SELECTION_PASTE;
+            case SelectionEvent.ActionType.CUT:
+                return MetricsEvent.ACTION_TEXT_SELECTION_CUT;
+            case SelectionEvent.ActionType.SHARE:
+                return MetricsEvent.ACTION_TEXT_SELECTION_SHARE;
+            case SelectionEvent.ActionType.SMART_SHARE:
+                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE;
+            case SelectionEvent.ActionType.DRAG:
+                return MetricsEvent.ACTION_TEXT_SELECTION_DRAG;
             case SelectionEvent.ActionType.ABANDON:
-                return MetricsEvent.TYPE_CLOSE;
-        }
-        if (event.isActionType()) {
-            if (event.isTerminal() && mSmartSelectionTriggered) {
-                if (matchesSmartSelectionBounds(event)) {
-                    // Smart selection accepted.
-                    return MetricsEvent.TYPE_SUCCESS;
-                } else if (containsOriginalSelection(event)) {
-                    // Smart selection rejected.
-                    return MetricsEvent.TYPE_FAILURE;
-                }
-                // User changed the original selection entirely.
-            }
-            return MetricsEvent.TYPE_ACTION;
-        } else {
-            return MetricsEvent.TYPE_UPDATE;
+                return MetricsEvent.ACTION_TEXT_SELECTION_ABANDON;
+            case SelectionEvent.ActionType.OTHER:
+                return MetricsEvent.ACTION_TEXT_SELECTION_OTHER;
+            case SelectionEvent.ActionType.SELECT_ALL:
+                return MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL;
+            case SelectionEvent.ActionType.RESET:
+                return MetricsEvent.ACTION_TEXT_SELECTION_RESET;
+            case SelectionEvent.EventType.SELECTION_STARTED:
+                return MetricsEvent.ACTION_TEXT_SELECTION_START;
+            case SelectionEvent.EventType.SELECTION_MODIFIED:
+                return MetricsEvent.ACTION_TEXT_SELECTION_MODIFY;
+            case SelectionEvent.EventType.SMART_SELECTION_SINGLE:
+                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE;
+            case SelectionEvent.EventType.SMART_SELECTION_MULTI:
+                return MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI;
+            case SelectionEvent.EventType.AUTO_SELECTION:
+                return MetricsEvent.ACTION_TEXT_SELECTION_AUTO;
+            default:
+                return MetricsEvent.VIEW_UNKNOWN;
         }
     }
 
-    private boolean matchesSmartSelectionBounds(SelectionEvent event) {
-        return event.mStart == mSmartIndices[0] && event.mEnd == mSmartIndices[1];
+    private static String getLogTypeString(int logType) {
+        switch (logType) {
+            case MetricsEvent.ACTION_TEXT_SELECTION_OVERTYPE:
+                return "OVERTYPE";
+            case MetricsEvent.ACTION_TEXT_SELECTION_COPY:
+                return "COPY";
+            case MetricsEvent.ACTION_TEXT_SELECTION_PASTE:
+                return "PASTE";
+            case MetricsEvent.ACTION_TEXT_SELECTION_CUT:
+                return "CUT";
+            case MetricsEvent.ACTION_TEXT_SELECTION_SHARE:
+                return "SHARE";
+            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SHARE:
+                return "SMART_SHARE";
+            case MetricsEvent.ACTION_TEXT_SELECTION_DRAG:
+                return "DRAG";
+            case MetricsEvent.ACTION_TEXT_SELECTION_ABANDON:
+                return "ABANDON";
+            case MetricsEvent.ACTION_TEXT_SELECTION_OTHER:
+                return "OTHER";
+            case MetricsEvent.ACTION_TEXT_SELECTION_SELECT_ALL:
+                return "SELECT_ALL";
+            case MetricsEvent.ACTION_TEXT_SELECTION_RESET:
+                return "RESET";
+            case MetricsEvent.ACTION_TEXT_SELECTION_START:
+                return "SELECTION_STARTED";
+            case MetricsEvent.ACTION_TEXT_SELECTION_MODIFY:
+                return "SELECTION_MODIFIED";
+            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_SINGLE:
+                return "SMART_SELECTION_SINGLE";
+            case MetricsEvent.ACTION_TEXT_SELECTION_SMART_MULTI:
+                return "SMART_SELECTION_MULTI";
+            case MetricsEvent.ACTION_TEXT_SELECTION_AUTO:
+                return "AUTO_SELECTION";
+            default:
+                return UNKNOWN;
+        }
     }
 
-    private boolean containsOriginalSelection(SelectionEvent event) {
-        return event.mStart <= mOrigStart && event.mEnd > mOrigStart;
+    private static int getLogSubType(SelectionEvent event) {
+        switch (event.mEntityType) {
+            case TextClassifier.TYPE_OTHER:
+                return MetricsEvent.TEXT_CLASSIFIER_TYPE_OTHER;
+            case TextClassifier.TYPE_EMAIL:
+                return MetricsEvent.TEXT_CLASSIFIER_TYPE_EMAIL;
+            case TextClassifier.TYPE_PHONE:
+                return MetricsEvent.TEXT_CLASSIFIER_TYPE_PHONE;
+            case TextClassifier.TYPE_ADDRESS:
+                return MetricsEvent.TEXT_CLASSIFIER_TYPE_ADDRESS;
+            case TextClassifier.TYPE_URL:
+                return MetricsEvent.TEXT_CLASSIFIER_TYPE_URL;
+            default:
+                return MetricsEvent.TEXT_CLASSIFIER_TYPE_UNKNOWN;
+        }
+    }
+
+    private static String getLogSubTypeString(int logSubType) {
+        switch (logSubType) {
+            case MetricsEvent.TEXT_CLASSIFIER_TYPE_OTHER:
+                return TextClassifier.TYPE_OTHER;
+            case MetricsEvent.TEXT_CLASSIFIER_TYPE_EMAIL:
+                return TextClassifier.TYPE_EMAIL;
+            case MetricsEvent.TEXT_CLASSIFIER_TYPE_PHONE:
+                return TextClassifier.TYPE_PHONE;
+            case MetricsEvent.TEXT_CLASSIFIER_TYPE_ADDRESS:
+                return TextClassifier.TYPE_ADDRESS;
+            case MetricsEvent.TEXT_CLASSIFIER_TYPE_URL:
+                return TextClassifier.TYPE_URL;
+            default:
+                return TextClassifier.TYPE_UNKNOWN;
+        }
     }
 
     private int getSmartDelta() {
@@ -211,8 +290,9 @@
             return (clamp(mSmartIndices[0] - mOrigStart) << 16)
                     | (clamp(mSmartIndices[1] - mOrigStart) & 0xffff);
         }
-        // If no smart selection, return start selection indices (i.e. [0, 1])
-        return /* (0 << 16) | */ (1 & 0xffff);
+        // If the smart selection model was not run, return invalid selection indices [0,0]. This
+        // allows us to tell from the terminal event alone whether the model was run.
+        return 0;
     }
 
     private int getEventDelta(SelectionEvent event) {
@@ -220,7 +300,7 @@
                 | (clamp(event.mEnd - mOrigStart) & 0xffff);
     }
 
-    private String getTag(SelectionEvent event) {
+    private String getVersionTag(@Nullable SelectionEvent event) {
         final String widgetType;
         switch (mWidgetType) {
             case WidgetType.TEXTVIEW:
@@ -238,7 +318,9 @@
             default:
                 widgetType = UNKNOWN;
         }
-        final String version = Objects.toString(event.mVersionTag, SelectionEvent.NO_VERSION_TAG);
+        final String version = event == null
+                ? SelectionEvent.NO_VERSION_TAG
+                : Objects.toString(event.mVersionTag, SelectionEvent.NO_VERSION_TAG);
         return String.format("%s/%s", widgetType, version);
     }
 
@@ -253,66 +335,17 @@
     private static void debugLog(LogMaker log) {
         if (!DEBUG_LOG_ENABLED) return;
 
-        final String tag = Objects.toString(log.getTaggedData(TAG), "tag");
+        final String tag = Objects.toString(log.getTaggedData(VERSION_TAG), "tag");
         final int index = Integer.parseInt(Objects.toString(log.getTaggedData(INDEX), ZERO));
-
-        final String event;
-        switch (log.getSubtype()) {
-            case SelectionEvent.ActionType.OVERTYPE:
-                event = "OVERTYPE";
-                break;
-            case SelectionEvent.ActionType.COPY:
-                event = "COPY";
-                break;
-            case SelectionEvent.ActionType.PASTE:
-                event = "PASTE";
-                break;
-            case SelectionEvent.ActionType.CUT:
-                event = "CUT";
-                break;
-            case SelectionEvent.ActionType.SHARE:
-                event = "SHARE";
-                break;
-            case SelectionEvent.ActionType.SMART_SHARE:
-                event = "SMART_SHARE";
-                break;
-            case SelectionEvent.ActionType.DRAG:
-                event = "DRAG";
-                break;
-            case SelectionEvent.ActionType.ABANDON:
-                event = "ABANDON";
-                break;
-            case SelectionEvent.ActionType.OTHER:
-                event = "OTHER";
-                break;
-            case SelectionEvent.ActionType.SELECT_ALL:
-                event = "SELECT_ALL";
-                break;
-            case SelectionEvent.ActionType.RESET:
-                event = "RESET";
-                break;
-            case SelectionEvent.EventType.SELECTION_STARTED:
-                String sessionId = Objects.toString(log.getTaggedData(SESSION_ID), "");
-                sessionId = sessionId.substring(sessionId.lastIndexOf("-") + 1);
-                Log.d(LOG_TAG, String.format("New selection session: %s(%s)", tag, sessionId));
-                event = "SELECTION_STARTED";
-                break;
-            case SelectionEvent.EventType.SELECTION_MODIFIED:
-                event = "SELECTION_MODIFIED";
-                break;
-            case SelectionEvent.EventType.SMART_SELECTION_SINGLE:
-                event = "SMART_SELECTION_SINGLE";
-                break;
-            case SelectionEvent.EventType.SMART_SELECTION_MULTI:
-                event = "SMART_SELECTION_MULTI";
-                break;
-            case SelectionEvent.EventType.AUTO_SELECTION:
-                event = "AUTO_SELECTION";
-                break;
-            default:
-                event = "UNKNOWN";
+        if (log.getType() == MetricsEvent.ACTION_TEXT_SELECTION_START) {
+            String sessionId = Objects.toString(log.getTaggedData(SESSION_ID), "");
+            sessionId = sessionId.substring(sessionId.lastIndexOf("-") + 1);
+            Log.d(LOG_TAG, String.format("New selection session: %s(%s)", tag, sessionId));
         }
 
+        final String type = getLogTypeString(log.getType());
+        final String subType = getLogSubTypeString(log.getSubtype());
+
         final int smartIndices = Integer.parseInt(
                 Objects.toString(log.getTaggedData(SMART_INDICES), ZERO));
         final int smartStart = (short) ((smartIndices & 0xffff0000) >> 16);
@@ -323,11 +356,8 @@
         final int eventStart = (short) ((eventIndices & 0xffff0000) >> 16);
         final int eventEnd = (short) (eventIndices & 0xffff);
 
-        final String entity = Objects.toString(
-                log.getTaggedData(ENTITY_TYPE), TextClassifier.TYPE_UNKNOWN);
-
-        Log.d(LOG_TAG, String.format("%2d: %s, context=%d,%d - old=%d,%d [%s] (%s)",
-                index, event, eventStart, eventEnd, smartStart, smartEnd, entity, tag));
+        Log.d(LOG_TAG, String.format("%2d: %s/%s, context=%d,%d - old=%d,%d (%s)",
+                index, type, subType, eventStart, eventEnd, smartStart, smartEnd, tag));
     }
 
     /**
diff --git a/android/view/textservice/TextServicesManager.java b/android/view/textservice/TextServicesManager.java
index f368c74..8e1f218 100644
--- a/android/view/textservice/TextServicesManager.java
+++ b/android/view/textservice/TextServicesManager.java
@@ -1,213 +1,58 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2016 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
+ * 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
+ *      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.
+ * 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 android.view.textservice;
 
-import android.annotation.SystemService;
-import android.content.Context;
 import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.ServiceManager.ServiceNotFoundException;
-import android.util.Log;
 import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
 
-import com.android.internal.textservice.ITextServicesManager;
-
 import java.util.Locale;
 
 /**
- * System API to the overall text services, which arbitrates interaction between applications
- * and text services.
- *
- * The user can change the current text services in Settings. And also applications can specify
- * the target text services.
- *
- * <h3>Architecture Overview</h3>
- *
- * <p>There are three primary parties involved in the text services
- * framework (TSF) architecture:</p>
- *
- * <ul>
- * <li> The <strong>text services manager</strong> as expressed by this class
- * is the central point of the system that manages interaction between all
- * other parts.  It is expressed as the client-side API here which exists
- * in each application context and communicates with a global system service
- * that manages the interaction across all processes.
- * <li> A <strong>text service</strong> implements a particular
- * interaction model allowing the client application to retrieve information of text.
- * The system binds to the current text service that is in use, causing it to be created and run.
- * <li> Multiple <strong>client applications</strong> arbitrate with the text service
- * manager for connections to text services.
- * </ul>
- *
- * <h3>Text services sessions</h3>
- * <ul>
- * <li>The <strong>spell checker session</strong> is one of the text services.
- * {@link android.view.textservice.SpellCheckerSession}</li>
- * </ul>
- *
+ * A stub class of TextServicesManager for Layout-Lib.
  */
-@SystemService(Context.TEXT_SERVICES_MANAGER_SERVICE)
 public final class TextServicesManager {
-    private static final String TAG = TextServicesManager.class.getSimpleName();
-    private static final boolean DBG = false;
-
-    private static TextServicesManager sInstance;
-
-    private final ITextServicesManager mService;
-
-    private TextServicesManager() throws ServiceNotFoundException {
-        mService = ITextServicesManager.Stub.asInterface(
-                ServiceManager.getServiceOrThrow(Context.TEXT_SERVICES_MANAGER_SERVICE));
-    }
+    private static final TextServicesManager sInstance = new TextServicesManager();
+    private static final SpellCheckerInfo[] EMPTY_SPELL_CHECKER_INFO = new SpellCheckerInfo[0];
 
     /**
      * Retrieve the global TextServicesManager instance, creating it if it doesn't already exist.
      * @hide
      */
     public static TextServicesManager getInstance() {
-        synchronized (TextServicesManager.class) {
-            if (sInstance == null) {
-                try {
-                    sInstance = new TextServicesManager();
-                } catch (ServiceNotFoundException e) {
-                    throw new IllegalStateException(e);
-                }
-            }
-            return sInstance;
-        }
+        return sInstance;
     }
 
-    /**
-     * Returns the language component of a given locale string.
-     */
-    private static String parseLanguageFromLocaleString(String locale) {
-        final int idx = locale.indexOf('_');
-        if (idx < 0) {
-            return locale;
-        } else {
-            return locale.substring(0, idx);
-        }
-    }
-
-    /**
-     * Get a spell checker session for the specified spell checker
-     * @param locale the locale for the spell checker. If {@code locale} is null and
-     * referToSpellCheckerLanguageSettings is true, the locale specified in Settings will be
-     * returned. If {@code locale} is not null and referToSpellCheckerLanguageSettings is true,
-     * the locale specified in Settings will be returned only when it is same as {@code locale}.
-     * Exceptionally, when referToSpellCheckerLanguageSettings is true and {@code locale} is
-     * only language (e.g. "en"), the specified locale in Settings (e.g. "en_US") will be
-     * selected.
-     * @param listener a spell checker session lister for getting results from a spell checker.
-     * @param referToSpellCheckerLanguageSettings if true, the session for one of enabled
-     * languages in settings will be returned.
-     * @return the spell checker session of the spell checker
-     */
     public SpellCheckerSession newSpellCheckerSession(Bundle bundle, Locale locale,
             SpellCheckerSessionListener listener, boolean referToSpellCheckerLanguageSettings) {
-        if (listener == null) {
-            throw new NullPointerException();
-        }
-        if (!referToSpellCheckerLanguageSettings && locale == null) {
-            throw new IllegalArgumentException("Locale should not be null if you don't refer"
-                    + " settings.");
-        }
-
-        if (referToSpellCheckerLanguageSettings && !isSpellCheckerEnabled()) {
-            return null;
-        }
-
-        final SpellCheckerInfo sci;
-        try {
-            sci = mService.getCurrentSpellChecker(null);
-        } catch (RemoteException e) {
-            return null;
-        }
-        if (sci == null) {
-            return null;
-        }
-        SpellCheckerSubtype subtypeInUse = null;
-        if (referToSpellCheckerLanguageSettings) {
-            subtypeInUse = getCurrentSpellCheckerSubtype(true);
-            if (subtypeInUse == null) {
-                return null;
-            }
-            if (locale != null) {
-                final String subtypeLocale = subtypeInUse.getLocale();
-                final String subtypeLanguage = parseLanguageFromLocaleString(subtypeLocale);
-                if (subtypeLanguage.length() < 2 || !locale.getLanguage().equals(subtypeLanguage)) {
-                    return null;
-                }
-            }
-        } else {
-            final String localeStr = locale.toString();
-            for (int i = 0; i < sci.getSubtypeCount(); ++i) {
-                final SpellCheckerSubtype subtype = sci.getSubtypeAt(i);
-                final String tempSubtypeLocale = subtype.getLocale();
-                final String tempSubtypeLanguage = parseLanguageFromLocaleString(tempSubtypeLocale);
-                if (tempSubtypeLocale.equals(localeStr)) {
-                    subtypeInUse = subtype;
-                    break;
-                } else if (tempSubtypeLanguage.length() >= 2 &&
-                        locale.getLanguage().equals(tempSubtypeLanguage)) {
-                    subtypeInUse = subtype;
-                }
-            }
-        }
-        if (subtypeInUse == null) {
-            return null;
-        }
-        final SpellCheckerSession session = new SpellCheckerSession(sci, mService, listener);
-        try {
-            mService.getSpellCheckerService(sci.getId(), subtypeInUse.getLocale(),
-                    session.getTextServicesSessionListener(),
-                    session.getSpellCheckerSessionListener(), bundle);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-        return session;
+        return null;
     }
 
     /**
      * @hide
      */
     public SpellCheckerInfo[] getEnabledSpellCheckers() {
-        try {
-            final SpellCheckerInfo[] retval = mService.getEnabledSpellCheckers();
-            if (DBG) {
-                Log.d(TAG, "getEnabledSpellCheckers: " + (retval != null ? retval.length : "null"));
-            }
-            return retval;
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return EMPTY_SPELL_CHECKER_INFO;
     }
 
     /**
      * @hide
      */
     public SpellCheckerInfo getCurrentSpellChecker() {
-        try {
-            // Passing null as a locale for ICS
-            return mService.getCurrentSpellChecker(null);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return null;
     }
 
     /**
@@ -215,22 +60,13 @@
      */
     public SpellCheckerSubtype getCurrentSpellCheckerSubtype(
             boolean allowImplicitlySelectedSubtype) {
-        try {
-            // Passing null as a locale until we support multiple enabled spell checker subtypes.
-            return mService.getCurrentSpellCheckerSubtype(null, allowImplicitlySelectedSubtype);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return null;
     }
 
     /**
      * @hide
      */
     public boolean isSpellCheckerEnabled() {
-        try {
-            return mService.isSpellCheckerEnabled();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        return false;
     }
 }
diff --git a/android/webkit/WebView.java b/android/webkit/WebView.java
index 419b7b2..202f204 100644
--- a/android/webkit/WebView.java
+++ b/android/webkit/WebView.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2008 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.
@@ -16,3016 +16,223 @@
 
 package android.webkit;
 
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.Widget;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Picture;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.net.http.SslCertificate;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.StrictMode;
-import android.print.PrintDocumentAdapter;
-import android.security.KeyChain;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.DragEvent;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewDebug;
-import android.view.ViewGroup;
-import android.view.ViewHierarchyEncoder;
-import android.view.ViewStructure;
-import android.view.ViewTreeObserver;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.accessibility.AccessibilityNodeProvider;
-import android.view.autofill.AutofillValue;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.view.textclassifier.TextClassifier;
-import android.widget.AbsoluteLayout;
+import com.android.layoutlib.bridge.MockView;
 
-import java.io.BufferedWriter;
-import java.io.File;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-import java.util.Map;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Picture;
+import android.os.Bundle;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.view.View;
 
 /**
- * <p>A View that displays web pages. This class is the basis upon which you
- * can roll your own web browser or simply display some online content within your Activity.
- * It uses the WebKit rendering engine to display
- * web pages and includes methods to navigate forward and backward
- * through a history, zoom in and out, perform text searches and more.
- *
- * <p>Note that, in order for your Activity to access the Internet and load web pages
- * in a WebView, you must add the {@code INTERNET} permissions to your
- * Android Manifest file:
- *
- * <pre>
- * {@code <uses-permission android:name="android.permission.INTERNET" />}
- * </pre>
- *
- * <p>This must be a child of the <a
- * href="{@docRoot}guide/topics/manifest/manifest-element.html">{@code <manifest>}</a>
- * element.
- *
- * <p>For more information, read
- * <a href="{@docRoot}guide/webapps/webview.html">Building Web Apps in WebView</a>.
- *
- * <h3>Basic usage</h3>
- *
- * <p>By default, a WebView provides no browser-like widgets, does not
- * enable JavaScript and web page errors are ignored. If your goal is only
- * to display some HTML as a part of your UI, this is probably fine;
- * the user won't need to interact with the web page beyond reading
- * it, and the web page won't need to interact with the user. If you
- * actually want a full-blown web browser, then you probably want to
- * invoke the Browser application with a URL Intent rather than show it
- * with a WebView. For example:
- * <pre>
- * Uri uri = Uri.parse("https://www.example.com");
- * Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- * startActivity(intent);
- * </pre>
- * <p>See {@link android.content.Intent} for more information.
- *
- * <p>To provide a WebView in your own Activity, include a {@code <WebView>} in your layout,
- * or set the entire Activity window as a WebView during {@link
- * android.app.Activity#onCreate(Bundle) onCreate()}:
- *
- * <pre class="prettyprint">
- * WebView webview = new WebView(this);
- * setContentView(webview);
- * </pre>
- *
- * <p>Then load the desired web page:
- *
- * <pre>
- * // Simplest usage: note that an exception will NOT be thrown
- * // if there is an error loading this page (see below).
- * webview.loadUrl("https://example.com/");
- *
- * // OR, you can also load from an HTML string:
- * String summary = "&lt;html>&lt;body>You scored &lt;b>192&lt;/b> points.&lt;/body>&lt;/html>";
- * webview.loadData(summary, "text/html", null);
- * // ... although note that there are restrictions on what this HTML can do.
- * // See the JavaDocs for {@link #loadData(String,String,String) loadData()} and {@link
- * #loadDataWithBaseURL(String,String,String,String,String) loadDataWithBaseURL()} for more info.
- * </pre>
- *
- * <p>A WebView has several customization points where you can add your
- * own behavior. These are:
- *
- * <ul>
- *   <li>Creating and setting a {@link android.webkit.WebChromeClient} subclass.
- *       This class is called when something that might impact a
- *       browser UI happens, for instance, progress updates and
- *       JavaScript alerts are sent here (see <a
- * href="{@docRoot}guide/developing/debug-tasks.html#DebuggingWebPages">Debugging Tasks</a>).
- *   </li>
- *   <li>Creating and setting a {@link android.webkit.WebViewClient} subclass.
- *       It will be called when things happen that impact the
- *       rendering of the content, eg, errors or form submissions. You
- *       can also intercept URL loading here (via {@link
- * android.webkit.WebViewClient#shouldOverrideUrlLoading(WebView,String)
- * shouldOverrideUrlLoading()}).</li>
- *   <li>Modifying the {@link android.webkit.WebSettings}, such as
- * enabling JavaScript with {@link android.webkit.WebSettings#setJavaScriptEnabled(boolean)
- * setJavaScriptEnabled()}. </li>
- *   <li>Injecting Java objects into the WebView using the
- *       {@link android.webkit.WebView#addJavascriptInterface} method. This
- *       method allows you to inject Java objects into a page's JavaScript
- *       context, so that they can be accessed by JavaScript in the page.</li>
- * </ul>
- *
- * <p>Here's a more complicated example, showing error handling,
- *    settings, and progress notification:
- *
- * <pre class="prettyprint">
- * // Let's display the progress in the activity title bar, like the
- * // browser app does.
- * getWindow().requestFeature(Window.FEATURE_PROGRESS);
- *
- * webview.getSettings().setJavaScriptEnabled(true);
- *
- * final Activity activity = this;
- * webview.setWebChromeClient(new WebChromeClient() {
- *   public void onProgressChanged(WebView view, int progress) {
- *     // Activities and WebViews measure progress with different scales.
- *     // The progress meter will automatically disappear when we reach 100%
- *     activity.setProgress(progress * 1000);
- *   }
- * });
- * webview.setWebViewClient(new WebViewClient() {
- *   public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
- *     Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
- *   }
- * });
- *
- * webview.loadUrl("https://developer.android.com/");
- * </pre>
- *
- * <h3>Zoom</h3>
- *
- * <p>To enable the built-in zoom, set
- * {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)}
- * (introduced in API level {@link android.os.Build.VERSION_CODES#CUPCAKE}).
- *
- * <p>NOTE: Using zoom if either the height or width is set to
- * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} may lead to undefined behavior
- * and should be avoided.
- *
- * <h3>Cookie and window management</h3>
- *
- * <p>For obvious security reasons, your application has its own
- * cache, cookie store etc.&mdash;it does not share the Browser
- * application's data.
- *
- * <p>By default, requests by the HTML to open new windows are
- * ignored. This is {@code true} whether they be opened by JavaScript or by
- * the target attribute on a link. You can customize your
- * {@link WebChromeClient} to provide your own behavior for opening multiple windows,
- * and render them in whatever manner you want.
- *
- * <p>The standard behavior for an Activity is to be destroyed and
- * recreated when the device orientation or any other configuration changes. This will cause
- * the WebView to reload the current page. If you don't want that, you
- * can set your Activity to handle the {@code orientation} and {@code keyboardHidden}
- * changes, and then just leave the WebView alone. It'll automatically
- * re-orient itself as appropriate. Read <a
- * href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime Changes</a> for
- * more information about how to handle configuration changes during runtime.
- *
- *
- * <h3>Building web pages to support different screen densities</h3>
- *
- * <p>The screen density of a device is based on the screen resolution. A screen with low density
- * has fewer available pixels per inch, where a screen with high density
- * has more &mdash; sometimes significantly more &mdash; pixels per inch. The density of a
- * screen is important because, other things being equal, a UI element (such as a button) whose
- * height and width are defined in terms of screen pixels will appear larger on the lower density
- * screen and smaller on the higher density screen.
- * For simplicity, Android collapses all actual screen densities into three generalized densities:
- * high, medium, and low.
- * <p>By default, WebView scales a web page so that it is drawn at a size that matches the default
- * appearance on a medium density screen. So, it applies 1.5x scaling on a high density screen
- * (because its pixels are smaller) and 0.75x scaling on a low density screen (because its pixels
- * are bigger).
- * Starting with API level {@link android.os.Build.VERSION_CODES#ECLAIR}, WebView supports DOM, CSS,
- * and meta tag features to help you (as a web developer) target screens with different screen
- * densities.
- * <p>Here's a summary of the features you can use to handle different screen densities:
- * <ul>
- * <li>The {@code window.devicePixelRatio} DOM property. The value of this property specifies the
- * default scaling factor used for the current device. For example, if the value of {@code
- * window.devicePixelRatio} is "1.0", then the device is considered a medium density (mdpi) device
- * and default scaling is not applied to the web page; if the value is "1.5", then the device is
- * considered a high density device (hdpi) and the page content is scaled 1.5x; if the
- * value is "0.75", then the device is considered a low density device (ldpi) and the content is
- * scaled 0.75x.</li>
- * <li>The {@code -webkit-device-pixel-ratio} CSS media query. Use this to specify the screen
- * densities for which this style sheet is to be used. The corresponding value should be either
- * "0.75", "1", or "1.5", to indicate that the styles are for devices with low density, medium
- * density, or high density screens, respectively. For example:
- * <pre>
- * &lt;link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio:1.5)" href="hdpi.css" /&gt;</pre>
- * <p>The {@code hdpi.css} stylesheet is only used for devices with a screen pixel ration of 1.5,
- * which is the high density pixel ratio.
- * </li>
- * </ul>
- *
- * <h3>HTML5 Video support</h3>
- *
- * <p>In order to support inline HTML5 video in your application you need to have hardware
- * acceleration turned on.
- *
- * <h3>Full screen support</h3>
- *
- * <p>In order to support full screen &mdash; for video or other HTML content &mdash; you need to set a
- * {@link android.webkit.WebChromeClient} and implement both
- * {@link WebChromeClient#onShowCustomView(View, WebChromeClient.CustomViewCallback)}
- * and {@link WebChromeClient#onHideCustomView()}. If the implementation of either of these two methods is
- * missing then the web contents will not be allowed to enter full screen. Optionally you can implement
- * {@link WebChromeClient#getVideoLoadingProgressView()} to customize the View displayed whilst a video
- * is loading.
- *
- * <h3>HTML5 Geolocation API support</h3>
- *
- * <p>For applications targeting Android N and later releases
- * (API level > {@link android.os.Build.VERSION_CODES#M}) the geolocation api is only supported on
- * secure origins such as https. For such applications requests to geolocation api on non-secure
- * origins are automatically denied without invoking the corresponding
- * {@link WebChromeClient#onGeolocationPermissionsShowPrompt(String, GeolocationPermissions.Callback)}
- * method.
- *
- * <h3>Layout size</h3>
- * <p>
- * It is recommended to set the WebView layout height to a fixed value or to
- * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT} instead of using
- * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}.
- * When using {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
- * for the height none of the WebView's parents should use a
- * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} layout height since that could result in
- * incorrect sizing of the views.
- *
- * <p>Setting the WebView's height to {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
- * enables the following behaviors:
- * <ul>
- * <li>The HTML body layout height is set to a fixed value. This means that elements with a height
- * relative to the HTML body may not be sized correctly. </li>
- * <li>For applications targeting {@link android.os.Build.VERSION_CODES#KITKAT} and earlier SDKs the
- * HTML viewport meta tag will be ignored in order to preserve backwards compatibility. </li>
- * </ul>
- *
- * <p>
- * Using a layout width of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} is not
- * supported. If such a width is used the WebView will attempt to use the width of the parent
- * instead.
- *
- * <h3>Metrics</h3>
- *
- * <p>
- * WebView may upload anonymous diagnostic data to Google when the user has consented. This data
- * helps Google improve WebView. Data is collected on a per-app basis for each app which has
- * instantiated a WebView. An individual app can opt out of this feature by putting the following
- * tag in its manifest:
- * <pre>
- * &lt;meta-data android:name="android.webkit.WebView.MetricsOptOut"
- *            android:value="true" /&gt;
- * </pre>
- * <p>
- * Data will only be uploaded for a given app if the user has consented AND the app has not opted
- * out.
- *
- * <h3>Safe Browsing</h3>
- *
- * <p>
- * If Safe Browsing is enabled, WebView will block malicious URLs and present a warning UI to the
- * user to allow them to navigate back safely or proceed to the malicious page.
- * <p>
- * The recommended way for apps to enable the feature is putting the following tag in the manifest:
- * <p>
- * <pre>
- * &lt;meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
- *            android:value="true" /&gt;
- * </pre>
+ * Mock version of the WebView.
+ * Only non override public methods from the real WebView have been added in there.
+ * Methods that take an unknown class as parameter or as return object, have been removed for now.
+ * 
+ * TODO: generate automatically.
  *
  */
-// Implementation notes.
-// The WebView is a thin API class that delegates its public API to a backend WebViewProvider
-// class instance. WebView extends {@link AbsoluteLayout} for backward compatibility reasons.
-// Methods are delegated to the provider implementation: all public API methods introduced in this
-// file are fully delegated, whereas public and protected methods from the View base classes are
-// only delegated where a specific need exists for them to do so.
-@Widget
-public class WebView extends AbsoluteLayout
-        implements ViewTreeObserver.OnGlobalFocusChangeListener,
-        ViewGroup.OnHierarchyChangeListener, ViewDebug.HierarchyHandler {
-
-    private static final String LOGTAG = "WebView";
-
-    // Throwing an exception for incorrect thread usage if the
-    // build target is JB MR2 or newer. Defaults to false, and is
-    // set in the WebView constructor.
-    private static volatile boolean sEnforceThreadChecking = false;
+public class WebView extends MockView {
 
     /**
-     *  Transportation object for returning WebView across thread boundaries.
-     */
-    public class WebViewTransport {
-        private WebView mWebview;
-
-        /**
-         * Sets the WebView to the transportation object.
-         *
-         * @param webview the WebView to transport
-         */
-        public synchronized void setWebView(WebView webview) {
-            mWebview = webview;
-        }
-
-        /**
-         * Gets the WebView object.
-         *
-         * @return the transported WebView object
-         */
-        public synchronized WebView getWebView() {
-            return mWebview;
-        }
-    }
-
-    /**
-     * URI scheme for telephone number.
-     */
-    public static final String SCHEME_TEL = "tel:";
-    /**
-     * URI scheme for email address.
-     */
-    public static final String SCHEME_MAILTO = "mailto:";
-    /**
-     * URI scheme for map address.
-     */
-    public static final String SCHEME_GEO = "geo:0,0?q=";
-
-    /**
-     * Interface to listen for find results.
-     */
-    public interface FindListener {
-        /**
-         * Notifies the listener about progress made by a find operation.
-         *
-         * @param activeMatchOrdinal the zero-based ordinal of the currently selected match
-         * @param numberOfMatches how many matches have been found
-         * @param isDoneCounting whether the find operation has actually completed. The listener
-         *                       may be notified multiple times while the
-         *                       operation is underway, and the numberOfMatches
-         *                       value should not be considered final unless
-         *                       isDoneCounting is {@code true}.
-         */
-        public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
-            boolean isDoneCounting);
-    }
-
-    /**
-     * Callback interface supplied to {@link #postVisualStateCallback} for receiving
-     * notifications about the visual state.
-     */
-    public static abstract class VisualStateCallback {
-        /**
-         * Invoked when the visual state is ready to be drawn in the next {@link #onDraw}.
-         *
-         * @param requestId The identifier passed to {@link #postVisualStateCallback} when this
-         *                  callback was posted.
-         */
-        public abstract void onComplete(long requestId);
-    }
-
-    /**
-     * Interface to listen for new pictures as they change.
-     *
-     * @deprecated This interface is now obsolete.
-     */
-    @Deprecated
-    public interface PictureListener {
-        /**
-         * Used to provide notification that the WebView's picture has changed.
-         * See {@link WebView#capturePicture} for details of the picture.
-         *
-         * @param view the WebView that owns the picture
-         * @param picture the new picture. Applications targeting
-         *     {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} or above
-         *     will always receive a {@code null} Picture.
-         * @deprecated Deprecated due to internal changes.
-         */
-        @Deprecated
-        public void onNewPicture(WebView view, Picture picture);
-    }
-
-    public static class HitTestResult {
-        /**
-         * Default HitTestResult, where the target is unknown.
-         */
-        public static final int UNKNOWN_TYPE = 0;
-        /**
-         * @deprecated This type is no longer used.
-         */
-        @Deprecated
-        public static final int ANCHOR_TYPE = 1;
-        /**
-         * HitTestResult for hitting a phone number.
-         */
-        public static final int PHONE_TYPE = 2;
-        /**
-         * HitTestResult for hitting a map address.
-         */
-        public static final int GEO_TYPE = 3;
-        /**
-         * HitTestResult for hitting an email address.
-         */
-        public static final int EMAIL_TYPE = 4;
-        /**
-         * HitTestResult for hitting an HTML::img tag.
-         */
-        public static final int IMAGE_TYPE = 5;
-        /**
-         * @deprecated This type is no longer used.
-         */
-        @Deprecated
-        public static final int IMAGE_ANCHOR_TYPE = 6;
-        /**
-         * HitTestResult for hitting a HTML::a tag with src=http.
-         */
-        public static final int SRC_ANCHOR_TYPE = 7;
-        /**
-         * HitTestResult for hitting a HTML::a tag with src=http + HTML::img.
-         */
-        public static final int SRC_IMAGE_ANCHOR_TYPE = 8;
-        /**
-         * HitTestResult for hitting an edit text area.
-         */
-        public static final int EDIT_TEXT_TYPE = 9;
-
-        private int mType;
-        private String mExtra;
-
-        /**
-         * @hide Only for use by WebViewProvider implementations
-         */
-        @SystemApi
-        public HitTestResult() {
-            mType = UNKNOWN_TYPE;
-        }
-
-        /**
-         * @hide Only for use by WebViewProvider implementations
-         */
-        @SystemApi
-        public void setType(int type) {
-            mType = type;
-        }
-
-        /**
-         * @hide Only for use by WebViewProvider implementations
-         */
-        @SystemApi
-        public void setExtra(String extra) {
-            mExtra = extra;
-        }
-
-        /**
-         * Gets the type of the hit test result. See the XXX_TYPE constants
-         * defined in this class.
-         *
-         * @return the type of the hit test result
-         */
-        public int getType() {
-            return mType;
-        }
-
-        /**
-         * Gets additional type-dependant information about the result. See
-         * {@link WebView#getHitTestResult()} for details. May either be {@code null}
-         * or contain extra information about this result.
-         *
-         * @return additional type-dependant information about the result
-         */
-        public String getExtra() {
-            return mExtra;
-        }
-    }
-
-    /**
-     * Constructs a new WebView with a Context object.
-     *
-     * @param context a Context object used to access application assets
+     * Construct a new WebView with a Context object.
+     * @param context A Context object used to access application assets.
      */
     public WebView(Context context) {
         this(context, null);
     }
 
     /**
-     * Constructs a new WebView with layout parameters.
-     *
-     * @param context a Context object used to access application assets
-     * @param attrs an AttributeSet passed to our parent
+     * Construct a new WebView with layout parameters.
+     * @param context A Context object used to access application assets.
+     * @param attrs An AttributeSet passed to our parent.
      */
     public WebView(Context context, AttributeSet attrs) {
         this(context, attrs, com.android.internal.R.attr.webViewStyle);
     }
 
     /**
-     * Constructs a new WebView with layout parameters and a default style.
-     *
-     * @param context a Context object used to access application assets
-     * @param attrs an AttributeSet passed to our parent
-     * @param defStyleAttr an attribute in the current theme that contains a
-     *        reference to a style resource that supplies default values for
-     *        the view. Can be 0 to not look for defaults.
+     * Construct a new WebView with layout parameters and a default style.
+     * @param context A Context object used to access application assets.
+     * @param attrs An AttributeSet passed to our parent.
+     * @param defStyle The default style resource ID.
      */
-    public WebView(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
+    public WebView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
     }
-
-    /**
-     * Constructs a new WebView with layout parameters and a default style.
-     *
-     * @param context a Context object used to access application assets
-     * @param attrs an AttributeSet passed to our parent
-     * @param defStyleAttr an attribute in the current theme that contains a
-     *        reference to a style resource that supplies default values for
-     *        the view. Can be 0 to not look for defaults.
-     * @param defStyleRes a resource identifier of a style resource that
-     *        supplies default values for the view, used only if
-     *        defStyleAttr is 0 or can not be found in the theme. Can be 0
-     *        to not look for defaults.
-     */
-    public WebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        this(context, attrs, defStyleAttr, defStyleRes, null, false);
-    }
-
-    /**
-     * Constructs a new WebView with layout parameters and a default style.
-     *
-     * @param context a Context object used to access application assets
-     * @param attrs an AttributeSet passed to our parent
-     * @param defStyleAttr an attribute in the current theme that contains a
-     *        reference to a style resource that supplies default values for
-     *        the view. Can be 0 to not look for defaults.
-     * @param privateBrowsing whether this WebView will be initialized in
-     *                        private mode
-     *
-     * @deprecated Private browsing is no longer supported directly via
-     * WebView and will be removed in a future release. Prefer using
-     * {@link WebSettings}, {@link WebViewDatabase}, {@link CookieManager}
-     * and {@link WebStorage} for fine-grained control of privacy data.
-     */
-    @Deprecated
-    public WebView(Context context, AttributeSet attrs, int defStyleAttr,
-            boolean privateBrowsing) {
-        this(context, attrs, defStyleAttr, 0, null, privateBrowsing);
-    }
-
-    /**
-     * Constructs a new WebView with layout parameters, a default style and a set
-     * of custom JavaScript interfaces to be added to this WebView at initialization
-     * time. This guarantees that these interfaces will be available when the JS
-     * context is initialized.
-     *
-     * @param context a Context object used to access application assets
-     * @param attrs an AttributeSet passed to our parent
-     * @param defStyleAttr an attribute in the current theme that contains a
-     *        reference to a style resource that supplies default values for
-     *        the view. Can be 0 to not look for defaults.
-     * @param javaScriptInterfaces a Map of interface names, as keys, and
-     *                             object implementing those interfaces, as
-     *                             values
-     * @param privateBrowsing whether this WebView will be initialized in
-     *                        private mode
-     * @hide This is used internally by dumprendertree, as it requires the JavaScript interfaces to
-     *       be added synchronously, before a subsequent loadUrl call takes effect.
-     */
-    protected WebView(Context context, AttributeSet attrs, int defStyleAttr,
-            Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
-        this(context, attrs, defStyleAttr, 0, javaScriptInterfaces, privateBrowsing);
-    }
-
-    /**
-     * @hide
-     */
-    @SuppressWarnings("deprecation")  // for super() call into deprecated base class constructor.
-    protected WebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes,
-            Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-
-        // WebView is important by default, unless app developer overrode attribute.
-        if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
-            setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
-        }
-
-        if (context == null) {
-            throw new IllegalArgumentException("Invalid context argument");
-        }
-        sEnforceThreadChecking = context.getApplicationInfo().targetSdkVersion >=
-                Build.VERSION_CODES.JELLY_BEAN_MR2;
-        checkThread();
-
-        ensureProviderCreated();
-        mProvider.init(javaScriptInterfaces, privateBrowsing);
-        // Post condition of creating a webview is the CookieSyncManager.getInstance() is allowed.
-        CookieSyncManager.setGetInstanceIsAllowed();
-    }
-
-    /**
-     * Specifies whether the horizontal scrollbar has overlay style.
-     *
-     * @deprecated This method has no effect.
-     * @param overlay {@code true} if horizontal scrollbar should have overlay style
-     */
-    @Deprecated
+    
+    // START FAKE PUBLIC METHODS
+    
     public void setHorizontalScrollbarOverlay(boolean overlay) {
     }
 
-    /**
-     * Specifies whether the vertical scrollbar has overlay style.
-     *
-     * @deprecated This method has no effect.
-     * @param overlay {@code true} if vertical scrollbar should have overlay style
-     */
-    @Deprecated
     public void setVerticalScrollbarOverlay(boolean overlay) {
     }
 
-    /**
-     * Gets whether horizontal scrollbar has overlay style.
-     *
-     * @deprecated This method is now obsolete.
-     * @return {@code true}
-     */
-    @Deprecated
     public boolean overlayHorizontalScrollbar() {
-        // The old implementation defaulted to true, so return true for consistency
-        return true;
-    }
-
-    /**
-     * Gets whether vertical scrollbar has overlay style.
-     *
-     * @deprecated This method is now obsolete.
-     * @return {@code false}
-     */
-    @Deprecated
-    public boolean overlayVerticalScrollbar() {
-        // The old implementation defaulted to false, so return false for consistency
         return false;
     }
 
-    /**
-     * Gets the visible height (in pixels) of the embedded title bar (if any).
-     *
-     * @deprecated This method is now obsolete.
-     * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
-     */
-    @Deprecated
-    public int getVisibleTitleHeight() {
-        checkThread();
-        return mProvider.getVisibleTitleHeight();
+    public boolean overlayVerticalScrollbar() {
+        return false;
     }
 
-    /**
-     * Gets the SSL certificate for the main top-level page or {@code null} if there is
-     * no certificate (the site is not secure).
-     *
-     * @return the SSL certificate for the main top-level page
-     */
-    public SslCertificate getCertificate() {
-        checkThread();
-        return mProvider.getCertificate();
-    }
-
-    /**
-     * Sets the SSL certificate for the main top-level page.
-     *
-     * @deprecated Calling this function has no useful effect, and will be
-     * ignored in future releases.
-     */
-    @Deprecated
-    public void setCertificate(SslCertificate certificate) {
-        checkThread();
-        mProvider.setCertificate(certificate);
-    }
-
-    //-------------------------------------------------------------------------
-    // Methods called by activity
-    //-------------------------------------------------------------------------
-
-    /**
-     * Sets a username and password pair for the specified host. This data is
-     * used by the WebView to autocomplete username and password fields in web
-     * forms. Note that this is unrelated to the credentials used for HTTP
-     * authentication.
-     *
-     * @param host the host that required the credentials
-     * @param username the username for the given host
-     * @param password the password for the given host
-     * @see WebViewDatabase#clearUsernamePassword
-     * @see WebViewDatabase#hasUsernamePassword
-     * @deprecated Saving passwords in WebView will not be supported in future versions.
-     */
-    @Deprecated
     public void savePassword(String host, String username, String password) {
-        checkThread();
-        mProvider.savePassword(host, username, password);
     }
 
-    /**
-     * Stores HTTP authentication credentials for a given host and realm to the {@link WebViewDatabase}
-     * instance.
-     *
-     * @param host the host to which the credentials apply
-     * @param realm the realm to which the credentials apply
-     * @param username the username
-     * @param password the password
-     * @deprecated Use {@link WebViewDatabase#setHttpAuthUsernamePassword} instead
-     */
-    @Deprecated
     public void setHttpAuthUsernamePassword(String host, String realm,
             String username, String password) {
-        checkThread();
-        mProvider.setHttpAuthUsernamePassword(host, realm, username, password);
     }
 
-    /**
-     * Retrieves HTTP authentication credentials for a given host and realm from the {@link
-     * WebViewDatabase} instance.
-     * @param host the host to which the credentials apply
-     * @param realm the realm to which the credentials apply
-     * @return the credentials as a String array, if found. The first element
-     *         is the username and the second element is the password. {@code null} if
-     *         no credentials are found.
-     * @deprecated Use {@link WebViewDatabase#getHttpAuthUsernamePassword} instead
-     */
-    @Deprecated
     public String[] getHttpAuthUsernamePassword(String host, String realm) {
-        checkThread();
-        return mProvider.getHttpAuthUsernamePassword(host, realm);
+        return null;
     }
 
-    /**
-     * Destroys the internal state of this WebView. This method should be called
-     * after this WebView has been removed from the view system. No other
-     * methods may be called on this WebView after destroy.
-     */
     public void destroy() {
-        checkThread();
-        mProvider.destroy();
     }
 
-    /**
-     * Enables platform notifications of data state and proxy changes.
-     * Notifications are enabled by default.
-     *
-     * @deprecated This method is now obsolete.
-     * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
-     */
-    @Deprecated
     public static void enablePlatformNotifications() {
-        // noop
     }
 
-    /**
-     * Disables platform notifications of data state and proxy changes.
-     * Notifications are enabled by default.
-     *
-     * @deprecated This method is now obsolete.
-     * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
-     */
-    @Deprecated
     public static void disablePlatformNotifications() {
-        // noop
     }
 
-    /**
-     * Used only by internal tests to free up memory.
-     *
-     * @hide
-     */
-    public static void freeMemoryForTests() {
-        getFactory().getStatics().freeMemoryForTests();
-    }
-
-    /**
-     * Informs WebView of the network state. This is used to set
-     * the JavaScript property window.navigator.isOnline and
-     * generates the online/offline event as specified in HTML5, sec. 5.7.7
-     *
-     * @param networkUp a boolean indicating if network is available
-     */
-    public void setNetworkAvailable(boolean networkUp) {
-        checkThread();
-        mProvider.setNetworkAvailable(networkUp);
-    }
-
-    /**
-     * Saves the state of this WebView used in
-     * {@link android.app.Activity#onSaveInstanceState}. Please note that this
-     * method no longer stores the display data for this WebView. The previous
-     * behavior could potentially leak files if {@link #restoreState} was never
-     * called.
-     *
-     * @param outState the Bundle to store this WebView's state
-     * @return the same copy of the back/forward list used to save the state. If
-     *         saveState fails, the returned list will be {@code null}.
-     */
-    public WebBackForwardList saveState(Bundle outState) {
-        checkThread();
-        return mProvider.saveState(outState);
-    }
-
-    /**
-     * Saves the current display data to the Bundle given. Used in conjunction
-     * with {@link #saveState}.
-     * @param b a Bundle to store the display data
-     * @param dest the file to store the serialized picture data. Will be
-     *             overwritten with this WebView's picture data.
-     * @return {@code true} if the picture was successfully saved
-     * @deprecated This method is now obsolete.
-     * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
-     */
-    @Deprecated
-    public boolean savePicture(Bundle b, final File dest) {
-        checkThread();
-        return mProvider.savePicture(b, dest);
-    }
-
-    /**
-     * Restores the display data that was saved in {@link #savePicture}. Used in
-     * conjunction with {@link #restoreState}. Note that this will not work if
-     * this WebView is hardware accelerated.
-     *
-     * @param b a Bundle containing the saved display data
-     * @param src the file where the picture data was stored
-     * @return {@code true} if the picture was successfully restored
-     * @deprecated This method is now obsolete.
-     * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
-     */
-    @Deprecated
-    public boolean restorePicture(Bundle b, File src) {
-        checkThread();
-        return mProvider.restorePicture(b, src);
-    }
-
-    /**
-     * Restores the state of this WebView from the given Bundle. This method is
-     * intended for use in {@link android.app.Activity#onRestoreInstanceState}
-     * and should be called to restore the state of this WebView. If
-     * it is called after this WebView has had a chance to build state (load
-     * pages, create a back/forward list, etc.) there may be undesirable
-     * side-effects. Please note that this method no longer restores the
-     * display data for this WebView.
-     *
-     * @param inState the incoming Bundle of state
-     * @return the restored back/forward list or {@code null} if restoreState failed
-     */
-    public WebBackForwardList restoreState(Bundle inState) {
-        checkThread();
-        return mProvider.restoreState(inState);
-    }
-
-    /**
-     * Loads the given URL with the specified additional HTTP headers.
-     * <p>
-     * Also see compatibility note on {@link #evaluateJavascript}.
-     *
-     * @param url the URL of the resource to load
-     * @param additionalHttpHeaders the additional headers to be used in the
-     *            HTTP request for this URL, specified as a map from name to
-     *            value. Note that if this map contains any of the headers
-     *            that are set by default by this WebView, such as those
-     *            controlling caching, accept types or the User-Agent, their
-     *            values may be overridden by this WebView's defaults.
-     */
-    public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
-        checkThread();
-        mProvider.loadUrl(url, additionalHttpHeaders);
-    }
-
-    /**
-     * Loads the given URL.
-     * <p>
-     * Also see compatibility note on {@link #evaluateJavascript}.
-     *
-     * @param url the URL of the resource to load
-     */
     public void loadUrl(String url) {
-        checkThread();
-        mProvider.loadUrl(url);
     }
 
-    /**
-     * Loads the URL with postData using "POST" method into this WebView. If url
-     * is not a network URL, it will be loaded with {@link #loadUrl(String)}
-     * instead, ignoring the postData param.
-     *
-     * @param url the URL of the resource to load
-     * @param postData the data will be passed to "POST" request, which must be
-     *     be "application/x-www-form-urlencoded" encoded.
-     */
-    public void postUrl(String url, byte[] postData) {
-        checkThread();
-        if (URLUtil.isNetworkUrl(url)) {
-            mProvider.postUrl(url, postData);
-        } else {
-            mProvider.loadUrl(url);
-        }
-    }
-
-    /**
-     * Loads the given data into this WebView using a 'data' scheme URL.
-     * <p>
-     * Note that JavaScript's same origin policy means that script running in a
-     * page loaded using this method will be unable to access content loaded
-     * using any scheme other than 'data', including 'http(s)'. To avoid this
-     * restriction, use {@link
-     * #loadDataWithBaseURL(String,String,String,String,String)
-     * loadDataWithBaseURL()} with an appropriate base URL.
-     * <p>
-     * The encoding parameter specifies whether the data is base64 or URL
-     * encoded. If the data is base64 encoded, the value of the encoding
-     * parameter must be 'base64'. For all other values of the parameter,
-     * including {@code null}, it is assumed that the data uses ASCII encoding for
-     * octets inside the range of safe URL characters and use the standard %xx
-     * hex encoding of URLs for octets outside that range. For example, '#',
-     * '%', '\', '?' should be replaced by %23, %25, %27, %3f respectively.
-     * <p>
-     * The 'data' scheme URL formed by this method uses the default US-ASCII
-     * charset. If you need need to set a different charset, you should form a
-     * 'data' scheme URL which explicitly specifies a charset parameter in the
-     * mediatype portion of the URL and call {@link #loadUrl(String)} instead.
-     * Note that the charset obtained from the mediatype portion of a data URL
-     * always overrides that specified in the HTML or XML document itself.
-     *
-     * @param data a String of data in the given encoding
-     * @param mimeType the MIME type of the data, e.g. 'text/html'
-     * @param encoding the encoding of the data
-     */
     public void loadData(String data, String mimeType, String encoding) {
-        checkThread();
-        mProvider.loadData(data, mimeType, encoding);
     }
 
-    /**
-     * Loads the given data into this WebView, using baseUrl as the base URL for
-     * the content. The base URL is used both to resolve relative URLs and when
-     * applying JavaScript's same origin policy. The historyUrl is used for the
-     * history entry.
-     * <p>
-     * Note that content specified in this way can access local device files
-     * (via 'file' scheme URLs) only if baseUrl specifies a scheme other than
-     * 'http', 'https', 'ftp', 'ftps', 'about' or 'javascript'.
-     * <p>
-     * If the base URL uses the data scheme, this method is equivalent to
-     * calling {@link #loadData(String,String,String) loadData()} and the
-     * historyUrl is ignored, and the data will be treated as part of a data: URL.
-     * If the base URL uses any other scheme, then the data will be loaded into
-     * the WebView as a plain string (i.e. not part of a data URL) and any URL-encoded
-     * entities in the string will not be decoded.
-     * <p>
-     * Note that the baseUrl is sent in the 'Referer' HTTP header when
-     * requesting subresources (images, etc.) of the page loaded using this method.
-     *
-     * @param baseUrl the URL to use as the page's base URL. If {@code null} defaults to
-     *                'about:blank'.
-     * @param data a String of data in the given encoding
-     * @param mimeType the MIMEType of the data, e.g. 'text/html'. If {@code null},
-     *                 defaults to 'text/html'.
-     * @param encoding the encoding of the data
-     * @param historyUrl the URL to use as the history entry. If {@code null} defaults
-     *                   to 'about:blank'. If non-null, this must be a valid URL.
-     */
     public void loadDataWithBaseURL(String baseUrl, String data,
-            String mimeType, String encoding, String historyUrl) {
-        checkThread();
-        mProvider.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
+            String mimeType, String encoding, String failUrl) {
     }
 
-    /**
-     * Asynchronously evaluates JavaScript in the context of the currently displayed page.
-     * If non-null, |resultCallback| will be invoked with any result returned from that
-     * execution. This method must be called on the UI thread and the callback will
-     * be made on the UI thread.
-     * <p>
-     * Compatibility note. Applications targeting {@link android.os.Build.VERSION_CODES#N} or
-     * later, JavaScript state from an empty WebView is no longer persisted across navigations like
-     * {@link #loadUrl(String)}. For example, global variables and functions defined before calling
-     * {@link #loadUrl(String)} will not exist in the loaded page. Applications should use
-     * {@link #addJavascriptInterface} instead to persist JavaScript objects across navigations.
-     *
-     * @param script the JavaScript to execute.
-     * @param resultCallback A callback to be invoked when the script execution
-     *                       completes with the result of the execution (if any).
-     *                       May be {@code null} if no notification of the result is required.
-     */
-    public void evaluateJavascript(String script, ValueCallback<String> resultCallback) {
-        checkThread();
-        mProvider.evaluateJavaScript(script, resultCallback);
-    }
-
-    /**
-     * Saves the current view as a web archive.
-     *
-     * @param filename the filename where the archive should be placed
-     */
-    public void saveWebArchive(String filename) {
-        checkThread();
-        mProvider.saveWebArchive(filename);
-    }
-
-    /**
-     * Saves the current view as a web archive.
-     *
-     * @param basename the filename where the archive should be placed
-     * @param autoname if {@code false}, takes basename to be a file. If {@code true}, basename
-     *                 is assumed to be a directory in which a filename will be
-     *                 chosen according to the URL of the current page.
-     * @param callback called after the web archive has been saved. The
-     *                 parameter for onReceiveValue will either be the filename
-     *                 under which the file was saved, or {@code null} if saving the
-     *                 file failed.
-     */
-    public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
-        checkThread();
-        mProvider.saveWebArchive(basename, autoname, callback);
-    }
-
-    /**
-     * Stops the current load.
-     */
     public void stopLoading() {
-        checkThread();
-        mProvider.stopLoading();
     }
 
-    /**
-     * Reloads the current URL.
-     */
     public void reload() {
-        checkThread();
-        mProvider.reload();
     }
 
-    /**
-     * Gets whether this WebView has a back history item.
-     *
-     * @return {@code true} iff this WebView has a back history item
-     */
     public boolean canGoBack() {
-        checkThread();
-        return mProvider.canGoBack();
+        return false;
     }
 
-    /**
-     * Goes back in the history of this WebView.
-     */
     public void goBack() {
-        checkThread();
-        mProvider.goBack();
     }
 
-    /**
-     * Gets whether this WebView has a forward history item.
-     *
-     * @return {@code true} iff this WebView has a forward history item
-     */
     public boolean canGoForward() {
-        checkThread();
-        return mProvider.canGoForward();
+        return false;
     }
 
-    /**
-     * Goes forward in the history of this WebView.
-     */
     public void goForward() {
-        checkThread();
-        mProvider.goForward();
     }
 
-    /**
-     * Gets whether the page can go back or forward the given
-     * number of steps.
-     *
-     * @param steps the negative or positive number of steps to move the
-     *              history
-     */
     public boolean canGoBackOrForward(int steps) {
-        checkThread();
-        return mProvider.canGoBackOrForward(steps);
+        return false;
     }
 
-    /**
-     * Goes to the history item that is the number of steps away from
-     * the current item. Steps is negative if backward and positive
-     * if forward.
-     *
-     * @param steps the number of steps to take back or forward in the back
-     *              forward list
-     */
     public void goBackOrForward(int steps) {
-        checkThread();
-        mProvider.goBackOrForward(steps);
     }
 
-    /**
-     * Gets whether private browsing is enabled in this WebView.
-     */
-    public boolean isPrivateBrowsingEnabled() {
-        checkThread();
-        return mProvider.isPrivateBrowsingEnabled();
-    }
-
-    /**
-     * Scrolls the contents of this WebView up by half the view size.
-     *
-     * @param top {@code true} to jump to the top of the page
-     * @return {@code true} if the page was scrolled
-     */
     public boolean pageUp(boolean top) {
-        checkThread();
-        return mProvider.pageUp(top);
+        return false;
     }
-
-    /**
-     * Scrolls the contents of this WebView down by half the page size.
-     *
-     * @param bottom {@code true} to jump to bottom of page
-     * @return {@code true} if the page was scrolled
-     */
+    
     public boolean pageDown(boolean bottom) {
-        checkThread();
-        return mProvider.pageDown(bottom);
+        return false;
     }
 
-    /**
-     * Posts a {@link VisualStateCallback}, which will be called when
-     * the current state of the WebView is ready to be drawn.
-     *
-     * <p>Because updates to the DOM are processed asynchronously, updates to the DOM may not
-     * immediately be reflected visually by subsequent {@link WebView#onDraw} invocations. The
-     * {@link VisualStateCallback} provides a mechanism to notify the caller when the contents of
-     * the DOM at the current time are ready to be drawn the next time the {@link WebView}
-     * draws.
-     *
-     * <p>The next draw after the callback completes is guaranteed to reflect all the updates to the
-     * DOM up to the point at which the {@link VisualStateCallback} was posted, but it may also
-     * contain updates applied after the callback was posted.
-     *
-     * <p>The state of the DOM covered by this API includes the following:
-     * <ul>
-     * <li>primitive HTML elements (div, img, span, etc..)</li>
-     * <li>images</li>
-     * <li>CSS animations</li>
-     * <li>WebGL</li>
-     * <li>canvas</li>
-     * </ul>
-     * It does not include the state of:
-     * <ul>
-     * <li>the video tag</li>
-     * </ul>
-     *
-     * <p>To guarantee that the {@link WebView} will successfully render the first frame
-     * after the {@link VisualStateCallback#onComplete} method has been called a set of conditions
-     * must be met:
-     * <ul>
-     * <li>If the {@link WebView}'s visibility is set to {@link View#VISIBLE VISIBLE} then
-     * the {@link WebView} must be attached to the view hierarchy.</li>
-     * <li>If the {@link WebView}'s visibility is set to {@link View#INVISIBLE INVISIBLE}
-     * then the {@link WebView} must be attached to the view hierarchy and must be made
-     * {@link View#VISIBLE VISIBLE} from the {@link VisualStateCallback#onComplete} method.</li>
-     * <li>If the {@link WebView}'s visibility is set to {@link View#GONE GONE} then the
-     * {@link WebView} must be attached to the view hierarchy and its
-     * {@link AbsoluteLayout.LayoutParams LayoutParams}'s width and height need to be set to fixed
-     * values and must be made {@link View#VISIBLE VISIBLE} from the
-     * {@link VisualStateCallback#onComplete} method.</li>
-     * </ul>
-     *
-     * <p>When using this API it is also recommended to enable pre-rasterization if the {@link
-     * WebView} is off screen to avoid flickering. See {@link WebSettings#setOffscreenPreRaster} for
-     * more details and do consider its caveats.
-     *
-     * @param requestId An id that will be returned in the callback to allow callers to match
-     *                  requests with callbacks.
-     * @param callback  The callback to be invoked.
-     */
-    public void postVisualStateCallback(long requestId, VisualStateCallback callback) {
-        checkThread();
-        mProvider.insertVisualStateCallback(requestId, callback);
-    }
-
-    /**
-     * Clears this WebView so that onDraw() will draw nothing but white background,
-     * and onMeasure() will return 0 if MeasureSpec is not MeasureSpec.EXACTLY.
-     * @deprecated Use WebView.loadUrl("about:blank") to reliably reset the view state
-     *             and release page resources (including any running JavaScript).
-     */
-    @Deprecated
     public void clearView() {
-        checkThread();
-        mProvider.clearView();
     }
-
-    /**
-     * Gets a new picture that captures the current contents of this WebView.
-     * The picture is of the entire document being displayed, and is not
-     * limited to the area currently displayed by this WebView. Also, the
-     * picture is a static copy and is unaffected by later changes to the
-     * content being displayed.
-     * <p>
-     * Note that due to internal changes, for API levels between
-     * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and
-     * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} inclusive, the
-     * picture does not include fixed position elements or scrollable divs.
-     * <p>
-     * Note that from {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} the returned picture
-     * should only be drawn into bitmap-backed Canvas - using any other type of Canvas will involve
-     * additional conversion at a cost in memory and performance. Also the
-     * {@link android.graphics.Picture#createFromStream} and
-     * {@link android.graphics.Picture#writeToStream} methods are not supported on the
-     * returned object.
-     *
-     * @deprecated Use {@link #onDraw} to obtain a bitmap snapshot of the WebView, or
-     * {@link #saveWebArchive} to save the content to a file.
-     *
-     * @return a picture that captures the current contents of this WebView
-     */
-    @Deprecated
+    
     public Picture capturePicture() {
-        checkThread();
-        return mProvider.capturePicture();
+        return null;
     }
 
-    /**
-     * @deprecated Use {@link #createPrintDocumentAdapter(String)} which requires user
-     *             to provide a print document name.
-     */
-    @Deprecated
-    public PrintDocumentAdapter createPrintDocumentAdapter() {
-        checkThread();
-        return mProvider.createPrintDocumentAdapter("default");
-    }
-
-    /**
-     * Creates a PrintDocumentAdapter that provides the content of this WebView for printing.
-     *
-     * The adapter works by converting the WebView contents to a PDF stream. The WebView cannot
-     * be drawn during the conversion process - any such draws are undefined. It is recommended
-     * to use a dedicated off screen WebView for the printing. If necessary, an application may
-     * temporarily hide a visible WebView by using a custom PrintDocumentAdapter instance
-     * wrapped around the object returned and observing the onStart and onFinish methods. See
-     * {@link android.print.PrintDocumentAdapter} for more information.
-     *
-     * @param documentName  The user-facing name of the printed document. See
-     *                      {@link android.print.PrintDocumentInfo}
-     */
-    public PrintDocumentAdapter createPrintDocumentAdapter(String documentName) {
-        checkThread();
-        return mProvider.createPrintDocumentAdapter(documentName);
-    }
-
-    /**
-     * Gets the current scale of this WebView.
-     *
-     * @return the current scale
-     *
-     * @deprecated This method is prone to inaccuracy due to race conditions
-     * between the web rendering and UI threads; prefer
-     * {@link WebViewClient#onScaleChanged}.
-     */
-    @Deprecated
-    @ViewDebug.ExportedProperty(category = "webview")
     public float getScale() {
-        checkThread();
-        return mProvider.getScale();
+        return 0;
     }
 
-    /**
-     * Sets the initial scale for this WebView. 0 means default.
-     * The behavior for the default scale depends on the state of
-     * {@link WebSettings#getUseWideViewPort()} and
-     * {@link WebSettings#getLoadWithOverviewMode()}.
-     * If the content fits into the WebView control by width, then
-     * the zoom is set to 100%. For wide content, the behavior
-     * depends on the state of {@link WebSettings#getLoadWithOverviewMode()}.
-     * If its value is {@code true}, the content will be zoomed out to be fit
-     * by width into the WebView control, otherwise not.
-     *
-     * If initial scale is greater than 0, WebView starts with this value
-     * as initial scale.
-     * Please note that unlike the scale properties in the viewport meta tag,
-     * this method doesn't take the screen density into account.
-     *
-     * @param scaleInPercent the initial scale in percent
-     */
     public void setInitialScale(int scaleInPercent) {
-        checkThread();
-        mProvider.setInitialScale(scaleInPercent);
     }
 
-    /**
-     * Invokes the graphical zoom picker widget for this WebView. This will
-     * result in the zoom widget appearing on the screen to control the zoom
-     * level of this WebView.
-     */
     public void invokeZoomPicker() {
-        checkThread();
-        mProvider.invokeZoomPicker();
     }
 
-    /**
-     * Gets a HitTestResult based on the current cursor node. If a HTML::a
-     * tag is found and the anchor has a non-JavaScript URL, the HitTestResult
-     * type is set to SRC_ANCHOR_TYPE and the URL is set in the "extra" field.
-     * If the anchor does not have a URL or if it is a JavaScript URL, the type
-     * will be UNKNOWN_TYPE and the URL has to be retrieved through
-     * {@link #requestFocusNodeHref} asynchronously. If a HTML::img tag is
-     * found, the HitTestResult type is set to IMAGE_TYPE and the URL is set in
-     * the "extra" field. A type of
-     * SRC_IMAGE_ANCHOR_TYPE indicates an anchor with a URL that has an image as
-     * a child node. If a phone number is found, the HitTestResult type is set
-     * to PHONE_TYPE and the phone number is set in the "extra" field of
-     * HitTestResult. If a map address is found, the HitTestResult type is set
-     * to GEO_TYPE and the address is set in the "extra" field of HitTestResult.
-     * If an email address is found, the HitTestResult type is set to EMAIL_TYPE
-     * and the email is set in the "extra" field of HitTestResult. Otherwise,
-     * HitTestResult type is set to UNKNOWN_TYPE.
-     */
-    public HitTestResult getHitTestResult() {
-        checkThread();
-        return mProvider.getHitTestResult();
-    }
-
-    /**
-     * Requests the anchor or image element URL at the last tapped point.
-     * If hrefMsg is {@code null}, this method returns immediately and does not
-     * dispatch hrefMsg to its target. If the tapped point hits an image,
-     * an anchor, or an image in an anchor, the message associates
-     * strings in named keys in its data. The value paired with the key
-     * may be an empty string.
-     *
-     * @param hrefMsg the message to be dispatched with the result of the
-     *                request. The message data contains three keys. "url"
-     *                returns the anchor's href attribute. "title" returns the
-     *                anchor's text. "src" returns the image's src attribute.
-     */
     public void requestFocusNodeHref(Message hrefMsg) {
-        checkThread();
-        mProvider.requestFocusNodeHref(hrefMsg);
     }
 
-    /**
-     * Requests the URL of the image last touched by the user. msg will be sent
-     * to its target with a String representing the URL as its object.
-     *
-     * @param msg the message to be dispatched with the result of the request
-     *            as the data member with "url" as key. The result can be {@code null}.
-     */
     public void requestImageRef(Message msg) {
-        checkThread();
-        mProvider.requestImageRef(msg);
     }
 
-    /**
-     * Gets the URL for the current page. This is not always the same as the URL
-     * passed to WebViewClient.onPageStarted because although the load for
-     * that URL has begun, the current page may not have changed.
-     *
-     * @return the URL for the current page
-     */
-    @ViewDebug.ExportedProperty(category = "webview")
     public String getUrl() {
-        checkThread();
-        return mProvider.getUrl();
+        return null;
     }
 
-    /**
-     * Gets the original URL for the current page. This is not always the same
-     * as the URL passed to WebViewClient.onPageStarted because although the
-     * load for that URL has begun, the current page may not have changed.
-     * Also, there may have been redirects resulting in a different URL to that
-     * originally requested.
-     *
-     * @return the URL that was originally requested for the current page
-     */
-    @ViewDebug.ExportedProperty(category = "webview")
-    public String getOriginalUrl() {
-        checkThread();
-        return mProvider.getOriginalUrl();
-    }
-
-    /**
-     * Gets the title for the current page. This is the title of the current page
-     * until WebViewClient.onReceivedTitle is called.
-     *
-     * @return the title for the current page
-     */
-    @ViewDebug.ExportedProperty(category = "webview")
     public String getTitle() {
-        checkThread();
-        return mProvider.getTitle();
+        return null;
     }
 
-    /**
-     * Gets the favicon for the current page. This is the favicon of the current
-     * page until WebViewClient.onReceivedIcon is called.
-     *
-     * @return the favicon for the current page
-     */
     public Bitmap getFavicon() {
-        checkThread();
-        return mProvider.getFavicon();
+        return null;
     }
 
-    /**
-     * Gets the touch icon URL for the apple-touch-icon <link> element, or
-     * a URL on this site's server pointing to the standard location of a
-     * touch icon.
-     *
-     * @hide
-     */
-    public String getTouchIconUrl() {
-        return mProvider.getTouchIconUrl();
-    }
-
-    /**
-     * Gets the progress for the current page.
-     *
-     * @return the progress for the current page between 0 and 100
-     */
     public int getProgress() {
-        checkThread();
-        return mProvider.getProgress();
+        return 0;
     }
-
-    /**
-     * Gets the height of the HTML content.
-     *
-     * @return the height of the HTML content
-     */
-    @ViewDebug.ExportedProperty(category = "webview")
+    
     public int getContentHeight() {
-        checkThread();
-        return mProvider.getContentHeight();
+        return 0;
     }
 
-    /**
-     * Gets the width of the HTML content.
-     *
-     * @return the width of the HTML content
-     * @hide
-     */
-    @ViewDebug.ExportedProperty(category = "webview")
-    public int getContentWidth() {
-        return mProvider.getContentWidth();
-    }
-
-    /**
-     * Pauses all layout, parsing, and JavaScript timers for all WebViews. This
-     * is a global requests, not restricted to just this WebView. This can be
-     * useful if the application has been paused.
-     */
     public void pauseTimers() {
-        checkThread();
-        mProvider.pauseTimers();
     }
 
-    /**
-     * Resumes all layout, parsing, and JavaScript timers for all WebViews.
-     * This will resume dispatching all timers.
-     */
     public void resumeTimers() {
-        checkThread();
-        mProvider.resumeTimers();
     }
 
-    /**
-     * Does a best-effort attempt to pause any processing that can be paused
-     * safely, such as animations and geolocation. Note that this call
-     * does not pause JavaScript. To pause JavaScript globally, use
-     * {@link #pauseTimers}.
-     *
-     * To resume WebView, call {@link #onResume}.
-     */
-    public void onPause() {
-        checkThread();
-        mProvider.onPause();
+    public void clearCache() {
     }
 
-    /**
-     * Resumes a WebView after a previous call to {@link #onPause}.
-     */
-    public void onResume() {
-        checkThread();
-        mProvider.onResume();
-    }
-
-    /**
-     * Gets whether this WebView is paused, meaning onPause() was called.
-     * Calling onResume() sets the paused state back to {@code false}.
-     *
-     * @hide
-     */
-    public boolean isPaused() {
-        return mProvider.isPaused();
-    }
-
-    /**
-     * Informs this WebView that memory is low so that it can free any available
-     * memory.
-     * @deprecated Memory caches are automatically dropped when no longer needed, and in response
-     *             to system memory pressure.
-     */
-    @Deprecated
-    public void freeMemory() {
-        checkThread();
-        mProvider.freeMemory();
-    }
-
-    /**
-     * Clears the resource cache. Note that the cache is per-application, so
-     * this will clear the cache for all WebViews used.
-     *
-     * @param includeDiskFiles if {@code false}, only the RAM cache is cleared
-     */
-    public void clearCache(boolean includeDiskFiles) {
-        checkThread();
-        mProvider.clearCache(includeDiskFiles);
-    }
-
-    /**
-     * Removes the autocomplete popup from the currently focused form field, if
-     * present. Note this only affects the display of the autocomplete popup,
-     * it does not remove any saved form data from this WebView's store. To do
-     * that, use {@link WebViewDatabase#clearFormData}.
-     */
     public void clearFormData() {
-        checkThread();
-        mProvider.clearFormData();
     }
 
-    /**
-     * Tells this WebView to clear its internal back/forward list.
-     */
     public void clearHistory() {
-        checkThread();
-        mProvider.clearHistory();
     }
 
-    /**
-     * Clears the SSL preferences table stored in response to proceeding with
-     * SSL certificate errors.
-     */
     public void clearSslPreferences() {
-        checkThread();
-        mProvider.clearSslPreferences();
     }
 
-    /**
-     * Clears the client certificate preferences stored in response
-     * to proceeding/cancelling client cert requests. Note that WebView
-     * automatically clears these preferences when it receives a
-     * {@link KeyChain#ACTION_STORAGE_CHANGED} intent. The preferences are
-     * shared by all the WebViews that are created by the embedder application.
-     *
-     * @param onCleared  A runnable to be invoked when client certs are cleared.
-     *                   The embedder can pass {@code null} if not interested in the
-     *                   callback. The runnable will be called in UI thread.
-     */
-    public static void clearClientCertPreferences(Runnable onCleared) {
-        getFactory().getStatics().clearClientCertPreferences(onCleared);
-    }
-
-    /**
-     * Starts Safe Browsing initialization.
-     * <p>
-     * URL loads are not guaranteed to be protected by Safe Browsing until after {@code callback} is
-     * invoked with {@code true}. Safe Browsing is not fully supported on all devices. For those
-     * devices {@code callback} will receive {@code false}.
-     * <p>
-     * This does not enable the Safe Browsing feature itself, and should only be called if Safe
-     * Browsing is enabled by the manifest tag or {@link WebSettings#setSafeBrowsingEnabled}. This
-     * prepares resources used for Safe Browsing.
-     * <p>
-     * This should be called with the Application Context (and will always use the Application
-     * context to do its work regardless).
-     *
-     * @param context Application Context.
-     * @param callback will be called on the UI thread with {@code true} if initialization is
-     * successful, {@code false} otherwise.
-     */
-    public static void startSafeBrowsing(Context context, ValueCallback<Boolean> callback) {
-        getFactory().getStatics().initSafeBrowsing(context, callback);
-    }
-
-    /**
-     * Sets the list of domains that are exempt from SafeBrowsing checks. The list is
-     * global for all the WebViews.
-     * <p>
-     * Each rule should take one of these:
-     * <table>
-     * <tr><th> Rule </th> <th> Example </th> <th> Matches Subdomain</th> </tr>
-     * <tr><td> HOSTNAME </td> <td> example.com </td> <td> Yes </td> </tr>
-     * <tr><td> .HOSTNAME </td> <td> .example.com </td> <td> No </td> </tr>
-     * <tr><td> IPV4_LITERAL </td> <td> 192.168.1.1 </td> <td> No </td></tr>
-     * <tr><td> IPV6_LITERAL_WITH_BRACKETS </td><td>[10:20:30:40:50:60:70:80]</td><td>No</td></tr>
-     * </table>
-     * <p>
-     * All other rules, including wildcards, are invalid.
-     *
-     * @param urls the list of URLs
-     * @param callback will be called with {@code true} if URLs are successfully added to the
-     * whitelist. It will be called with {@code false} if any URLs are malformed. The callback will
-     * be run on the UI thread
-     */
-    public static void setSafeBrowsingWhitelist(@NonNull List<String> urls,
-            @Nullable ValueCallback<Boolean> callback) {
-        getFactory().getStatics().setSafeBrowsingWhitelist(urls, callback);
-    }
-
-    /**
-     * Returns a URL pointing to the privacy policy for Safe Browsing reporting.
-     *
-     * @return the url pointing to a privacy policy document which can be displayed to users.
-     */
-    @NonNull
-    public static Uri getSafeBrowsingPrivacyPolicyUrl() {
-        return getFactory().getStatics().getSafeBrowsingPrivacyPolicyUrl();
-    }
-
-    /**
-     * Gets the WebBackForwardList for this WebView. This contains the
-     * back/forward list for use in querying each item in the history stack.
-     * This is a copy of the private WebBackForwardList so it contains only a
-     * snapshot of the current state. Multiple calls to this method may return
-     * different objects. The object returned from this method will not be
-     * updated to reflect any new state.
-     */
-    public WebBackForwardList copyBackForwardList() {
-        checkThread();
-        return mProvider.copyBackForwardList();
-
-    }
-
-    /**
-     * Registers the listener to be notified as find-on-page operations
-     * progress. This will replace the current listener.
-     *
-     * @param listener an implementation of {@link FindListener}
-     */
-    public void setFindListener(FindListener listener) {
-        checkThread();
-        setupFindListenerIfNeeded();
-        mFindListener.mUserFindListener = listener;
-    }
-
-    /**
-     * Highlights and scrolls to the next match found by
-     * {@link #findAllAsync}, wrapping around page boundaries as necessary.
-     * Notifies any registered {@link FindListener}. If {@link #findAllAsync(String)}
-     * has not been called yet, or if {@link #clearMatches} has been called since the
-     * last find operation, this function does nothing.
-     *
-     * @param forward the direction to search
-     * @see #setFindListener
-     */
-    public void findNext(boolean forward) {
-        checkThread();
-        mProvider.findNext(forward);
-    }
-
-    /**
-     * Finds all instances of find on the page and highlights them.
-     * Notifies any registered {@link FindListener}.
-     *
-     * @param find the string to find
-     * @return the number of occurrences of the String "find" that were found
-     * @deprecated {@link #findAllAsync} is preferred.
-     * @see #setFindListener
-     */
-    @Deprecated
-    public int findAll(String find) {
-        checkThread();
-        StrictMode.noteSlowCall("findAll blocks UI: prefer findAllAsync");
-        return mProvider.findAll(find);
-    }
-
-    /**
-     * Finds all instances of find on the page and highlights them,
-     * asynchronously. Notifies any registered {@link FindListener}.
-     * Successive calls to this will cancel any pending searches.
-     *
-     * @param find the string to find.
-     * @see #setFindListener
-     */
-    public void findAllAsync(String find) {
-        checkThread();
-        mProvider.findAllAsync(find);
-    }
-
-    /**
-     * Starts an ActionMode for finding text in this WebView.  Only works if this
-     * WebView is attached to the view system.
-     *
-     * @param text if non-null, will be the initial text to search for.
-     *             Otherwise, the last String searched for in this WebView will
-     *             be used to start.
-     * @param showIme if {@code true}, show the IME, assuming the user will begin typing.
-     *                If {@code false} and text is non-null, perform a find all.
-     * @return {@code true} if the find dialog is shown, {@code false} otherwise
-     * @deprecated This method does not work reliably on all Android versions;
-     *             implementing a custom find dialog using WebView.findAllAsync()
-     *             provides a more robust solution.
-     */
-    @Deprecated
-    public boolean showFindDialog(String text, boolean showIme) {
-        checkThread();
-        return mProvider.showFindDialog(text, showIme);
-    }
-
-    /**
-     * Gets the first substring consisting of the address of a physical
-     * location. Currently, only addresses in the United States are detected,
-     * and consist of:
-     * <ul>
-     *   <li>a house number</li>
-     *   <li>a street name</li>
-     *   <li>a street type (Road, Circle, etc), either spelled out or
-     *       abbreviated</li>
-     *   <li>a city name</li>
-     *   <li>a state or territory, either spelled out or two-letter abbr</li>
-     *   <li>an optional 5 digit or 9 digit zip code</li>
-     * </ul>
-     * All names must be correctly capitalized, and the zip code, if present,
-     * must be valid for the state. The street type must be a standard USPS
-     * spelling or abbreviation. The state or territory must also be spelled
-     * or abbreviated using USPS standards. The house number may not exceed
-     * five digits.
-     *
-     * @param addr the string to search for addresses
-     * @return the address, or if no address is found, {@code null}
-     */
     public static String findAddress(String addr) {
-        // TODO: Rewrite this in Java so it is not needed to start up chromium
-        // Could also be deprecated
-        return getFactory().getStatics().findAddress(addr);
+        return null;
     }
 
-    /**
-     * For apps targeting the L release, WebView has a new default behavior that reduces
-     * memory footprint and increases performance by intelligently choosing
-     * the portion of the HTML document that needs to be drawn. These
-     * optimizations are transparent to the developers. However, under certain
-     * circumstances, an App developer may want to disable them:
-     * <ol>
-     *   <li>When an app uses {@link #onDraw} to do own drawing and accesses portions
-     *       of the page that is way outside the visible portion of the page.</li>
-     *   <li>When an app uses {@link #capturePicture} to capture a very large HTML document.
-     *       Note that capturePicture is a deprecated API.</li>
-     * </ol>
-     * Enabling drawing the entire HTML document has a significant performance
-     * cost. This method should be called before any WebViews are created.
-     */
-    public static void enableSlowWholeDocumentDraw() {
-        getFactory().getStatics().enableSlowWholeDocumentDraw();
-    }
-
-    /**
-     * Clears the highlighting surrounding text matches created by
-     * {@link #findAllAsync}.
-     */
-    public void clearMatches() {
-        checkThread();
-        mProvider.clearMatches();
-    }
-
-    /**
-     * Queries the document to see if it contains any image references. The
-     * message object will be dispatched with arg1 being set to 1 if images
-     * were found and 0 if the document does not reference any images.
-     *
-     * @param response the message that will be dispatched with the result
-     */
     public void documentHasImages(Message response) {
-        checkThread();
-        mProvider.documentHasImages(response);
     }
 
-    /**
-     * Sets the WebViewClient that will receive various notifications and
-     * requests. This will replace the current handler.
-     *
-     * @param client an implementation of WebViewClient
-     * @see #getWebViewClient
-     */
     public void setWebViewClient(WebViewClient client) {
-        checkThread();
-        mProvider.setWebViewClient(client);
     }
 
-    /**
-     * Gets the WebViewClient.
-     *
-     * @return the WebViewClient, or a default client if not yet set
-     * @see #setWebViewClient
-     */
-    public WebViewClient getWebViewClient() {
-        checkThread();
-        return mProvider.getWebViewClient();
-    }
-
-    /**
-     * Registers the interface to be used when content can not be handled by
-     * the rendering engine, and should be downloaded instead. This will replace
-     * the current handler.
-     *
-     * @param listener an implementation of DownloadListener
-     */
     public void setDownloadListener(DownloadListener listener) {
-        checkThread();
-        mProvider.setDownloadListener(listener);
     }
 
-    /**
-     * Sets the chrome handler. This is an implementation of WebChromeClient for
-     * use in handling JavaScript dialogs, favicons, titles, and the progress.
-     * This will replace the current handler.
-     *
-     * @param client an implementation of WebChromeClient
-     * @see #getWebChromeClient
-     */
     public void setWebChromeClient(WebChromeClient client) {
-        checkThread();
-        mProvider.setWebChromeClient(client);
     }
 
-    /**
-     * Gets the chrome handler.
-     *
-     * @return the WebChromeClient, or {@code null} if not yet set
-     * @see #setWebChromeClient
-     */
-    public WebChromeClient getWebChromeClient() {
-        checkThread();
-        return mProvider.getWebChromeClient();
+    public void addJavascriptInterface(Object obj, String interfaceName) {
     }
 
-    /**
-     * Sets the Picture listener. This is an interface used to receive
-     * notifications of a new Picture.
-     *
-     * @param listener an implementation of WebView.PictureListener
-     * @deprecated This method is now obsolete.
-     */
-    @Deprecated
-    public void setPictureListener(PictureListener listener) {
-        checkThread();
-        mProvider.setPictureListener(listener);
-    }
-
-    /**
-     * Injects the supplied Java object into this WebView. The object is
-     * injected into the JavaScript context of the main frame, using the
-     * supplied name. This allows the Java object's methods to be
-     * accessed from JavaScript. For applications targeted to API
-     * level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
-     * and above, only public methods that are annotated with
-     * {@link android.webkit.JavascriptInterface} can be accessed from JavaScript.
-     * For applications targeted to API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN} or below,
-     * all public methods (including the inherited ones) can be accessed, see the
-     * important security note below for implications.
-     * <p> Note that injected objects will not appear in JavaScript until the page is next
-     * (re)loaded. JavaScript should be enabled before injecting the object. For example:
-     * <pre>
-     * class JsObject {
-     *    {@literal @}JavascriptInterface
-     *    public String toString() { return "injectedObject"; }
-     * }
-     * webview.getSettings().setJavaScriptEnabled(true);
-     * webView.addJavascriptInterface(new JsObject(), "injectedObject");
-     * webView.loadData("<!DOCTYPE html><title></title>", "text/html", null);
-     * webView.loadUrl("javascript:alert(injectedObject.toString())");</pre>
-     * <p>
-     * <strong>IMPORTANT:</strong>
-     * <ul>
-     * <li> This method can be used to allow JavaScript to control the host
-     * application. This is a powerful feature, but also presents a security
-     * risk for apps targeting {@link android.os.Build.VERSION_CODES#JELLY_BEAN} or earlier.
-     * Apps that target a version later than {@link android.os.Build.VERSION_CODES#JELLY_BEAN}
-     * are still vulnerable if the app runs on a device running Android earlier than 4.2.
-     * The most secure way to use this method is to target {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
-     * and to ensure the method is called only when running on Android 4.2 or later.
-     * With these older versions, JavaScript could use reflection to access an
-     * injected object's public fields. Use of this method in a WebView
-     * containing untrusted content could allow an attacker to manipulate the
-     * host application in unintended ways, executing Java code with the
-     * permissions of the host application. Use extreme care when using this
-     * method in a WebView which could contain untrusted content.</li>
-     * <li> JavaScript interacts with Java object on a private, background
-     * thread of this WebView. Care is therefore required to maintain thread
-     * safety.
-     * </li>
-     * <li> The Java object's fields are not accessible.</li>
-     * <li> For applications targeted to API level {@link android.os.Build.VERSION_CODES#LOLLIPOP}
-     * and above, methods of injected Java objects are enumerable from
-     * JavaScript.</li>
-     * </ul>
-     *
-     * @param object the Java object to inject into this WebView's JavaScript
-     *               context. {@code null} values are ignored.
-     * @param name the name used to expose the object in JavaScript
-     */
-    public void addJavascriptInterface(Object object, String name) {
-        checkThread();
-        mProvider.addJavascriptInterface(object, name);
-    }
-
-    /**
-     * Removes a previously injected Java object from this WebView. Note that
-     * the removal will not be reflected in JavaScript until the page is next
-     * (re)loaded. See {@link #addJavascriptInterface}.
-     *
-     * @param name the name used to expose the object in JavaScript
-     */
-    public void removeJavascriptInterface(String name) {
-        checkThread();
-        mProvider.removeJavascriptInterface(name);
-    }
-
-    /**
-     * Creates a message channel to communicate with JS and returns the message
-     * ports that represent the endpoints of this message channel. The HTML5 message
-     * channel functionality is described
-     * <a href="https://html.spec.whatwg.org/multipage/comms.html#messagechannel">here
-     * </a>
-     *
-     * <p>The returned message channels are entangled and already in started state.
-     *
-     * @return the two message ports that form the message channel.
-     */
-    public WebMessagePort[] createWebMessageChannel() {
-        checkThread();
-        return mProvider.createWebMessageChannel();
-    }
-
-    /**
-     * Post a message to main frame. The embedded application can restrict the
-     * messages to a certain target origin. See
-     * <a href="https://html.spec.whatwg.org/multipage/comms.html#posting-messages">
-     * HTML5 spec</a> for how target origin can be used.
-     * <p>
-     * A target origin can be set as a wildcard ("*"). However this is not recommended.
-     * See the page above for security issues.
-     *
-     * @param message the WebMessage
-     * @param targetOrigin the target origin.
-     */
-    public void postWebMessage(WebMessage message, Uri targetOrigin) {
-        checkThread();
-        mProvider.postMessageToMainFrame(message, targetOrigin);
-    }
-
-    /**
-     * Gets the WebSettings object used to control the settings for this
-     * WebView.
-     *
-     * @return a WebSettings object that can be used to control this WebView's
-     *         settings
-     */
-    public WebSettings getSettings() {
-        checkThread();
-        return mProvider.getSettings();
-    }
-
-    /**
-     * Enables debugging of web contents (HTML / CSS / JavaScript)
-     * loaded into any WebViews of this application. This flag can be enabled
-     * in order to facilitate debugging of web layouts and JavaScript
-     * code running inside WebViews. Please refer to WebView documentation
-     * for the debugging guide.
-     *
-     * The default is {@code false}.
-     *
-     * @param enabled whether to enable web contents debugging
-     */
-    public static void setWebContentsDebuggingEnabled(boolean enabled) {
-        getFactory().getStatics().setWebContentsDebuggingEnabled(enabled);
-    }
-
-    /**
-     * Gets the list of currently loaded plugins.
-     *
-     * @return the list of currently loaded plugins
-     * @deprecated This was used for Gears, which has been deprecated.
-     * @hide
-     */
-    @Deprecated
-    public static synchronized PluginList getPluginList() {
-        return new PluginList();
-    }
-
-    /**
-     * @deprecated This was used for Gears, which has been deprecated.
-     * @hide
-     */
-    @Deprecated
-    public void refreshPlugins(boolean reloadOpenPages) {
-        checkThread();
-    }
-
-    /**
-     * Puts this WebView into text selection mode. Do not rely on this
-     * functionality; it will be deprecated in the future.
-     *
-     * @deprecated This method is now obsolete.
-     * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
-     */
-    @Deprecated
-    public void emulateShiftHeld() {
-        checkThread();
-    }
-
-    /**
-     * @deprecated WebView no longer needs to implement
-     * ViewGroup.OnHierarchyChangeListener.  This method does nothing now.
-     */
-    @Override
-    // Cannot add @hide as this can always be accessed via the interface.
-    @Deprecated
-    public void onChildViewAdded(View parent, View child) {}
-
-    /**
-     * @deprecated WebView no longer needs to implement
-     * ViewGroup.OnHierarchyChangeListener.  This method does nothing now.
-     */
-    @Override
-    // Cannot add @hide as this can always be accessed via the interface.
-    @Deprecated
-    public void onChildViewRemoved(View p, View child) {}
-
-    /**
-     * @deprecated WebView should not have implemented
-     * ViewTreeObserver.OnGlobalFocusChangeListener. This method does nothing now.
-     */
-    @Override
-    // Cannot add @hide as this can always be accessed via the interface.
-    @Deprecated
-    public void onGlobalFocusChanged(View oldFocus, View newFocus) {
-    }
-
-    /**
-     * @deprecated Only the default case, {@code true}, will be supported in a future version.
-     */
-    @Deprecated
-    public void setMapTrackballToArrowKeys(boolean setMap) {
-        checkThread();
-        mProvider.setMapTrackballToArrowKeys(setMap);
-    }
-
-
-    public void flingScroll(int vx, int vy) {
-        checkThread();
-        mProvider.flingScroll(vx, vy);
-    }
-
-    /**
-     * Gets the zoom controls for this WebView, as a separate View. The caller
-     * is responsible for inserting this View into the layout hierarchy.
-     * <p/>
-     * API level {@link android.os.Build.VERSION_CODES#CUPCAKE} introduced
-     * built-in zoom mechanisms for the WebView, as opposed to these separate
-     * zoom controls. The built-in mechanisms are preferred and can be enabled
-     * using {@link WebSettings#setBuiltInZoomControls}.
-     *
-     * @deprecated the built-in zoom mechanisms are preferred
-     * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN}
-     */
-    @Deprecated
     public View getZoomControls() {
-        checkThread();
-        return mProvider.getZoomControls();
+        return null;
     }
 
-    /**
-     * Gets whether this WebView can be zoomed in.
-     *
-     * @return {@code true} if this WebView can be zoomed in
-     *
-     * @deprecated This method is prone to inaccuracy due to race conditions
-     * between the web rendering and UI threads; prefer
-     * {@link WebViewClient#onScaleChanged}.
-     */
-    @Deprecated
-    public boolean canZoomIn() {
-        checkThread();
-        return mProvider.canZoomIn();
-    }
-
-    /**
-     * Gets whether this WebView can be zoomed out.
-     *
-     * @return {@code true} if this WebView can be zoomed out
-     *
-     * @deprecated This method is prone to inaccuracy due to race conditions
-     * between the web rendering and UI threads; prefer
-     * {@link WebViewClient#onScaleChanged}.
-     */
-    @Deprecated
-    public boolean canZoomOut() {
-        checkThread();
-        return mProvider.canZoomOut();
-    }
-
-    /**
-     * Performs a zoom operation in this WebView.
-     *
-     * @param zoomFactor the zoom factor to apply. The zoom factor will be clamped to the WebView's
-     * zoom limits. This value must be in the range 0.01 to 100.0 inclusive.
-     */
-    public void zoomBy(float zoomFactor) {
-        checkThread();
-        if (zoomFactor < 0.01)
-            throw new IllegalArgumentException("zoomFactor must be greater than 0.01.");
-        if (zoomFactor > 100.0)
-            throw new IllegalArgumentException("zoomFactor must be less than 100.");
-        mProvider.zoomBy(zoomFactor);
-    }
-
-    /**
-     * Performs zoom in in this WebView.
-     *
-     * @return {@code true} if zoom in succeeds, {@code false} if no zoom changes
-     */
     public boolean zoomIn() {
-        checkThread();
-        return mProvider.zoomIn();
+        return false;
     }
 
-    /**
-     * Performs zoom out in this WebView.
-     *
-     * @return {@code true} if zoom out succeeds, {@code false} if no zoom changes
-     */
     public boolean zoomOut() {
-        checkThread();
-        return mProvider.zoomOut();
-    }
-
-    /**
-     * @deprecated This method is now obsolete.
-     * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}
-     */
-    @Deprecated
-    public void debugDump() {
-        checkThread();
-    }
-
-    /**
-     * See {@link ViewDebug.HierarchyHandler#dumpViewHierarchyWithProperties(BufferedWriter, int)}
-     * @hide
-     */
-    @Override
-    public void dumpViewHierarchyWithProperties(BufferedWriter out, int level) {
-        mProvider.dumpViewHierarchyWithProperties(out, level);
-    }
-
-    /**
-     * See {@link ViewDebug.HierarchyHandler#findHierarchyView(String, int)}
-     * @hide
-     */
-    @Override
-    public View findHierarchyView(String className, int hashCode) {
-        return mProvider.findHierarchyView(className, hashCode);
-    }
-
-    /** @hide */
-    @IntDef({
-        RENDERER_PRIORITY_WAIVED,
-        RENDERER_PRIORITY_BOUND,
-        RENDERER_PRIORITY_IMPORTANT
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface RendererPriority {}
-
-    /**
-     * The renderer associated with this WebView is bound with
-     * {@link Context#BIND_WAIVE_PRIORITY}. At this priority level
-     * {@link WebView} renderers will be strong targets for out of memory
-     * killing.
-     *
-     * Use with {@link #setRendererPriorityPolicy}.
-     */
-    public static final int RENDERER_PRIORITY_WAIVED = 0;
-    /**
-     * The renderer associated with this WebView is bound with
-     * the default priority for services.
-     *
-     * Use with {@link #setRendererPriorityPolicy}.
-     */
-    public static final int RENDERER_PRIORITY_BOUND = 1;
-    /**
-     * The renderer associated with this WebView is bound with
-     * {@link Context#BIND_IMPORTANT}.
-     *
-     * Use with {@link #setRendererPriorityPolicy}.
-     */
-    public static final int RENDERER_PRIORITY_IMPORTANT = 2;
-
-    /**
-     * Set the renderer priority policy for this {@link WebView}. The
-     * priority policy will be used to determine whether an out of
-     * process renderer should be considered to be a target for OOM
-     * killing.
-     *
-     * Because a renderer can be associated with more than one
-     * WebView, the final priority it is computed as the maximum of
-     * any attached WebViews. When a WebView is destroyed it will
-     * cease to be considerered when calculating the renderer
-     * priority. Once no WebViews remain associated with the renderer,
-     * the priority of the renderer will be reduced to
-     * {@link #RENDERER_PRIORITY_WAIVED}.
-     *
-     * The default policy is to set the priority to
-     * {@link #RENDERER_PRIORITY_IMPORTANT} regardless of visibility,
-     * and this should not be changed unless the caller also handles
-     * renderer crashes with
-     * {@link WebViewClient#onRenderProcessGone}. Any other setting
-     * will result in WebView renderers being killed by the system
-     * more aggressively than the application.
-     *
-     * @param rendererRequestedPriority the minimum priority at which
-     *        this WebView desires the renderer process to be bound.
-     * @param waivedWhenNotVisible if {@code true}, this flag specifies that
-     *        when this WebView is not visible, it will be treated as
-     *        if it had requested a priority of
-     *        {@link #RENDERER_PRIORITY_WAIVED}.
-     */
-    public void setRendererPriorityPolicy(
-            @RendererPriority int rendererRequestedPriority,
-            boolean waivedWhenNotVisible) {
-        mProvider.setRendererPriorityPolicy(rendererRequestedPriority, waivedWhenNotVisible);
-    }
-
-    /**
-     * Get the requested renderer priority for this WebView.
-     *
-     * @return the requested renderer priority policy.
-     */
-    @RendererPriority
-    public int getRendererRequestedPriority() {
-        return mProvider.getRendererRequestedPriority();
-    }
-
-    /**
-     * Return whether this WebView requests a priority of
-     * {@link #RENDERER_PRIORITY_WAIVED} when not visible.
-     *
-     * @return whether this WebView requests a priority of
-     * {@link #RENDERER_PRIORITY_WAIVED} when not visible.
-     */
-    public boolean getRendererPriorityWaivedWhenNotVisible() {
-        return mProvider.getRendererPriorityWaivedWhenNotVisible();
-    }
-
-    /**
-     * Sets the {@link TextClassifier} for this WebView.
-     */
-    public void setTextClassifier(@Nullable TextClassifier textClassifier) {
-        mProvider.setTextClassifier(textClassifier);
-    }
-
-    /**
-     * Returns the {@link TextClassifier} used by this WebView.
-     * If no TextClassifier has been set, this WebView uses the default set by the system.
-     */
-    @NonNull
-    public TextClassifier getTextClassifier() {
-        return mProvider.getTextClassifier();
-    }
-
-    //-------------------------------------------------------------------------
-    // Interface for WebView providers
-    //-------------------------------------------------------------------------
-
-    /**
-     * Gets the WebViewProvider. Used by providers to obtain the underlying
-     * implementation, e.g. when the application responds to
-     * WebViewClient.onCreateWindow() request.
-     *
-     * @hide WebViewProvider is not public API.
-     */
-    @SystemApi
-    public WebViewProvider getWebViewProvider() {
-        return mProvider;
-    }
-
-    /**
-     * Callback interface, allows the provider implementation to access non-public methods
-     * and fields, and make super-class calls in this WebView instance.
-     * @hide Only for use by WebViewProvider implementations
-     */
-    @SystemApi
-    public class PrivateAccess {
-        // ---- Access to super-class methods ----
-        public int super_getScrollBarStyle() {
-            return WebView.super.getScrollBarStyle();
-        }
-
-        public void super_scrollTo(int scrollX, int scrollY) {
-            WebView.super.scrollTo(scrollX, scrollY);
-        }
-
-        public void super_computeScroll() {
-            WebView.super.computeScroll();
-        }
-
-        public boolean super_onHoverEvent(MotionEvent event) {
-            return WebView.super.onHoverEvent(event);
-        }
-
-        public boolean super_performAccessibilityAction(int action, Bundle arguments) {
-            return WebView.super.performAccessibilityActionInternal(action, arguments);
-        }
-
-        public boolean super_performLongClick() {
-            return WebView.super.performLongClick();
-        }
-
-        public boolean super_setFrame(int left, int top, int right, int bottom) {
-            return WebView.super.setFrame(left, top, right, bottom);
-        }
-
-        public boolean super_dispatchKeyEvent(KeyEvent event) {
-            return WebView.super.dispatchKeyEvent(event);
-        }
-
-        public boolean super_onGenericMotionEvent(MotionEvent event) {
-            return WebView.super.onGenericMotionEvent(event);
-        }
-
-        public boolean super_requestFocus(int direction, Rect previouslyFocusedRect) {
-            return WebView.super.requestFocus(direction, previouslyFocusedRect);
-        }
-
-        public void super_setLayoutParams(ViewGroup.LayoutParams params) {
-            WebView.super.setLayoutParams(params);
-        }
-
-        public void super_startActivityForResult(Intent intent, int requestCode) {
-            WebView.super.startActivityForResult(intent, requestCode);
-        }
-
-        // ---- Access to non-public methods ----
-        public void overScrollBy(int deltaX, int deltaY,
-                int scrollX, int scrollY,
-                int scrollRangeX, int scrollRangeY,
-                int maxOverScrollX, int maxOverScrollY,
-                boolean isTouchEvent) {
-            WebView.this.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY,
-                    maxOverScrollX, maxOverScrollY, isTouchEvent);
-        }
-
-        public void awakenScrollBars(int duration) {
-            WebView.this.awakenScrollBars(duration);
-        }
-
-        public void awakenScrollBars(int duration, boolean invalidate) {
-            WebView.this.awakenScrollBars(duration, invalidate);
-        }
-
-        public float getVerticalScrollFactor() {
-            return WebView.this.getVerticalScrollFactor();
-        }
-
-        public float getHorizontalScrollFactor() {
-            return WebView.this.getHorizontalScrollFactor();
-        }
-
-        public void setMeasuredDimension(int measuredWidth, int measuredHeight) {
-            WebView.this.setMeasuredDimension(measuredWidth, measuredHeight);
-        }
-
-        public void onScrollChanged(int l, int t, int oldl, int oldt) {
-            WebView.this.onScrollChanged(l, t, oldl, oldt);
-        }
-
-        public int getHorizontalScrollbarHeight() {
-            return WebView.this.getHorizontalScrollbarHeight();
-        }
-
-        public void super_onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar,
-                int l, int t, int r, int b) {
-            WebView.super.onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b);
-        }
-
-        // ---- Access to (non-public) fields ----
-        /** Raw setter for the scroll X value, without invoking onScrollChanged handlers etc. */
-        public void setScrollXRaw(int scrollX) {
-            WebView.this.mScrollX = scrollX;
-        }
-
-        /** Raw setter for the scroll Y value, without invoking onScrollChanged handlers etc. */
-        public void setScrollYRaw(int scrollY) {
-            WebView.this.mScrollY = scrollY;
-        }
-
-    }
-
-    //-------------------------------------------------------------------------
-    // Package-private internal stuff
-    //-------------------------------------------------------------------------
-
-    // Only used by android.webkit.FindActionModeCallback.
-    void setFindDialogFindListener(FindListener listener) {
-        checkThread();
-        setupFindListenerIfNeeded();
-        mFindListener.mFindDialogFindListener = listener;
-    }
-
-    // Only used by android.webkit.FindActionModeCallback.
-    void notifyFindDialogDismissed() {
-        checkThread();
-        mProvider.notifyFindDialogDismissed();
-    }
-
-    //-------------------------------------------------------------------------
-    // Private internal stuff
-    //-------------------------------------------------------------------------
-
-    private WebViewProvider mProvider;
-
-    /**
-     * In addition to the FindListener that the user may set via the WebView.setFindListener
-     * API, FindActionModeCallback will register it's own FindListener. We keep them separate
-     * via this class so that the two FindListeners can potentially exist at once.
-     */
-    private class FindListenerDistributor implements FindListener {
-        private FindListener mFindDialogFindListener;
-        private FindListener mUserFindListener;
-
-        @Override
-        public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
-                boolean isDoneCounting) {
-            if (mFindDialogFindListener != null) {
-                mFindDialogFindListener.onFindResultReceived(activeMatchOrdinal, numberOfMatches,
-                        isDoneCounting);
-            }
-
-            if (mUserFindListener != null) {
-                mUserFindListener.onFindResultReceived(activeMatchOrdinal, numberOfMatches,
-                        isDoneCounting);
-            }
-        }
-    }
-    private FindListenerDistributor mFindListener;
-
-    private void setupFindListenerIfNeeded() {
-        if (mFindListener == null) {
-            mFindListener = new FindListenerDistributor();
-            mProvider.setFindListener(mFindListener);
-        }
-    }
-
-    private void ensureProviderCreated() {
-        checkThread();
-        if (mProvider == null) {
-            // As this can get called during the base class constructor chain, pass the minimum
-            // number of dependencies here; the rest are deferred to init().
-            mProvider = getFactory().createWebView(this, new PrivateAccess());
-        }
-    }
-
-    private static WebViewFactoryProvider getFactory() {
-        return WebViewFactory.getProvider();
-    }
-
-    private final Looper mWebViewThread = Looper.myLooper();
-
-    private void checkThread() {
-        // Ignore mWebViewThread == null because this can be called during in the super class
-        // constructor, before this class's own constructor has even started.
-        if (mWebViewThread != null && Looper.myLooper() != mWebViewThread) {
-            Throwable throwable = new Throwable(
-                    "A WebView method was called on thread '" +
-                    Thread.currentThread().getName() + "'. " +
-                    "All WebView methods must be called on the same thread. " +
-                    "(Expected Looper " + mWebViewThread + " called on " + Looper.myLooper() +
-                    ", FYI main Looper is " + Looper.getMainLooper() + ")");
-            Log.w(LOGTAG, Log.getStackTraceString(throwable));
-            StrictMode.onWebViewMethodCalledOnWrongThread(throwable);
-
-            if (sEnforceThreadChecking) {
-                throw new RuntimeException(throwable);
-            }
-        }
-    }
-
-    //-------------------------------------------------------------------------
-    // Override View methods
-    //-------------------------------------------------------------------------
-
-    // TODO: Add a test that enumerates all methods in ViewDelegte & ScrollDelegate, and ensures
-    // there's a corresponding override (or better, caller) for each of them in here.
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mProvider.getViewDelegate().onAttachedToWindow();
-    }
-
-    /** @hide */
-    @Override
-    protected void onDetachedFromWindowInternal() {
-        mProvider.getViewDelegate().onDetachedFromWindow();
-        super.onDetachedFromWindowInternal();
-    }
-
-    /** @hide */
-    @Override
-    public void onMovedToDisplay(int displayId, Configuration config) {
-        mProvider.getViewDelegate().onMovedToDisplay(displayId, config);
-    }
-
-    @Override
-    public void setLayoutParams(ViewGroup.LayoutParams params) {
-        mProvider.getViewDelegate().setLayoutParams(params);
-    }
-
-    @Override
-    public void setOverScrollMode(int mode) {
-        super.setOverScrollMode(mode);
-        // This method may be called in the constructor chain, before the WebView provider is
-        // created.
-        ensureProviderCreated();
-        mProvider.getViewDelegate().setOverScrollMode(mode);
-    }
-
-    @Override
-    public void setScrollBarStyle(int style) {
-        mProvider.getViewDelegate().setScrollBarStyle(style);
-        super.setScrollBarStyle(style);
-    }
-
-    @Override
-    protected int computeHorizontalScrollRange() {
-        return mProvider.getScrollDelegate().computeHorizontalScrollRange();
-    }
-
-    @Override
-    protected int computeHorizontalScrollOffset() {
-        return mProvider.getScrollDelegate().computeHorizontalScrollOffset();
-    }
-
-    @Override
-    protected int computeVerticalScrollRange() {
-        return mProvider.getScrollDelegate().computeVerticalScrollRange();
-    }
-
-    @Override
-    protected int computeVerticalScrollOffset() {
-        return mProvider.getScrollDelegate().computeVerticalScrollOffset();
-    }
-
-    @Override
-    protected int computeVerticalScrollExtent() {
-        return mProvider.getScrollDelegate().computeVerticalScrollExtent();
-    }
-
-    @Override
-    public void computeScroll() {
-        mProvider.getScrollDelegate().computeScroll();
-    }
-
-    @Override
-    public boolean onHoverEvent(MotionEvent event) {
-        return mProvider.getViewDelegate().onHoverEvent(event);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        return mProvider.getViewDelegate().onTouchEvent(event);
-    }
-
-    @Override
-    public boolean onGenericMotionEvent(MotionEvent event) {
-        return mProvider.getViewDelegate().onGenericMotionEvent(event);
-    }
-
-    @Override
-    public boolean onTrackballEvent(MotionEvent event) {
-        return mProvider.getViewDelegate().onTrackballEvent(event);
-    }
-
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        return mProvider.getViewDelegate().onKeyDown(keyCode, event);
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        return mProvider.getViewDelegate().onKeyUp(keyCode, event);
-    }
-
-    @Override
-    public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
-        return mProvider.getViewDelegate().onKeyMultiple(keyCode, repeatCount, event);
-    }
-
-    /*
-    TODO: These are not currently implemented in WebViewClassic, but it seems inconsistent not
-    to be delegating them too.
-
-    @Override
-    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
-        return mProvider.getViewDelegate().onKeyPreIme(keyCode, event);
-    }
-    @Override
-    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
-        return mProvider.getViewDelegate().onKeyLongPress(keyCode, event);
-    }
-    @Override
-    public boolean onKeyShortcut(int keyCode, KeyEvent event) {
-        return mProvider.getViewDelegate().onKeyShortcut(keyCode, event);
-    }
-    */
-
-    @Override
-    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
-        AccessibilityNodeProvider provider =
-                mProvider.getViewDelegate().getAccessibilityNodeProvider();
-        return provider == null ? super.getAccessibilityNodeProvider() : provider;
-    }
-
-    @Deprecated
-    @Override
-    public boolean shouldDelayChildPressedState() {
-        return mProvider.getViewDelegate().shouldDelayChildPressedState();
-    }
-
-    @Override
-    public CharSequence getAccessibilityClassName() {
-        return WebView.class.getName();
-    }
-
-    @Override
-    public void onProvideVirtualStructure(ViewStructure structure) {
-        mProvider.getViewDelegate().onProvideVirtualStructure(structure);
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * <p>The {@link ViewStructure} traditionally represents a {@link View}, while for web pages
-     * it represent HTML nodes. Hence, it's necessary to "map" the HTML properties in a way that is
-     * understood by the {@link android.service.autofill.AutofillService} implementations:
-     *
-     * <ol>
-     *   <li>Only the HTML nodes inside a {@code FORM} are generated.
-     *   <li>The source of the HTML is set using {@link ViewStructure#setWebDomain(String)} in the
-     *   node representing the WebView.
-     *   <li>If a web page has multiple {@code FORM}s, only the data for the current form is
-     *   represented&mdash;if the user taps a field from another form, then the current autofill
-     *   context is canceled (by calling {@link android.view.autofill.AutofillManager#cancel()} and
-     *   a new context is created for that {@code FORM}.
-     *   <li>Similarly, if the page has {@code IFRAME} nodes, they are not initially represented in
-     *   the view structure until the user taps a field from a {@code FORM} inside the
-     *   {@code IFRAME}, in which case it would be treated the same way as multiple forms described
-     *   above, except that the {@link ViewStructure#setWebDomain(String) web domain} of the
-     *   {@code FORM} contains the {@code src} attribute from the {@code IFRAME} node.
-     *   <li>If the Android SDK provides a similar View, then should be set with the
-     *   fully-qualified class name of such view.
-     *   <li>The W3C autofill field ({@code autocomplete} tag attribute) maps to
-     *       {@link ViewStructure#setAutofillHints(String[])}.
-     *   <li>The {@code type} attribute of {@code INPUT} tags maps to
-     *       {@link ViewStructure#setInputType(int)}.
-     *   <li>The {@code value} attribute of {@code INPUT} tags maps to
-     *       {@link ViewStructure#setText(CharSequence)}.
-     *   <li>If the view is editalbe, the {@link ViewStructure#setAutofillType(int)} and
-     *   {@link ViewStructure#setAutofillValue(AutofillValue)} must be set.
-     *   <li>The {@code placeholder} attribute maps to {@link ViewStructure#setHint(CharSequence)}.
-     *   <li>Other HTML attributes can be represented through
-     *   {@link ViewStructure#setHtmlInfo(android.view.ViewStructure.HtmlInfo)}.
-     * </ol>
-     *
-     * <p>It should also call {@code structure.setDataIsSensitive(false)} for fields whose value
-     * were not dynamically changed (for example, through Javascript).
-     *
-     * <p>Example1: an HTML form with 2 fields for username and password.
-     *
-     * <pre class="prettyprint">
-     *    &lt;input type="text" name="username" id="user" value="Type your username" autocomplete="username" placeholder="Email or username"&gt;
-     *    &lt;input type="password" name="password" id="pass" autocomplete="current-password" placeholder="Password"&gt;
-     * </pre>
-     *
-     * <p>Would map to:
-     *
-     * <pre class="prettyprint">
-     *     int index = structure.addChildCount(2);
-     *     ViewStructure username = structure.newChild(index);
-     *     username.setAutofillId(structure.getAutofillId(), 1); // id 1 - first child
-     *     username.setClassName("input");
-     *     username.setInputType("android.widget.EditText");
-     *     username.setAutofillHints("username");
-     *     username.setHtmlInfo(username.newHtmlInfoBuilder("input")
-     *         .addAttribute("type", "text")
-     *         .addAttribute("name", "username")
-     *         .addAttribute("id", "user")
-     *         .build());
-     *     username.setHint("Email or username");
-     *     username.setAutofillType(View.AUTOFILL_TYPE_TEXT);
-     *     username.setAutofillValue(AutofillValue.forText("Type your username"));
-     *     username.setText("Type your username");
-     *     // Value of the field is not sensitive because it was not dynamically changed:
-     *     username.setDataIsSensitive(false);
-     *
-     *     ViewStructure password = structure.newChild(index + 1);
-     *     username.setAutofillId(structure, 2); // id 2 - second child
-     *     password.setInputType("android.widget.EditText");
-     *     password.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
-     *     password.setAutofillHints("current-password");
-     *     password.setHtmlInfo(password.newHtmlInfoBuilder("input")
-     *         .addAttribute("type", "password")
-     *         .addAttribute("name", "password")
-     *         .addAttribute("id", "pass")
-     *         .build());
-     *     password.setHint("Password");
-     *     password.setAutofillType(View.AUTOFILL_TYPE_TEXT);
-     * </pre>
-     *
-     * <p>Example2: an IFRAME tag.
-     *
-     * <pre class="prettyprint">
-     *    &lt;iframe src="https://example.com/login"/&gt;
-     * </pre>
-     *
-     * <p>Would map to:
-     *
-     * <pre class="prettyprint">
-     *     int index = structure.addChildCount(1);
-     *     ViewStructure iframe = structure.newChildFor(index);
-     *     iframe.setAutofillId(structure.getAutofillId(), 1);
-     *     iframe.setHtmlInfo(iframe.newHtmlInfoBuilder("iframe")
-     *         .addAttribute("src", "https://example.com/login")
-     *         .build());
-     * </pre>
-     */
-    @Override
-    public void onProvideAutofillVirtualStructure(ViewStructure structure, int flags) {
-        mProvider.getViewDelegate().onProvideAutofillVirtualStructure(structure, flags);
-    }
-
-    @Override
-    public void autofill(SparseArray<AutofillValue>values) {
-        mProvider.getViewDelegate().autofill(values);
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
-        super.onInitializeAccessibilityNodeInfoInternal(info);
-        mProvider.getViewDelegate().onInitializeAccessibilityNodeInfo(info);
-    }
-
-    /** @hide */
-    @Override
-    public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEventInternal(event);
-        mProvider.getViewDelegate().onInitializeAccessibilityEvent(event);
-    }
-
-    /** @hide */
-    @Override
-    public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
-        return mProvider.getViewDelegate().performAccessibilityAction(action, arguments);
-    }
-
-    /** @hide */
-    @Override
-    protected void onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar,
-            int l, int t, int r, int b) {
-        mProvider.getViewDelegate().onDrawVerticalScrollBar(canvas, scrollBar, l, t, r, b);
-    }
-
-    @Override
-    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
-        mProvider.getViewDelegate().onOverScrolled(scrollX, scrollY, clampedX, clampedY);
-    }
-
-    @Override
-    protected void onWindowVisibilityChanged(int visibility) {
-        super.onWindowVisibilityChanged(visibility);
-        mProvider.getViewDelegate().onWindowVisibilityChanged(visibility);
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        mProvider.getViewDelegate().onDraw(canvas);
-    }
-
-    @Override
-    public boolean performLongClick() {
-        return mProvider.getViewDelegate().performLongClick();
-    }
-
-    @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        mProvider.getViewDelegate().onConfigurationChanged(newConfig);
-    }
-
-    /**
-     * Creates a new InputConnection for an InputMethod to interact with the WebView.
-     * This is similar to {@link View#onCreateInputConnection} but note that WebView
-     * calls InputConnection methods on a thread other than the UI thread.
-     * If these methods are overridden, then the overriding methods should respect
-     * thread restrictions when calling View methods or accessing data.
-     */
-    @Override
-    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-        return mProvider.getViewDelegate().onCreateInputConnection(outAttrs);
-    }
-
-    @Override
-    public boolean onDragEvent(DragEvent event) {
-        return mProvider.getViewDelegate().onDragEvent(event);
-    }
-
-    @Override
-    protected void onVisibilityChanged(View changedView, int visibility) {
-        super.onVisibilityChanged(changedView, visibility);
-        // This method may be called in the constructor chain, before the WebView provider is
-        // created.
-        ensureProviderCreated();
-        mProvider.getViewDelegate().onVisibilityChanged(changedView, visibility);
-    }
-
-    @Override
-    public void onWindowFocusChanged(boolean hasWindowFocus) {
-        mProvider.getViewDelegate().onWindowFocusChanged(hasWindowFocus);
-        super.onWindowFocusChanged(hasWindowFocus);
-    }
-
-    @Override
-    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
-        mProvider.getViewDelegate().onFocusChanged(focused, direction, previouslyFocusedRect);
-        super.onFocusChanged(focused, direction, previouslyFocusedRect);
-    }
-
-    /** @hide */
-    @Override
-    protected boolean setFrame(int left, int top, int right, int bottom) {
-        return mProvider.getViewDelegate().setFrame(left, top, right, bottom);
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int ow, int oh) {
-        super.onSizeChanged(w, h, ow, oh);
-        mProvider.getViewDelegate().onSizeChanged(w, h, ow, oh);
-    }
-
-    @Override
-    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
-        super.onScrollChanged(l, t, oldl, oldt);
-        mProvider.getViewDelegate().onScrollChanged(l, t, oldl, oldt);
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        return mProvider.getViewDelegate().dispatchKeyEvent(event);
-    }
-
-    @Override
-    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
-        return mProvider.getViewDelegate().requestFocus(direction, previouslyFocusedRect);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        mProvider.getViewDelegate().onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    @Override
-    public boolean requestChildRectangleOnScreen(View child, Rect rect, boolean immediate) {
-        return mProvider.getViewDelegate().requestChildRectangleOnScreen(child, rect, immediate);
-    }
-
-    @Override
-    public void setBackgroundColor(int color) {
-        mProvider.getViewDelegate().setBackgroundColor(color);
-    }
-
-    @Override
-    public void setLayerType(int layerType, Paint paint) {
-        super.setLayerType(layerType, paint);
-        mProvider.getViewDelegate().setLayerType(layerType, paint);
-    }
-
-    @Override
-    protected void dispatchDraw(Canvas canvas) {
-        mProvider.getViewDelegate().preDispatchDraw(canvas);
-        super.dispatchDraw(canvas);
-    }
-
-    @Override
-    public void onStartTemporaryDetach() {
-        super.onStartTemporaryDetach();
-        mProvider.getViewDelegate().onStartTemporaryDetach();
-    }
-
-    @Override
-    public void onFinishTemporaryDetach() {
-        super.onFinishTemporaryDetach();
-        mProvider.getViewDelegate().onFinishTemporaryDetach();
-    }
-
-    @Override
-    public Handler getHandler() {
-        return mProvider.getViewDelegate().getHandler(super.getHandler());
-    }
-
-    @Override
-    public View findFocus() {
-        return mProvider.getViewDelegate().findFocus(super.findFocus());
-    }
-
-    /**
-     * If WebView has already been loaded into the current process this method will return the
-     * package that was used to load it. Otherwise, the package that would be used if the WebView
-     * was loaded right now will be returned; this does not cause WebView to be loaded, so this
-     * information may become outdated at any time.
-     * The WebView package changes either when the current WebView package is updated, disabled, or
-     * uninstalled. It can also be changed through a Developer Setting.
-     * If the WebView package changes, any app process that has loaded WebView will be killed. The
-     * next time the app starts and loads WebView it will use the new WebView package instead.
-     * @return the current WebView package, or {@code null} if there is none.
-     */
-    public static PackageInfo getCurrentWebViewPackage() {
-        PackageInfo webviewPackage = WebViewFactory.getLoadedPackageInfo();
-        if (webviewPackage != null) {
-            return webviewPackage;
-        }
-
-        try {
-            return WebViewFactory.getUpdateService().getCurrentWebViewPackage();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Receive the result from a previous call to {@link #startActivityForResult(Intent, int)}.
-     *
-     * @param requestCode The integer request code originally supplied to
-     *                    startActivityForResult(), allowing you to identify who this
-     *                    result came from.
-     * @param resultCode The integer result code returned by the child activity
-     *                   through its setResult().
-     * @param data An Intent, which can return result data to the caller
-     *               (various data can be attached to Intent "extras").
-     * @hide
-     */
-    @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        mProvider.getViewDelegate().onActivityResult(requestCode, resultCode, data);
-    }
-
-    /** @hide */
-    @Override
-    protected void encodeProperties(@NonNull ViewHierarchyEncoder encoder) {
-        super.encodeProperties(encoder);
-
-        checkThread();
-        encoder.addProperty("webview:contentHeight", mProvider.getContentHeight());
-        encoder.addProperty("webview:contentWidth", mProvider.getContentWidth());
-        encoder.addProperty("webview:scale", mProvider.getScale());
-        encoder.addProperty("webview:title", mProvider.getTitle());
-        encoder.addProperty("webview:url", mProvider.getUrl());
-        encoder.addProperty("webview:originalUrl", mProvider.getOriginalUrl());
+        return false;
     }
 }
diff --git a/android/widget/DatePickerCalendarDelegate.java b/android/widget/DatePickerCalendarDelegate.java
index 60b4757..e40023d 100644
--- a/android/widget/DatePickerCalendarDelegate.java
+++ b/android/widget/DatePickerCalendarDelegate.java
@@ -22,11 +22,10 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.icu.text.DateFormat;
 import android.icu.text.DisplayContext;
-import android.icu.text.SimpleDateFormat;
 import android.icu.util.Calendar;
 import android.os.Parcelable;
-import android.text.format.DateFormat;
 import android.util.AttributeSet;
 import android.util.StateSet;
 import android.view.HapticFeedbackConstants;
@@ -62,8 +61,8 @@
     private static final int[] ATTRS_DISABLED_ALPHA = new int[] {
             com.android.internal.R.attr.disabledAlpha};
 
-    private SimpleDateFormat mYearFormat;
-    private SimpleDateFormat mMonthDayFormat;
+    private DateFormat mYearFormat;
+    private DateFormat mMonthDayFormat;
 
     // Top-level container.
     private ViewGroup mContainer;
@@ -273,19 +272,16 @@
     /**
      * Listener called when the user clicks on a header item.
      */
-    private final OnClickListener mOnHeaderClickListener = new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            tryVibrate();
+    private final OnClickListener mOnHeaderClickListener = v -> {
+        tryVibrate();
 
-            switch (v.getId()) {
-                case R.id.date_picker_header_year:
-                    setCurrentView(VIEW_YEAR);
-                    break;
-                case R.id.date_picker_header_date:
-                    setCurrentView(VIEW_MONTH_DAY);
-                    break;
-            }
+        switch (v.getId()) {
+            case R.id.date_picker_header_year:
+                setCurrentView(VIEW_YEAR);
+                break;
+            case R.id.date_picker_header_date:
+                setCurrentView(VIEW_MONTH_DAY);
+                break;
         }
     };
 
@@ -299,10 +295,9 @@
         }
 
         // Update the date formatter.
-        final String datePattern = DateFormat.getBestDateTimePattern(locale, "EMMMd");
-        mMonthDayFormat = new SimpleDateFormat(datePattern, locale);
+        mMonthDayFormat = DateFormat.getInstanceForSkeleton("EMMMd", locale);
         mMonthDayFormat.setContext(DisplayContext.CAPITALIZATION_FOR_STANDALONE);
-        mYearFormat = new SimpleDateFormat("y", locale);
+        mYearFormat = DateFormat.getInstanceForSkeleton("y", locale);
 
         // Update the header text.
         onCurrentDateChanged(false);
@@ -344,14 +339,11 @@
             case VIEW_YEAR:
                 final int year = mCurrentDate.get(Calendar.YEAR);
                 mYearPickerView.setYear(year);
-                mYearPickerView.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        mYearPickerView.requestFocus();
-                        final View selected = mYearPickerView.getSelectedView();
-                        if (selected != null) {
-                            selected.requestFocus();
-                        }
+                mYearPickerView.post(() -> {
+                    mYearPickerView.requestFocus();
+                    final View selected = mYearPickerView.getSelectedView();
+                    if (selected != null) {
+                        selected.requestFocus();
                     }
                 });
 
diff --git a/android/widget/Editor.java b/android/widget/Editor.java
index d23dfe4..0f61724 100644
--- a/android/widget/Editor.java
+++ b/android/widget/Editor.java
@@ -41,6 +41,7 @@
 import android.graphics.RectF;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.metrics.LogMaker;
 import android.os.Bundle;
 import android.os.LocaleList;
 import android.os.Parcel;
@@ -1585,49 +1586,49 @@
         outText.startOffset = 0;
         outText.selectionStart = mTextView.getSelectionStart();
         outText.selectionEnd = mTextView.getSelectionEnd();
+        outText.hint = mTextView.getHint();
         return true;
     }
 
     boolean reportExtractedText() {
         final Editor.InputMethodState ims = mInputMethodState;
-        if (ims != null) {
-            final boolean contentChanged = ims.mContentChanged;
-            if (contentChanged || ims.mSelectionModeChanged) {
-                ims.mContentChanged = false;
-                ims.mSelectionModeChanged = false;
-                final ExtractedTextRequest req = ims.mExtractedTextRequest;
-                if (req != null) {
-                    InputMethodManager imm = InputMethodManager.peekInstance();
-                    if (imm != null) {
-                        if (TextView.DEBUG_EXTRACT) {
-                            Log.v(TextView.LOG_TAG, "Retrieving extracted start="
-                                    + ims.mChangedStart
-                                    + " end=" + ims.mChangedEnd
-                                    + " delta=" + ims.mChangedDelta);
-                        }
-                        if (ims.mChangedStart < 0 && !contentChanged) {
-                            ims.mChangedStart = EXTRACT_NOTHING;
-                        }
-                        if (extractTextInternal(req, ims.mChangedStart, ims.mChangedEnd,
-                                ims.mChangedDelta, ims.mExtractedText)) {
-                            if (TextView.DEBUG_EXTRACT) {
-                                Log.v(TextView.LOG_TAG,
-                                        "Reporting extracted start="
-                                                + ims.mExtractedText.partialStartOffset
-                                                + " end=" + ims.mExtractedText.partialEndOffset
-                                                + ": " + ims.mExtractedText.text);
-                            }
-
-                            imm.updateExtractedText(mTextView, req.token, ims.mExtractedText);
-                            ims.mChangedStart = EXTRACT_UNKNOWN;
-                            ims.mChangedEnd = EXTRACT_UNKNOWN;
-                            ims.mChangedDelta = 0;
-                            ims.mContentChanged = false;
-                            return true;
-                        }
-                    }
-                }
+        if (ims == null) {
+            return false;
+        }
+        ims.mSelectionModeChanged = false;
+        final ExtractedTextRequest req = ims.mExtractedTextRequest;
+        if (req == null) {
+            return false;
+        }
+        final InputMethodManager imm = InputMethodManager.peekInstance();
+        if (imm == null) {
+            return false;
+        }
+        if (TextView.DEBUG_EXTRACT) {
+            Log.v(TextView.LOG_TAG, "Retrieving extracted start="
+                    + ims.mChangedStart
+                    + " end=" + ims.mChangedEnd
+                    + " delta=" + ims.mChangedDelta);
+        }
+        if (ims.mChangedStart < 0 && !ims.mContentChanged) {
+            ims.mChangedStart = EXTRACT_NOTHING;
+        }
+        if (extractTextInternal(req, ims.mChangedStart, ims.mChangedEnd,
+                ims.mChangedDelta, ims.mExtractedText)) {
+            if (TextView.DEBUG_EXTRACT) {
+                Log.v(TextView.LOG_TAG,
+                        "Reporting extracted start="
+                                + ims.mExtractedText.partialStartOffset
+                                + " end=" + ims.mExtractedText.partialEndOffset
+                                + ": " + ims.mExtractedText.text);
             }
+
+            imm.updateExtractedText(mTextView, req.token, ims.mExtractedText);
+            ims.mChangedStart = EXTRACT_UNKNOWN;
+            ims.mChangedEnd = EXTRACT_UNKNOWN;
+            ims.mChangedDelta = 0;
+            ims.mContentChanged = false;
+            return true;
         }
         return false;
     }
@@ -3932,6 +3933,10 @@
                         textClassification.getLabel())
                         .setIcon(textClassification.getIcon())
                         .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
+                mMetricsLogger.write(
+                        new LogMaker(MetricsEvent.TEXT_SELECTION_MENU_ITEM_ASSIST)
+                                .setType(MetricsEvent.TYPE_OPEN)
+                                .setSubtype(textClassification.getLogType()));
             }
         }
 
@@ -3973,6 +3978,9 @@
                                 .onClick(mTextView);
                     }
                 }
+                mMetricsLogger.action(
+                        MetricsEvent.ACTION_TEXT_SELECTION_MENU_ITEM_ASSIST,
+                        textClassification.getLogType());
                 stopTextActionMode();
                 return true;
             }
diff --git a/android/widget/TextView.java b/android/widget/TextView.java
index 4b6c4d3..efcc3a2 100644
--- a/android/widget/TextView.java
+++ b/android/widget/TextView.java
@@ -5547,6 +5547,14 @@
      */
     @android.view.RemotableViewMethod
     public final void setHint(CharSequence hint) {
+        setHintInternal(hint);
+
+        if (isInputMethodTarget()) {
+            mEditor.reportExtractedText();
+        }
+    }
+
+    private void setHintInternal(CharSequence hint) {
         mHint = TextUtils.stringOrSpannedString(hint);
 
         if (mLayout != null) {
@@ -7644,6 +7652,8 @@
         } else {
             MetaKeyKeyListener.stopSelecting(this, sp);
         }
+
+        setHintInternal(text.hint);
     }
 
     /**
@@ -8433,7 +8443,8 @@
 
         if (mMaxMode != LINES) {
             desired = Math.min(desired, mMaximum);
-        } else if (cap && linecount > mMaximum && layout instanceof DynamicLayout) {
+        } else if (cap && linecount > mMaximum && (layout instanceof DynamicLayout
+                || layout instanceof BoringLayout)) {
             desired = layout.getLineTop(mMaximum);
 
             if (dr != null) {
diff --git a/com/android/commands/sm/Sm.java b/com/android/commands/sm/Sm.java
index 658d662..a9a4118 100644
--- a/com/android/commands/sm/Sm.java
+++ b/com/android/commands/sm/Sm.java
@@ -16,6 +16,10 @@
 
 package com.android.commands.sm;
 
+import static android.os.storage.StorageManager.PROP_ADOPTABLE_FBE;
+import static android.os.storage.StorageManager.PROP_HAS_ADOPTABLE;
+import static android.os.storage.StorageManager.PROP_VIRTUAL_DISK;
+
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
@@ -134,7 +138,15 @@
     }
 
     public void runHasAdoptable() {
-        System.out.println(SystemProperties.getBoolean(StorageManager.PROP_HAS_ADOPTABLE, false));
+        final boolean hasHardware = SystemProperties.getBoolean(PROP_HAS_ADOPTABLE, false)
+                || SystemProperties.getBoolean(PROP_VIRTUAL_DISK, false);
+        final boolean hasSoftware;
+        if (StorageManager.isFileEncryptedNativeOnly()) {
+            hasSoftware = SystemProperties.getBoolean(PROP_ADOPTABLE_FBE, false);
+        } else {
+            hasSoftware = true;
+        }
+        System.out.println(hasHardware && hasSoftware);
     }
 
     public void runGetPrimaryStorageUuid() throws RemoteException {
diff --git a/com/android/ex/photo/ActionBarWrapper.java b/com/android/ex/photo/ActionBarWrapper.java
index 6d4d4d2..ae62197 100644
--- a/com/android/ex/photo/ActionBarWrapper.java
+++ b/com/android/ex/photo/ActionBarWrapper.java
@@ -1,7 +1,8 @@
 package com.android.ex.photo;
 
-import android.app.ActionBar;
+
 import android.graphics.drawable.Drawable;
+import android.support.v7.app.ActionBar;
 
 /**
  * Wrapper around {@link ActionBar}.
diff --git a/com/android/ex/photo/PhotoViewActivity.java b/com/android/ex/photo/PhotoViewActivity.java
index 7b53918..a5c4a43 100644
--- a/com/android/ex/photo/PhotoViewActivity.java
+++ b/com/android/ex/photo/PhotoViewActivity.java
@@ -21,14 +21,14 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
-import android.support.v4.app.FragmentActivity;
+import android.support.v7.app.AppCompatActivity;
 import android.view.Menu;
 import android.view.MenuItem;
 
 /**
  * Activity to view the contents of an album.
  */
-public class PhotoViewActivity extends FragmentActivity
+public class PhotoViewActivity extends AppCompatActivity
         implements PhotoViewController.ActivityInterface {
 
     private PhotoViewController mController;
@@ -41,7 +41,7 @@
         mController.onCreate(savedInstanceState);
     }
 
-    public PhotoViewController createController() {
+    protected PhotoViewController createController() {
         return new PhotoViewController(this);
     }
 
@@ -122,7 +122,7 @@
     @Override
     public ActionBarInterface getActionBarInterface() {
         if (mActionBar == null) {
-            mActionBar = new ActionBarWrapper(getActionBar());
+            mActionBar = new ActionBarWrapper(getSupportActionBar());
         }
         return mActionBar;
     }
diff --git a/com/android/internal/os/BatteryStatsImpl.java b/com/android/internal/os/BatteryStatsImpl.java
index c58ff05..0bd2981 100644
--- a/com/android/internal/os/BatteryStatsImpl.java
+++ b/com/android/internal/os/BatteryStatsImpl.java
@@ -119,7 +119,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 165 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 166 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS;
@@ -195,6 +195,13 @@
         return mKernelMemoryStats;
     }
 
+    /** Container for Resource Power Manager stats. Updated by updateRpmStatsLocked. */
+    private final RpmStats mTmpRpmStats = new RpmStats();
+    /** The soonest the RPM stats can be updated after it was last updated. */
+    private static final long RPM_STATS_UPDATE_FREQ_MS = 1000;
+    /** Last time that RPM stats were updated by updateRpmStatsLocked. */
+    private long mLastRpmStatsUpdateTimeMs = -RPM_STATS_UPDATE_FREQ_MS;
+
     public interface BatteryCallback {
         public void batteryNeedsCpuUpdate();
         public void batteryPowerChanged(boolean onBattery);
@@ -202,6 +209,7 @@
     }
 
     public interface PlatformIdleStateCallback {
+        public void fillLowPowerStats(RpmStats rpmStats);
         public String getPlatformLowPowerStats();
         public String getSubsystemLowPowerStats();
     }
@@ -279,7 +287,8 @@
         int UPDATE_WIFI = 0x02;
         int UPDATE_RADIO = 0x04;
         int UPDATE_BT = 0x08;
-        int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT;
+        int UPDATE_RPM = 0x10; // 16
+        int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM;
 
         Future<?> scheduleSync(String reason, int flags);
         Future<?> scheduleCpuSyncDueToRemovedUid(int uid);
@@ -628,6 +637,25 @@
     protected PowerProfile mPowerProfile;
 
     /*
+     * Holds a SamplingTimer associated with each Resource Power Manager state and voter,
+     * recording their times when on-battery (regardless of screen state).
+     */
+    private final HashMap<String, SamplingTimer> mRpmStats = new HashMap<>();
+    /** Times for each Resource Power Manager state and voter when screen-off and on-battery. */
+    private final HashMap<String, SamplingTimer> mScreenOffRpmStats = new HashMap<>();
+
+    @Override
+    public Map<String, ? extends Timer> getRpmStats() {
+        return mRpmStats;
+    }
+
+    // TODO: Note: screenOffRpmStats has been disabled via SCREEN_OFF_RPM_STATS_ENABLED.
+    @Override
+    public Map<String, ? extends Timer> getScreenOffRpmStats() {
+        return mScreenOffRpmStats;
+    }
+
+    /*
      * Holds a SamplingTimer associated with each kernel wakelock name being tracked.
      */
     private final HashMap<String, SamplingTimer> mKernelWakelockStats = new HashMap<>();
@@ -2648,6 +2676,26 @@
         }
     }
 
+    /** Get Resource Power Manager stats. Create a new one if it doesn't already exist. */
+    public SamplingTimer getRpmTimerLocked(String name) {
+        SamplingTimer rpmt = mRpmStats.get(name);
+        if (rpmt == null) {
+            rpmt = new SamplingTimer(mClocks, mOnBatteryTimeBase);
+            mRpmStats.put(name, rpmt);
+        }
+        return rpmt;
+    }
+
+    /** Get Screen-off Resource Power Manager stats. Create new one if it doesn't already exist. */
+    public SamplingTimer getScreenOffRpmTimerLocked(String name) {
+        SamplingTimer rpmt = mScreenOffRpmStats.get(name);
+        if (rpmt == null) {
+            rpmt = new SamplingTimer(mClocks, mOnBatteryScreenOffTimeBase);
+            mScreenOffRpmStats.put(name, rpmt);
+        }
+        return rpmt;
+    }
+
     /*
      * Get the wakeup reason counter, and create a new one if one
      * doesn't already exist.
@@ -3536,6 +3584,12 @@
                 updateKernelWakelocksLocked();
                 updateBatteryPropertiesLocked();
             }
+            // This if{} is only necessary due to SCREEN_OFF_RPM_STATS_ENABLED, which exists because
+            // updateRpmStatsLocked is too slow to run each screen change. When the speed is
+            // improved, remove the surrounding if{}.
+            if (SCREEN_OFF_RPM_STATS_ENABLED || updateOnBatteryTimeBase) {
+                updateRpmStatsLocked(); // if either OnBattery or OnBatteryScreenOff timebase changes.
+            }
             if (DEBUG_ENERGY_CPU) {
                 Slog.d(TAG, "Updating cpu time because screen is now " + (screenOff ? "off" : "on")
                         + " and battery is " + (unplugged ? "on" : "off"));
@@ -9501,6 +9555,19 @@
             }
         }
 
+        if (mRpmStats.size() > 0) {
+            for (SamplingTimer timer : mRpmStats.values()) {
+                mOnBatteryTimeBase.remove(timer);
+            }
+            mRpmStats.clear();
+        }
+        if (mScreenOffRpmStats.size() > 0) {
+            for (SamplingTimer timer : mScreenOffRpmStats.values()) {
+                mOnBatteryScreenOffTimeBase.remove(timer);
+            }
+            mScreenOffRpmStats.clear();
+        }
+
         if (mKernelWakelockStats.size() > 0) {
             for (SamplingTimer timer : mKernelWakelockStats.values()) {
                 mOnBatteryScreenOffTimeBase.remove(timer);
@@ -10203,6 +10270,61 @@
     }
 
     /**
+     * Read and record Resource Power Manager (RPM) state and voter times.
+     * If RPM stats were fetched more recently than RPM_STATS_UPDATE_FREQ_MS ago, uses the old data
+     * instead of fetching it anew.
+     */
+    public void updateRpmStatsLocked() {
+        if (mPlatformIdleStateCallback == null) return;
+        long now = SystemClock.elapsedRealtime();
+        if (now - mLastRpmStatsUpdateTimeMs >= RPM_STATS_UPDATE_FREQ_MS) {
+            mPlatformIdleStateCallback.fillLowPowerStats(mTmpRpmStats);
+            mLastRpmStatsUpdateTimeMs = now;
+        }
+
+        for (Map.Entry<String, RpmStats.PowerStatePlatformSleepState> pstate
+                : mTmpRpmStats.mPlatformLowPowerStats.entrySet()) {
+
+            // Update values for this platform state.
+            final String pName = pstate.getKey();
+            final long pTimeUs = pstate.getValue().mTimeMs * 1000;
+            final int pCount = pstate.getValue().mCount;
+            getRpmTimerLocked(pName).update(pTimeUs, pCount);
+            if (SCREEN_OFF_RPM_STATS_ENABLED) {
+                getScreenOffRpmTimerLocked(pName).update(pTimeUs, pCount);
+            }
+
+            // Update values for each voter of this platform state.
+            for (Map.Entry<String, RpmStats.PowerStateElement> voter
+                    : pstate.getValue().mVoters.entrySet()) {
+                final String vName = pName + "." + voter.getKey();
+                final long vTimeUs = voter.getValue().mTimeMs * 1000;
+                final int vCount = voter.getValue().mCount;
+                getRpmTimerLocked(vName).update(vTimeUs, vCount);
+                if (SCREEN_OFF_RPM_STATS_ENABLED) {
+                    getScreenOffRpmTimerLocked(vName).update(vTimeUs, vCount);
+                }
+            }
+        }
+
+        for (Map.Entry<String, RpmStats.PowerStateSubsystem> subsys
+                : mTmpRpmStats.mSubsystemLowPowerStats.entrySet()) {
+
+            final String subsysName = subsys.getKey();
+            for (Map.Entry<String, RpmStats.PowerStateElement> sstate
+                    : subsys.getValue().mStates.entrySet()) {
+                final String name = subsysName + "." + sstate.getKey();
+                final long timeUs = sstate.getValue().mTimeMs * 1000;
+                final int count = sstate.getValue().mCount;
+                getRpmTimerLocked(name).update(timeUs, count);
+                if (SCREEN_OFF_RPM_STATS_ENABLED) {
+                    getScreenOffRpmTimerLocked(name).update(timeUs, count);
+                }
+            }
+        }
+    }
+
+    /**
      * Read and distribute kernel wake lock use across apps.
      */
     public void updateKernelWakelocksLocked() {
@@ -11725,6 +11847,27 @@
         mBluetoothScanNesting = 0;
         mBluetoothScanTimer.readSummaryFromParcelLocked(in);
 
+        int NRPMS = in.readInt();
+        if (NRPMS > 10000) {
+            throw new ParcelFormatException("File corrupt: too many rpm stats " + NRPMS);
+        }
+        for (int irpm = 0; irpm < NRPMS; irpm++) {
+            if (in.readInt() != 0) {
+                String rpmName = in.readString();
+                getRpmTimerLocked(rpmName).readSummaryFromParcelLocked(in);
+            }
+        }
+        int NSORPMS = in.readInt();
+        if (NSORPMS > 10000) {
+            throw new ParcelFormatException("File corrupt: too many screen-off rpm stats " + NSORPMS);
+        }
+        for (int irpm = 0; irpm < NSORPMS; irpm++) {
+            if (in.readInt() != 0) {
+                String rpmName = in.readString();
+                getScreenOffRpmTimerLocked(rpmName).readSummaryFromParcelLocked(in);
+            }
+        }
+
         int NKW = in.readInt();
         if (NKW > 10000) {
             throw new ParcelFormatException("File corrupt: too many kernel wake locks " + NKW);
@@ -12111,6 +12254,29 @@
         mCameraOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mBluetoothScanTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
 
+        out.writeInt(mRpmStats.size());
+        for (Map.Entry<String, SamplingTimer> ent : mRpmStats.entrySet()) {
+            Timer rpmt = ent.getValue();
+            if (rpmt != null) {
+                out.writeInt(1);
+                out.writeString(ent.getKey());
+                rpmt.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+            } else {
+                out.writeInt(0);
+            }
+        }
+        out.writeInt(mScreenOffRpmStats.size());
+        for (Map.Entry<String, SamplingTimer> ent : mScreenOffRpmStats.entrySet()) {
+            Timer rpmt = ent.getValue();
+            if (rpmt != null) {
+                out.writeInt(1);
+                out.writeString(ent.getKey());
+                rpmt.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+            } else {
+                out.writeInt(0);
+            }
+        }
+
         out.writeInt(mKernelWakelockStats.size());
         for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
             Timer kwlt = ent.getValue();
@@ -12568,6 +12734,25 @@
         mDischargeScreenOffCounter = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mLastWriteTime = in.readLong();
 
+        mRpmStats.clear();
+        int NRPMS = in.readInt();
+        for (int irpm = 0; irpm < NRPMS; irpm++) {
+            if (in.readInt() != 0) {
+                String rpmName = in.readString();
+                SamplingTimer rpmt = new SamplingTimer(mClocks, mOnBatteryTimeBase, in);
+                mRpmStats.put(rpmName, rpmt);
+            }
+        }
+        mScreenOffRpmStats.clear();
+        int NSORPMS = in.readInt();
+        for (int irpm = 0; irpm < NSORPMS; irpm++) {
+            if (in.readInt() != 0) {
+                String rpmName = in.readString();
+                SamplingTimer rpmt = new SamplingTimer(mClocks, mOnBatteryScreenOffTimeBase, in);
+                mScreenOffRpmStats.put(rpmName, rpmt);
+            }
+        }
+
         mKernelWakelockStats.clear();
         int NKW = in.readInt();
         for (int ikw = 0; ikw < NKW; ikw++) {
@@ -12731,6 +12916,29 @@
         mDischargeScreenOffCounter.writeToParcel(out);
         out.writeLong(mLastWriteTime);
 
+        out.writeInt(mRpmStats.size());
+        for (Map.Entry<String, SamplingTimer> ent : mRpmStats.entrySet()) {
+            SamplingTimer rpmt = ent.getValue();
+            if (rpmt != null) {
+                out.writeInt(1);
+                out.writeString(ent.getKey());
+                rpmt.writeToParcel(out, uSecRealtime);
+            } else {
+                out.writeInt(0);
+            }
+        }
+        out.writeInt(mScreenOffRpmStats.size());
+        for (Map.Entry<String, SamplingTimer> ent : mScreenOffRpmStats.entrySet()) {
+            SamplingTimer rpmt = ent.getValue();
+            if (rpmt != null) {
+                out.writeInt(1);
+                out.writeString(ent.getKey());
+                rpmt.writeToParcel(out, uSecRealtime);
+            } else {
+                out.writeInt(0);
+            }
+        }
+
         if (inclUids) {
             out.writeInt(mKernelWakelockStats.size());
             for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
@@ -12755,6 +12963,7 @@
                 }
             }
         } else {
+            // TODO: There should be two 0's printed here, not just one.
             out.writeInt(0);
         }
 
diff --git a/com/android/internal/os/ClassLoaderFactory.java b/com/android/internal/os/ClassLoaderFactory.java
index b2b769e..387857f 100644
--- a/com/android/internal/os/ClassLoaderFactory.java
+++ b/com/android/internal/os/ClassLoaderFactory.java
@@ -88,12 +88,20 @@
         final ClassLoader classLoader = createClassLoader(dexPath, librarySearchPath, parent,
                 classloaderName);
 
+        boolean isForVendor = false;
+        for (String path : dexPath.split(":")) {
+            if (path.startsWith("/vendor/")) {
+                isForVendor = true;
+                break;
+            }
+        }
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "createClassloaderNamespace");
         String errorMessage = createClassloaderNamespace(classLoader,
                                                          targetSdkVersion,
                                                          librarySearchPath,
                                                          libraryPermittedPath,
-                                                         isNamespaceShared);
+                                                         isNamespaceShared,
+                                                         isForVendor);
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
         if (errorMessage != null) {
@@ -108,5 +116,6 @@
                                                             int targetSdkVersion,
                                                             String librarySearchPath,
                                                             String libraryPermittedPath,
-                                                            boolean isNamespaceShared);
+                                                            boolean isNamespaceShared,
+                                                            boolean isForVendor);
 }
diff --git a/com/android/internal/os/RpmStats.java b/com/android/internal/os/RpmStats.java
new file mode 100644
index 0000000..befc76e
--- /dev/null
+++ b/com/android/internal/os/RpmStats.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 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 com.android.internal.os;
+
+import android.util.ArrayMap;
+
+import java.util.Map;
+
+/**
+ * Container for Resource Power Manager states and their data.
+ * Values can be populated by the BatteryStatsService.fillLowPowerStats jni function.
+ */
+public final class RpmStats {
+    public Map<String, PowerStatePlatformSleepState> mPlatformLowPowerStats = new ArrayMap<>();
+    public Map<String, PowerStateSubsystem> mSubsystemLowPowerStats = new ArrayMap<>();
+
+    /**
+     * Finds the PowerStatePlatformSleepState with the given name (creating it if it doesn't exist),
+     * updates its timeMs and count, and returns it.
+     */
+    @SuppressWarnings("unused")
+    public PowerStatePlatformSleepState getAndUpdatePlatformState(
+            String name, long timeMs, int count) {
+
+        PowerStatePlatformSleepState e = mPlatformLowPowerStats.get(name);
+        if (e == null) {
+            e = new PowerStatePlatformSleepState();
+            mPlatformLowPowerStats.put(name, e);
+        }
+        e.mTimeMs = timeMs;
+        e.mCount = count;
+        return e;
+    }
+
+    /**
+     * Returns the PowerStateSubsystem with the given name (creating it if it doesn't exist).
+     */
+    public PowerStateSubsystem getSubsystem(String name) {
+        PowerStateSubsystem e = mSubsystemLowPowerStats.get(name);
+        if (e == null) {
+            e = new PowerStateSubsystem();
+            mSubsystemLowPowerStats.put(name, e);
+        }
+        return e;
+    }
+
+    /** Represents a subsystem state or a platform voter. */
+    public static class PowerStateElement {
+        public long mTimeMs; // totalTimeInMsecVotedForSinceBoot
+        public int mCount; // totalNumberOfTimesVotedSinceBoot
+
+        private PowerStateElement(long timeMs, int count) {
+            this.mTimeMs = timeMs;
+            this.mCount = count;
+        }
+    }
+
+    /** Represents a PowerStatePlatformSleepState, per hardware/interfaces/power/1.0/types.hal */
+    public static class PowerStatePlatformSleepState {
+        public long mTimeMs; // residencyInMsecSinceBoot
+        public int mCount; // totalTransitions
+        public Map<String, PowerStateElement> mVoters = new ArrayMap<>(); // voters for this platform-level sleep state
+
+        /**
+         * Updates (creating if necessary) the voter with the given name, with the given timeMs and
+         * count.
+         */
+        @SuppressWarnings("unused")
+        public void putVoter(String name, long timeMs, int count) {
+            PowerStateElement e = mVoters.get(name);
+            if (e == null) {
+                mVoters.put(name, new PowerStateElement(timeMs, count));
+            } else {
+                e.mTimeMs = timeMs;
+                e.mCount = count;
+            }
+        }
+    }
+
+    /** Represents a PowerStateSubsystem, per hardware/interfaces/power/1.1/types.hal */
+    public static class PowerStateSubsystem {
+        public Map<String, PowerStateElement> mStates = new ArrayMap<>(); // sleep states supported by this susbsystem
+
+        /**
+         * Updates (creating if necessary) the subsystem state with the given name, with the given
+         * timeMs and count.
+         */
+        @SuppressWarnings("unused")
+        public void putState(String name, long timeMs, int count) {
+            PowerStateElement e = mStates.get(name);
+            if (e == null) {
+                mStates.put(name, new PowerStateElement(timeMs, count));
+            } else {
+                e.mTimeMs = timeMs;
+                e.mCount = count;
+            }
+        }
+    }
+}
diff --git a/com/android/internal/os/Zygote.java b/com/android/internal/os/Zygote.java
index 4e4b5b8..5ee0918 100644
--- a/com/android/internal/os/Zygote.java
+++ b/com/android/internal/os/Zygote.java
@@ -26,7 +26,7 @@
 /** @hide */
 public final class Zygote {
     /*
-    * Bit values for "debugFlags" argument.  The definitions are duplicated
+    * Bit values for "runtimeFlags" argument.  The definitions are duplicated
     * in the native code.
     */
 
@@ -73,7 +73,7 @@
      * fork()ing and and before spawning any threads.
      * @param gids null-ok; a list of UNIX gids that the new process should
      * setgroups() to after fork and before spawning any threads.
-     * @param debugFlags bit flags that enable debugging features.
+     * @param runtimeFlags bit flags that enable ART features.
      * @param rlimits null-ok an array of rlimit tuples, with the second
      * dimension having a length of 3 and representing
      * (resource, rlim_cur, rlim_max). These are set via the posix
@@ -94,18 +94,18 @@
      * @return 0 if this is the child, pid of the child
      * if this is the parent, or -1 on error.
      */
-    public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
+    public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
           int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
           int[] fdsToIgnore, String instructionSet, String appDataDir) {
         VM_HOOKS.preFork();
         // Resets nice priority for zygote process.
         resetNicePriority();
         int pid = nativeForkAndSpecialize(
-                  uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
+                  uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
                   fdsToIgnore, instructionSet, appDataDir);
         // Enable tracing as soon as possible for the child process.
         if (pid == 0) {
-            Trace.setTracingEnabled(true, debugFlags);
+            Trace.setTracingEnabled(true, runtimeFlags);
 
             // Note that this event ends at the end of handleChildProc,
             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -114,7 +114,7 @@
         return pid;
     }
 
-    native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int debugFlags,
+    native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int runtimeFlags,
           int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
           int[] fdsToIgnore, String instructionSet, String appDataDir);
 
@@ -135,7 +135,7 @@
      * fork()ing and and before spawning any threads.
      * @param gids null-ok; a list of UNIX gids that the new process should
      * setgroups() to after fork and before spawning any threads.
-     * @param debugFlags bit flags that enable debugging features.
+     * @param runtimeFlags bit flags that enable ART features.
      * @param rlimits null-ok an array of rlimit tuples, with the second
      * dimension having a length of 3 and representing
      * (resource, rlim_cur, rlim_max). These are set via the posix
@@ -146,22 +146,22 @@
      * @return 0 if this is the child, pid of the child
      * if this is the parent, or -1 on error.
      */
-    public static int forkSystemServer(int uid, int gid, int[] gids, int debugFlags,
+    public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
             int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
         VM_HOOKS.preFork();
         // Resets nice priority for zygote process.
         resetNicePriority();
         int pid = nativeForkSystemServer(
-                uid, gid, gids, debugFlags, rlimits, permittedCapabilities, effectiveCapabilities);
+                uid, gid, gids, runtimeFlags, rlimits, permittedCapabilities, effectiveCapabilities);
         // Enable tracing as soon as we enter the system_server.
         if (pid == 0) {
-            Trace.setTracingEnabled(true, debugFlags);
+            Trace.setTracingEnabled(true, runtimeFlags);
         }
         VM_HOOKS.postForkCommon();
         return pid;
     }
 
-    native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags,
+    native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
             int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
 
     /**
@@ -175,9 +175,9 @@
      */
     native protected static void nativeUnmountStorageOnInit();
 
-    private static void callPostForkChildHooks(int debugFlags, boolean isSystemServer,
+    private static void callPostForkChildHooks(int runtimeFlags, boolean isSystemServer,
             String instructionSet) {
-        VM_HOOKS.postForkChild(debugFlags, isSystemServer, instructionSet);
+        VM_HOOKS.postForkChild(runtimeFlags, isSystemServer, instructionSet);
     }
 
     /**
diff --git a/com/android/internal/os/ZygoteConnection.java b/com/android/internal/os/ZygoteConnection.java
index 9fa3239..6a87b1f 100644
--- a/com/android/internal/os/ZygoteConnection.java
+++ b/com/android/internal/os/ZygoteConnection.java
@@ -220,7 +220,7 @@
         fd = null;
 
         pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
-                parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
+                parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                 parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
                 parsedArgs.appDataDir);
 
@@ -349,11 +349,9 @@
         int[] gids;
 
         /**
-         * From --enable-jdwp, --enable-checkjni, --enable-assert,
-         * --enable-safemode, --generate-debug-info, --enable-jni-logging,
-         * --java-debuggable, and --native-debuggable.
+         * From --runtime-flags.
          */
-        int debugFlags;
+        int runtimeFlags;
 
         /** From --mount-external */
         int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
@@ -469,26 +467,11 @@
                     targetSdkVersionSpecified = true;
                     targetSdkVersion = Integer.parseInt(
                             arg.substring(arg.indexOf('=') + 1));
-                } else if (arg.equals("--enable-jdwp")) {
-                    debugFlags |= Zygote.DEBUG_ENABLE_JDWP;
-                } else if (arg.equals("--enable-safemode")) {
-                    debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
-                } else if (arg.equals("--enable-checkjni")) {
-                    debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
-                } else if (arg.equals("--generate-debug-info")) {
-                    debugFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
-                } else if (arg.equals("--always-jit")) {
-                    debugFlags |= Zygote.DEBUG_ALWAYS_JIT;
-                } else if (arg.equals("--native-debuggable")) {
-                    debugFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE;
-                } else if (arg.equals("--java-debuggable")) {
-                    debugFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
-                } else if (arg.equals("--enable-jni-logging")) {
-                    debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
-                } else if (arg.equals("--enable-assert")) {
-                    debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
                 } else if (arg.equals("--runtime-args")) {
                     seenRuntimeArgs = true;
+                } else if (arg.startsWith("--runtime-flags=")) {
+                    runtimeFlags = Integer.parseInt(
+                            arg.substring(arg.indexOf('=') + 1));
                 } else if (arg.startsWith("--seinfo=")) {
                     if (seInfoSpecified) {
                         throw new IllegalArgumentException(
@@ -704,7 +687,7 @@
      */
     public static void applyDebuggerSystemProperty(Arguments args) {
         if (RoSystemProperties.DEBUGGABLE) {
-            args.debugFlags |= Zygote.DEBUG_ENABLE_JDWP;
+            args.runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
         }
     }
 
@@ -726,7 +709,7 @@
         int peerUid = peer.getUid();
 
         if (args.invokeWith != null && peerUid != 0 &&
-            (args.debugFlags & Zygote.DEBUG_ENABLE_JDWP) == 0) {
+            (args.runtimeFlags & Zygote.DEBUG_ENABLE_JDWP) == 0) {
             throw new ZygoteSecurityException("Peer is permitted to specify an"
                     + "explicit invoke-with wrapper command only for debuggable"
                     + "applications.");
diff --git a/com/android/internal/os/ZygoteInit.java b/com/android/internal/os/ZygoteInit.java
index 7058193..4abab28 100644
--- a/com/android/internal/os/ZygoteInit.java
+++ b/com/android/internal/os/ZygoteInit.java
@@ -669,7 +669,7 @@
             pid = Zygote.forkSystemServer(
                     parsedArgs.uid, parsedArgs.gid,
                     parsedArgs.gids,
-                    parsedArgs.debugFlags,
+                    parsedArgs.runtimeFlags,
                     null,
                     parsedArgs.permittedCapabilities,
                     parsedArgs.effectiveCapabilities);
diff --git a/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
index abaf061..ab30878 100644
--- a/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -136,6 +136,7 @@
             "UTLTE", "UTWiFi"};
 
     private TelephonyMetrics mMetrics;
+    private boolean mCarrierConfigLoaded = false;
 
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -220,6 +221,7 @@
                     log("onReceive : Updating mAllowEmergencyVideoCalls = " +
                             mAllowEmergencyVideoCalls);
                 }
+                mCarrierConfigLoaded  = true;
             } else if (TelecomManager.ACTION_CHANGE_DEFAULT_DIALER.equals(intent.getAction())) {
                 mDefaultDialerUid.set(getPackageUid(context, intent.getStringExtra(
                         TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME)));
@@ -726,6 +728,8 @@
             return NetworkStats.UID_ALL;
         }
 
+        // Initialize to UID_ALL so at least it can be counted to overall data usage if
+        // the dialer's package uid is not available.
         int uid = NetworkStats.UID_ALL;
         try {
             uid = context.getPackageManager().getPackageUid(pkg, 0);
@@ -778,6 +782,11 @@
             multiEndpoint.setExternalCallStateListener(
                     mPhone.getExternalCallTracker().getExternalCallStateListener());
         }
+
+        if (mCarrierConfigLoaded) {
+            ImsManager.updateImsServiceConfig(mPhone.getContext(),
+                    mPhone.getPhoneId(), true);
+        }
     }
 
     private void stopListeningForCalls() {
@@ -3007,6 +3016,17 @@
         // a separate entry if uid is different from the previous snapshot.
         NetworkStats vtDataUsageUidSnapshot = new NetworkStats(currentTime, 1);
         vtDataUsageUidSnapshot.combineAllValues(mVtDataUsageUidSnapshot);
+
+        // The dialer uid might not be initialized correctly during boot up due to telecom service
+        // not ready or its default dialer cache not ready. So we double check again here to see if
+        // default dialer uid is really not available.
+        if (mDefaultDialerUid.get() == NetworkStats.UID_ALL) {
+            final TelecomManager telecomManager =
+                    (TelecomManager) mPhone.getContext().getSystemService(Context.TELECOM_SERVICE);
+            mDefaultDialerUid.set(
+                    getPackageUid(mPhone.getContext(), telecomManager.getDefaultDialerPackage()));
+        }
+
         // Since the modem only reports the total vt data usage rather than rx/tx separately,
         // the only thing we can do here is splitting the usage into half rx and half tx.
         vtDataUsageUidSnapshot.combineValues(new NetworkStats.Entry(
diff --git a/com/android/providers/settings/SettingsProtoDumpUtil.java b/com/android/providers/settings/SettingsProtoDumpUtil.java
index 502b254..ec6f831 100644
--- a/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1443,6 +1443,9 @@
         dumpSetting(s, p,
                 Settings.Secure.NOTIFICATION_BADGING,
                 SecureSettingsProto.NOTIFICATION_BADGING);
+        dumpSetting(s, p,
+                Settings.Secure.BACKUP_MANAGER_CONSTANTS,
+                SecureSettingsProto.BACKUP_MANAGER_CONSTANTS);
     }
 
     private static void dumpProtoSystemSettingsLocked(
diff --git a/com/android/providers/settings/SettingsProvider.java b/com/android/providers/settings/SettingsProvider.java
index 7d7f9ae..a463db6 100644
--- a/com/android/providers/settings/SettingsProvider.java
+++ b/com/android/providers/settings/SettingsProvider.java
@@ -2896,7 +2896,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 148;
+            private static final int SETTINGS_VERSION = 149;
 
             private final int mUserId;
 
@@ -3456,6 +3456,23 @@
                     currentVersion = 148;
                 }
 
+                if (currentVersion == 148) {
+                    // Version 149: Set the default value for BACKUP_MANAGER_CONSTANTS.
+                    final SettingsState systemSecureSettings = getSecureSettingsLocked(userId);
+                    final String oldValue = systemSecureSettings.getSettingLocked(
+                            Settings.Secure.BACKUP_MANAGER_CONSTANTS).getValue();
+                    if (TextUtils.equals(null, oldValue)) {
+                        final String defaultValue = getContext().getResources().getString(
+                                R.string.def_backup_manager_constants);
+                        if (!TextUtils.isEmpty(defaultValue)) {
+                            systemSecureSettings.insertSettingLocked(
+                                    Settings.Secure.BACKUP_MANAGER_CONSTANTS, defaultValue, null,
+                                    true, SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+                    }
+
+                    currentVersion = 149;
+                }
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/com/android/server/ConnectivityService.java b/com/android/server/ConnectivityService.java
index e70a294..bfe5040 100644
--- a/com/android/server/ConnectivityService.java
+++ b/com/android/server/ConnectivityService.java
@@ -4443,12 +4443,9 @@
 
     private void updateInterfaces(LinkProperties newLp, LinkProperties oldLp, int netId,
                                   NetworkCapabilities caps) {
-        CompareResult<String> interfaceDiff = new CompareResult<String>();
-        if (oldLp != null) {
-            interfaceDiff = oldLp.compareAllInterfaceNames(newLp);
-        } else if (newLp != null) {
-            interfaceDiff.added = newLp.getAllInterfaceNames();
-        }
+        CompareResult<String> interfaceDiff = new CompareResult<String>(
+                oldLp != null ? oldLp.getAllInterfaceNames() : null,
+                newLp != null ? newLp.getAllInterfaceNames() : null);
         for (String iface : interfaceDiff.added) {
             try {
                 if (DBG) log("Adding iface " + iface + " to network " + netId);
@@ -4474,12 +4471,10 @@
      * @return true if routes changed between oldLp and newLp
      */
     private boolean updateRoutes(LinkProperties newLp, LinkProperties oldLp, int netId) {
-        CompareResult<RouteInfo> routeDiff = new CompareResult<RouteInfo>();
-        if (oldLp != null) {
-            routeDiff = oldLp.compareAllRoutes(newLp);
-        } else if (newLp != null) {
-            routeDiff.added = newLp.getAllRoutes();
-        }
+        // Compare the route diff to determine which routes should be added and removed.
+        CompareResult<RouteInfo> routeDiff = new CompareResult<RouteInfo>(
+                oldLp != null ? oldLp.getAllRoutes() : null,
+                newLp != null ? newLp.getAllRoutes() : null);
 
         // add routes before removing old in case it helps with continuous connectivity
 
diff --git a/com/android/server/NativeDaemonConnector.java b/com/android/server/NativeDaemonConnector.java
index f5f7732..b5a8332 100644
--- a/com/android/server/NativeDaemonConnector.java
+++ b/com/android/server/NativeDaemonConnector.java
@@ -24,11 +24,13 @@
 import android.os.Message;
 import android.os.PowerManager;
 import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.util.LocalLog;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
+import com.android.server.power.ShutdownThread;
 import com.google.android.collect.Lists;
 
 import java.io.FileDescriptor;
@@ -136,6 +138,12 @@
                 listenToSocket();
             } catch (Exception e) {
                 loge("Error in NativeDaemonConnector: " + e);
+                String shutdownAct = SystemProperties.get(
+                        ShutdownThread.SHUTDOWN_ACTION_PROPERTY, "");
+                if (shutdownAct != null && shutdownAct.length() > 0) {
+                    // The device is in middle of shutdown.
+                    break;
+                }
                 SystemClock.sleep(5000);
             }
         }
diff --git a/com/android/server/StorageManagerService.java b/com/android/server/StorageManagerService.java
index ceb94ff..c0fcfd0 100644
--- a/com/android/server/StorageManagerService.java
+++ b/com/android/server/StorageManagerService.java
@@ -57,10 +57,13 @@
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.IVold;
+import android.os.IVoldListener;
+import android.os.IVoldTaskListener;
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
 import android.os.ParcelableException;
+import android.os.PersistableBundle;
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteCallbackList;
@@ -143,6 +146,7 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -286,10 +290,6 @@
         public static final int VOLUME_PATH_CHANGED = 655;
         public static final int VOLUME_INTERNAL_PATH_CHANGED = 656;
         public static final int VOLUME_DESTROYED = 659;
-
-        public static final int MOVE_STATUS = 660;
-        public static final int BENCHMARK_RESULT = 661;
-        public static final int TRIM_RESULT = 662;
     }
 
     private static final int VERSION_INIT = 1;
@@ -672,8 +672,8 @@
                         Slog.e(TAG, "Unable to record last fstrim!");
                     }
 
-                    final int flags = shouldBenchmark() ? StorageManager.FSTRIM_FLAG_BENCHMARK : 0;
-                    fstrim(flags);
+                    // TODO: Reintroduce shouldBenchmark() test
+                    fstrim(0);
 
                     // invoke the completion callback, if any
                     // TODO: fstrim is non-blocking, so remove this useless callback
@@ -1128,26 +1128,21 @@
     @Override
     public boolean onEvent(int code, String raw, String[] cooked) {
         synchronized (mLock) {
-            return onEventLocked(code, raw, cooked);
+            try {
+                return onEventLocked(code, raw, cooked);
+            } catch (RemoteException e) {
+                throw e.rethrowAsRuntimeException();
+            }
         }
     }
 
-    private boolean onEventLocked(int code, String raw, String[] cooked) {
+    private boolean onEventLocked(int code, String raw, String[] cooked) throws RemoteException {
         switch (code) {
             case VoldResponseCode.DISK_CREATED: {
                 if (cooked.length != 3) break;
-                final String id = cooked[1];
-                int flags = Integer.parseInt(cooked[2]);
-                if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false)
-                        || mForceAdoptable) {
-                    flags |= DiskInfo.FLAG_ADOPTABLE;
-                }
-                // Adoptable storage isn't currently supported on FBE devices
-                if (StorageManager.isFileEncryptedNativeOnly()
-                        && !SystemProperties.getBoolean(StorageManager.PROP_ADOPTABLE_FBE, false)) {
-                    flags &= ~DiskInfo.FLAG_ADOPTABLE;
-                }
-                mDisks.put(id, new DiskInfo(id, flags));
+                final String diskId = cooked[1];
+                final int flags = Integer.parseInt(cooked[2]);
+                mListener.onDiskCreated(diskId, flags);
                 break;
             }
             case VoldResponseCode.DISK_SIZE_CHANGED: {
@@ -1171,10 +1166,8 @@
             }
             case VoldResponseCode.DISK_SCANNED: {
                 if (cooked.length != 2) break;
-                final DiskInfo disk = mDisks.get(cooked[1]);
-                if (disk != null) {
-                    onDiskScannedLocked(disk);
-                }
+                final String diskId = cooked[1];
+                mListener.onDiskScanned(diskId);
                 break;
             }
             case VoldResponseCode.DISK_SYS_PATH_CHANGED: {
@@ -1187,34 +1180,24 @@
             }
             case VoldResponseCode.DISK_DESTROYED: {
                 if (cooked.length != 2) break;
-                final DiskInfo disk = mDisks.remove(cooked[1]);
-                if (disk != null) {
-                    mCallbacks.notifyDiskDestroyed(disk);
-                }
+                final String diskId = cooked[1];
+                mListener.onDiskDestroyed(diskId);
                 break;
             }
 
             case VoldResponseCode.VOLUME_CREATED: {
-                final String id = cooked[1];
+                final String volId = cooked[1];
                 final int type = Integer.parseInt(cooked[2]);
                 final String diskId = TextUtils.nullIfEmpty(cooked[3]);
                 final String partGuid = TextUtils.nullIfEmpty(cooked[4]);
-
-                final DiskInfo disk = mDisks.get(diskId);
-                final VolumeInfo vol = new VolumeInfo(id, type, disk, partGuid);
-                mVolumes.put(id, vol);
-                onVolumeCreatedLocked(vol);
+                mListener.onVolumeCreated(volId, type, diskId, partGuid);
                 break;
             }
             case VoldResponseCode.VOLUME_STATE_CHANGED: {
                 if (cooked.length != 3) break;
-                final VolumeInfo vol = mVolumes.get(cooked[1]);
-                if (vol != null) {
-                    final int oldState = vol.state;
-                    final int newState = Integer.parseInt(cooked[2]);
-                    vol.state = newState;
-                    onVolumeStateChangedLocked(vol, oldState, newState);
-                }
+                final String volId = cooked[1];
+                final int state = Integer.parseInt(cooked[2]);
+                mListener.onVolumeStateChanged(volId, state);
                 break;
             }
             case VoldResponseCode.VOLUME_FS_TYPE_CHANGED: {
@@ -1247,71 +1230,24 @@
             }
             case VoldResponseCode.VOLUME_PATH_CHANGED: {
                 if (cooked.length != 3) break;
-                final VolumeInfo vol = mVolumes.get(cooked[1]);
-                if (vol != null) {
-                    vol.path = cooked[2];
-                }
+                final String volId = cooked[1];
+                final String path = cooked[2];
+                mListener.onVolumePathChanged(volId, path);
                 break;
             }
             case VoldResponseCode.VOLUME_INTERNAL_PATH_CHANGED: {
                 if (cooked.length != 3) break;
-                final VolumeInfo vol = mVolumes.get(cooked[1]);
-                if (vol != null) {
-                    vol.internalPath = cooked[2];
-                }
+                final String volId = cooked[1];
+                final String internalPath = cooked[2];
+                mListener.onVolumeInternalPathChanged(volId, internalPath);
                 break;
             }
             case VoldResponseCode.VOLUME_DESTROYED: {
                 if (cooked.length != 2) break;
-                mVolumes.remove(cooked[1]);
+                final String volId = cooked[1];
+                mListener.onVolumeDestroyed(volId);
                 break;
             }
-
-            case VoldResponseCode.MOVE_STATUS: {
-                final int status = Integer.parseInt(cooked[1]);
-                onMoveStatusLocked(status);
-                break;
-            }
-            case VoldResponseCode.BENCHMARK_RESULT: {
-                if (cooked.length != 7) break;
-                final String path = cooked[1];
-                final String ident = cooked[2];
-                final long create = Long.parseLong(cooked[3]);
-                final long drop = Long.parseLong(cooked[4]);
-                final long run = Long.parseLong(cooked[5]);
-                final long destroy = Long.parseLong(cooked[6]);
-
-                final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class);
-                dropBox.addText(TAG_STORAGE_BENCHMARK, scrubPath(path)
-                        + " " + ident + " " + create + " " + run + " " + destroy);
-
-                final VolumeRecord rec = findRecordForPath(path);
-                if (rec != null) {
-                    rec.lastBenchMillis = System.currentTimeMillis();
-                    writeSettingsLocked();
-                }
-
-                break;
-            }
-            case VoldResponseCode.TRIM_RESULT: {
-                if (cooked.length != 4) break;
-                final String path = cooked[1];
-                final long bytes = Long.parseLong(cooked[2]);
-                final long time = Long.parseLong(cooked[3]);
-
-                final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class);
-                dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path)
-                        + " " + bytes + " " + time);
-
-                final VolumeRecord rec = findRecordForPath(path);
-                if (rec != null) {
-                    rec.lastTrimMillis = System.currentTimeMillis();
-                    writeSettingsLocked();
-                }
-
-                break;
-            }
-
             default: {
                 Slog.d(TAG, "Unhandled vold event " + code);
             }
@@ -1320,6 +1256,120 @@
         return true;
     }
 
+    private final IVoldListener mListener = new IVoldListener.Stub() {
+        @Override
+        public void onDiskCreated(String diskId, int flags) {
+            synchronized (mLock) {
+                if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false)
+                        || mForceAdoptable) {
+                    flags |= DiskInfo.FLAG_ADOPTABLE;
+                }
+                // Adoptable storage isn't currently supported on FBE devices
+                if (StorageManager.isFileEncryptedNativeOnly()
+                        && !SystemProperties.getBoolean(StorageManager.PROP_ADOPTABLE_FBE, false)) {
+                    flags &= ~DiskInfo.FLAG_ADOPTABLE;
+                }
+                mDisks.put(diskId, new DiskInfo(diskId, flags));
+            }
+        }
+
+        @Override
+        public void onDiskScanned(String diskId) {
+            synchronized (mLock) {
+                final DiskInfo disk = mDisks.get(diskId);
+                if (disk != null) {
+                    onDiskScannedLocked(disk);
+                }
+            }
+        }
+
+        @Override
+        public void onDiskMetadataChanged(String diskId, long sizeBytes, String label,
+                String sysPath) {
+            synchronized (mLock) {
+                final DiskInfo disk = mDisks.get(diskId);
+                if (disk != null) {
+                    disk.size = sizeBytes;
+                    disk.label = label;
+                    disk.sysPath = sysPath;
+                }
+            }
+        }
+
+        @Override
+        public void onDiskDestroyed(String diskId) {
+            synchronized (mLock) {
+                final DiskInfo disk = mDisks.remove(diskId);
+                if (disk != null) {
+                    mCallbacks.notifyDiskDestroyed(disk);
+                }
+            }
+        }
+
+        @Override
+        public void onVolumeCreated(String volId, int type, String diskId, String partGuid) {
+            synchronized (mLock) {
+                final DiskInfo disk = mDisks.get(diskId);
+                final VolumeInfo vol = new VolumeInfo(volId, type, disk, partGuid);
+                mVolumes.put(volId, vol);
+                onVolumeCreatedLocked(vol);
+            }
+        }
+
+        @Override
+        public void onVolumeStateChanged(String volId, int state) {
+            synchronized (mLock) {
+                final VolumeInfo vol = mVolumes.get(volId);
+                if (vol != null) {
+                    final int oldState = vol.state;
+                    final int newState = state;
+                    vol.state = newState;
+                    onVolumeStateChangedLocked(vol, oldState, newState);
+                }
+            }
+        }
+
+        @Override
+        public void onVolumeMetadataChanged(String volId, String fsType, String fsUuid,
+                String fsLabel) {
+            synchronized (mLock) {
+                final VolumeInfo vol = mVolumes.get(volId);
+                if (vol != null) {
+                    vol.fsType = fsType;
+                    vol.fsUuid = fsUuid;
+                    vol.fsLabel = fsLabel;
+                }
+            }
+        }
+
+        @Override
+        public void onVolumePathChanged(String volId, String path) {
+            synchronized (mLock) {
+                final VolumeInfo vol = mVolumes.get(volId);
+                if (vol != null) {
+                    vol.path = path;
+                }
+            }
+        }
+
+        @Override
+        public void onVolumeInternalPathChanged(String volId, String internalPath) {
+            synchronized (mLock) {
+                final VolumeInfo vol = mVolumes.get(volId);
+                if (vol != null) {
+                    vol.internalPath = internalPath;
+                }
+            }
+        }
+
+        @Override
+        public void onVolumeDestroyed(String volId) {
+            synchronized (mLock) {
+                mVolumes.remove(volId);
+            }
+        }
+    };
+
     private void onDiskScannedLocked(DiskInfo disk) {
         int volumeCount = 0;
         for (int i = 0; i < mVolumes.size(); i++) {
@@ -1661,12 +1711,19 @@
 
         if (binder != null) {
             mVold = IVold.Stub.asInterface(binder);
+            try {
+                mVold.setListener(mListener);
+                return;
+            } catch (RemoteException e) {
+                Slog.w(TAG, "vold listener rejected; trying again", e);
+            }
         } else {
             Slog.w(TAG, "vold not found; trying again");
-            BackgroundThread.getHandler().postDelayed(() -> {
-                connect();
-            }, DateUtils.SECOND_IN_MILLIS);
         }
+
+        BackgroundThread.getHandler().postDelayed(() -> {
+            connect();
+        }, DateUtils.SECOND_IN_MILLIS);
     }
 
     private void systemReady() {
@@ -1923,15 +1980,39 @@
         enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
         waitForReady();
 
+        // TODO: refactor for callers to provide a listener
         try {
-            // TODO: make benchmark async so we don't block other commands
-            if (ENABLE_BINDER) {
-                return mVold.benchmark(volId);
-            } else {
-                final NativeDaemonEvent res = mConnector.execute(3 * DateUtils.MINUTE_IN_MILLIS,
-                        "volume", "benchmark", volId);
-                return Long.parseLong(res.getMessage());
-            }
+            final CompletableFuture<PersistableBundle> result = new CompletableFuture<>();
+            mVold.benchmark(volId, new IVoldTaskListener.Stub() {
+                @Override
+                public void onStatus(int status, PersistableBundle extras) {
+                    // Not currently used
+                }
+
+                @Override
+                public void onFinished(int status, PersistableBundle extras) {
+                    result.complete(extras);
+
+                    final String path = extras.getString("path");
+                    final String ident = extras.getString("ident");
+                    final long create = extras.getLong("create");
+                    final long run = extras.getLong("run");
+                    final long destroy = extras.getLong("destroy");
+
+                    final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class);
+                    dropBox.addText(TAG_STORAGE_BENCHMARK, scrubPath(path)
+                            + " " + ident + " " + create + " " + run + " " + destroy);
+
+                    synchronized (mLock) {
+                        final VolumeRecord rec = findRecordForPath(path);
+                        if (rec != null) {
+                            rec.lastBenchMillis = System.currentTimeMillis();
+                            writeSettingsLocked();
+                        }
+                    }
+                }
+            });
+            return result.get(3, TimeUnit.MINUTES).getLong("run", Long.MAX_VALUE);
         } catch (Exception e) {
             Slog.wtf(TAG, e);
             return Long.MAX_VALUE;
@@ -2095,16 +2176,36 @@
         } else {
             cmd = "dotrim";
         }
-        if ((flags & StorageManager.FSTRIM_FLAG_BENCHMARK) != 0) {
-            cmd += "bench";
-        }
 
         try {
-            if (ENABLE_BINDER) {
-                mVold.fstrim(flags);
-            } else {
-                mConnector.execute("fstrim", cmd);
-            }
+            mVold.fstrim(flags, new IVoldTaskListener.Stub() {
+                @Override
+                public void onStatus(int status, PersistableBundle extras) {
+                    // Ignore trim failures
+                    if (status != 0) return;
+
+                    final String path = extras.getString("path");
+                    final long bytes = extras.getLong("bytes");
+                    final long time = extras.getLong("time");
+
+                    final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class);
+                    dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path) + " " + bytes + " " + time);
+
+                    synchronized (mLock) {
+                        final VolumeRecord rec = findRecordForPath(path);
+                        if (rec != null) {
+                            rec.lastTrimMillis = System.currentTimeMillis();
+                            writeSettingsLocked();
+                        }
+                    }
+                }
+
+                @Override
+                public void onFinished(int status, PersistableBundle extras) {
+                    // Not currently used
+                    // TODO: benchmark when desired
+                }
+            });
         } catch (Exception e) {
             Slog.wtf(TAG, e);
         }
@@ -2284,11 +2385,19 @@
         }
 
         try {
-            if (ENABLE_BINDER) {
-                mVold.moveStorage(from.id, to.id);
-            } else {
-                mConnector.execute("volume", "move_storage", from.id, to.id);
-            }
+            mVold.moveStorage(from.id, to.id, new IVoldTaskListener.Stub() {
+                @Override
+                public void onStatus(int status, PersistableBundle extras) {
+                    synchronized (mLock) {
+                        onMoveStatusLocked(status);
+                    }
+                }
+
+                @Override
+                public void onFinished(int status, PersistableBundle extras) {
+                    // Not currently used
+                }
+            });
         } catch (Exception e) {
             Slog.wtf(TAG, e);
         }
@@ -2769,15 +2878,15 @@
 
     @Override
     public int decryptStorage(String password) {
-        if (TextUtils.isEmpty(password)) {
-            throw new IllegalArgumentException("password cannot be empty");
-        }
-
         mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
                 "no permission to access the crypt keeper");
 
         waitForReady();
 
+        if (TextUtils.isEmpty(password)) {
+            throw new IllegalArgumentException("password cannot be empty");
+        }
+
         if (DEBUG_EVENTS) {
             Slog.i(TAG, "decrypting storage...");
         }
@@ -2792,6 +2901,7 @@
                         Slog.wtf(TAG, e);
                     }
                 }, DateUtils.SECOND_IN_MILLIS);
+                return 0;
             } catch (Exception e) {
                 Slog.wtf(TAG, e);
                 return StorageManager.ENCRYPTION_STATE_ERROR_UNKNOWN;
@@ -2825,30 +2935,28 @@
     }
 
     public int encryptStorage(int type, String password) {
-        if (TextUtils.isEmpty(password) && type != StorageManager.CRYPT_TYPE_DEFAULT) {
-            throw new IllegalArgumentException("password cannot be empty");
-        }
-
         mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
             "no permission to access the crypt keeper");
 
         waitForReady();
 
+        if (type == StorageManager.CRYPT_TYPE_DEFAULT) {
+            password = "";
+        } else if (TextUtils.isEmpty(password)) {
+            throw new IllegalArgumentException("password cannot be empty");
+        }
+
         if (DEBUG_EVENTS) {
             Slog.i(TAG, "encrypting storage...");
         }
 
         try {
-            if (type == StorageManager.CRYPT_TYPE_DEFAULT) {
-                if (ENABLE_BINDER) {
-                    mVold.fdeEnable(type, null, IVold.ENCRYPTION_FLAG_IN_PLACE);
-                } else {
+            if (ENABLE_BINDER) {
+                mVold.fdeEnable(type, password, IVold.ENCRYPTION_FLAG_IN_PLACE);
+            } else {
+                if (type == StorageManager.CRYPT_TYPE_DEFAULT) {
                     mCryptConnector.execute("cryptfs", "enablecrypto", "inplace",
                             CRYPTO_TYPES[type]);
-                }
-            } else {
-                if (ENABLE_BINDER) {
-                    mVold.fdeEnable(type, password, IVold.ENCRYPTION_FLAG_IN_PLACE);
                 } else {
                     mCryptConnector.execute("cryptfs", "enablecrypto", "inplace",
                             CRYPTO_TYPES[type], new SensitiveArg(password));
@@ -2872,6 +2980,12 @@
 
         waitForReady();
 
+        if (type == StorageManager.CRYPT_TYPE_DEFAULT) {
+            password = "";
+        } else if (TextUtils.isEmpty(password)) {
+            throw new IllegalArgumentException("password cannot be empty");
+        }
+
         if (DEBUG_EVENTS) {
             Slog.i(TAG, "changing encryption password...");
         }
diff --git a/com/android/server/SystemConfig.java b/com/android/server/SystemConfig.java
index 7778892..b5031f2 100644
--- a/com/android/server/SystemConfig.java
+++ b/com/android/server/SystemConfig.java
@@ -44,7 +44,9 @@
 import java.io.FileReader;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Loads global system configuration info.
@@ -60,6 +62,7 @@
     private static final int ALLOW_PERMISSIONS = 0x04;
     private static final int ALLOW_APP_CONFIGS = 0x08;
     private static final int ALLOW_PRIVAPP_PERMISSIONS = 0x10;
+    private static final int ALLOW_OEM_PERMISSIONS = 0x20;
     private static final int ALLOW_ALL = ~0;
 
     // Group-ids that are given to all packages as read from etc/permissions/*.xml.
@@ -143,6 +146,8 @@
     final ArrayMap<String, ArraySet<String>> mPrivAppPermissions = new ArrayMap<>();
     final ArrayMap<String, ArraySet<String>> mPrivAppDenyPermissions = new ArrayMap<>();
 
+    final ArrayMap<String, ArrayMap<String, Boolean>> mOemPermissions = new ArrayMap<>();
+
     public static SystemConfig getInstance() {
         synchronized (SystemConfig.class) {
             if (sInstance == null) {
@@ -224,13 +229,23 @@
         return mPrivAppDenyPermissions.get(packageName);
     }
 
+    public Map<String, Boolean> getOemPermissions(String packageName) {
+        final Map<String, Boolean> oemPermissions = mOemPermissions.get(packageName);
+        if (oemPermissions != null) {
+            return oemPermissions;
+        }
+        return Collections.emptyMap();
+    }
+
     SystemConfig() {
         // Read configuration from system
         readPermissions(Environment.buildPath(
                 Environment.getRootDirectory(), "etc", "sysconfig"), ALLOW_ALL);
+
         // Read configuration from the old permissions dir
         readPermissions(Environment.buildPath(
                 Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
+
         // Allow Vendor to customize system configs around libs, features, permissions and apps
         int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PERMISSIONS |
                 ALLOW_APP_CONFIGS;
@@ -238,17 +253,20 @@
                 Environment.getVendorDirectory(), "etc", "sysconfig"), vendorPermissionFlag);
         readPermissions(Environment.buildPath(
                 Environment.getVendorDirectory(), "etc", "permissions"), vendorPermissionFlag);
+
         // Allow ODM to customize system configs around libs, features and apps
         int odmPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_APP_CONFIGS;
         readPermissions(Environment.buildPath(
                 Environment.getOdmDirectory(), "etc", "sysconfig"), odmPermissionFlag);
         readPermissions(Environment.buildPath(
                 Environment.getOdmDirectory(), "etc", "permissions"), odmPermissionFlag);
-        // Only allow OEM to customize features
+
+        // Allow OEM to customize features and OEM permissions
+        int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS;
         readPermissions(Environment.buildPath(
-                Environment.getOemDirectory(), "etc", "sysconfig"), ALLOW_FEATURES);
+                Environment.getOemDirectory(), "etc", "sysconfig"), oemPermissionFlag);
         readPermissions(Environment.buildPath(
-                Environment.getOemDirectory(), "etc", "permissions"), ALLOW_FEATURES);
+                Environment.getOemDirectory(), "etc", "permissions"), oemPermissionFlag);
     }
 
     void readPermissions(File libraryDir, int permissionFlag) {
@@ -327,6 +345,7 @@
             boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0;
             boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;
             boolean allowPrivappPermissions = (permissionFlag & ALLOW_PRIVAPP_PERMISSIONS) != 0;
+            boolean allowOemPermissions = (permissionFlag & ALLOW_OEM_PERMISSIONS) != 0;
             while (true) {
                 XmlUtils.nextElement(parser);
                 if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
@@ -569,6 +588,8 @@
                     XmlUtils.skipCurrentTag(parser);
                 } else if ("privapp-permissions".equals(name) && allowPrivappPermissions) {
                     readPrivAppPermissions(parser);
+                } else if ("oem-permissions".equals(name) && allowOemPermissions) {
+                    readOemPermissions(parser);
                 } else {
                     XmlUtils.skipCurrentTag(parser);
                     continue;
@@ -695,4 +716,40 @@
             mPrivAppDenyPermissions.put(packageName, denyPermissions);
         }
     }
+
+    void readOemPermissions(XmlPullParser parser) throws IOException, XmlPullParserException {
+        final String packageName = parser.getAttributeValue(null, "package");
+        if (TextUtils.isEmpty(packageName)) {
+            Slog.w(TAG, "package is required for <oem-permissions> in "
+                    + parser.getPositionDescription());
+            return;
+        }
+
+        ArrayMap<String, Boolean> permissions = mOemPermissions.get(packageName);
+        if (permissions == null) {
+            permissions = new ArrayMap<>();
+        }
+        final int depth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, depth)) {
+            final String name = parser.getName();
+            if ("permission".equals(name)) {
+                final String permName = parser.getAttributeValue(null, "name");
+                if (TextUtils.isEmpty(permName)) {
+                    Slog.w(TAG, "name is required for <permission> in "
+                            + parser.getPositionDescription());
+                    continue;
+                }
+                permissions.put(permName, Boolean.TRUE);
+            } else if ("deny-permission".equals(name)) {
+                String permName = parser.getAttributeValue(null, "name");
+                if (TextUtils.isEmpty(permName)) {
+                    Slog.w(TAG, "name is required for <deny-permission> in "
+                            + parser.getPositionDescription());
+                    continue;
+                }
+                permissions.put(permName, Boolean.FALSE);
+            }
+        }
+        mOemPermissions.put(packageName, permissions);
+    }
 }
diff --git a/com/android/server/am/ActivityManagerService.java b/com/android/server/am/ActivityManagerService.java
index d515326..02eb3b4 100644
--- a/com/android/server/am/ActivityManagerService.java
+++ b/com/android/server/am/ActivityManagerService.java
@@ -3839,38 +3839,38 @@
                     uid = 0;
                 }
             }
-            int debugFlags = 0;
+            int runtimeFlags = 0;
             if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
-                debugFlags |= Zygote.DEBUG_ENABLE_JDWP;
-                debugFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
+                runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
+                runtimeFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
                 // Also turn on CheckJNI for debuggable apps. It's quite
                 // awkward to turn on otherwise.
-                debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
+                runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
             }
             // Run the app in safe mode if its manifest requests so or the
             // system is booted in safe mode.
             if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 ||
                 mSafeMode == true) {
-                debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
+                runtimeFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
             }
             if ("1".equals(SystemProperties.get("debug.checkjni"))) {
-                debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
+                runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
             }
             String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");
             if ("true".equals(genDebugInfoProperty)) {
-                debugFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
+                runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
             }
             if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
-                debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
+                runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
             }
             if ("1".equals(SystemProperties.get("debug.assert"))) {
-                debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
+                runtimeFlags |= Zygote.DEBUG_ENABLE_ASSERT;
             }
             if (mNativeDebuggingApp != null && mNativeDebuggingApp.equals(app.processName)) {
                 // Enable all debug flags required by the native debugger.
-                debugFlags |= Zygote.DEBUG_ALWAYS_JIT;          // Don't interpret anything
-                debugFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; // Generate debug info
-                debugFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE;   // Disbale optimizations
+                runtimeFlags |= Zygote.DEBUG_ALWAYS_JIT;          // Don't interpret anything
+                runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; // Generate debug info
+                runtimeFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE;   // Disbale optimizations
                 mNativeDebuggingApp = null;
             }
 
@@ -3919,12 +3919,12 @@
             ProcessStartResult startResult;
             if (hostingType.equals("webview_service")) {
                 startResult = startWebView(entryPoint,
-                        app.processName, uid, uid, gids, debugFlags, mountExternal,
+                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, null, null);
             } else {
                 startResult = Process.start(entryPoint,
-                        app.processName, uid, uid, gids, debugFlags, mountExternal,
+                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                         app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                         app.info.dataDir, invokeWith, null);
             }
@@ -7007,7 +7007,7 @@
                 mProfileProc = app;
                 profilerInfo = (mProfilerInfo != null && mProfilerInfo.profileFile != null) ?
                         new ProfilerInfo(mProfilerInfo) : null;
-                agent = profilerInfo.agent;
+                agent = mProfilerInfo != null ? mProfilerInfo.agent : null;
             } else if (app.instr != null && app.instr.mProfileFile != null) {
                 profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
                         null);
diff --git a/com/android/server/am/ActivityStack.java b/com/android/server/am/ActivityStack.java
index 8c9e399..a6a702f 100644
--- a/com/android/server/am/ActivityStack.java
+++ b/com/android/server/am/ActivityStack.java
@@ -1199,7 +1199,6 @@
      * process of going to sleep (checkReadyForSleep will be called when that process finishes).
      */
     boolean goToSleepIfPossible(boolean shuttingDown) {
-        final ActivityRecord topActivity = topActivity();
         boolean shouldSleep = true;
 
         if (mResumedActivity != null) {
@@ -1224,11 +1223,6 @@
             // Still waiting for something to pause; can't sleep yet.
             if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Sleep still waiting to pause " + mPausingActivity);
             shouldSleep = false;
-        } else if (topActivity != null && topActivity.state == ActivityState.PAUSED) {
-            // Our top activity is currently paused, we need to ensure we move it to the stopped
-            // state.
-            stopActivityLocked(topActivity);
-            shouldSleep = false;
         }
 
         if (!shuttingDown) {
diff --git a/com/android/server/am/BatteryExternalStatsWorker.java b/com/android/server/am/BatteryExternalStatsWorker.java
index 5fac397..f3ccba5 100644
--- a/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/com/android/server/am/BatteryExternalStatsWorker.java
@@ -264,6 +264,10 @@
                 mStats.updateKernelMemoryBandwidthLocked();
             }
 
+            if ((updateFlags & UPDATE_RPM) != 0) {
+                mStats.updateRpmStatsLocked();
+            }
+
             if (bluetoothInfo != null) {
                 if (bluetoothInfo.isValid()) {
                     mStats.updateBluetoothStateLocked(bluetoothInfo);
diff --git a/com/android/server/am/BatteryStatsService.java b/com/android/server/am/BatteryStatsService.java
index 37f6a2d..3105e37 100644
--- a/com/android/server/am/BatteryStatsService.java
+++ b/com/android/server/am/BatteryStatsService.java
@@ -49,6 +49,7 @@
 import com.android.internal.os.BatteryStatsHelper;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.PowerProfile;
+import com.android.internal.os.RpmStats;
 import com.android.internal.util.DumpUtils;
 import com.android.server.LocalServices;
 import com.android.server.power.BatterySaverPolicy.ServiceType;
@@ -85,6 +86,7 @@
     private final Context mContext;
     private final BatteryExternalStatsWorker mWorker;
 
+    private native void getLowPowerStats(RpmStats rpmStats);
     private native int getPlatformLowPowerStats(ByteBuffer outBuffer);
     private native int getSubsystemLowPowerStats(ByteBuffer outBuffer);
     private CharsetDecoder mDecoderStat = StandardCharsets.UTF_8
@@ -96,6 +98,19 @@
     private CharBuffer mUtf16BufferStat = CharBuffer.allocate(MAX_LOW_POWER_STATS_SIZE);
     private static final int MAX_LOW_POWER_STATS_SIZE = 512;
 
+    /**
+     * Replaces the information in the given rpmStats with up-to-date information.
+     */
+    @Override
+    public void fillLowPowerStats(RpmStats rpmStats) {
+        if (DBG) Slog.d(TAG, "begin getLowPowerStats");
+        try {
+            getLowPowerStats(rpmStats);
+        } finally {
+            if (DBG) Slog.d(TAG, "end getLowPowerStats");
+        }
+    }
+
     @Override
     public String getPlatformLowPowerStats() {
         if (DBG) Slog.d(TAG, "begin getPlatformLowPowerStats");
@@ -120,7 +135,7 @@
 
     @Override
     public String getSubsystemLowPowerStats() {
-        Slog.d(TAG, "begin getSubsystemLowPowerStats");
+        if (DBG) Slog.d(TAG, "begin getSubsystemLowPowerStats");
         try {
             mUtf8BufferStat.clear();
             mUtf16BufferStat.clear();
@@ -136,7 +151,7 @@
             mUtf16BufferStat.flip();
             return mUtf16BufferStat.toString();
         } finally {
-            Slog.d(TAG, "end getSubsystemLowPowerStats");
+            if (DBG) Slog.d(TAG, "end getSubsystemLowPowerStats");
         }
     }
 
@@ -493,14 +508,14 @@
             mStats.noteStartSensorLocked(uid, sensor);
         }
     }
-    
+
     public void noteStopSensor(int uid, int sensor) {
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteStopSensorLocked(uid, sensor);
         }
     }
-    
+
     public void noteVibratorOn(int uid, long durationMillis) {
         enforceCallingPermission();
         synchronized (mStats) {
@@ -521,23 +536,28 @@
             mStats.noteStartGpsLocked(uid);
         }
     }
-    
+
     public void noteStopGps(int uid) {
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteStopGpsLocked(uid);
         }
     }
-        
+
     public void noteScreenState(int state) {
         enforceCallingPermission();
         if (DBG) Slog.d(TAG, "begin noteScreenState");
         synchronized (mStats) {
             mStats.noteScreenStateLocked(state);
+            // TODO: remove this once we figure out properly where and how
+            // SCREEN_EVENT = 1003
+            // State key: 1
+            // State value: state. We can change this to our own def later.
+            StatsLog.writeArray(1003, 1, state);
         }
         if (DBG) Slog.d(TAG, "end noteScreenState");
     }
-    
+
     public void noteScreenBrightness(int brightness) {
         enforceCallingPermission();
         synchronized (mStats) {
diff --git a/com/android/server/audio/PlaybackActivityMonitor.java b/com/android/server/audio/PlaybackActivityMonitor.java
index 27e4d14..d1a37af 100644
--- a/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/com/android/server/audio/PlaybackActivityMonitor.java
@@ -171,7 +171,7 @@
             }
         }
         if (change) {
-            dispatchPlaybackChange();
+            dispatchPlaybackChange(false);
         }
     }
 
@@ -210,7 +210,7 @@
             }
         }
         if (change) {
-            dispatchPlaybackChange();
+            dispatchPlaybackChange(event == AudioPlaybackConfiguration.PLAYER_STATE_RELEASED);
         }
     }
 
@@ -244,6 +244,14 @@
         pw.println("\nPlaybackActivityMonitor dump time: "
                 + DateFormat.getTimeInstance().format(new Date()));
         synchronized(mPlayerLock) {
+            pw.println("\n  playback listeners:");
+            synchronized(mClients) {
+                for (PlayMonitorClient pmc : mClients) {
+                    pw.print(" " + (pmc.mIsPrivileged ? "(S)" : "(P)")
+                            + pmc.toString());
+                }
+            }
+            pw.println("\n");
             // all players
             pw.println("\n  players:");
             final List<Integer> piidIntList = new ArrayList<Integer>(mPlayers.keySet());
@@ -268,7 +276,7 @@
             for (int uid : mBannedUids) {
                 pw.print(" " + uid);
             }
-            pw.println();
+            pw.println("\n");
             // log
             mEventLogger.dump(pw);
         }
@@ -293,7 +301,11 @@
         return true;
     }
 
-    private void dispatchPlaybackChange() {
+    /**
+     * Sends new list after update of playback configurations
+     * @param iplayerReleased indicates if the change was due to a player being released
+     */
+    private void dispatchPlaybackChange(boolean iplayerReleased) {
         synchronized (mClients) {
             // typical use case, nobody is listening, don't do any work
             if (mClients.isEmpty()) {
@@ -324,9 +336,12 @@
                     // do not spam the logs if there are problems communicating with this client
                     if (pmc.mErrorCount < PlayMonitorClient.MAX_ERRORS) {
                         if (pmc.mIsPrivileged) {
-                            pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsSystem);
+                            pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsSystem,
+                                    iplayerReleased);
                         } else {
-                            pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsPublic);
+                            // non-system clients don't have the control interface IPlayer, so
+                            // they don't need to flush commands when a player was released
+                            pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsPublic, false);
                         }
                     }
                 } catch (RemoteException e) {
diff --git a/com/android/server/autofill/AutofillManagerServiceImpl.java b/com/android/server/autofill/AutofillManagerServiceImpl.java
index f54731c..f2f96f8 100644
--- a/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -34,6 +34,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Bundle;
@@ -481,10 +482,16 @@
         sendStateToClients(true);
     }
 
+    @NonNull
     CharSequence getServiceLabel() {
         return mInfo.getServiceInfo().loadLabel(mContext.getPackageManager());
     }
 
+    @NonNull
+    Drawable getServiceIcon() {
+        return mInfo.getServiceInfo().loadIcon(mContext.getPackageManager());
+    }
+
     /**
      * Initializes the last fill selection after an autofill service returned a new
      * {@link FillResponse}.
diff --git a/com/android/server/autofill/Session.java b/com/android/server/autofill/Session.java
index 5ca7614..171053f 100644
--- a/com/android/server/autofill/Session.java
+++ b/com/android/server/autofill/Session.java
@@ -977,8 +977,8 @@
                 mService.logSaveShown(id, mClientState);
                 final IAutoFillManagerClient client = getClient();
                 mPendingSaveUi = new PendingUi(mActivityToken, id, client);
-                getUiForShowing().showSaveUi(mService.getServiceLabel(), saveInfo,
-                        valueFinder, mPackageName, this, mPendingSaveUi);
+                getUiForShowing().showSaveUi(mService.getServiceLabel(), mService.getServiceIcon(),
+                        saveInfo, valueFinder, mPackageName, this, mPendingSaveUi);
                 if (client != null) {
                     try {
                         client.setSaveUiState(id, true);
@@ -1789,7 +1789,8 @@
 
     /**
      * Checks whether this session is hiding the Save UI to handle a custom description link for
-     * a specific {@code token} created by {@link PendingUi#PendingUi(IBinder)}.
+     * a specific {@code token} created by
+     * {@link PendingUi#PendingUi(IBinder, int, IAutoFillManagerClient)}.
      */
     boolean isSaveUiPendingForToken(@NonNull IBinder token) {
         return isSaveUiPending() && token.equals(mPendingSaveUi.getToken());
diff --git a/com/android/server/autofill/ui/AutoFillUI.java b/com/android/server/autofill/ui/AutoFillUI.java
index a6f6713..cac2bff 100644
--- a/com/android/server/autofill/ui/AutoFillUI.java
+++ b/com/android/server/autofill/ui/AutoFillUI.java
@@ -22,6 +22,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.IntentSender;
+import android.graphics.drawable.Drawable;
 import android.metrics.LogMaker;
 import android.os.Bundle;
 import android.os.Handler;
@@ -244,8 +245,8 @@
     /**
      * Shows the UI asking the user to save for autofill.
      */
-    public void showSaveUi(@NonNull CharSequence providerLabel, @NonNull SaveInfo info,
-            @NonNull ValueFinder valueFinder, @NonNull String packageName,
+    public void showSaveUi(@NonNull CharSequence serviceLabel, @NonNull Drawable serviceIcon,
+            @NonNull SaveInfo info,@NonNull ValueFinder valueFinder, @NonNull String packageName,
             @NonNull AutoFillUiCallback callback, @NonNull PendingUi pendingSaveUi) {
         if (sVerbose) Slog.v(TAG, "showSaveUi() for " + packageName + ": " + info);
         int numIds = 0;
@@ -261,8 +262,8 @@
                 return;
             }
             hideAllUiThread(callback);
-            mSaveUi = new SaveUi(mContext, pendingSaveUi, providerLabel, info, valueFinder,
-                    mOverlayControl, new SaveUi.OnSaveListener() {
+            mSaveUi = new SaveUi(mContext, pendingSaveUi, serviceLabel, serviceIcon, info,
+                    valueFinder, mOverlayControl, new SaveUi.OnSaveListener() {
                 @Override
                 public void onSave() {
                     log.setType(MetricsProto.MetricsEvent.TYPE_ACTION);
diff --git a/com/android/server/autofill/ui/SaveUi.java b/com/android/server/autofill/ui/SaveUi.java
index 160c84c..d0b2e92 100644
--- a/com/android/server/autofill/ui/SaveUi.java
+++ b/com/android/server/autofill/ui/SaveUi.java
@@ -22,10 +22,14 @@
 import android.annotation.NonNull;
 import android.app.Dialog;
 import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -43,7 +47,7 @@
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.autofill.AutofillManager;
-import android.view.autofill.IAutoFillManagerClient;
+import android.widget.ImageView;
 import android.widget.RemoteViews;
 import android.widget.ScrollView;
 import android.widget.TextView;
@@ -121,9 +125,9 @@
     private boolean mDestroyed;
 
     SaveUi(@NonNull Context context, @NonNull PendingUi pendingUi,
-           @NonNull CharSequence providerLabel, @NonNull SaveInfo info,
-           @NonNull ValueFinder valueFinder, @NonNull OverlayControl overlayControl,
-           @NonNull OnSaveListener listener) {
+           @NonNull CharSequence serviceLabel, @NonNull Drawable serviceIcon,
+           @NonNull SaveInfo info, @NonNull ValueFinder valueFinder,
+           @NonNull OverlayControl overlayControl, @NonNull OnSaveListener listener) {
         mPendingUi= pendingUi;
         mListener = new OneTimeListener(listener);
         mOverlayControl = overlayControl;
@@ -155,24 +159,25 @@
         switch (types.size()) {
             case 1:
                 mTitle = Html.fromHtml(context.getString(R.string.autofill_save_title_with_type,
-                        types.valueAt(0), providerLabel), 0);
+                        types.valueAt(0), serviceLabel), 0);
                 break;
             case 2:
                 mTitle = Html.fromHtml(context.getString(R.string.autofill_save_title_with_2types,
-                        types.valueAt(0), types.valueAt(1), providerLabel), 0);
+                        types.valueAt(0), types.valueAt(1), serviceLabel), 0);
                 break;
             case 3:
                 mTitle = Html.fromHtml(context.getString(R.string.autofill_save_title_with_3types,
-                        types.valueAt(0), types.valueAt(1), types.valueAt(2), providerLabel), 0);
+                        types.valueAt(0), types.valueAt(1), types.valueAt(2), serviceLabel), 0);
                 break;
             default:
                 // Use generic if more than 3 or invalid type (size 0).
                 mTitle = Html.fromHtml(
-                        context.getString(R.string.autofill_save_title, providerLabel), 0);
+                        context.getString(R.string.autofill_save_title, serviceLabel), 0);
         }
-
         titleView.setText(mTitle);
 
+        setServiceIcon(context, view, serviceIcon);
+
         ScrollView subtitleContainer = null;
         final CustomDescription customDescription = info.getCustomDescription();
         if (customDescription != null) {
@@ -284,6 +289,30 @@
         show();
     }
 
+    private void setServiceIcon(Context context, View view, Drawable serviceIcon) {
+        final ImageView iconView = view.findViewById(R.id.autofill_save_icon);
+        final Resources res = context.getResources();
+
+        final int maxWidth = res.getDimensionPixelSize(R.dimen.autofill_save_icon_max_size);
+        final int maxHeight = maxWidth;
+        final int actualWidth = serviceIcon.getMinimumWidth();
+        final int actualHeight = serviceIcon.getMinimumHeight();
+
+        if (actualWidth <= maxWidth && actualHeight <= maxHeight) {
+            if (sDebug) {
+                Slog.d(TAG, "Addingservice icon "
+                        + "(" + actualWidth + "x" + actualHeight + ") as it's less than maximum "
+                        + "(" + maxWidth + "x" + maxHeight + ").");
+            }
+            iconView.setImageDrawable(serviceIcon);
+        } else {
+            Slog.w(TAG, "Not adding service icon of size "
+                    + "(" + actualWidth + "x" + actualHeight + ") because maximum is "
+                    + "(" + maxWidth + "x" + maxHeight + ").");
+            iconView.setVisibility(View.INVISIBLE);
+        }
+    }
+
     /**
      * Update the pending UI, if any.
      *
diff --git a/com/android/server/backup/BackupManagerConstants.java b/com/android/server/backup/BackupManagerConstants.java
new file mode 100644
index 0000000..cd60182
--- /dev/null
+++ b/com/android/server/backup/BackupManagerConstants.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2017 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 com.android.server.backup;
+
+import android.app.AlarmManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
+import android.util.Slog;
+
+/**
+ * Class to access backup manager constants.
+ *
+ * The backup manager constants are encoded as a key value list separated by commas
+ * and stored as a Settings.Secure.
+ */
+class BackupManagerConstants extends ContentObserver {
+    private static final String TAG = "BackupManagerConstants";
+
+    // Key names stored in the secure settings value.
+    private static final String KEY_VALUE_BACKUP_INTERVAL_MILLISECONDS =
+            "key_value_backup_interval_milliseconds";
+    private static final String KEY_VALUE_BACKUP_FUZZ_MILLISECONDS =
+            "key_value_backup_fuzz_milliseconds";
+    private static final String KEY_VALUE_BACKUP_REQUIRE_CHARGING =
+            "key_value_backup_require_charging";
+    private static final String KEY_VALUE_BACKUP_REQUIRED_NETWORK_TYPE =
+            "key_value_backup_required_network_type";
+    private static final String FULL_BACKUP_INTERVAL_MILLISECONDS =
+            "full_backup_interval_milliseconds";
+    private static final String FULL_BACKUP_REQUIRE_CHARGING =
+            "full_backup_require_charging";
+    private static final String FULL_BACKUP_REQUIRED_NETWORK_TYPE =
+            "full_backup_required_network_type";
+
+    // Hard coded default values.
+    private static final long DEFAULT_KEY_VALUE_BACKUP_INTERVAL_MILLISECONDS =
+            4 * AlarmManager.INTERVAL_HOUR;
+    private static final long DEFAULT_KEY_VALUE_BACKUP_FUZZ_MILLISECONDS =
+            10 * 60 * 1000;
+    private static final boolean DEFAULT_KEY_VALUE_BACKUP_REQUIRE_CHARGING = true;
+    private static final int DEFAULT_KEY_VALUE_BACKUP_REQUIRED_NETWORK_TYPE = 1;
+    private static final long DEFAULT_FULL_BACKUP_INTERVAL_MILLISECONDS =
+            24 * AlarmManager.INTERVAL_HOUR;
+    private static final boolean DEFAULT_FULL_BACKUP_REQUIRE_CHARGING = true;
+    private static final int DEFAULT_FULL_BACKUP_REQUIRED_NETWORK_TYPE = 2;
+
+    // Backup manager constants.
+    private long mKeyValueBackupIntervalMilliseconds;
+    private long mKeyValueBackupFuzzMilliseconds;
+    private boolean mKeyValueBackupRequireCharging;
+    private int mKeyValueBackupRequiredNetworkType;
+    private long mFullBackupIntervalMilliseconds;
+    private boolean mFullBackupRequireCharging;
+    private int mFullBackupRequiredNetworkType;
+
+    private ContentResolver mResolver;
+    private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+    public BackupManagerConstants(Handler handler, ContentResolver resolver) {
+        super(handler);
+        mResolver = resolver;
+        updateSettings();
+    }
+
+    public void start() {
+        mResolver.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.BACKUP_MANAGER_CONSTANTS), false, this);
+    }
+
+    @Override
+    public void onChange(boolean selfChange, Uri uri) {
+        updateSettings();
+    }
+
+    private synchronized void updateSettings() {
+        try {
+            mParser.setString(Settings.Secure.getString(mResolver,
+                    Settings.Secure.BACKUP_MANAGER_CONSTANTS));
+        } catch (IllegalArgumentException e) {
+            // Failed to parse the settings string. Use defaults.
+            Slog.e(TAG, "Bad backup manager constants: " + e.getMessage());
+        }
+
+        mKeyValueBackupIntervalMilliseconds = mParser.getLong(
+                KEY_VALUE_BACKUP_INTERVAL_MILLISECONDS,
+                DEFAULT_KEY_VALUE_BACKUP_INTERVAL_MILLISECONDS);
+        mKeyValueBackupFuzzMilliseconds = mParser.getLong(KEY_VALUE_BACKUP_FUZZ_MILLISECONDS,
+                DEFAULT_KEY_VALUE_BACKUP_FUZZ_MILLISECONDS);
+        mKeyValueBackupRequireCharging = mParser.getBoolean(KEY_VALUE_BACKUP_REQUIRE_CHARGING,
+                DEFAULT_KEY_VALUE_BACKUP_REQUIRE_CHARGING);
+        mKeyValueBackupRequiredNetworkType = mParser.getInt(KEY_VALUE_BACKUP_REQUIRED_NETWORK_TYPE,
+                DEFAULT_KEY_VALUE_BACKUP_REQUIRED_NETWORK_TYPE);
+        mFullBackupIntervalMilliseconds = mParser.getLong(FULL_BACKUP_INTERVAL_MILLISECONDS,
+                DEFAULT_FULL_BACKUP_INTERVAL_MILLISECONDS);
+        mFullBackupRequireCharging = mParser.getBoolean(FULL_BACKUP_REQUIRE_CHARGING,
+                DEFAULT_FULL_BACKUP_REQUIRE_CHARGING);
+        mFullBackupRequiredNetworkType = mParser.getInt(FULL_BACKUP_REQUIRED_NETWORK_TYPE,
+                DEFAULT_FULL_BACKUP_REQUIRED_NETWORK_TYPE);
+    }
+
+    // The following are access methods for the individual parameters.
+    // To be sure to retrieve values from the same set of settings,
+    // group the calls of these methods in a block syncrhonized on
+    // a reference of this object.
+    public synchronized long getKeyValueBackupIntervalMilliseconds() {
+        if (BackupManagerService.DEBUG_SCHEDULING) {
+            Slog.v(TAG, "getKeyValueBackupIntervalMilliseconds(...) returns "
+                    + mKeyValueBackupIntervalMilliseconds);
+        }
+        return mKeyValueBackupIntervalMilliseconds;
+    }
+
+    public synchronized long getKeyValueBackupFuzzMilliseconds() {
+        if (BackupManagerService.DEBUG_SCHEDULING) {
+            Slog.v(TAG, "getKeyValueBackupFuzzMilliseconds(...) returns "
+                    + mKeyValueBackupFuzzMilliseconds);
+        }
+        return mKeyValueBackupFuzzMilliseconds;
+    }
+
+    public synchronized boolean getKeyValueBackupRequireCharging() {
+        if (BackupManagerService.DEBUG_SCHEDULING) {
+            Slog.v(TAG, "getKeyValueBackupRequireCharging(...) returns "
+                    + mKeyValueBackupRequireCharging);
+        }
+        return mKeyValueBackupRequireCharging;
+    }
+
+    public synchronized int getKeyValueBackupRequiredNetworkType() {
+        if (BackupManagerService.DEBUG_SCHEDULING) {
+            Slog.v(TAG, "getKeyValueBackupRequiredNetworkType(...) returns "
+                    + mKeyValueBackupRequiredNetworkType);
+        }
+        return mKeyValueBackupRequiredNetworkType;
+    }
+
+    public synchronized long getFullBackupIntervalMilliseconds() {
+        if (BackupManagerService.DEBUG_SCHEDULING) {
+            Slog.v(TAG, "getFullBackupIntervalMilliseconds(...) returns "
+                    + mFullBackupIntervalMilliseconds);
+        }
+        return mFullBackupIntervalMilliseconds;
+    }
+
+    public synchronized boolean getFullBackupRequireCharging() {
+        if (BackupManagerService.DEBUG_SCHEDULING) {
+            Slog.v(TAG, "getFullBackupRequireCharging(...) returns " + mFullBackupRequireCharging);
+        }
+        return mFullBackupRequireCharging;
+
+    }
+
+    public synchronized int getFullBackupRequiredNetworkType() {
+        if (BackupManagerService.DEBUG_SCHEDULING) {
+            Slog.v(TAG, "getFullBackupRequiredNetworkType(...) returns "
+                    + mFullBackupRequiredNetworkType);
+        }
+        return mFullBackupRequiredNetworkType;
+    }
+}
diff --git a/com/android/server/backup/BackupManagerService.java b/com/android/server/backup/BackupManagerService.java
index 2a2e1fb..f525797 100644
--- a/com/android/server/backup/BackupManagerService.java
+++ b/com/android/server/backup/BackupManagerService.java
@@ -293,14 +293,12 @@
     // order to give them time to enter the backup password.
     static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
 
-    // How long between attempts to perform a full-data backup of any given app
-    static final long MIN_FULL_BACKUP_INTERVAL = 1000 * 60 * 60 * 24; // one day
-
     // If an app is busy when we want to do a full-data backup, how long to defer the retry.
     // This is fuzzed, so there are two parameters; backoff_min + Rand[0, backoff_fuzz)
     static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60;  // one hour
     static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2;  // two hours
 
+    BackupManagerConstants mConstants;
     Context mContext;
     private PackageManager mPackageManager;
     IPackageManager mPackageManagerBinder;
@@ -457,7 +455,7 @@
                 if (mProvisioned && !wasProvisioned && mEnabled) {
                     // we're now good to go, so start the backup alarms
                     if (MORE_DEBUG) Slog.d(TAG, "Now provisioned, so starting backups");
-                    KeyValueBackupJob.schedule(mContext);
+                    KeyValueBackupJob.schedule(mContext, mConstants);
                     scheduleNextFullBackupJob(0);
                 }
             }
@@ -1323,6 +1321,8 @@
         mJournalDir.mkdirs();   // creates mBaseStateDir along the way
         mJournal = null;        // will be created on first use
 
+        mConstants = new BackupManagerConstants(mBackupHandler, mContext.getContentResolver());
+
         // Set up the various sorts of package tracking we do
         mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
         initPackageTracking();
@@ -2478,8 +2478,8 @@
             }
             // We don't want the backup jobs to kick in any time soon.
             // Reschedules them to run in the distant future.
-            KeyValueBackupJob.schedule(mContext, BUSY_BACKOFF_MIN_MILLIS);
-            FullBackupJob.schedule(mContext, 2 * BUSY_BACKOFF_MIN_MILLIS);
+            KeyValueBackupJob.schedule(mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
+            FullBackupJob.schedule(mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
         } finally {
             Binder.restoreCallingIdentity(oldToken);
         }
@@ -3558,7 +3558,7 @@
                 Slog.w(TAG, "Unable to contact transport for recommended backoff: " + e.getMessage());
                 delay = 0;  // use the scheduler's default
             }
-            KeyValueBackupJob.schedule(mContext, delay);
+            KeyValueBackupJob.schedule(mContext, delay, mConstants);
 
             for (BackupRequest request : mOriginalQueue) {
                 dataChangedImpl(request.packageName);
@@ -5370,12 +5370,12 @@
                 // due.
                 final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
                 final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
-                final long appLatency = (timeSinceLast < MIN_FULL_BACKUP_INTERVAL)
-                        ? (MIN_FULL_BACKUP_INTERVAL - timeSinceLast) : 0;
+                final long interval = mConstants.getFullBackupIntervalMilliseconds();
+                final long appLatency = (timeSinceLast < interval) ? (interval - timeSinceLast) : 0;
                 final long latency = Math.max(transportMinLatency, appLatency);
                 Runnable r = new Runnable() {
                     @Override public void run() {
-                        FullBackupJob.schedule(mContext, latency);
+                        FullBackupJob.schedule(mContext, latency, mConstants);
                     }
                 };
                 mBackupHandler.postDelayed(r, 2500);
@@ -5470,9 +5470,15 @@
      */
     @Override
     public boolean beginFullBackup(FullBackupJob scheduledJob) {
-        long now = System.currentTimeMillis();
+        final long now = System.currentTimeMillis();
         FullBackupEntry entry = null;
-        long latency = MIN_FULL_BACKUP_INTERVAL;
+        final long fullBackupInterval;
+        final long keyValueBackupInterval;
+        synchronized (mConstants) {
+            fullBackupInterval = mConstants.getFullBackupIntervalMilliseconds();
+            keyValueBackupInterval = mConstants.getKeyValueBackupIntervalMilliseconds();
+        }
+        long latency = fullBackupInterval;
 
         if (!mEnabled || !mProvisioned) {
             // Backups are globally disabled, so don't proceed.  We also don't reschedule
@@ -5491,7 +5497,7 @@
                 mPowerManager.getPowerSaveState(ServiceType.FULL_BACKUP);
         if (result.batterySaverEnabled) {
             if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
-            FullBackupJob.schedule(mContext, KeyValueBackupJob.BATCH_INTERVAL);
+            FullBackupJob.schedule(mContext, keyValueBackupInterval, mConstants);
             return false;
         }
 
@@ -5534,20 +5540,20 @@
                     // Typically this means we haven't run a key/value backup yet.  Back off
                     // full-backup operations by the key/value job's run interval so that
                     // next time we run, we are likely to be able to make progress.
-                    latency = KeyValueBackupJob.BATCH_INTERVAL;
+                    latency = keyValueBackupInterval;
                 }
 
                 if (runBackup) {
                     entry = mFullBackupQueue.get(0);
                     long timeSinceRun = now - entry.lastBackup;
-                    runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL);
+                    runBackup = (timeSinceRun >= fullBackupInterval);
                     if (!runBackup) {
                         // It's too early to back up the next thing in the queue, so bow out
                         if (MORE_DEBUG) {
                             Slog.i(TAG, "Device ready but too early to back up next app");
                         }
                         // Wait until the next app in the queue falls due for a full data backup
-                        latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
+                        latency = fullBackupInterval - timeSinceRun;
                         break;  // we know we aren't doing work yet, so bail.
                     }
 
@@ -5583,8 +5589,7 @@
                             // This relocates the app's entry from the head of the queue to
                             // its order-appropriate position further down, so upon looping
                             // a new candidate will be considered at the head.
-                            enqueueFullBackup(entry.packageName,
-                                    nextEligible - MIN_FULL_BACKUP_INTERVAL);
+                            enqueueFullBackup(entry.packageName, nextEligible - fullBackupInterval);
                         }
                     } catch (NameNotFoundException nnf) {
                         // So, we think we want to back this up, but it turns out the package
@@ -5605,7 +5610,7 @@
                 final long deferTime = latency;     // pin for the closure
                 mBackupHandler.post(new Runnable() {
                     @Override public void run() {
-                        FullBackupJob.schedule(mContext, deferTime);
+                        FullBackupJob.schedule(mContext, deferTime, mConstants);
                     }
                 });
                 return false;
@@ -9851,7 +9856,7 @@
         }
 
         // ...and schedule a backup pass if necessary
-        KeyValueBackupJob.schedule(mContext);
+        KeyValueBackupJob.schedule(mContext, mConstants);
     }
 
     // Note: packageName is currently unused, but may be in the future
@@ -10014,7 +10019,7 @@
                 mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
         if (result.batterySaverEnabled) {
             if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
-            KeyValueBackupJob.schedule(mContext);   // try again in several hours
+            KeyValueBackupJob.schedule(mContext, mConstants);   // try again in several hours
         } else {
             if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
             synchronized (mQueueLock) {
@@ -10387,7 +10392,7 @@
             synchronized (mQueueLock) {
                 if (enable && !wasEnabled && mProvisioned) {
                     // if we've just been enabled, start scheduling backup passes
-                    KeyValueBackupJob.schedule(mContext);
+                    KeyValueBackupJob.schedule(mContext, mConstants);
                     scheduleNextFullBackupJob(0);
                 } else if (!enable) {
                     // No longer enabled, so stop running backups
diff --git a/com/android/server/backup/FullBackupJob.java b/com/android/server/backup/FullBackupJob.java
index 7ad7657..82638b4 100644
--- a/com/android/server/backup/FullBackupJob.java
+++ b/com/android/server/backup/FullBackupJob.java
@@ -34,12 +34,14 @@
 
     JobParameters mParams;
 
-    public static void schedule(Context ctx, long minDelay) {
+    public static void schedule(Context ctx, long minDelay, BackupManagerConstants constants) {
         JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
-        JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sIdleService)
-                .setRequiresDeviceIdle(true)
-                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
-                .setRequiresCharging(true);
+        JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sIdleService);
+        synchronized (constants) {
+            builder.setRequiresDeviceIdle(true)
+                    .setRequiredNetworkType(constants.getFullBackupRequiredNetworkType())
+                    .setRequiresCharging(constants.getFullBackupRequireCharging());
+        }
         if (minDelay > 0) {
             builder.setMinimumLatency(minDelay);
         }
diff --git a/com/android/server/backup/KeyValueBackupJob.java b/com/android/server/backup/KeyValueBackupJob.java
index e3ff5ce..d8411e2 100644
--- a/com/android/server/backup/KeyValueBackupJob.java
+++ b/com/android/server/backup/KeyValueBackupJob.java
@@ -38,12 +38,6 @@
             new ComponentName("android", KeyValueBackupJob.class.getName());
     private static final int JOB_ID = 0x5039;
 
-    // Minimum wait time between backups even while we're on charger
-    static final long BATCH_INTERVAL = 4 * AlarmManager.INTERVAL_HOUR;
-
-    // Random variation in next-backup scheduling time to avoid server load spikes
-    private static final int FUZZ_MILLIS = 10 * 60 * 1000;
-
     // Once someone asks for a backup, this is how long we hold off until we find
     // an on-charging opportunity.  If we hit this max latency we will run the operation
     // regardless.  Privileged callers can always trigger an immediate pass via
@@ -53,31 +47,43 @@
     private static boolean sScheduled = false;
     private static long sNextScheduled = 0;
 
-    public static void schedule(Context ctx) {
-        schedule(ctx, 0);
+    public static void schedule(Context ctx, BackupManagerConstants constants) {
+        schedule(ctx, 0, constants);
     }
 
-    public static void schedule(Context ctx, long delay) {
+    public static void schedule(Context ctx, long delay, BackupManagerConstants constants) {
         synchronized (KeyValueBackupJob.class) {
-            if (!sScheduled) {
-                if (delay <= 0) {
-                    delay = BATCH_INTERVAL + new Random().nextInt(FUZZ_MILLIS);
-                }
-                if (BackupManagerService.DEBUG_SCHEDULING) {
-                    Slog.v(TAG, "Scheduling k/v pass in "
-                            + (delay / 1000 / 60) + " minutes");
-                }
-                JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
-                JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sKeyValueJobService)
-                        .setMinimumLatency(delay)
-                        .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
-                        .setRequiresCharging(true)
-                        .setOverrideDeadline(MAX_DEFERRAL);
-                js.schedule(builder.build());
-
-                sNextScheduled = System.currentTimeMillis() + delay;
-                sScheduled = true;
+            if (sScheduled) {
+                return;
             }
+
+            final long interval;
+            final long fuzz;
+            final int networkType;
+            final boolean needsCharging;
+
+            synchronized (constants) {
+                interval = constants.getKeyValueBackupIntervalMilliseconds();
+                fuzz = constants.getKeyValueBackupFuzzMilliseconds();
+                networkType = constants.getKeyValueBackupRequiredNetworkType();
+                needsCharging = constants.getKeyValueBackupRequireCharging();
+            }
+            if (delay <= 0) {
+                delay = interval + new Random().nextInt((int) fuzz);
+            }
+            if (BackupManagerService.DEBUG_SCHEDULING) {
+                Slog.v(TAG, "Scheduling k/v pass in " + (delay / 1000 / 60) + " minutes");
+            }
+            JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, sKeyValueJobService)
+                    .setMinimumLatency(delay)
+                    .setRequiredNetworkType(networkType)
+                    .setRequiresCharging(needsCharging)
+                    .setOverrideDeadline(MAX_DEFERRAL);
+            JobScheduler js = (JobScheduler) ctx.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+            js.schedule(builder.build());
+
+            sNextScheduled = System.currentTimeMillis() + delay;
+            sScheduled = true;
         }
     }
 
diff --git a/com/android/server/backup/RefactoredBackupManagerService.java b/com/android/server/backup/RefactoredBackupManagerService.java
index 9e8ec64..141f920 100644
--- a/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/com/android/server/backup/RefactoredBackupManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server.backup;
 
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND;
+
 import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT;
 import static com.android.server.backup.internal.BackupHandler.MSG_FULL_CONFIRMATION_TIMEOUT;
 import static com.android.server.backup.internal.BackupHandler.MSG_OP_COMPLETE;
@@ -212,14 +213,12 @@
     // order to give them time to enter the backup password.
     private static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
 
-    // How long between attempts to perform a full-data backup of any given app
-    private static final long MIN_FULL_BACKUP_INTERVAL = 1000 * 60 * 60 * 24; // one day
-
     // If an app is busy when we want to do a full-data backup, how long to defer the retry.
     // This is fuzzed, so there are two parameters; backoff_min + Rand[0, backoff_fuzz)
     private static final long BUSY_BACKOFF_MIN_MILLIS = 1000 * 60 * 60;  // one hour
     private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2;  // two hours
 
+    private BackupManagerConstants mConstants;
     private Context mContext;
     private PackageManager mPackageManager;
     private IPackageManager mPackageManagerBinder;
@@ -297,6 +296,10 @@
         return sInstance;
     }
 
+    public BackupManagerConstants getConstants() {
+        return mConstants;
+    }
+
     public Context getContext() {
         return mContext;
     }
@@ -604,10 +607,10 @@
      * {@link RefactoredBackupManagerService#waitUntilOperationComplete(int)} is
      * used in various places to 'wait' for notifyAll and detect change of pending state of an
      * operation. So typically, an operation will be removed from this array by:
-     *   - BackupRestoreTask#handleCancel and
-     *   - BackupRestoreTask#operationComplete OR waitUntilOperationComplete. Do not remove at both
-     *     these places because waitUntilOperationComplete relies on the operation being present to
-     *     determine its completion status.
+     * - BackupRestoreTask#handleCancel and
+     * - BackupRestoreTask#operationComplete OR waitUntilOperationComplete. Do not remove at both
+     * these places because waitUntilOperationComplete relies on the operation being present to
+     * determine its completion status.
      *
      * If type of operation is OP_BACKUP, it is a task running backups. It provides a handle to
      * cancel backup tasks.
@@ -623,7 +626,8 @@
     private File mBaseStateDir;
     private File mDataDir;
     private File mJournalDir;
-    @Nullable private DataChangedJournal mJournal;
+    @Nullable
+    private DataChangedJournal mJournal;
 
     private final SecureRandom mRng = new SecureRandom();
 
@@ -753,6 +757,8 @@
         mJournalDir.mkdirs();   // creates mBaseStateDir along the way
         mJournal = null;        // will be created on first use
 
+        mConstants = new BackupManagerConstants(mBackupHandler, mContext.getContentResolver());
+
         // Set up the various sorts of package tracking we do
         mFullBackupScheduleFile = new File(mBaseStateDir, "fb-schedule");
         initPackageTracking();
@@ -1532,8 +1538,9 @@
         }
 
         if (!mEnabled || !mProvisioned) {
-            Slog.i(TAG, "Backup requested but e=" + mEnabled + " p=" +mProvisioned);
-            BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+            Slog.i(TAG, "Backup requested but e=" + mEnabled + " p=" + mProvisioned);
+            BackupObserverUtils.sendBackupFinished(observer,
+                    BackupManager.ERROR_BACKUP_NOT_ALLOWED);
             final int logTag = mProvisioned
                     ? BackupManagerMonitor.LOG_EVENT_ID_BACKUP_DISABLED
                     : BackupManagerMonitor.LOG_EVENT_ID_DEVICE_NOT_PROVISIONED;
@@ -1626,8 +1633,8 @@
             }
             // We don't want the backup jobs to kick in any time soon.
             // Reschedules them to run in the distant future.
-            KeyValueBackupJob.schedule(mContext, BUSY_BACKOFF_MIN_MILLIS);
-            FullBackupJob.schedule(mContext, 2 * BUSY_BACKOFF_MIN_MILLIS);
+            KeyValueBackupJob.schedule(mContext, BUSY_BACKOFF_MIN_MILLIS, mConstants);
+            FullBackupJob.schedule(mContext, 2 * BUSY_BACKOFF_MIN_MILLIS, mConstants);
         } finally {
             Binder.restoreCallingIdentity(oldToken);
         }
@@ -1835,13 +1842,13 @@
                 // due.
                 final long upcomingLastBackup = mFullBackupQueue.get(0).lastBackup;
                 final long timeSinceLast = System.currentTimeMillis() - upcomingLastBackup;
-                final long appLatency = (timeSinceLast < MIN_FULL_BACKUP_INTERVAL)
-                        ? (MIN_FULL_BACKUP_INTERVAL - timeSinceLast) : 0;
+                final long interval = mConstants.getFullBackupIntervalMilliseconds();
+                final long appLatency = (timeSinceLast < interval) ? (interval - timeSinceLast) : 0;
                 final long latency = Math.max(transportMinLatency, appLatency);
                 Runnable r = new Runnable() {
                     @Override
                     public void run() {
-                        FullBackupJob.schedule(mContext, latency);
+                        FullBackupJob.schedule(mContext, latency, mConstants);
                     }
                 };
                 mBackupHandler.postDelayed(r, 2500);
@@ -1932,13 +1939,19 @@
      * <p>This is the "start a full backup operation" entry point called by the scheduled job.
      *
      * @return Whether ongoing work will continue.  The return value here will be passed
-     *         along as the return value to the scheduled job's onStartJob() callback.
+     * along as the return value to the scheduled job's onStartJob() callback.
      */
     @Override
     public boolean beginFullBackup(FullBackupJob scheduledJob) {
-        long now = System.currentTimeMillis();
+        final long now = System.currentTimeMillis();
+        final long fullBackupInterval;
+        final long keyValueBackupInterval;
+        synchronized (mConstants) {
+            fullBackupInterval = mConstants.getFullBackupIntervalMilliseconds();
+            keyValueBackupInterval = mConstants.getKeyValueBackupIntervalMilliseconds();
+        }
         FullBackupEntry entry = null;
-        long latency = MIN_FULL_BACKUP_INTERVAL;
+        long latency = fullBackupInterval;
 
         if (!mEnabled || !mProvisioned) {
             // Backups are globally disabled, so don't proceed.  We also don't reschedule
@@ -1957,7 +1970,7 @@
                 mPowerManager.getPowerSaveState(ServiceType.FULL_BACKUP);
         if (result.batterySaverEnabled) {
             if (DEBUG) Slog.i(TAG, "Deferring scheduled full backups in battery saver mode");
-            FullBackupJob.schedule(mContext, KeyValueBackupJob.BATCH_INTERVAL);
+            FullBackupJob.schedule(mContext, keyValueBackupInterval, mConstants);
             return false;
         }
 
@@ -2000,20 +2013,20 @@
                     // Typically this means we haven't run a key/value backup yet.  Back off
                     // full-backup operations by the key/value job's run interval so that
                     // next time we run, we are likely to be able to make progress.
-                    latency = KeyValueBackupJob.BATCH_INTERVAL;
+                    latency = keyValueBackupInterval;
                 }
 
                 if (runBackup) {
                     entry = mFullBackupQueue.get(0);
                     long timeSinceRun = now - entry.lastBackup;
-                    runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL);
+                    runBackup = (timeSinceRun >= fullBackupInterval);
                     if (!runBackup) {
                         // It's too early to back up the next thing in the queue, so bow out
                         if (MORE_DEBUG) {
                             Slog.i(TAG, "Device ready but too early to back up next app");
                         }
                         // Wait until the next app in the queue falls due for a full data backup
-                        latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
+                        latency = fullBackupInterval - timeSinceRun;
                         break;  // we know we aren't doing work yet, so bail.
                     }
 
@@ -2049,8 +2062,7 @@
                             // This relocates the app's entry from the head of the queue to
                             // its order-appropriate position further down, so upon looping
                             // a new candidate will be considered at the head.
-                            enqueueFullBackup(entry.packageName,
-                                    nextEligible - MIN_FULL_BACKUP_INTERVAL);
+                            enqueueFullBackup(entry.packageName, nextEligible - fullBackupInterval);
                         }
                     } catch (NameNotFoundException nnf) {
                         // So, we think we want to back this up, but it turns out the package
@@ -2072,7 +2084,7 @@
                 mBackupHandler.post(new Runnable() {
                     @Override
                     public void run() {
-                        FullBackupJob.schedule(mContext, deferTime);
+                        FullBackupJob.schedule(mContext, deferTime, mConstants);
                     }
                 });
                 return false;
@@ -2153,7 +2165,7 @@
         }
 
         // ...and schedule a backup pass if necessary
-        KeyValueBackupJob.schedule(mContext);
+        KeyValueBackupJob.schedule(mContext, mConstants);
     }
 
     // Note: packageName is currently unused, but may be in the future
@@ -2222,7 +2234,8 @@
     // Run an initialize operation for the given transport
     @Override
     public void initializeTransports(String[] transportNames, IBackupObserver observer) {
-        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "initializeTransport");
+        mContext.enforceCallingPermission(android.Manifest.permission.BACKUP,
+                "initializeTransport");
         if (MORE_DEBUG || true) {
             Slog.v(TAG, "initializeTransport(): " + Arrays.asList(transportNames));
         }
@@ -2296,7 +2309,7 @@
                 mPowerManager.getPowerSaveState(ServiceType.KEYVALUE_BACKUP);
         if (result.batterySaverEnabled) {
             if (DEBUG) Slog.v(TAG, "Not running backup while in battery save mode");
-            KeyValueBackupJob.schedule(mContext);   // try again in several hours
+            KeyValueBackupJob.schedule(mContext, mConstants);   // try again in several hours
         } else {
             if (DEBUG) Slog.v(TAG, "Scheduling immediate backup pass");
             synchronized (mQueueLock) {
@@ -2672,7 +2685,7 @@
             synchronized (mQueueLock) {
                 if (enable && !wasEnabled && mProvisioned) {
                     // if we've just been enabled, start scheduling backup passes
-                    KeyValueBackupJob.schedule(mContext);
+                    KeyValueBackupJob.schedule(mContext, mConstants);
                     scheduleNextFullBackupJob(0);
                 } else if (!enable) {
                     // No longer enabled, so stop running backups
@@ -2808,31 +2821,34 @@
         Slog.v(TAG, "selectBackupTransportAsync() called with transport " +
                 transport.flattenToShortString());
 
-        mTransportManager.ensureTransportReady(transport, new TransportManager.TransportReadyCallback() {
-            @Override
-            public void onSuccess(String transportName) {
-                mTransportManager.selectTransport(transportName);
-                Settings.Secure.putString(mContext.getContentResolver(),
-                        Settings.Secure.BACKUP_TRANSPORT,
-                        mTransportManager.getCurrentTransportName());
-                Slog.v(TAG, "Transport successfully selected: " + transport.flattenToShortString());
-                try {
-                    listener.onSuccess(transportName);
-                } catch (RemoteException e) {
-                    // Nothing to do here.
-                }
-            }
+        mTransportManager.ensureTransportReady(transport,
+                new TransportManager.TransportReadyCallback() {
+                    @Override
+                    public void onSuccess(String transportName) {
+                        mTransportManager.selectTransport(transportName);
+                        Settings.Secure.putString(mContext.getContentResolver(),
+                                Settings.Secure.BACKUP_TRANSPORT,
+                                mTransportManager.getCurrentTransportName());
+                        Slog.v(TAG, "Transport successfully selected: "
+                                + transport.flattenToShortString());
+                        try {
+                            listener.onSuccess(transportName);
+                        } catch (RemoteException e) {
+                            // Nothing to do here.
+                        }
+                    }
 
-            @Override
-            public void onFailure(int reason) {
-                Slog.v(TAG, "Failed to select transport: " + transport.flattenToShortString());
-                try {
-                    listener.onFailure(reason);
-                } catch (RemoteException e) {
-                    // Nothing to do here.
-                }
-            }
-        });
+                    @Override
+                    public void onFailure(int reason) {
+                        Slog.v(TAG,
+                                "Failed to select transport: " + transport.flattenToShortString());
+                        try {
+                            listener.onFailure(reason);
+                        } catch (RemoteException e) {
+                            // Nothing to do here.
+                        }
+                    }
+                });
 
         Binder.restoreCallingIdentity(oldId);
     }
diff --git a/com/android/server/backup/internal/PerformBackupTask.java b/com/android/server/backup/internal/PerformBackupTask.java
index 5d4fcf4..ce4f906 100644
--- a/com/android/server/backup/internal/PerformBackupTask.java
+++ b/com/android/server/backup/internal/PerformBackupTask.java
@@ -1060,7 +1060,8 @@
             Slog.w(TAG, "Unable to contact transport for recommended backoff: " + e.getMessage());
             delay = 0;  // use the scheduler's default
         }
-        KeyValueBackupJob.schedule(backupManagerService.getContext(), delay);
+        KeyValueBackupJob.schedule(backupManagerService.getContext(), delay,
+                backupManagerService.getConstants());
 
         for (BackupRequest request : mOriginalQueue) {
             backupManagerService.dataChangedImpl(request.packageName);
diff --git a/com/android/server/backup/internal/ProvisionedObserver.java b/com/android/server/backup/internal/ProvisionedObserver.java
index e92b8be..1001d3a 100644
--- a/com/android/server/backup/internal/ProvisionedObserver.java
+++ b/com/android/server/backup/internal/ProvisionedObserver.java
@@ -53,7 +53,8 @@
                 if (MORE_DEBUG) {
                     Slog.d(TAG, "Now provisioned, so starting backups");
                 }
-                KeyValueBackupJob.schedule(backupManagerService.getContext());
+                KeyValueBackupJob.schedule(backupManagerService.getContext(),
+                        backupManagerService.getConstants());
                 backupManagerService.scheduleNextFullBackupJob(0);
             }
         }
diff --git a/com/android/server/connectivity/IpConnectivityEventBuilder.java b/com/android/server/connectivity/IpConnectivityEventBuilder.java
index ee38219..22330e6 100644
--- a/com/android/server/connectivity/IpConnectivityEventBuilder.java
+++ b/com/android/server/connectivity/IpConnectivityEventBuilder.java
@@ -38,6 +38,7 @@
 import android.net.metrics.NetworkEvent;
 import android.net.metrics.RaEvent;
 import android.net.metrics.ValidationProbeEvent;
+import android.net.metrics.WakeupStats;
 import android.os.Parcelable;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -115,6 +116,22 @@
         return out;
     }
 
+    public static IpConnectivityEvent toProto(WakeupStats in) {
+        IpConnectivityLogClass.WakeupStats wakeupStats =
+                new IpConnectivityLogClass.WakeupStats();
+        in.updateDuration();
+        wakeupStats.durationSec = in.durationSec;
+        wakeupStats.totalWakeups = in.totalWakeups;
+        wakeupStats.rootWakeups = in.rootWakeups;
+        wakeupStats.systemWakeups = in.systemWakeups;
+        wakeupStats.nonApplicationWakeups = in.nonApplicationWakeups;
+        wakeupStats.applicationWakeups = in.applicationWakeups;
+        wakeupStats.unroutedWakeups = in.unroutedWakeups;
+        final IpConnectivityEvent out = buildEvent(0, 0, in.iface);
+        out.setWakeupStats(wakeupStats);
+        return out;
+    }
+
     private static IpConnectivityEvent buildEvent(int netId, long transports, String ifname) {
         final IpConnectivityEvent ev = new IpConnectivityEvent();
         ev.networkId = netId;
diff --git a/com/android/server/connectivity/NetdEventListenerService.java b/com/android/server/connectivity/NetdEventListenerService.java
index 4094083..6f7ace2 100644
--- a/com/android/server/connectivity/NetdEventListenerService.java
+++ b/com/android/server/connectivity/NetdEventListenerService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.connectivity;
 
+import static android.util.TimeUtils.NANOS_PER_MS;
+
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.INetdEventCallback;
@@ -25,9 +27,12 @@
 import android.net.metrics.DnsEvent;
 import android.net.metrics.INetdEventListener;
 import android.net.metrics.IpConnectivityLog;
+import android.net.metrics.WakeupEvent;
+import android.net.metrics.WakeupStats;
 import android.os.RemoteException;
 import android.text.format.DateUtils;
 import android.util.Log;
+import android.util.ArrayMap;
 import android.util.SparseArray;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -59,12 +64,28 @@
     private static final int CONNECT_LATENCY_FILL_RATE    = 15 * (int) DateUtils.SECOND_IN_MILLIS;
     private static final int CONNECT_LATENCY_MAXIMUM_RECORDS = 20000;
 
+    @VisibleForTesting
+    static final int WAKEUP_EVENT_BUFFER_LENGTH = 1024;
+    // TODO: dedup this String constant with the one used in
+    // ConnectivityService#wakeupModifyInterface().
+    @VisibleForTesting
+    static final String WAKEUP_EVENT_IFACE_PREFIX = "iface:";
+
     // Sparse arrays of DNS and connect events, grouped by net id.
     @GuardedBy("this")
     private final SparseArray<DnsEvent> mDnsEvents = new SparseArray<>();
     @GuardedBy("this")
     private final SparseArray<ConnectStats> mConnectEvents = new SparseArray<>();
 
+    // Array of aggregated wakeup event stats, grouped by interface name.
+    @GuardedBy("this")
+    private final ArrayMap<String, WakeupStats> mWakeupStats = new ArrayMap<>();
+    // Ring buffer array for storing packet wake up events sent by Netd.
+    @GuardedBy("this")
+    private final WakeupEvent[] mWakeupEvents = new WakeupEvent[WAKEUP_EVENT_BUFFER_LENGTH];
+    @GuardedBy("this")
+    private long mWakeupEventCursor = 0;
+
     private final ConnectivityManager mCm;
 
     @GuardedBy("this")
@@ -137,11 +158,62 @@
 
     @Override
     public synchronized void onWakeupEvent(String prefix, int uid, int gid, long timestampNs) {
+        maybeVerboseLog("onWakeupEvent(%s, %d, %d, %sns)", prefix, uid, gid, timestampNs);
+
+        // TODO: add ip protocol and port
+
+        String iface = prefix.replaceFirst(WAKEUP_EVENT_IFACE_PREFIX, "");
+        final long timestampMs;
+        if (timestampNs > 0) {
+            timestampMs = timestampNs / NANOS_PER_MS;
+        } else {
+            timestampMs = System.currentTimeMillis();
+        }
+
+        addWakupEvent(iface, timestampMs, uid);
+    }
+
+    @GuardedBy("this")
+    private void addWakupEvent(String iface, long timestampMs, int uid) {
+        int index = wakeupEventIndex(mWakeupEventCursor);
+        mWakeupEventCursor++;
+        WakeupEvent event = new WakeupEvent();
+        event.iface = iface;
+        event.timestampMs = timestampMs;
+        event.uid = uid;
+        mWakeupEvents[index] = event;
+        WakeupStats stats = mWakeupStats.get(iface);
+        if (stats == null) {
+            stats = new WakeupStats(iface);
+            mWakeupStats.put(iface, stats);
+        }
+        stats.countEvent(event);
+    }
+
+    @GuardedBy("this")
+    private WakeupEvent[] getWakeupEvents() {
+        int length = (int) Math.min(mWakeupEventCursor, (long) mWakeupEvents.length);
+        WakeupEvent[] out = new WakeupEvent[length];
+        // Reverse iteration from youngest event to oldest event.
+        long inCursor = mWakeupEventCursor - 1;
+        int outIdx = out.length - 1;
+        while (outIdx >= 0) {
+            out[outIdx--] = mWakeupEvents[wakeupEventIndex(inCursor--)];
+        }
+        return out;
+    }
+
+    private static int wakeupEventIndex(long cursor) {
+        return (int) Math.abs(cursor % WAKEUP_EVENT_BUFFER_LENGTH);
     }
 
     public synchronized void flushStatistics(List<IpConnectivityEvent> events) {
         flushProtos(events, mConnectEvents, IpConnectivityEventBuilder::toProto);
         flushProtos(events, mDnsEvents, IpConnectivityEventBuilder::toProto);
+        for (int i = 0; i < mWakeupStats.size(); i++) {
+            events.add(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i)));
+        }
+        mWakeupStats.clear();
     }
 
     public synchronized void dump(PrintWriter writer) {
@@ -153,13 +225,22 @@
     }
 
     public synchronized void list(PrintWriter pw) {
-        listEvents(pw, mConnectEvents, (x) -> x);
-        listEvents(pw, mDnsEvents, (x) -> x);
+        listEvents(pw, mConnectEvents, (x) -> x, "\n");
+        listEvents(pw, mDnsEvents, (x) -> x, "\n");
+        for (int i = 0; i < mWakeupStats.size(); i++) {
+            pw.println(mWakeupStats.valueAt(i));
+        }
+        for (WakeupEvent wakeup : getWakeupEvents()) {
+            pw.println(wakeup);
+        }
     }
 
     public synchronized void listAsProtos(PrintWriter pw) {
-        listEvents(pw, mConnectEvents, IpConnectivityEventBuilder::toProto);
-        listEvents(pw, mDnsEvents, IpConnectivityEventBuilder::toProto);
+        listEvents(pw, mConnectEvents, IpConnectivityEventBuilder::toProto, "");
+        listEvents(pw, mDnsEvents, IpConnectivityEventBuilder::toProto, "");
+        for (int i = 0; i < mWakeupStats.size(); i++) {
+            pw.print(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i)));
+        }
     }
 
     private static <T> void flushProtos(List<IpConnectivityEvent> out, SparseArray<T> in,
@@ -170,10 +251,13 @@
         in.clear();
     }
 
-    public static <T> void listEvents(
-            PrintWriter pw, SparseArray<T> events, Function<T, Object> mapper) {
+    private static <T> void listEvents(
+            PrintWriter pw, SparseArray<T> events, Function<T, Object> mapper, String separator) {
+        // Proto derived Classes have toString method that adds a \n at the end.
+        // Let the caller control that by passing in the line separator explicitly.
         for (int i = 0; i < events.size(); i++) {
-            pw.println(mapper.apply(events.valueAt(i)).toString());
+            pw.print(mapper.apply(events.valueAt(i)));
+            pw.print(separator);
         }
     }
 
diff --git a/com/android/server/connectivity/NetworkAgentInfo.java b/com/android/server/connectivity/NetworkAgentInfo.java
index e96f4b0..a4d7242 100644
--- a/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/com/android/server/connectivity/NetworkAgentInfo.java
@@ -278,6 +278,10 @@
         return mHandler;
     }
 
+    public Network network() {
+        return network;
+    }
+
     // Functions for manipulating the requests satisfied by this network.
     //
     // These functions must only called on ConnectivityService's main thread.
diff --git a/com/android/server/connectivity/NetworkMonitor.java b/com/android/server/connectivity/NetworkMonitor.java
index d3a9354..8b886d6 100644
--- a/com/android/server/connectivity/NetworkMonitor.java
+++ b/com/android/server/connectivity/NetworkMonitor.java
@@ -20,7 +20,6 @@
 import static android.net.CaptivePortal.APP_RETURN_UNWANTED;
 import static android.net.CaptivePortal.APP_RETURN_WANTED_AS_IS;
 
-import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -229,6 +228,8 @@
     // Delay between reevaluations once a captive portal has been found.
     private static final int CAPTIVE_PORTAL_REEVALUATE_DELAY_MS = 10*60*1000;
 
+    private static final int NUM_VALIDATION_LOG_LINES = 20;
+
     private final Context mContext;
     private final Handler mConnectivityServiceHandler;
     private final NetworkAgentInfo mNetworkAgentInfo;
@@ -236,9 +237,15 @@
     private final int mNetId;
     private final TelephonyManager mTelephonyManager;
     private final WifiManager mWifiManager;
-    private final AlarmManager mAlarmManager;
     private final NetworkRequest mDefaultRequest;
     private final IpConnectivityLog mMetricsLog;
+    private final NetworkMonitorSettings mSettings;
+
+    // Configuration values for captive portal detection probes.
+    private final String mCaptivePortalUserAgent;
+    private final URL mCaptivePortalHttpsUrl;
+    private final URL mCaptivePortalHttpUrl;
+    private final URL[] mCaptivePortalFallbackUrls;
 
     @VisibleForTesting
     protected boolean mIsCaptivePortalCheckEnabled;
@@ -262,40 +269,37 @@
 
     private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null;
 
-    private final LocalLog validationLogs = new LocalLog(20); // 20 lines
+    private final LocalLog validationLogs = new LocalLog(NUM_VALIDATION_LOG_LINES);
 
     private final Stopwatch mEvaluationTimer = new Stopwatch();
 
     // This variable is set before transitioning to the mCaptivePortalState.
     private CaptivePortalProbeResult mLastPortalProbeResult = CaptivePortalProbeResult.FAILED;
 
-    // Configuration values for captive portal detection probes.
-    private final String mCaptivePortalUserAgent;
-    private final URL mCaptivePortalHttpsUrl;
-    private final URL mCaptivePortalHttpUrl;
-    private final URL[] mCaptivePortalFallbackUrls;
     private int mNextFallbackUrlIndex = 0;
 
     public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
             NetworkRequest defaultRequest) {
-        this(context, handler, networkAgentInfo, defaultRequest, new IpConnectivityLog());
+        this(context, handler, networkAgentInfo, defaultRequest, new IpConnectivityLog(),
+                NetworkMonitorSettings.DEFAULT);
     }
 
     @VisibleForTesting
     protected NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
-            NetworkRequest defaultRequest, IpConnectivityLog logger) {
+            NetworkRequest defaultRequest, IpConnectivityLog logger,
+            NetworkMonitorSettings settings) {
         // Add suffix indicating which NetworkMonitor we're talking about.
         super(TAG + networkAgentInfo.name());
 
         mContext = context;
         mMetricsLog = logger;
         mConnectivityServiceHandler = handler;
+        mSettings = settings;
         mNetworkAgentInfo = networkAgentInfo;
-        mNetwork = new OneAddressPerFamilyNetwork(networkAgentInfo.network);
+        mNetwork = new OneAddressPerFamilyNetwork(networkAgentInfo.network());
         mNetId = mNetwork.netId;
         mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
         mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
         mDefaultRequest = defaultRequest;
 
         addState(mDefaultState);
@@ -305,16 +309,12 @@
             addState(mCaptivePortalState, mMaybeNotifyState);
         setInitialState(mDefaultState);
 
-        mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.CAPTIVE_PORTAL_MODE, Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT)
-                != Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE;
-        mUseHttps = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.CAPTIVE_PORTAL_USE_HTTPS, 1) == 1;
-
-        mCaptivePortalUserAgent = getCaptivePortalUserAgent(context);
-        mCaptivePortalHttpsUrl = makeURL(getCaptivePortalServerHttpsUrl(context));
-        mCaptivePortalHttpUrl = makeURL(getCaptivePortalServerHttpUrl(context));
-        mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls(context);
+        mIsCaptivePortalCheckEnabled = getIsCaptivePortalCheckEnabled();
+        mUseHttps = getUseHttpsValidation();
+        mCaptivePortalUserAgent = getCaptivePortalUserAgent();
+        mCaptivePortalHttpsUrl = makeURL(getCaptivePortalServerHttpsUrl());
+        mCaptivePortalHttpUrl = makeURL(getCaptivePortalServerHttpUrl(settings, context));
+        mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls();
 
         start();
     }
@@ -705,19 +705,42 @@
         }
     }
 
-    private static String getCaptivePortalServerHttpsUrl(Context context) {
-        return getSetting(context, Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, DEFAULT_HTTPS_URL);
+    public boolean getIsCaptivePortalCheckEnabled() {
+        String symbol = Settings.Global.CAPTIVE_PORTAL_MODE;
+        int defaultValue = Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT;
+        int mode = mSettings.getSetting(mContext, symbol, defaultValue);
+        return mode != Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE;
     }
 
+    public boolean getUseHttpsValidation() {
+        return mSettings.getSetting(mContext, Settings.Global.CAPTIVE_PORTAL_USE_HTTPS, 1) == 1;
+    }
+
+    public boolean getWifiScansAlwaysAvailableDisabled() {
+        return mSettings.getSetting(mContext, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0;
+    }
+
+    private String getCaptivePortalServerHttpsUrl() {
+        return mSettings.getSetting(mContext,
+                Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, DEFAULT_HTTPS_URL);
+    }
+
+    // Static for direct access by ConnectivityService
     public static String getCaptivePortalServerHttpUrl(Context context) {
-        return getSetting(context, Settings.Global.CAPTIVE_PORTAL_HTTP_URL, DEFAULT_HTTP_URL);
+        return getCaptivePortalServerHttpUrl(NetworkMonitorSettings.DEFAULT, context);
     }
 
-    private URL[] makeCaptivePortalFallbackUrls(Context context) {
+    public static String getCaptivePortalServerHttpUrl(
+            NetworkMonitorSettings settings, Context context) {
+        return settings.getSetting(
+                context, Settings.Global.CAPTIVE_PORTAL_HTTP_URL, DEFAULT_HTTP_URL);
+    }
+
+    private URL[] makeCaptivePortalFallbackUrls() {
         String separator = ",";
-        String firstUrl = getSetting(context,
+        String firstUrl = mSettings.getSetting(mContext,
                 Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, DEFAULT_FALLBACK_URL);
-        String joinedUrls = firstUrl + separator + getSetting(context,
+        String joinedUrls = firstUrl + separator + mSettings.getSetting(mContext,
                 Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, DEFAULT_OTHER_FALLBACK_URLS);
         List<URL> urls = new ArrayList<>();
         for (String s : joinedUrls.split(separator)) {
@@ -733,13 +756,9 @@
         return urls.toArray(new URL[urls.size()]);
     }
 
-    private static String getCaptivePortalUserAgent(Context context) {
-        return getSetting(context, Settings.Global.CAPTIVE_PORTAL_USER_AGENT, DEFAULT_USER_AGENT);
-    }
-
-    private static String getSetting(Context context, String symbol, String defaultValue) {
-        final String value = Settings.Global.getString(context.getContentResolver(), symbol);
-        return value != null ? value : defaultValue;
+    private String getCaptivePortalUserAgent() {
+        return mSettings.getSetting(mContext,
+                Settings.Global.CAPTIVE_PORTAL_USER_AGENT, DEFAULT_USER_AGENT);
     }
 
     private URL nextFallbackUrl() {
@@ -1035,12 +1054,13 @@
      */
     private void sendNetworkConditionsBroadcast(boolean responseReceived, boolean isCaptivePortal,
             long requestTimestampMs, long responseTimestampMs) {
-        if (Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0) {
+        if (getWifiScansAlwaysAvailableDisabled()) {
             return;
         }
 
-        if (systemReady == false) return;
+        if (!systemReady) {
+            return;
+        }
 
         Intent latencyBroadcast = new Intent(ACTION_NETWORK_CONDITIONS_MEASURED);
         switch (mNetworkAgentInfo.networkInfo.getType()) {
@@ -1144,4 +1164,24 @@
         ev.durationMs = durationMs;
         mMetricsLog.log(mNetId, transports, ev);
     }
+
+    @VisibleForTesting
+    public interface NetworkMonitorSettings {
+        int getSetting(Context context, String symbol, int defaultValue);
+        String getSetting(Context context, String symbol, String defaultValue);
+
+        static NetworkMonitorSettings DEFAULT = new DefaultNetworkMonitorSettings();
+    }
+
+    @VisibleForTesting
+    public static class DefaultNetworkMonitorSettings implements NetworkMonitorSettings {
+        public int getSetting(Context context, String symbol, int defaultValue) {
+            return Settings.Global.getInt(context.getContentResolver(), symbol, defaultValue);
+        }
+
+        public String getSetting(Context context, String symbol, String defaultValue) {
+            final String value = Settings.Global.getString(context.getContentResolver(), symbol);
+            return value != null ? value : defaultValue;
+        }
+    }
 }
diff --git a/com/android/server/locksettings/LockSettingsService.java b/com/android/server/locksettings/LockSettingsService.java
index 018b5fa..11043bd 100644
--- a/com/android/server/locksettings/LockSettingsService.java
+++ b/com/android/server/locksettings/LockSettingsService.java
@@ -1142,12 +1142,13 @@
                 continue;
             }
             try {
-                result.put(userId, getDecryptedPasswordForTiedProfile(userId));
+                result.put(managedUserId, getDecryptedPasswordForTiedProfile(managedUserId));
             } catch (KeyStoreException | UnrecoverableKeyException | NoSuchAlgorithmException
                     | NoSuchPaddingException | InvalidKeyException
                     | InvalidAlgorithmParameterException | IllegalBlockSizeException
                     | BadPaddingException | CertificateException | IOException e) {
-                // ignore
+                Slog.e(TAG, "getDecryptedPasswordsForAllTiedProfiles failed for user " +
+                    managedUserId, e);
             }
         }
         return result;
diff --git a/com/android/server/media/AudioPlaybackMonitor.java b/com/android/server/media/AudioPlaybackMonitor.java
index f6f7676..791ee82 100644
--- a/com/android/server/media/AudioPlaybackMonitor.java
+++ b/com/android/server/media/AudioPlaybackMonitor.java
@@ -100,7 +100,11 @@
      * @param configs List of the current audio playback configuration
      */
     @Override
-    public void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs) {
+    public void dispatchPlaybackConfigChange(List<AudioPlaybackConfiguration> configs,
+            boolean flush) {
+        if (flush) {
+            Binder.flushPendingCommands();
+        }
         final long token = Binder.clearCallingIdentity();
         try {
             List<Integer> newActiveAudioPlaybackClientUids = new ArrayList<>();
diff --git a/com/android/server/net/NetworkPolicyManagerService.java b/com/android/server/net/NetworkPolicyManagerService.java
index 6dfb469..90dab2c 100644
--- a/com/android/server/net/NetworkPolicyManagerService.java
+++ b/com/android/server/net/NetworkPolicyManagerService.java
@@ -113,16 +113,20 @@
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
 import android.net.IConnectivityManager;
 import android.net.INetworkManagementEventObserver;
 import android.net.INetworkPolicyListener;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
 import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
 import android.net.NetworkIdentity;
 import android.net.NetworkPolicy;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkQuotaInfo;
+import android.net.NetworkRequest;
 import android.net.NetworkState;
 import android.net.NetworkTemplate;
 import android.net.TrafficStats;
@@ -446,6 +450,10 @@
     @GuardedBy("mUidRulesFirstLock")
     final SparseIntArray mUidState = new SparseIntArray();
 
+    /** Map from network ID to last observed meteredness state */
+    @GuardedBy("mNetworkPoliciesSecondLock")
+    private final SparseBooleanArray mNetworkMetered = new SparseBooleanArray();
+
     private final RemoteCallbackList<INetworkPolicyListener>
             mListeners = new RemoteCallbackList<>();
 
@@ -748,6 +756,10 @@
                     ACTION_CARRIER_CONFIG_CHANGED);
             mContext.registerReceiver(mCarrierConfigReceiver, carrierConfigFilter, null, mHandler);
 
+            // listen for meteredness changes
+            mContext.getSystemService(ConnectivityManager.class).registerNetworkCallback(
+                    new NetworkRequest.Builder().build(), mNetworkCallback);
+
             mUsageStats.addAppIdleStateChangeListener(new AppIdleStateChangeListener());
             // tell systemReady() that the service has been initialized
             initCompleteSignal.countDown();
@@ -936,6 +948,26 @@
             synchronized (mUidRulesFirstLock) {
                 synchronized (mNetworkPoliciesSecondLock) {
                     upgradeWifiMeteredOverrideAL();
+                }
+            }
+            // Only need to perform upgrade logic once
+            mContext.unregisterReceiver(this);
+        }
+    };
+
+    private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+        @Override
+        public void onCapabilitiesChanged(Network network,
+                NetworkCapabilities networkCapabilities) {
+            if (network == null || networkCapabilities == null) return;
+
+            synchronized (mNetworkPoliciesSecondLock) {
+                final boolean oldMetered = mNetworkMetered.get(network.netId, false);
+                final boolean newMetered = !networkCapabilities
+                        .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+
+                if ((oldMetered != newMetered) || mNetworkMetered.indexOfKey(network.netId) < 0) {
+                    mNetworkMetered.put(network.netId, newMetered);
                     updateNetworkRulesNL();
                 }
             }
diff --git a/com/android/server/notification/NotificationManagerService.java b/com/android/server/notification/NotificationManagerService.java
index fd41346..fe39fcc 100644
--- a/com/android/server/notification/NotificationManagerService.java
+++ b/com/android/server/notification/NotificationManagerService.java
@@ -755,9 +755,12 @@
                 if (r != null) {
                     r.stats.onExpansionChanged(userAction, expanded);
                     final long now = System.currentTimeMillis();
-                    MetricsLogger.action(r.getLogMaker(now)
-                            .setCategory(MetricsEvent.NOTIFICATION_ITEM)
-                            .setType(MetricsEvent.TYPE_DETAIL));
+                    if (userAction) {
+                        MetricsLogger.action(r.getLogMaker(now)
+                                .setCategory(MetricsEvent.NOTIFICATION_ITEM)
+                                .setType(expanded ? MetricsEvent.TYPE_DETAIL
+                                        : MetricsEvent.TYPE_COLLAPSE));
+                    }
                     EventLogTags.writeNotificationExpansion(key,
                             userAction ? 1 : 0, expanded ? 1 : 0,
                             r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now));
@@ -2770,17 +2773,22 @@
         public void setNotificationPolicyAccessGranted(String pkg, boolean granted)
                 throws RemoteException {
             checkCallerIsSystemOrShell();
-            if (!mActivityManager.isLowRamDevice()) {
-                mConditionProviders.setPackageOrComponentEnabled(
-                        pkg, getCallingUserHandle().getIdentifier(), true, granted);
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                if (!mActivityManager.isLowRamDevice()) {
+                    mConditionProviders.setPackageOrComponentEnabled(
+                            pkg, getCallingUserHandle().getIdentifier(), true, granted);
 
-                getContext().sendBroadcastAsUser(new Intent(
-                        NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
-                                .setPackage(pkg)
-                                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
-                        getCallingUserHandle(), null);
+                    getContext().sendBroadcastAsUser(new Intent(
+                            NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
+                                    .setPackage(pkg)
+                                    .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
+                            getCallingUserHandle(), null);
 
-                savePolicyFile();
+                    savePolicyFile();
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
         }
 
@@ -2862,19 +2870,25 @@
                 boolean granted) throws RemoteException {
             Preconditions.checkNotNull(listener);
             checkCallerIsSystemOrShell();
-            if (!mActivityManager.isLowRamDevice()) {
-                mConditionProviders.setPackageOrComponentEnabled(listener.flattenToString(),
-                        userId, false, granted);
-                mListeners.setPackageOrComponentEnabled(listener.flattenToString(),
-                        userId, true, granted);
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                if (!mActivityManager.isLowRamDevice()) {
+                    mConditionProviders.setPackageOrComponentEnabled(listener.flattenToString(),
+                            userId, false, granted);
+                    mListeners.setPackageOrComponentEnabled(listener.flattenToString(),
+                            userId, true, granted);
 
-                getContext().sendBroadcastAsUser(new Intent(
-                        NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
-                                .setPackage(listener.getPackageName())
-                                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
-                        getCallingUserHandle(), null);
+                    getContext().sendBroadcastAsUser(new Intent(
+                                    NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
 
-                savePolicyFile();
+                                    .setPackage(listener.getPackageName())
+                                    .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
+                            getCallingUserHandle(), null);
+
+                    savePolicyFile();
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
         }
 
@@ -2883,19 +2897,24 @@
                 int userId, boolean granted) throws RemoteException {
             Preconditions.checkNotNull(assistant);
             checkCallerIsSystemOrShell();
-            if (!mActivityManager.isLowRamDevice()) {
-                mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(),
-                        userId, false, granted);
-                mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(),
-                        userId, true, granted);
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                if (!mActivityManager.isLowRamDevice()) {
+                    mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(),
+                            userId, false, granted);
+                    mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(),
+                            userId, true, granted);
 
-                getContext().sendBroadcastAsUser(new Intent(
-                        NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
-                                .setPackage(assistant.getPackageName())
-                                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
-                        getCallingUserHandle(), null);
+                    getContext().sendBroadcastAsUser(new Intent(
+                            NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED)
+                                    .setPackage(assistant.getPackageName())
+                                    .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY),
+                            getCallingUserHandle(), null);
 
-                savePolicyFile();
+                    savePolicyFile();
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
         }
 
diff --git a/com/android/server/pm/PackageInstallerSession.java b/com/android/server/pm/PackageInstallerSession.java
index 1fd5969..ff6e5b3 100644
--- a/com/android/server/pm/PackageInstallerSession.java
+++ b/com/android/server/pm/PackageInstallerSession.java
@@ -88,6 +88,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.content.PackageHelper;
+import com.android.internal.os.SomeArgs;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
@@ -118,6 +119,7 @@
     private static final String REMOVE_SPLIT_MARKER_EXTENSION = ".removed";
 
     private static final int MSG_COMMIT = 0;
+    private static final int MSG_ON_PACKAGE_INSTALLED = 1;
 
     /** XML constants used for persisting a session */
     static final String TAG_SESSION = "session";
@@ -274,15 +276,36 @@
     private final Handler.Callback mHandlerCallback = new Handler.Callback() {
         @Override
         public boolean handleMessage(Message msg) {
-            synchronized (mLock) {
-                try {
-                    commitLocked();
-                } catch (PackageManagerException e) {
-                    final String completeMsg = ExceptionUtils.getCompleteMessage(e);
-                    Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
-                    destroyInternal();
-                    dispatchSessionFinished(e.error, completeMsg, null);
-                }
+            switch (msg.what) {
+                case MSG_COMMIT:
+                    synchronized (mLock) {
+                        try {
+                            commitLocked();
+                        } catch (PackageManagerException e) {
+                            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
+                            Slog.e(TAG,
+                                    "Commit of session " + sessionId + " failed: " + completeMsg);
+                            destroyInternal();
+                            dispatchSessionFinished(e.error, completeMsg, null);
+                        }
+                    }
+
+                    break;
+                case MSG_ON_PACKAGE_INSTALLED:
+                    final SomeArgs args = (SomeArgs) msg.obj;
+                    final String packageName = (String) args.arg1;
+                    final String message = (String) args.arg2;
+                    final Bundle extras = (Bundle) args.arg3;
+                    final IPackageInstallObserver2 observer = (IPackageInstallObserver2) args.arg4;
+                    final int returnCode = args.argi1;
+                    args.recycle();
+
+                    try {
+                        observer.onPackageInstalled(packageName, returnCode, message, extras);
+                    } catch (RemoteException ignored) {
+                    }
+
+                    break;
             }
 
             return true;
@@ -1468,10 +1491,17 @@
         }
 
         if (observer != null) {
-            try {
-                observer.onPackageInstalled(packageName, returnCode, msg, extras);
-            } catch (RemoteException ignored) {
-            }
+            // Execute observer.onPackageInstalled on different tread as we don't want callers
+            // inside the system server have to worry about catching the callbacks while they are
+            // calling into the session
+            final SomeArgs args = SomeArgs.obtain();
+            args.arg1 = packageName;
+            args.arg2 = msg;
+            args.arg3 = extras;
+            args.arg4 = observer;
+            args.argi1 = returnCode;
+
+            mHandler.obtainMessage(MSG_ON_PACKAGE_INSTALLED, args).sendToTarget();
         }
 
         final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
diff --git a/com/android/server/pm/PackageManagerService.java b/com/android/server/pm/PackageManagerService.java
index 7185420..ff52e0e 100644
--- a/com/android/server/pm/PackageManagerService.java
+++ b/com/android/server/pm/PackageManagerService.java
@@ -82,6 +82,7 @@
 import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.pm.PackageParser.PARSE_IS_OEM;
 import static android.content.pm.PackageParser.PARSE_IS_PRIVILEGED;
 import static android.content.pm.PackageParser.isApkFile;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
@@ -2650,7 +2651,8 @@
             final File oemAppDir = new File(Environment.getOemDirectory(), "app");
             scanDirTracedLI(oemAppDir, mDefParseFlags
                     | PackageParser.PARSE_IS_SYSTEM
-                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
+                    | PackageParser.PARSE_IS_SYSTEM_DIR
+                    | PackageParser.PARSE_IS_OEM, scanFlags, 0);
 
             // Prune any system packages that no longer exist.
             final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
@@ -2820,7 +2822,8 @@
                                     | PackageParser.PARSE_IS_SYSTEM_DIR;
                         } else if (FileUtils.contains(oemAppDir, scanFile)) {
                             reparseFlags = PackageParser.PARSE_IS_SYSTEM
-                                    | PackageParser.PARSE_IS_SYSTEM_DIR;
+                                    | PackageParser.PARSE_IS_SYSTEM_DIR
+                                    | PackageParser.PARSE_IS_OEM;
                         } else {
                             Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
                             continue;
@@ -9345,6 +9348,13 @@
             } else {
                 updatedPkg.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
             }
+            // If new package is not located in "/oem" (e.g. due to an OTA),
+            // it needs to drop FLAG_OEM.
+            if (locationIsOem(scanFile)) {
+                updatedPkg.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_OEM;
+            } else {
+                updatedPkg.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_OEM;
+            }
 
             if (ps != null && !ps.codePath.equals(scanFile)) {
                 // The path has changed from what was last scanned...  check the
@@ -9463,6 +9473,12 @@
             if ((updatedPkg.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
                 policyFlags |= PackageParser.PARSE_IS_PRIVILEGED;
             }
+
+            // An updated OEM app will not have the PARSE_IS_OEM
+            // flag set initially
+            if ((updatedPkg.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0) {
+                policyFlags |= PackageParser.PARSE_IS_OEM;
+            }
         }
 
         // Verify certificates against what was last scanned
@@ -11183,6 +11199,10 @@
             pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
         }
 
+        if ((policyFlags&PackageParser.PARSE_IS_OEM) != 0) {
+            pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_OEM;
+        }
+
         if (!isSystemApp(pkg)) {
             // Only system apps can use these features.
             pkg.mOriginalPackages = null;
@@ -13254,6 +13274,8 @@
 
     private boolean grantSignaturePermission(String perm, PackageParser.Package pkg,
             BasePermission bp, PermissionsState origPermissions) {
+        boolean oemPermission = (bp.protectionLevel
+                & PermissionInfo.PROTECTION_FLAG_OEM) != 0;
         boolean privilegedPermission = (bp.protectionLevel
                 & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0;
         boolean privappPermissionsDisable =
@@ -13291,9 +13313,9 @@
                         == PackageManager.SIGNATURE_MATCH)
                 || (compareSignatures(mPlatformPackage.mSignatures, pkg.mSignatures)
                         == PackageManager.SIGNATURE_MATCH);
-        if (!allowed && privilegedPermission) {
+        if (!allowed && (privilegedPermission || oemPermission)) {
             if (isSystemApp(pkg)) {
-                // For updated system applications, a system permission
+                // For updated system applications, a privileged/oem permission
                 // is granted only if it had been defined by the original application.
                 if (pkg.isUpdatedSystemApp()) {
                     final PackageSetting sysPs = mSettings
@@ -13302,7 +13324,9 @@
                         // If the original was granted this permission, we take
                         // that grant decision as read and propagate it to the
                         // update.
-                        if (sysPs.isPrivileged()) {
+                        if ((privilegedPermission && sysPs.isPrivileged())
+                                || (oemPermission && sysPs.isOem()
+                                        && canGrantOemPermission(sysPs, perm))) {
                             allowed = true;
                         }
                     } else {
@@ -13312,32 +13336,39 @@
                         // before.  In this case we do want to allow the app to
                         // now get the new permission if the ancestral apk is
                         // privileged to get it.
-                        if (sysPs != null && sysPs.pkg != null && sysPs.isPrivileged()) {
-                            // TODO(gboyer): This is the same as isPackageRequestingPermission().
-                            for (int j = 0; j < sysPs.pkg.requestedPermissions.size(); j++) {
-                                if (perm.equals(sysPs.pkg.requestedPermissions.get(j))) {
-                                    allowed = true;
-                                    break;
-                                }
-                            }
+                        if (sysPs != null && sysPs.pkg != null
+                                && isPackageRequestingPermission(sysPs.pkg, perm)
+                                && ((privilegedPermission && sysPs.isPrivileged())
+                                        || (oemPermission && sysPs.isOem()
+                                                && canGrantOemPermission(sysPs, perm)))) {
+                            allowed = true;
                         }
                         // Also if a privileged parent package on the system image or any of
-                        // its children requested a privileged permission, the updated child
+                        // its children requested a privileged/oem permission, the updated child
                         // packages can also get the permission.
                         if (pkg.parentPackage != null) {
                             final PackageSetting disabledSysParentPs = mSettings
                                     .getDisabledSystemPkgLPr(pkg.parentPackage.packageName);
-                            if (disabledSysParentPs != null && disabledSysParentPs.pkg != null
-                                    && disabledSysParentPs.isPrivileged()) {
-                                if (isPackageRequestingPermission(disabledSysParentPs.pkg, perm)) {
+                            final PackageParser.Package disabledSysParentPkg =
+                                    (disabledSysParentPs == null || disabledSysParentPs.pkg == null)
+                                    ? null : disabledSysParentPs.pkg;
+                            if (disabledSysParentPkg != null
+                                    && ((privilegedPermission && disabledSysParentPs.isPrivileged())
+                                            || (oemPermission && disabledSysParentPs.isOem()))) {
+                                if (isPackageRequestingPermission(disabledSysParentPkg, perm)
+                                        && canGrantOemPermission(disabledSysParentPs, perm)) {
                                     allowed = true;
-                                } else if (disabledSysParentPs.pkg.childPackages != null) {
-                                    final int count = disabledSysParentPs.pkg.childPackages.size();
+                                } else if (disabledSysParentPkg.childPackages != null) {
+                                    final int count = disabledSysParentPkg.childPackages.size();
                                     for (int i = 0; i < count; i++) {
-                                        PackageParser.Package disabledSysChildPkg =
-                                                disabledSysParentPs.pkg.childPackages.get(i);
-                                        if (isPackageRequestingPermission(disabledSysChildPkg,
-                                                perm)) {
+                                        final PackageParser.Package disabledSysChildPkg =
+                                                disabledSysParentPkg.childPackages.get(i);
+                                        final PackageSetting disabledSysChildPs =
+                                                mSettings.getDisabledSystemPkgLPr(
+                                                        disabledSysChildPkg.packageName);
+                                        if (isPackageRequestingPermission(disabledSysChildPkg, perm)
+                                                && canGrantOemPermission(
+                                                        disabledSysChildPs, perm)) {
                                             allowed = true;
                                             break;
                                         }
@@ -13347,7 +13378,10 @@
                         }
                     }
                 } else {
-                    allowed = isPrivilegedApp(pkg);
+                    allowed = (privilegedPermission && isPrivilegedApp(pkg))
+                            || (oemPermission && isOemApp(pkg)
+                                    && canGrantOemPermission(
+                                            mSettings.getPackageLPr(pkg.packageName), perm));
                 }
             }
         }
@@ -13394,6 +13428,20 @@
         return allowed;
     }
 
+    private static boolean canGrantOemPermission(PackageSetting ps, String permission) {
+        if (!ps.isOem()) {
+            return false;
+        }
+        // all oem permissions must explicitly be granted or denied
+        final Boolean granted =
+                SystemConfig.getInstance().getOemPermissions(ps.name).get(permission);
+        if (granted == null) {
+            throw new IllegalStateException("OEM permission" + permission + " requested by package "
+                    + ps.name + " must be explicitly declared granted or not");
+        }
+        return Boolean.TRUE == granted;
+    }
+
     private boolean isPackageRequestingPermission(PackageParser.Package pkg, String permission) {
         final int permCount = pkg.requestedPermissions.size();
         for (int j = 0; j < permCount; j++) {
@@ -17787,13 +17835,17 @@
 
         boolean sysPkg = (isSystemApp(oldPackage));
         if (sysPkg) {
-            // Set the system/privileged flags as needed
+            // Set the system/privileged/oem flags as needed
             final boolean privileged =
                     (oldPackage.applicationInfo.privateFlags
                             & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
+            final boolean oem =
+                    (oldPackage.applicationInfo.privateFlags
+                            & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
             final int systemPolicyFlags = policyFlags
                     | PackageParser.PARSE_IS_SYSTEM
-                    | (privileged ? PackageParser.PARSE_IS_PRIVILEGED : 0);
+                    | (privileged ? PARSE_IS_PRIVILEGED : 0)
+                    | (oem ? PARSE_IS_OEM : 0);
 
             replaceSystemPackageLIF(oldPackage, pkg, systemPolicyFlags, scanFlags,
                     user, allUsers, installerPackageName, res, installReason);
@@ -18644,6 +18696,7 @@
                             + perm.info.name + "; Removing ephemeral.");
                     perm.info.protectionLevel &= ~PermissionInfo.PROTECTION_FLAG_INSTANT;
                 }
+
                 // Check whether the newly-scanned package wants to define an already-defined perm
                 if (bp != null) {
                     // If the defining package is signed with our cert, it's okay.  This
@@ -18999,6 +19052,10 @@
         return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
     }
 
+    private static boolean isOemApp(PackageParser.Package pkg) {
+        return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
+    }
+
     private static boolean hasDomainURLs(PackageParser.Package pkg) {
         return (pkg.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS) != 0;
     }
@@ -19752,6 +19809,16 @@
         return false;
     }
 
+    static boolean locationIsOem(File path) {
+        try {
+            return path.getCanonicalPath().startsWith(
+                    Environment.getOemDirectory().getCanonicalPath());
+        } catch (IOException e) {
+            Slog.e(TAG, "Unable to access code path " + path);
+        }
+        return false;
+    }
+
     /*
      * Tries to delete system package.
      */
@@ -19869,6 +19936,9 @@
         if (isPrivileged || locationIsPrivileged(codePath)) {
             parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
         }
+        if (locationIsOem(codePath)) {
+            parseFlags |= PackageParser.PARSE_IS_OEM;
+        }
 
         final PackageParser.Package newPkg =
                 scanPackageTracedLI(codePath, parseFlags, 0 /*scanFlags*/, 0 /*currentTime*/, null);
diff --git a/com/android/server/pm/PackageManagerShellCommand.java b/com/android/server/pm/PackageManagerShellCommand.java
index 909ffab..930e4f0 100644
--- a/com/android/server/pm/PackageManagerShellCommand.java
+++ b/com/android/server/pm/PackageManagerShellCommand.java
@@ -54,6 +54,7 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.PrintWriterPrinter;
 import com.android.internal.content.PackageHelper;
@@ -75,6 +76,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Map;
 import java.util.WeakHashMap;
 import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.TimeUnit;
@@ -150,6 +152,8 @@
                     return runGetPrivappPermissions();
                 case "get-privapp-deny-permissions":
                     return runGetPrivappDenyPermissions();
+                case "get-oem-permissions":
+                    return runGetOemPermissions();
                 case "get-instantapp-resolver":
                     return runGetInstantAppResolver();
                 case "has-feature":
@@ -1308,6 +1312,24 @@
         return 0;
     }
 
+    private int runGetOemPermissions() {
+        final String pkg = getNextArg();
+        if (pkg == null) {
+            System.err.println("Error: no package specified.");
+            return 1;
+        }
+        final Map<String, Boolean> oemPermissions = SystemConfig.getInstance()
+                .getOemPermissions(pkg);
+        if (oemPermissions == null || oemPermissions.isEmpty()) {
+            getOutPrintWriter().println("{}");
+        } else {
+            oemPermissions.forEach((permission, granted) ->
+                getOutPrintWriter().println(permission + " granted:" + granted)
+            );
+        }
+        return 0;
+    }
+
     private int runGetInstantAppResolver() {
         final PrintWriter pw = getOutPrintWriter();
         try {
@@ -1730,6 +1752,10 @@
         pw.println("  has-feature FEATURE_NAME [version]");
         pw.println("   prints true and returns exit status 0 when system has a FEATURE_NAME,");
         pw.println("   otherwise prints false and returns exit status 1");
+        pw.println("  get-privileged-permissions TARGET-PACKAGE");
+        pw.println("   prints all privileged permissions for a package.");
+        pw.println("  get-oem-permissions TARGET-PACKAGE");
+        pw.println("   prints all OEM permissions for a package.");
         pw.println();
         Intent.printIntentArgsHelp(pw , "");
     }
diff --git a/com/android/server/pm/PackageSetting.java b/com/android/server/pm/PackageSetting.java
index b4bba88..52bf641 100644
--- a/com/android/server/pm/PackageSetting.java
+++ b/com/android/server/pm/PackageSetting.java
@@ -113,6 +113,10 @@
         return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
     }
 
+    public boolean isOem() {
+        return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM) != 0;
+    }
+
     public boolean isForwardLocked() {
         return (pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK) != 0;
     }
diff --git a/com/android/server/pm/SettingBase.java b/com/android/server/pm/SettingBase.java
index 71e8d51..e17cec0 100644
--- a/com/android/server/pm/SettingBase.java
+++ b/com/android/server/pm/SettingBase.java
@@ -58,6 +58,7 @@
     void setPrivateFlags(int pkgPrivateFlags) {
         this.pkgPrivateFlags = pkgPrivateFlags
                 & (ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
+                | ApplicationInfo.PRIVATE_FLAG_OEM
                 | ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK
                 | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
     }
diff --git a/com/android/server/pm/Settings.java b/com/android/server/pm/Settings.java
index 56835f6..51d3e10 100644
--- a/com/android/server/pm/Settings.java
+++ b/com/android/server/pm/Settings.java
@@ -869,6 +869,8 @@
         pkgSetting.pkgFlags |= pkgFlags & ApplicationInfo.FLAG_SYSTEM;
         pkgSetting.pkgPrivateFlags |=
                 pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
+        pkgSetting.pkgPrivateFlags |=
+                pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_OEM;
         pkgSetting.primaryCpuAbiString = primaryCpuAbi;
         pkgSetting.secondaryCpuAbiString = secondaryCpuAbi;
         if (childPkgNames != null) {
@@ -4529,21 +4531,24 @@
     };
 
     private static final Object[] PRIVATE_FLAG_DUMP_SPEC = new Object[] {
-        ApplicationInfo.PRIVATE_FLAG_HIDDEN, "HIDDEN",
-        ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
-        ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK, "FORWARD_LOCK",
-        ApplicationInfo.PRIVATE_FLAG_PRIVILEGED, "PRIVILEGED",
-        ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS, "HAS_DOMAIN_URLS",
-        ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE, "DEFAULT_TO_DEVICE_PROTECTED_STORAGE",
-        ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE, "DIRECT_BOOT_AWARE",
-        ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE, "PARTIALLY_DIRECT_BOOT_AWARE",
-        ApplicationInfo.PRIVATE_FLAG_INSTANT, "EPHEMERAL",
-        ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER, "REQUIRED_FOR_SYSTEM_USER",
-        ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE, "PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE",
-        ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE, "PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE",
-        ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION, "PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION",
-        ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND, "BACKUP_IN_FOREGROUND",
-        ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY, "STATIC_SHARED_LIBRARY",
+            ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE, "PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE",
+            ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION, "PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION",
+            ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE, "PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE",
+            ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND, "BACKUP_IN_FOREGROUND",
+            ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE",
+            ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE, "DEFAULT_TO_DEVICE_PROTECTED_STORAGE",
+            ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE, "DIRECT_BOOT_AWARE",
+            ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK, "FORWARD_LOCK",
+            ApplicationInfo.PRIVATE_FLAG_HAS_DOMAIN_URLS, "HAS_DOMAIN_URLS",
+            ApplicationInfo.PRIVATE_FLAG_HIDDEN, "HIDDEN",
+            ApplicationInfo.PRIVATE_FLAG_INSTANT, "EPHEMERAL",
+            ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING, "ISOLATED_SPLIT_LOADING",
+            ApplicationInfo.PRIVATE_FLAG_OEM, "OEM",
+            ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE, "PARTIALLY_DIRECT_BOOT_AWARE",
+            ApplicationInfo.PRIVATE_FLAG_PRIVILEGED, "PRIVILEGED",
+            ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER, "REQUIRED_FOR_SYSTEM_USER",
+            ApplicationInfo.PRIVATE_FLAG_STATIC_SHARED_LIBRARY, "STATIC_SHARED_LIBRARY",
+            ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD, "VIRTUAL_PRELOAD",
     };
 
     void dumpVersionLPr(IndentingPrintWriter pw) {
diff --git a/com/android/server/timezone/IntentHelperImpl.java b/com/android/server/timezone/IntentHelperImpl.java
index 11928b9..6db70cd 100644
--- a/com/android/server/timezone/IntentHelperImpl.java
+++ b/com/android/server/timezone/IntentHelperImpl.java
@@ -53,20 +53,34 @@
         // The intent filter that triggers when package update events happen that indicate there may
         // be work to do.
         IntentFilter packageIntentFilter = new IntentFilter();
-        // Either of these mean a downgrade?
-        packageIntentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        packageIntentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+
         packageIntentFilter.addDataScheme("package");
         packageIntentFilter.addDataSchemeSpecificPart(
                 updaterAppPackageName, PatternMatcher.PATTERN_LITERAL);
         packageIntentFilter.addDataSchemeSpecificPart(
                 dataAppPackageName, PatternMatcher.PATTERN_LITERAL);
+
+        // ACTION_PACKAGE_ADDED is fired when a package is upgraded or downgraded (in addition to
+        // ACTION_PACKAGE_REMOVED and ACTION_PACKAGE_REPLACED). A system/priv-app can never be
+        // removed entirely so we do not need to trigger on ACTION_PACKAGE_REMOVED or
+        // ACTION_PACKAGE_FULLY_REMOVED.
+        packageIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+
+        // ACTION_PACKAGE_CHANGED is used when a package is disabled / re-enabled. It is not
+        // strictly necessary to trigger on this but it won't hurt anything and may catch some cases
+        // where a package has changed while disabled.
+        // Note: ACTION_PACKAGE_CHANGED is not fired when updating a suspended app, but
+        // ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and ACTION_PACKAGE_REPLACED are (and the app
+        // is left in an unsuspended state after this).
+        packageIntentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+
+        // We do not register for ACTION_PACKAGE_RESTARTED because it doesn't imply an update.
+        // We do not register for ACTION_PACKAGE_DATA_CLEARED because the updater / data apps are
+        // not expected to need local data.
+
         Receiver packageUpdateReceiver = new Receiver(listener, true /* packageUpdated */);
         mContext.registerReceiver(packageUpdateReceiver, packageIntentFilter);
 
-        // TODO(nfuller): Add more exotic intents as needed. e.g.
-        // packageIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        // Also, disabled...?
         mReliabilityReceiver = new Receiver(listener, false /* packageUpdated */);
     }
 
diff --git a/com/android/server/timezone/RulesManagerService.java b/com/android/server/timezone/RulesManagerService.java
index 50f27ed..3ad4419 100644
--- a/com/android/server/timezone/RulesManagerService.java
+++ b/com/android/server/timezone/RulesManagerService.java
@@ -343,16 +343,20 @@
         @Override
         public void run() {
             EventLogTags.writeTimezoneUninstallStarted(toStringOrNull(mCheckToken));
-            boolean success = false;
+            boolean packageTrackerStatus = false;
             try {
-                success = mInstaller.stageUninstall();
-                // Right now we just have success (0) / failure (1). All clients should be checking
-                // against SUCCESS. More granular failures may be added in future.
-                int resultCode = success ? Callback.SUCCESS
-                        : Callback.ERROR_UNKNOWN_FAILURE;
+                int uninstallResult = mInstaller.stageUninstall();
+                packageTrackerStatus = (uninstallResult == TimeZoneDistroInstaller.UNINSTALL_SUCCESS
+                        || uninstallResult == TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED);
+
+                // Right now we just have Callback.SUCCESS / Callback.ERROR_UNKNOWN_FAILURE for
+                // uninstall. All clients should be checking against SUCCESS. More granular failures
+                // may be added in future.
+                int callbackResultCode =
+                        packageTrackerStatus ? Callback.SUCCESS : Callback.ERROR_UNKNOWN_FAILURE;
                 EventLogTags.writeTimezoneUninstallComplete(
-                        toStringOrNull(mCheckToken), resultCode);
-                sendFinishedStatus(mCallback, resultCode);
+                        toStringOrNull(mCheckToken), callbackResultCode);
+                sendFinishedStatus(mCallback, callbackResultCode);
             } catch (Exception e) {
                 EventLogTags.writeTimezoneUninstallComplete(
                         toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE);
@@ -360,7 +364,7 @@
                 sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
             } finally {
                 // Notify the package tracker that the operation is now complete.
-                mPackageTracker.recordCheckResult(mCheckToken, success);
+                mPackageTracker.recordCheckResult(mCheckToken, packageTrackerStatus);
 
                 mOperationInProgress.set(false);
             }
diff --git a/com/android/server/wallpaper/WallpaperManagerService.java b/com/android/server/wallpaper/WallpaperManagerService.java
index b963561..b888ec2 100644
--- a/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1138,7 +1138,13 @@
         mMonitor = new MyPackageMonitor();
         mMonitor.register(context, null, UserHandle.ALL, true);
         getWallpaperDir(UserHandle.USER_SYSTEM).mkdirs();
+
+        // Initialize state from the persistent store, then guarantee that the
+        // WallpaperData for the system imagery is instantiated & active, creating
+        // it from defaults if necessary.
         loadSettingsLocked(UserHandle.USER_SYSTEM, false);
+        getWallpaperSafeLocked(UserHandle.USER_SYSTEM, FLAG_SYSTEM);
+
         mColorsChangedListeners = new SparseArray<>();
     }
 
@@ -1295,15 +1301,13 @@
     }
 
     void switchUser(int userId, IRemoteCallback reply) {
-        WallpaperData systemWallpaper;
-        WallpaperData lockWallpaper;
+        final WallpaperData systemWallpaper;
+        final WallpaperData lockWallpaper;
         synchronized (mLock) {
             mCurrentUserId = userId;
             systemWallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
-            lockWallpaper = mLockWallpaperMap.get(userId);
-            if (lockWallpaper == null) {
-                lockWallpaper = systemWallpaper;
-            }
+            final WallpaperData tmpLockWallpaper = mLockWallpaperMap.get(userId);
+            lockWallpaper = tmpLockWallpaper == null ? systemWallpaper : tmpLockWallpaper;
             // Not started watching yet, in case wallpaper data was loaded for other reasons.
             if (systemWallpaper.wallpaperObserver == null) {
                 systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper);
@@ -1311,8 +1315,13 @@
             }
             switchWallpaper(systemWallpaper, reply);
         }
-        notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM);
-        notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
+
+        // Offload color extraction to another thread since switchUser will be called
+        // from the main thread.
+        FgThread.getHandler().post(() -> {
+            notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM);
+            notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
+        });
     }
 
     void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) {
@@ -1627,13 +1636,10 @@
                     (which == FLAG_LOCK) ? mLockWallpaperMap : mWallpaperMap;
             WallpaperData wallpaper = whichSet.get(wallpaperUserId);
             if (wallpaper == null) {
-                // common case, this is the first lookup post-boot of the system or
-                // unified lock, so we bring up the saved state lazily now and recheck.
-                loadSettingsLocked(wallpaperUserId, false);
-                wallpaper = whichSet.get(wallpaperUserId);
-                if (wallpaper == null) {
-                    return null;
-                }
+                // There is no established wallpaper imagery of this type (expected
+                // only for lock wallpapers; a system WallpaperData is established at
+                // user switch)
+                return null;
             }
             try {
                 if (outParams != null) {
@@ -1658,7 +1664,7 @@
     @Override
     public WallpaperInfo getWallpaperInfo(int userId) {
         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
-                Binder.getCallingUid(), userId, false, true, "getWallpaperIdForUser", null);
+                Binder.getCallingUid(), userId, false, true, "getWallpaperInfo", null);
         synchronized (mLock) {
             WallpaperData wallpaper = mWallpaperMap.get(userId);
             if (wallpaper != null && wallpaper.connection != null) {
@@ -2260,7 +2266,7 @@
     private void writeWallpaperAttributes(XmlSerializer out, String tag, WallpaperData wallpaper)
             throws IllegalArgumentException, IllegalStateException, IOException {
         if (DEBUG) {
-            Slog.v(TAG, "writeWallpaperAttributes");
+            Slog.v(TAG, "writeWallpaperAttributes id=" + wallpaper.wallpaperId);
         }
         out.startTag(null, tag);
         out.attribute(null, "id", Integer.toString(wallpaper.wallpaperId));
@@ -2359,6 +2365,9 @@
      * the event yet.  We use this safe method when we don't care about this ordering and just
      * want to update the data.  The data is going to be applied when the user switch observer
      * is eventually executed.
+     *
+     * Important: this method loads settings to initialize the given user's wallpaper data if
+     * there is no current in-memory state.
      */
     private WallpaperData getWallpaperSafeLocked(int userId, int which) {
         // We're setting either just system (work with the system wallpaper),
@@ -2397,8 +2406,6 @@
     }
 
     private void loadSettingsLocked(int userId, boolean keepDimensionHints) {
-        if (DEBUG) Slog.v(TAG, "loadSettingsLocked");
-
         JournaledFile journal = makeJournaledFile(userId);
         FileInputStream stream = null;
         File file = journal.chooseForRead();
diff --git a/com/android/server/wifi/SupplicantStaIfaceHal.java b/com/android/server/wifi/SupplicantStaIfaceHal.java
index d2182fc..c300c3a 100644
--- a/com/android/server/wifi/SupplicantStaIfaceHal.java
+++ b/com/android/server/wifi/SupplicantStaIfaceHal.java
@@ -69,6 +69,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -434,7 +435,22 @@
         synchronized (mLock) {
             logd("connectToNetwork " + config.configKey());
             if (WifiConfigurationUtil.isSameNetwork(config, mCurrentNetworkLocalConfig)) {
-                logd("Network is already saved, will not trigger remove and add operation.");
+                String networkSelectionBSSID = config.getNetworkSelectionStatus()
+                        .getNetworkSelectionBSSID();
+                String networkSelectionBSSIDCurrent =
+                        mCurrentNetworkLocalConfig.getNetworkSelectionStatus()
+                                .getNetworkSelectionBSSID();
+                if (Objects.equals(networkSelectionBSSID, networkSelectionBSSIDCurrent)) {
+                    logd("Network is already saved, will not trigger remove and add operation.");
+                } else {
+                    logd("Network is already saved, but need to update BSSID.");
+                    if (!setCurrentNetworkBssid(
+                            config.getNetworkSelectionStatus().getNetworkSelectionBSSID())) {
+                        loge("Failed to set current network BSSID.");
+                        return false;
+                    }
+                    mCurrentNetworkLocalConfig = new WifiConfiguration(config);
+                }
             } else {
                 mCurrentNetworkRemoteHandle = null;
                 mCurrentNetworkLocalConfig = null;
diff --git a/com/android/server/wifi/WifiConfigManager.java b/com/android/server/wifi/WifiConfigManager.java
index 75b39b2..f8b33cb 100644
--- a/com/android/server/wifi/WifiConfigManager.java
+++ b/com/android/server/wifi/WifiConfigManager.java
@@ -1107,9 +1107,10 @@
      * Removes the specified network configuration from our database.
      *
      * @param config provided WifiConfiguration object.
+     * @param uid UID of the app requesting the network deletion.
      * @return true if successful, false otherwise.
      */
-    private boolean removeNetworkInternal(WifiConfiguration config) {
+    private boolean removeNetworkInternal(WifiConfiguration config, int uid) {
         if (mVerboseLoggingEnabled) {
             Log.v(TAG, "Removing network " + config.getPrintableSsid());
         }
@@ -1127,7 +1128,9 @@
 
         localLog("removeNetworkInternal: removed config."
                 + " netId=" + config.networkId
-                + " configKey=" + config.configKey());
+                + " configKey=" + config.configKey()
+                + " uid=" + Integer.toString(uid)
+                + " name=" + mContext.getPackageManager().getNameForUid(uid));
         return true;
     }
 
@@ -1152,7 +1155,7 @@
                     + config.configKey());
             return false;
         }
-        if (!removeNetworkInternal(config)) {
+        if (!removeNetworkInternal(config, uid)) {
             Log.e(TAG, "Failed to remove network " + config.getPrintableSsid());
             return false;
         }
diff --git a/com/android/server/wifi/WifiConfigurationUtil.java b/com/android/server/wifi/WifiConfigurationUtil.java
index dadc8a4..ea58549 100644
--- a/com/android/server/wifi/WifiConfigurationUtil.java
+++ b/com/android/server/wifi/WifiConfigurationUtil.java
@@ -437,9 +437,10 @@
 
     /**
      * Check if the provided two networks are the same.
+     * Note: This does not check if network selection BSSID's are the same.
      *
-     * @param config      Configuration corresponding to a network.
-     * @param config1      Configuration corresponding to another network.
+     * @param config  Configuration corresponding to a network.
+     * @param config1 Configuration corresponding to another network.
      *
      * @return true if |config| and |config1| are the same network.
      *         false otherwise.
@@ -457,13 +458,6 @@
         if (!Objects.equals(config.SSID, config1.SSID)) {
             return false;
         }
-        String networkSelectionBSSID = config.getNetworkSelectionStatus()
-                .getNetworkSelectionBSSID();
-        String networkSelectionBSSID1 = config1.getNetworkSelectionStatus()
-                .getNetworkSelectionBSSID();
-        if (!Objects.equals(networkSelectionBSSID, networkSelectionBSSID1)) {
-            return false;
-        }
         if (WifiConfigurationUtil.hasCredentialChanged(config, config1)) {
             return false;
         }
diff --git a/com/android/server/wm/DisplayContent.java b/com/android/server/wm/DisplayContent.java
index f0b9f17..6cf608a 100644
--- a/com/android/server/wm/DisplayContent.java
+++ b/com/android/server/wm/DisplayContent.java
@@ -3070,7 +3070,7 @@
                 final boolean foundTargetWs =
                         (w.mAppToken != null && w.mAppToken.token == appToken)
                                 || (mScreenshotApplicationState.appWin != null && wallpaperOnly);
-                if (foundTargetWs && winAnim.getShown()) {
+                if (foundTargetWs && winAnim.getShown() && winAnim.mLastAlpha > 0f) {
                     mScreenshotApplicationState.screenshotReady = true;
                 }
 
diff --git a/com/android/settingslib/applications/PackageManagerWrapper.java b/com/android/settingslib/applications/PackageManagerWrapper.java
deleted file mode 100644
index 6c79a61..0000000
--- a/com/android/settingslib/applications/PackageManagerWrapper.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2017 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 com.android.settingslib.applications;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
-
-import java.util.List;
-
-/**
- * This interface replicates a subset of the android.content.pm.PackageManager (PM). The interface
- * exists so that we can use a thin wrapper around the PM in production code and a mock in tests.
- * We cannot directly mock or shadow the PM, because some of the methods we rely on are newer than
- * the API version supported by Robolectric.
- */
-public interface PackageManagerWrapper {
-
-    /**
-     * Returns the real {@code PackageManager} object.
-     */
-    PackageManager getPackageManager();
-
-    /**
-     * Calls {@code PackageManager.getInstalledApplicationsAsUser()}.
-     *
-     * @see android.content.pm.PackageManager#getInstalledApplicationsAsUser
-     */
-    List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId);
-
-    /**
-     * Calls {@code PackageManager.hasSystemFeature()}.
-     *
-     * @see android.content.pm.PackageManager#hasSystemFeature
-     */
-    boolean hasSystemFeature(String name);
-
-    /**
-     * Calls {@code PackageManager.queryIntentActivitiesAsUser()}.
-     *
-     * @see android.content.pm.PackageManager#queryIntentActivitiesAsUser
-     */
-    List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId);
-
-    /**
-     * Calls {@code PackageManager.getInstallReason()}.
-     *
-     * @see android.content.pm.PackageManager#getInstallReason
-     */
-    int getInstallReason(String packageName, UserHandle user);
-
-    /**
-     * Calls {@code PackageManager.getApplicationInfoAsUser}
-     */
-    ApplicationInfo getApplicationInfoAsUser(String packageName, int i, int userId)
-            throws PackageManager.NameNotFoundException;
-
-    /**
-     * Calls {@code PackageManager.setDefaultBrowserPackageNameAsUser}
-     */
-    boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId);
-
-    /**
-     * Calls {@code PackageManager.getDefaultBrowserPackageNameAsUser}
-     */
-    String getDefaultBrowserPackageNameAsUser(int userId);
-
-    /**
-     * Calls {@code PackageManager.getHomeActivities}
-     */
-    ComponentName getHomeActivities(List<ResolveInfo> homeActivities);
-
-    /**
-     * Calls {@code PackageManager.queryIntentServicesAsUser}
-     */
-    List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int i, int user);
-
-    /**
-     * Calls {@code PackageManager.replacePreferredActivity}
-     */
-    void replacePreferredActivity(IntentFilter homeFilter, int matchCategoryEmpty,
-            ComponentName[] componentNames, ComponentName component);
-
-    /**
-     * Gets information about a particular package from the package manager.
-     * @param packageName The name of the package we would like information about.
-     * @param i additional options flags. see javadoc for {@link PackageManager#getPackageInfo(String, int)}
-     * @return The PackageInfo for the requested package
-     * @throws NameNotFoundException
-     */
-    PackageInfo getPackageInfo(String packageName, int i) throws NameNotFoundException;
-
-    /**
-     * Retrieves the icon associated with this particular set of ApplicationInfo
-     * @param info The ApplicationInfo to retrieve the icon for
-     * @return The icon as a drawable.
-     */
-    Drawable getUserBadgedIcon(ApplicationInfo info);
-
-    /**
-     * Retrieves the label associated with the particular set of ApplicationInfo
-     * @param app The ApplicationInfo to retrieve the label for
-     * @return the label as a CharSequence
-     */
-    CharSequence loadLabel(ApplicationInfo app);
-
-    /**
-     * Retrieve all activities that can be performed for the given intent.
-     */
-    List<ResolveInfo> queryIntentActivities(Intent intent, int flags);
-}
diff --git a/com/android/settingslib/applications/PackageManagerWrapperImpl.java b/com/android/settingslib/applications/PackageManagerWrapperImpl.java
deleted file mode 100644
index dcb40b2..0000000
--- a/com/android/settingslib/applications/PackageManagerWrapperImpl.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2017 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 com.android.settingslib.applications;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Drawable;
-import android.os.UserHandle;
-
-import java.util.List;
-
-/**
- * A thin wrapper class that simplifies testing by putting a mockable layer between the application
- * and the PackageManager. This class only provides access to the minimum number of functions from
- * the PackageManager needed for DeletionHelper to work.
- */
-public class PackageManagerWrapperImpl implements PackageManagerWrapper {
-
-    private final PackageManager mPm;
-
-    public PackageManagerWrapperImpl(PackageManager pm) {
-        mPm = pm;
-    }
-
-    @Override
-    public PackageManager getPackageManager() {
-        return mPm;
-    }
-
-    @Override
-    public List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId) {
-        return mPm.getInstalledApplicationsAsUser(flags, userId);
-    }
-
-    @Override
-    public boolean hasSystemFeature(String name) {
-        return mPm.hasSystemFeature(name);
-    }
-
-    @Override
-    public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId) {
-        return mPm.queryIntentActivitiesAsUser(intent, flags, userId);
-    }
-
-    @Override
-    public int getInstallReason(String packageName, UserHandle user) {
-        return mPm.getInstallReason(packageName, user);
-    }
-
-    @Override
-    public ApplicationInfo getApplicationInfoAsUser(String packageName, int i, int userId)
-            throws PackageManager.NameNotFoundException {
-        return mPm.getApplicationInfoAsUser(packageName, i, userId);
-    }
-
-    @Override
-    public boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId) {
-        return mPm.setDefaultBrowserPackageNameAsUser(packageName, userId);
-    }
-
-    @Override
-    public String getDefaultBrowserPackageNameAsUser(int userId) {
-        return mPm.getDefaultBrowserPackageNameAsUser(userId);
-    }
-
-    @Override
-    public ComponentName getHomeActivities(List<ResolveInfo> homeActivities) {
-        return mPm.getHomeActivities(homeActivities);
-    }
-
-    @Override
-    public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int i, int user) {
-        return mPm.queryIntentServicesAsUser(intent, i, user);
-    }
-
-    @Override
-    public void replacePreferredActivity(IntentFilter homeFilter, int matchCategoryEmpty,
-            ComponentName[] componentNames, ComponentName component) {
-        mPm.replacePreferredActivity(homeFilter, matchCategoryEmpty, componentNames, component);
-    }
-
-    @Override
-    public PackageInfo getPackageInfo(String packageName, int i) throws NameNotFoundException {
-        return mPm.getPackageInfo(packageName, i);
-    }
-
-    @Override
-    public Drawable getUserBadgedIcon(ApplicationInfo info) {
-        return mPm.getUserBadgedIcon(mPm.loadUnbadgedItemIcon(info, info),
-                new UserHandle(UserHandle.getUserId(info.uid)));
-    }
-
-    @Override
-    public CharSequence loadLabel(ApplicationInfo app) {
-        return app.loadLabel(mPm);
-    }
-
-    @Override
-    public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
-        return mPm.queryIntentActivities(intent, flags);
-    }
-}
diff --git a/com/android/settingslib/graph/BatteryMeterDrawableBase.java b/com/android/settingslib/graph/BatteryMeterDrawableBase.java
index d588a66..817989a 100644
--- a/com/android/settingslib/graph/BatteryMeterDrawableBase.java
+++ b/com/android/settingslib/graph/BatteryMeterDrawableBase.java
@@ -292,6 +292,7 @@
     @Override
     public void draw(Canvas c) {
         final int level = mLevel;
+        final Rect bounds = getBounds();
 
         if (level == -1) return;
 
@@ -300,8 +301,10 @@
         final int width = (int) (getAspectRatio() * mHeight);
         final int px = (mWidth - width) / 2;
         final int buttonHeight = Math.round(height * mButtonHeightFraction);
+        final int left = mPadding.left + bounds.left;
+        final int top = bounds.bottom - mPadding.bottom - height;
 
-        mFrame.set(mPadding.left, mPadding.top, width + mPadding.left, height + mPadding.top);
+        mFrame.set(left, top, width + left, height + top);
         mFrame.offset(px, 0);
 
         // button-frame: area above the battery body
diff --git a/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java b/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
index 61790b9..b7fd404 100644
--- a/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
+++ b/com/android/settingslib/graph/BluetoothDeviceLayerDrawable.java
@@ -24,6 +24,7 @@
 import android.graphics.Matrix;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
+import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
@@ -47,7 +48,7 @@
 
     /**
      * Create the {@link LayerDrawable} that contains bluetooth device icon and battery icon.
-     * This is a vertical layout drawable while bluetooth icon at top and battery icon at bottom.
+     * This is a horizontal layout drawable while bluetooth icon at start and battery icon at end.
      *
      * @param context      used to get the spec for icon
      * @param resId        represents the bluetooth device drawable
@@ -55,55 +56,44 @@
      */
     public static BluetoothDeviceLayerDrawable createLayerDrawable(Context context, int resId,
             int batteryLevel) {
+        return createLayerDrawable(context, resId, batteryLevel, 1 /*iconScale*/);
+    }
+
+    /**
+     * Create the {@link LayerDrawable} that contains bluetooth device icon and battery icon.
+     * This is a horizontal layout drawable while bluetooth icon at start and battery icon at end.
+     *
+     * @param context      used to get the spec for icon
+     * @param resId        represents the bluetooth device drawable
+     * @param batteryLevel the battery level for bluetooth device
+     * @param iconScale    the ratio of height between battery icon and bluetooth icon
+     */
+    public static BluetoothDeviceLayerDrawable createLayerDrawable(Context context, int resId,
+            int batteryLevel, float iconScale) {
         final Drawable deviceDrawable = context.getDrawable(resId);
 
         final BatteryMeterDrawable batteryDrawable = new BatteryMeterDrawable(context,
                 R.color.meter_background_color, batteryLevel);
-        final int pad = context.getResources()
-                .getDimensionPixelSize(R.dimen.bt_battery_padding);
-        batteryDrawable.setPadding(0, pad, 0, pad);
+        final int pad = context.getResources().getDimensionPixelSize(R.dimen.bt_battery_padding);
+        batteryDrawable.setPadding(pad, pad, pad, pad);
 
         final BluetoothDeviceLayerDrawable drawable = new BluetoothDeviceLayerDrawable(
-                new Drawable[]{deviceDrawable,
-                        rotateDrawable(context.getResources(), batteryDrawable)});
-        // Set the bluetooth icon at the top
-        drawable.setLayerGravity(0 /* index of deviceDrawable */, Gravity.TOP);
-        // Set battery icon right below the bluetooth icon
-        drawable.setLayerInset(1 /* index of batteryDrawable */, 0,
-                deviceDrawable.getIntrinsicHeight(), 0, 0);
+                new Drawable[]{deviceDrawable, batteryDrawable});
+        // Set the bluetooth icon at the left
+        drawable.setLayerGravity(0 /* index of deviceDrawable */, Gravity.START);
+        // Set battery icon to the right of the bluetooth icon
+        drawable.setLayerInsetStart(1 /* index of batteryDrawable */,
+                deviceDrawable.getIntrinsicWidth());
+        drawable.setLayerInsetTop(1 /* index of batteryDrawable */,
+                (int) (deviceDrawable.getIntrinsicHeight() * (1 - iconScale)));
 
-        drawable.setConstantState(context, resId, batteryLevel);
+        drawable.setConstantState(context, resId, batteryLevel, iconScale);
 
         return drawable;
     }
 
-    /**
-     * Rotate the {@code drawable} by 90 degree clockwise and return rotated {@link Drawable}
-     */
-    private static Drawable rotateDrawable(Resources res, Drawable drawable) {
-        // Get the bitmap from drawable
-        final Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
-                drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
-        final Canvas canvas = new Canvas(bitmap);
-        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
-        drawable.draw(canvas);
-
-        // Create rotate matrix
-        final Matrix matrix = new Matrix();
-        matrix.postRotate(
-                res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_LTR
-                        ? 90 : 270);
-
-        // Create new bitmap with rotate matrix
-        final Bitmap rotateBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
-                bitmap.getHeight(), matrix, true);
-        bitmap.recycle();
-
-        return new BitmapDrawable(res, rotateBitmap);
-    }
-
-    public void setConstantState(Context context, int resId, int batteryLevel) {
-        mState = new BluetoothDeviceLayerDrawableState(context, resId, batteryLevel);
+    public void setConstantState(Context context, int resId, int batteryLevel, float iconScale) {
+        mState = new BluetoothDeviceLayerDrawableState(context, resId, batteryLevel, iconScale);
     }
 
     @Override
@@ -149,17 +139,19 @@
         Context context;
         int resId;
         int batteryLevel;
+        float iconScale;
 
         public BluetoothDeviceLayerDrawableState(Context context, int resId,
-                int batteryLevel) {
+                int batteryLevel, float iconScale) {
             this.context = context;
             this.resId = resId;
             this.batteryLevel = batteryLevel;
+            this.iconScale = iconScale;
         }
 
         @Override
         public Drawable newDrawable() {
-            return createLayerDrawable(context, resId, batteryLevel);
+            return createLayerDrawable(context, resId, batteryLevel, iconScale);
         }
 
         @Override
diff --git a/com/android/settingslib/inputmethod/InputMethodPreference.java b/com/android/settingslib/inputmethod/InputMethodPreference.java
index 1bbc878..5e25f51 100644
--- a/com/android/settingslib/inputmethod/InputMethodPreference.java
+++ b/com/android/settingslib/inputmethod/InputMethodPreference.java
@@ -34,6 +34,7 @@
 import android.view.inputmethod.InputMethodSubtype;
 import android.widget.Toast;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.InputMethodUtils;
 import com.android.settingslib.R;
 import com.android.settingslib.RestrictedLockUtils;
@@ -91,20 +92,28 @@
     public InputMethodPreference(final Context context, final InputMethodInfo imi,
             final boolean isImeEnabler, final boolean isAllowedByOrganization,
             final OnSavePreferenceListener onSaveListener) {
+        this(context, imi, imi.loadLabel(context.getPackageManager()), isAllowedByOrganization,
+                onSaveListener);
+        if (!isImeEnabler) {
+            // Remove switch widget.
+            setWidgetLayoutResource(NO_WIDGET);
+        }
+    }
+
+    @VisibleForTesting
+    InputMethodPreference(final Context context, final InputMethodInfo imi,
+            final CharSequence title, final boolean isAllowedByOrganization,
+            final OnSavePreferenceListener onSaveListener) {
         super(context);
         setPersistent(false);
         mImi = imi;
         mIsAllowedByOrganization = isAllowedByOrganization;
         mOnSaveListener = onSaveListener;
-        if (!isImeEnabler) {
-            // Remove switch widget.
-            setWidgetLayoutResource(NO_WIDGET);
-        }
         // Disable on/off switch texts.
         setSwitchTextOn(EMPTY_TEXT);
         setSwitchTextOff(EMPTY_TEXT);
         setKey(imi.getId());
-        setTitle(imi.loadLabel(context.getPackageManager()));
+        setTitle(title);
         final String settingsActivity = imi.getSettingsActivity();
         if (TextUtils.isEmpty(settingsActivity)) {
             setIntent(null);
@@ -283,18 +292,18 @@
         if (this == rhs) {
             return 0;
         }
-        if (mHasPriorityInSorting == rhs.mHasPriorityInSorting) {
-            final CharSequence t0 = getTitle();
-            final CharSequence t1 = rhs.getTitle();
-            if (TextUtils.isEmpty(t0)) {
-                return 1;
-            }
-            if (TextUtils.isEmpty(t1)) {
-                return -1;
-            }
-            return collator.compare(t0.toString(), t1.toString());
+        if (mHasPriorityInSorting != rhs.mHasPriorityInSorting) {
+            // Prefer always checked system IMEs
+            return mHasPriorityInSorting ? -1 : 1;
         }
-        // Prefer always checked system IMEs
-        return mHasPriorityInSorting ? -1 : 1;
+        final CharSequence title = getTitle();
+        final CharSequence rhsTitle = rhs.getTitle();
+        final boolean emptyTitle = TextUtils.isEmpty(title);
+        final boolean rhsEmptyTitle = TextUtils.isEmpty(rhsTitle);
+        if (!emptyTitle && !rhsEmptyTitle) {
+            return collator.compare(title.toString(), rhsTitle.toString());
+        }
+        // For historical reasons, an empty text needs to be put at the first.
+        return (emptyTitle ? -1 : 0) - (rhsEmptyTitle ? -1 : 0);
     }
 }
diff --git a/com/android/settingslib/wrapper/PackageManagerWrapper.java b/com/android/settingslib/wrapper/PackageManagerWrapper.java
new file mode 100644
index 0000000..cd62bc3
--- /dev/null
+++ b/com/android/settingslib/wrapper/PackageManagerWrapper.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2017 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 com.android.settingslib.wrapper;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.storage.VolumeInfo;
+
+import java.util.List;
+
+/**
+ * A thin wrapper class that simplifies testing by putting a mockable layer between the application
+ * and the PackageManager. This class only provides access to the minimum number of functions from
+ * the PackageManager needed for DeletionHelper to work.
+ */
+public class PackageManagerWrapper {
+
+    private final PackageManager mPm;
+
+    public PackageManagerWrapper(PackageManager pm) {
+        mPm = pm;
+    }
+
+    /**
+     * Returns the real {@code PackageManager} object.
+     */
+    public PackageManager getPackageManager() {
+        return mPm;
+    }
+
+    /**
+     * Calls {@code PackageManager.getInstalledApplicationsAsUser()}.
+     *
+     * @see android.content.pm.PackageManager#getInstalledApplicationsAsUser
+     */
+    public List<ApplicationInfo> getInstalledApplicationsAsUser(int flags, int userId) {
+        return mPm.getInstalledApplicationsAsUser(flags, userId);
+    }
+
+    /**
+     * Calls {@code PackageManager.hasSystemFeature()}.
+     *
+     * @see android.content.pm.PackageManager#hasSystemFeature
+     */
+    public boolean hasSystemFeature(String name) {
+        return mPm.hasSystemFeature(name);
+    }
+
+    /**
+     * Calls {@code PackageManager.queryIntentActivitiesAsUser()}.
+     *
+     * @see android.content.pm.PackageManager#queryIntentActivitiesAsUser
+     */
+    public List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId) {
+        return mPm.queryIntentActivitiesAsUser(intent, flags, userId);
+    }
+
+    /**
+     * Calls {@code PackageManager.getInstallReason()}.
+     *
+     * @see android.content.pm.PackageManager#getInstallReason
+     */
+    public int getInstallReason(String packageName, UserHandle user) {
+        return mPm.getInstallReason(packageName, user);
+    }
+
+    /**
+     * Calls {@code PackageManager.getApplicationInfoAsUser}
+     */
+    public ApplicationInfo getApplicationInfoAsUser(String packageName, int i, int userId)
+            throws PackageManager.NameNotFoundException {
+        return mPm.getApplicationInfoAsUser(packageName, i, userId);
+    }
+
+    /**
+     * Calls {@code PackageManager.setDefaultBrowserPackageNameAsUser}
+     */
+    public boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId) {
+        return mPm.setDefaultBrowserPackageNameAsUser(packageName, userId);
+    }
+
+    /**
+     * Calls {@code PackageManager.getDefaultBrowserPackageNameAsUser}
+     */
+    public String getDefaultBrowserPackageNameAsUser(int userId) {
+        return mPm.getDefaultBrowserPackageNameAsUser(userId);
+    }
+
+    /**
+     * Calls {@code PackageManager.getHomeActivities}
+     */
+    public ComponentName getHomeActivities(List<ResolveInfo> homeActivities) {
+        return mPm.getHomeActivities(homeActivities);
+    }
+
+    /**
+     * Calls {@code PackageManager.queryIntentServicesAsUser}
+     */
+    public List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int i, int user) {
+        return mPm.queryIntentServicesAsUser(intent, i, user);
+    }
+
+    /**
+     * Calls {@code PackageManager.replacePreferredActivity}
+     */
+    public void replacePreferredActivity(IntentFilter homeFilter, int matchCategoryEmpty,
+            ComponentName[] componentNames, ComponentName component) {
+        mPm.replacePreferredActivity(homeFilter, matchCategoryEmpty, componentNames, component);
+    }
+
+    /**
+     * Gets information about a particular package from the package manager.
+     * @param packageName The name of the package we would like information about.
+     * @param i additional options flags. see javadoc for
+     * {@link PackageManager#getPackageInfo(String, int)}
+     * @return The PackageInfo for the requested package
+     * @throws NameNotFoundException
+     */
+    public PackageInfo getPackageInfo(String packageName, int i) throws NameNotFoundException {
+        return mPm.getPackageInfo(packageName, i);
+    }
+
+    /**
+     * Retrieves the icon associated with this particular set of ApplicationInfo
+     * @param info The ApplicationInfo to retrieve the icon for
+     * @return The icon as a drawable.
+     */
+    public Drawable getUserBadgedIcon(ApplicationInfo info) {
+        return mPm.getUserBadgedIcon(mPm.loadUnbadgedItemIcon(info, info),
+                new UserHandle(UserHandle.getUserId(info.uid)));
+    }
+
+    /**
+     * Retrieves the label associated with the particular set of ApplicationInfo
+     * @param app The ApplicationInfo to retrieve the label for
+     * @return the label as a CharSequence
+     */
+    public CharSequence loadLabel(ApplicationInfo app) {
+        return app.loadLabel(mPm);
+    }
+
+    /**
+     * Retrieve all activities that can be performed for the given intent.
+     */
+    public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
+        return mPm.queryIntentActivities(intent, flags);
+    }
+
+    /**
+     * Calls {@code PackageManager.getPrimaryStorageCurrentVolume}
+     */
+    public VolumeInfo getPrimaryStorageCurrentVolume() {
+        return mPm.getPrimaryStorageCurrentVolume();
+    }
+
+    /**
+     * Calls {@code PackageManager.deletePackageAsUser}
+     */
+    public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer, int flags,
+            int userId) {
+        mPm.deletePackageAsUser(packageName, observer, flags, userId);
+    }
+
+    /**
+     * Calls {@code PackageManager.getPackageUidAsUser}
+     */
+    public int getPackageUidAsUser(String pkg, int userId)
+            throws PackageManager.NameNotFoundException {
+        return mPm.getPackageUidAsUser(pkg, userId);
+    }
+}
diff --git a/com/android/systemui/doze/DozeFactory.java b/com/android/systemui/doze/DozeFactory.java
index 9b1842a..6f8bcff 100644
--- a/com/android/systemui/doze/DozeFactory.java
+++ b/com/android/systemui/doze/DozeFactory.java
@@ -58,22 +58,20 @@
                 params);
 
         DozeMachine machine = new DozeMachine(wrappedService, config, wakeLock);
-        DozeScreenBrightness screenBrightness = createDozeScreenBrightness(
-                context, wrappedService, sensorManager, host, handler);
         machine.setParts(new DozeMachine.Part[]{
                 new DozePauser(handler, machine, alarmManager, new AlwaysOnDisplayPolicy(context)),
                 new DozeFalsingManagerAdapter(FalsingManager.getInstance(context)),
                 createDozeTriggers(context, sensorManager, host, alarmManager, config, params,
-                        handler, screenBrightness, wakeLock, machine),
+                        handler, wakeLock, machine),
                 createDozeUi(context, host, wakeLock, machine, handler, alarmManager),
                 new DozeScreenState(wrappedService, handler),
-                screenBrightness,
+                createDozeScreenBrightness(context, wrappedService, sensorManager, host, handler),
         });
 
         return machine;
     }
 
-    private DozeScreenBrightness createDozeScreenBrightness(Context context,
+    private DozeMachine.Part createDozeScreenBrightness(Context context,
             DozeMachine.Service service, SensorManager sensorManager, DozeHost host,
             Handler handler) {
         Sensor sensor = DozeSensors.findSensorWithType(sensorManager,
@@ -84,11 +82,10 @@
 
     private DozeTriggers createDozeTriggers(Context context, SensorManager sensorManager,
             DozeHost host, AlarmManager alarmManager, AmbientDisplayConfiguration config,
-            DozeParameters params, Handler handler, DozeScreenBrightness screenBrightness,
-            WakeLock wakeLock, DozeMachine machine) {
+            DozeParameters params, Handler handler, WakeLock wakeLock, DozeMachine machine) {
         boolean allowPulseTriggers = true;
         return new DozeTriggers(context, machine, host, alarmManager, config, params,
-                sensorManager, handler, screenBrightness, wakeLock, allowPulseTriggers);
+                sensorManager, handler, wakeLock, allowPulseTriggers);
     }
 
     private DozeMachine.Part createDozeUi(Context context, DozeHost host, WakeLock wakeLock,
diff --git a/com/android/systemui/doze/DozeScreenBrightness.java b/com/android/systemui/doze/DozeScreenBrightness.java
index 92f8d8c..03407e2 100644
--- a/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/com/android/systemui/doze/DozeScreenBrightness.java
@@ -37,10 +37,11 @@
     private final Sensor mLightSensor;
     private final int[] mSensorToBrightness;
     private final int[] mSensorToScrimOpacity;
+
     private boolean mRegistered;
-    private boolean mReady = true;
-    private ReadyListener mReadyListener;
     private int mDefaultDozeBrightness;
+    private boolean mPaused = false;
+    private int mLastSensorValue = -1;
 
     public DozeScreenBrightness(Context context, DozeMachine.Service service,
             SensorManager sensorManager, Sensor lightSensor, DozeHost host,
@@ -86,22 +87,38 @@
                 setLightSensorEnabled(false);
                 break;
         }
+        if (newState != DozeMachine.State.FINISH) {
+            setPaused(newState == DozeMachine.State.DOZE_AOD_PAUSED);
+        }
     }
 
     @Override
     public void onSensorChanged(SensorEvent event) {
         if (mRegistered) {
-            int sensorValue = (int) event.values[0];
-            int brightness = computeBrightness(sensorValue);
-            if (brightness > 0) {
+            mLastSensorValue = (int) event.values[0];
+            updateBrightnessAndReady();
+        }
+    }
+
+    private void updateBrightnessAndReady() {
+        if (mRegistered) {
+            int brightness = computeBrightness(mLastSensorValue);
+            boolean brightnessReady = brightness > 0;
+            if (brightnessReady) {
                 mDozeService.setDozeScreenBrightness(brightness);
             }
-            // If the brightness is zero or negative, this indicates that the brightness sensor is
-            // covered or reports that the screen should be off, therefore we're not ready to turn
-            // on the screen yet.
-            setReady(brightness > 0);
 
-            int scrimOpacity = computeScrimOpacity(sensorValue);
+            int scrimOpacity = -1;
+            if (mPaused) {
+                // If AOD is paused, force the screen black until the
+                // sensor reports a new brightness. This ensures that when the screen comes on
+                // again, it will only show after the brightness sensor has stabilized,
+                // avoiding a potential flicker.
+                scrimOpacity = 255;
+            } else if (brightnessReady) {
+                // Only unblank scrim once brightness is ready.
+                scrimOpacity = computeScrimOpacity(mLastSensorValue);
+            }
             if (scrimOpacity >= 0) {
                 mDozeHost.setAodDimmingScrim(scrimOpacity / 255f);
             }
@@ -128,47 +145,28 @@
 
     private void resetBrightnessToDefault() {
         mDozeService.setDozeScreenBrightness(mDefaultDozeBrightness);
+        mDozeHost.setAodDimmingScrim(0f);
     }
 
     private void setLightSensorEnabled(boolean enabled) {
         if (enabled && !mRegistered && mLightSensor != null) {
             // Wait until we get an event from the sensor until indicating ready.
-            setReady(false);
             mRegistered = mSensorManager.registerListener(this, mLightSensor,
                     SensorManager.SENSOR_DELAY_NORMAL, mHandler);
+            mLastSensorValue = -1;
         } else if (!enabled && mRegistered) {
             mSensorManager.unregisterListener(this);
             mRegistered = false;
+            mLastSensorValue = -1;
             // Sensor is not enabled, hence we use the default brightness and are always ready.
-            setReady(true);
         }
     }
 
-    private void setReady(boolean ready) {
-        if (ready != mReady) {
-            mReady = ready;
-            if (mReadyListener != null) {
-                mReadyListener.onBrightnessReadyChanged(mReady);
-            }
+    private void setPaused(boolean paused) {
+        if (mPaused != paused) {
+            mPaused = paused;
+            updateBrightnessAndReady();
         }
     }
 
-    public void setBrightnessReadyListener(ReadyListener l) {
-        mReadyListener = l;
-        l.onBrightnessReadyChanged(mReady);
-    }
-
-    /**
-     * @return true if the screen brightness is properly calculated.
-     *
-     * Can be used to wait for transitioning out of the paused state, such that we don't turn the
-     * display on before the display brightness is properly calculated.
-     */
-    public boolean isReady() {
-        return mReady;
-    }
-
-    public interface ReadyListener {
-        void onBrightnessReadyChanged(boolean ready);
-    }
 }
diff --git a/com/android/systemui/doze/DozeTriggers.java b/com/android/systemui/doze/DozeTriggers.java
index 8d8c55c..f7a258a 100644
--- a/com/android/systemui/doze/DozeTriggers.java
+++ b/com/android/systemui/doze/DozeTriggers.java
@@ -33,7 +33,6 @@
 import android.text.format.Formatter;
 import android.util.Log;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.hardware.AmbientDisplayConfiguration;
 import com.android.internal.util.Preconditions;
 import com.android.systemui.statusbar.phone.DozeParameters;
@@ -66,7 +65,6 @@
     private final boolean mAllowPulseTriggers;
     private final UiModeManager mUiModeManager;
     private final TriggerReceiver mBroadcastReceiver = new TriggerReceiver();
-    private final DozeScreenBrightness mDozeScreenBrightness;
 
     private long mNotificationPulseTime;
     private boolean mPulsePending;
@@ -75,7 +73,7 @@
     public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost,
             AlarmManager alarmManager, AmbientDisplayConfiguration config,
             DozeParameters dozeParameters, SensorManager sensorManager, Handler handler,
-            DozeScreenBrightness brightness, WakeLock wakeLock, boolean allowPulseTriggers) {
+            WakeLock wakeLock, boolean allowPulseTriggers) {
         mContext = context;
         mMachine = machine;
         mDozeHost = dozeHost;
@@ -89,7 +87,6 @@
                 config, wakeLock, this::onSensor, this::onProximityFar,
                 new AlwaysOnDisplayPolicy(context));
         mUiModeManager = mContext.getSystemService(UiModeManager.class);
-        mDozeScreenBrightness = brightness;
     }
 
     private void onNotification() {
@@ -162,41 +159,16 @@
     private void onProximityFar(boolean far) {
         final boolean near = !far;
         final DozeMachine.State state = mMachine.getState();
+        final boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
+        final boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
+        final boolean aod = (state == DozeMachine.State.DOZE_AOD);
 
         if (state == DozeMachine.State.DOZE_PULSING) {
             boolean ignoreTouch = near;
             if (DEBUG) Log.i(TAG, "Prox changed, ignore touch = " + ignoreTouch);
             mDozeHost.onIgnoreTouchWhilePulsing(ignoreTouch);
         }
-
-        recalculatePausing();
-    }
-
-    private void onBrightnessReady(boolean brightnessReady) {
-        // Post because this is sometimes called during state transitions and we cannot query
-        // the machine's state while it's transitioning.
-        mHandler.post(this::recalculatePausing);
-    }
-
-    private void recalculatePausing() {
-        boolean brightnessReady = mDozeScreenBrightness.isReady();
-        Boolean proxCurrentlyFar = mDozeSensors.isProximityCurrentlyFar();
-
-        // Treat UNKNOWN the same as FAR, such that we don't pause the display just because
-        // the prox has unknown state.
-        boolean proximityFar = proxCurrentlyFar == null || proxCurrentlyFar;
-        recalculatePausing(proximityFar, brightnessReady);
-    }
-
-    @VisibleForTesting
-    void recalculatePausing(boolean proximityFar, boolean brightnessReady) {
-        final boolean near = !proximityFar;
-        final DozeMachine.State state = mMachine.getState();
-        final boolean paused = (state == DozeMachine.State.DOZE_AOD_PAUSED);
-        final boolean pausing = (state == DozeMachine.State.DOZE_AOD_PAUSING);
-        final boolean aod = (state == DozeMachine.State.DOZE_AOD);
-
-        if (proximityFar && (pausing || paused && brightnessReady)) {
+        if (far && (paused || pausing)) {
             if (DEBUG) Log.i(TAG, "Prox FAR, unpausing AOD");
             mMachine.requestState(DozeMachine.State.DOZE_AOD);
         } else if (near && aod) {
@@ -211,7 +183,6 @@
             case INITIALIZED:
                 mBroadcastReceiver.register(mContext);
                 mDozeHost.addCallback(mHostCallback);
-                mDozeScreenBrightness.setBrightnessReadyListener(this::onBrightnessReady);
                 checkTriggersAtInit();
                 break;
             case DOZE:
diff --git a/com/android/systemui/qs/tileimpl/SlashImageView.java b/com/android/systemui/qs/tileimpl/SlashImageView.java
index 13912fe..97e9c3d 100644
--- a/com/android/systemui/qs/tileimpl/SlashImageView.java
+++ b/com/android/systemui/qs/tileimpl/SlashImageView.java
@@ -48,6 +48,7 @@
             mSlash = null;
             super.setImageDrawable(null);
         } else if (mSlash == null) {
+            setImageLevel(drawable.getLevel());
             super.setImageDrawable(drawable);
         } else {
             mSlash.setAnimationEnabled(mAnimationEnabled);
diff --git a/com/android/systemui/qs/tiles/BluetoothTile.java b/com/android/systemui/qs/tiles/BluetoothTile.java
index 774f0b3..8d62f2a 100644
--- a/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -136,7 +136,9 @@
                     int batteryLevel = lastDevice.getBatteryLevel();
                     if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
                         BluetoothDeviceLayerDrawable drawable = createLayerDrawable(mContext,
-                                R.drawable.ic_qs_bluetooth_connected, batteryLevel);
+                                R.drawable.ic_qs_bluetooth_connected, batteryLevel,
+                                mContext.getResources().getFraction(
+                                        R.fraction.bt_battery_scale_fraction, 1, 1));
                         state.icon = new DrawableIcon(drawable);
                     }
                 }
diff --git a/com/android/systemui/recents/Recents.java b/com/android/systemui/recents/Recents.java
index 3e1522d..406bcac 100644
--- a/com/android/systemui/recents/Recents.java
+++ b/com/android/systemui/recents/Recents.java
@@ -567,9 +567,11 @@
 
     @Override
     public void appTransitionFinished() {
-        // Fallback, reset the flag once an app transition ends
-        EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(
-                false /* waitingForTransitionStart */));
+        if (!Recents.getConfiguration().isLowRamDevice) {
+            // Fallback, reset the flag once an app transition ends
+            EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(
+                    false /* waitingForTransitionStart */));
+        }
     }
 
     /**
diff --git a/com/android/systemui/stackdivider/DividerView.java b/com/android/systemui/stackdivider/DividerView.java
index bf98a83..6bfef20 100644
--- a/com/android/systemui/stackdivider/DividerView.java
+++ b/com/android/systemui/stackdivider/DividerView.java
@@ -66,9 +66,7 @@
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
 import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
-import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
 import com.android.systemui.recents.events.activity.UndockingTaskEvent;
-import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
 import com.android.systemui.recents.events.ui.RecentsGrowingEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.stackdivider.events.StartedDragingEvent;
@@ -124,7 +122,7 @@
     private int mTouchSlop;
     private boolean mBackgroundLifted;
     private boolean mIsInMinimizeInteraction;
-    private int mDividerPositionBeforeMinimized;
+    private SnapTarget mSnapTargetBeforeMinimized;
 
     private int mDividerInsets;
     private int mDisplayWidth;
@@ -318,6 +316,12 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         EventBus.getDefault().register(this);
+
+        // Save the current target if not minimized once attached to window
+        if (mHomeStackResizable && mDockSide != WindowManager.DOCKED_INVALID
+                && !mIsInMinimizeInteraction) {
+            saveSnapTargetBeforeMinimized(mSnapTargetBeforeMinimized);
+        }
     }
 
     @Override
@@ -381,8 +385,10 @@
                 (isHorizontalDivision() ? mDisplayHeight : mDisplayWidth));
         mSnapAlgorithm = null;
         initializeSnapAlgorithm();
-        mDividerPositionBeforeMinimized = mSnapAlgorithm.calculateNonDismissingSnapTarget(position)
-                .position;
+
+        // Set the snap target before minimized but do not save until divider is attached and not
+        // minimized because it does not know its minimized state yet.
+        mSnapTargetBeforeMinimized = mSnapAlgorithm.calculateNonDismissingSnapTarget(position);
     }
 
     public WindowManagerProxy getWindowManagerProxy() {
@@ -601,6 +607,11 @@
             mEntranceAnimationRunning = false;
             mExitAnimationRunning = false;
             EventBus.getDefault().send(new StoppedDragingEvent());
+
+            // Record last snap target the divider moved to
+            if (mHomeStackResizable && !mIsInMinimizeInteraction) {
+                saveSnapTargetBeforeMinimized(snapTarget);
+            }
         };
         Runnable notCancelledEndAction = () -> {
             // Reset minimized divider position after unminimized state animation finishes
@@ -629,15 +640,15 @@
                     delay = mSfChoreographer.getSurfaceFlingerOffsetMs();
                 }
                 if (delay == 0) {
-                    endAction.run();
                     if (!mCancelled) {
                         notCancelledEndAction.run();
                     }
+                    endAction.run();
                 } else {
-                    mHandler.postDelayed(endAction, delay);
                     if (!mCancelled) {
                         mHandler.postDelayed(notCancelledEndAction, delay);
                     }
+                    mHandler.postDelayed(endAction, delay);
                 }
             }
         });
@@ -744,12 +755,9 @@
             if (mIsInMinimizeInteraction != minimized) {
                 if (minimized) {
                     mIsInMinimizeInteraction = true;
-                    int position = mMinimizedSnapAlgorithm.getMiddleTarget().position;
-                    resizeStack(position, position, mMinimizedSnapAlgorithm.getMiddleTarget());
+                    resizeStack(mMinimizedSnapAlgorithm.getMiddleTarget());
                 } else {
-                    resizeStack(mDividerPositionBeforeMinimized, mDividerPositionBeforeMinimized,
-                            mSnapAlgorithm.calculateNonDismissingSnapTarget(
-                                    mDividerPositionBeforeMinimized));
+                    resizeStack(mSnapTargetBeforeMinimized);
                     mIsInMinimizeInteraction = false;
                 }
             }
@@ -786,20 +794,15 @@
             mDockedStackMinimized = minimized;
         } else if (mDockedStackMinimized != minimized) {
             mIsInMinimizeInteraction = true;
-            if (minimized && (mCurrentAnimator == null || !mCurrentAnimator.isRunning())
-                    && (mDividerPositionBeforeMinimized <= 0 || !mAdjustedForIme)) {
-                savePositionBeforeMinimized();
-            }
             mMinimizedSnapAlgorithm = null;
             mDockedStackMinimized = minimized;
             initializeSnapAlgorithm();
             stopDragging(minimized
-                            ? mDividerPositionBeforeMinimized
+                            ? mSnapTargetBeforeMinimized.position
                             : getCurrentPosition(),
                     minimized
                             ? mMinimizedSnapAlgorithm.getMiddleTarget()
-                            : mSnapAlgorithm.calculateNonDismissingSnapTarget(
-                                    mDividerPositionBeforeMinimized),
+                            : mSnapTargetBeforeMinimized,
                     animDuration, Interpolators.FAST_OUT_SLOW_IN, 0);
             setAdjustedForIme(false, animDuration);
         }
@@ -844,17 +847,11 @@
                 .setDuration(animDuration)
                 .start();
         mAdjustedForIme = adjustedForIme;
-
-        // Only get new position if home stack is resizable, ime is open and not minimized
-        // (including the animation)
-        if (mHomeStackResizable && adjustedForIme && !mIsInMinimizeInteraction) {
-            savePositionBeforeMinimized();
-        }
     }
 
-    private void savePositionBeforeMinimized() {
-        mDividerPositionBeforeMinimized = getCurrentPosition();
-        mState.mRatioPositionBeforeMinimized = (float) mDividerPositionBeforeMinimized /
+    private void saveSnapTargetBeforeMinimized(SnapTarget target) {
+        mSnapTargetBeforeMinimized = target;
+        mState.mRatioPositionBeforeMinimized = (float) target.position /
                 (isHorizontalDivision() ? mDisplayHeight : mDisplayWidth);
     }
 
@@ -872,7 +869,6 @@
         updateDisplayInfo();
     }
 
-
     public void notifyDockSideChanged(int newDockSide) {
         mDockSide = newDockSide;
         mMinimizedShadow.setDockSide(mDockSide);
@@ -934,6 +930,10 @@
         mSfChoreographer.scheduleAtSfVsync(mHandler, message);
     }
 
+    private void resizeStack(SnapTarget taskSnapTarget) {
+        resizeStack(taskSnapTarget.position, taskSnapTarget.position, taskSnapTarget);
+    }
+
     public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) {
         if (mRemoved) {
             // This divider view has been removed so shouldn't have any additional influence.
@@ -952,8 +952,9 @@
 
         mLastResizeRect.set(mDockedRect);
         if (mHomeStackResizable && mIsInMinimizeInteraction) {
-            calculateBoundsForPosition(mDividerPositionBeforeMinimized, mDockSide, mDockedTaskRect);
-            calculateBoundsForPosition(mDividerPositionBeforeMinimized,
+            calculateBoundsForPosition(mSnapTargetBeforeMinimized.position, mDockSide,
+                    mDockedTaskRect);
+            calculateBoundsForPosition(mSnapTargetBeforeMinimized.position,
                     DockedDividerUtils.invertDockSide(mDockSide), mOtherTaskRect);
             mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedTaskRect,
                     mOtherTaskRect, null);
diff --git a/com/android/systemui/statusbar/ExpandableNotificationRow.java b/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 7067bc1..7fe7f39 100644
--- a/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -64,10 +64,10 @@
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
 import com.android.systemui.statusbar.NotificationGuts.GutsContent;
 import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
-import com.android.systemui.statusbar.notification.AboveShelfObserver;
 import com.android.systemui.statusbar.notification.HybridNotificationView;
 import com.android.systemui.statusbar.notification.NotificationInflater;
 import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.NotificationViewWrapper;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -436,6 +436,9 @@
         } else {
             minHeight = mNotificationMinHeight;
         }
+        NotificationViewWrapper collapsedWrapper = layout.getVisibleWrapper(
+                NotificationContentView.VISIBLE_TYPE_CONTRACTED);
+        minHeight += collapsedWrapper.getMinHeightIncrease(mUseIncreasedCollapsedHeight);
         boolean headsUpCustom = layout.getHeadsUpChild() != null &&
                 layout.getHeadsUpChild().getId()
                         != com.android.internal.R.id.status_bar_latest_event_content;
@@ -447,6 +450,11 @@
         } else {
             headsUpheight = mMaxHeadsUpHeight;
         }
+        NotificationViewWrapper headsUpWrapper = layout.getVisibleWrapper(
+                NotificationContentView.VISIBLE_TYPE_HEADSUP);
+        if (headsUpWrapper != null) {
+            headsUpheight += headsUpWrapper.getMinHeightIncrease(mUseIncreasedCollapsedHeight);
+        }
         layout.setHeights(minHeight, headsUpheight, mNotificationMaxHeight,
                 mNotificationAmbientHeight);
     }
diff --git a/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index bb979eb..9bfa7a9 100644
--- a/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -25,6 +25,7 @@
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
+import com.android.systemui.R;
 import com.android.systemui.statusbar.CrossFadeHelper;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.TransformableView;
@@ -47,6 +48,7 @@
 
     private int mContentHeight;
     private int mMinHeightHint;
+    private boolean mColorized;
 
     protected NotificationTemplateViewWrapper(Context ctx, View view,
             ExpandableNotificationRow row) {
@@ -162,7 +164,9 @@
     public void onContentUpdated(ExpandableNotificationRow row) {
         // Reinspect the notification. Before the super call, because the super call also updates
         // the transformation types and we need to have our values set by then.
-        resolveTemplateViews(row.getStatusBarNotification());
+        StatusBarNotification sbn = row.getStatusBarNotification();
+        resolveTemplateViews(sbn);
+        mColorized = sbn.getNotification().isColorized();
         super.onContentUpdated(row);
     }
 
@@ -265,6 +269,17 @@
         updateActionOffset();
     }
 
+    @Override
+    public int getMinHeightIncrease(boolean useIncreasedCollapsedHeight) {
+        if (mColorized) {
+            int dimen = useIncreasedCollapsedHeight
+                    ? R.dimen.notification_height_increase_colorized_increased
+                    : R.dimen.notification_height_increase_colorized;
+            return mRow.getResources().getDimensionPixelSize(dimen);
+        }
+        return super.getMinHeightIncrease(useIncreasedCollapsedHeight);
+    }
+
     private void updateActionOffset() {
         if (mActionsContainer != null) {
             // We should never push the actions higher than they are in the headsup view.
diff --git a/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index 5200d69..085bce9 100644
--- a/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -190,4 +190,14 @@
     public boolean disallowSingleClick(float x, float y) {
         return false;
     }
+
+    /**
+     * Get the amount that the minheight is allowed to be increased based on this layout.
+     *
+     * @param increasedHeight is the view allowed to show even bigger, i.e for messaging layouts
+     * @return
+     */
+    public int getMinHeightIncrease(boolean increasedHeight) {
+        return 0;
+    }
 }
diff --git a/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 165ed78..99debee 100644
--- a/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -70,6 +70,7 @@
                 }
             };
     private final Runnable mRemoveViewRunnable = this::removeView;
+    private int mStatusBarHeight;
 
     public KeyguardBouncer(Context context, ViewMediatorCallback callback,
             LockPatternUtils lockPatternUtils, ViewGroup container,
@@ -129,7 +130,9 @@
             mRoot.setVisibility(View.VISIBLE);
             mKeyguardView.onResume();
             showPromptReason(mBouncerPromptReason);
-            if (mKeyguardView.getHeight() != 0) {
+            // We might still be collapsed and the view didn't have time to layout yet or still
+            // be small, let's wait on the predraw to do the animation in that case.
+            if (mKeyguardView.getHeight() != 0 && mKeyguardView.getHeight() != mStatusBarHeight) {
                 mKeyguardView.startAppearAnimation();
             } else {
                 mKeyguardView.getViewTreeObserver().addOnPreDrawListener(
@@ -252,6 +255,8 @@
         mKeyguardView.setLockPatternUtils(mLockPatternUtils);
         mKeyguardView.setViewMediatorCallback(mCallback);
         mContainer.addView(mRoot, mContainer.getChildCount());
+        mStatusBarHeight = mRoot.getResources().getDimensionPixelOffset(
+                com.android.systemui.R.dimen.status_bar_height);
         mRoot.setVisibility(View.INVISIBLE);
 
         final WindowInsets rootInsets = mRoot.getRootWindowInsets();
diff --git a/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 88a5626..0f246c6 100644
--- a/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -25,10 +25,8 @@
 import android.graphics.Paint;
 import android.graphics.drawable.Icon;
 import android.os.AsyncTask;
-import android.os.UserHandle;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
-import android.provider.Settings;
 import android.support.v4.util.ArrayMap;
 import android.support.v4.util.ArraySet;
 import android.util.AttributeSet;
@@ -39,10 +37,8 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.AlphaOptimizedFrameLayout;
 import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.stack.AnimationFilter;
 import com.android.systemui.statusbar.stack.AnimationProperties;
-import com.android.systemui.statusbar.stack.StackStateAnimator;
 import com.android.systemui.statusbar.stack.ViewState;
 
 import java.util.ArrayList;
@@ -84,7 +80,10 @@
     }.setDuration(CANNED_ANIMATION_DURATION)
             .setCustomInterpolator(View.TRANSLATION_Y, Interpolators.ICON_OVERSHOT);
 
-    private static final AnimationProperties mTempProperties = new AnimationProperties() {
+    /**
+     * Temporary AnimationProperties to avoid unnecessary allocations.
+     */
+    private static final AnimationProperties sTempProperties = new AnimationProperties() {
         private AnimationFilter mAnimationFilter = new AnimationFilter();
 
         @Override
@@ -102,15 +101,6 @@
         }
     }.setDuration(200).setDelay(50);
 
-    private static final AnimationProperties UNDARK_PROPERTIES = new AnimationProperties() {
-        private AnimationFilter mAnimationFilter = new AnimationFilter()
-                .animateX();
-
-        @Override
-        public AnimationFilter getAnimationFilter() {
-            return mAnimationFilter;
-        }
-    }.setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP);
     public static final int MAX_VISIBLE_ICONS_WHEN_DARK = 5;
 
     private boolean mShowAllIcons = true;
@@ -478,9 +468,6 @@
             View view = getChildAt(i);
             if (view instanceof StatusBarIconView) {
                 ((StatusBarIconView) view).setDark(dark, fade, delay);
-                if (!dark && fade) {
-                    getIconState((StatusBarIconView) view).justUndarkened = true;
-                }
             }
         }
     }
@@ -551,7 +538,6 @@
         public boolean useFullTransitionAmount;
         public boolean useLinearTransitionAmount;
         public boolean translateContent;
-        public boolean justUndarkened;
         public int iconColor = StatusBarIconView.NO_COLOR;
         public boolean noAnimations;
         public boolean isLastExpandIcon;
@@ -563,8 +549,7 @@
                 StatusBarIconView icon = (StatusBarIconView) view;
                 boolean animate = false;
                 AnimationProperties animationProperties = null;
-                boolean animationsAllowed = (mAnimationsEnabled || justUndarkened)
-                        && !mDisallowNextAnimation
+                boolean animationsAllowed = mAnimationsEnabled && !mDisallowNextAnimation
                         && !noAnimations;
                 if (animationsAllowed) {
                     if (justAdded || justReplaced) {
@@ -576,9 +561,6 @@
                             animationProperties = ADD_ICON_PROPERTIES;
                             animate = true;
                         }
-                    } else if (justUndarkened) {
-                        animationProperties = UNDARK_PROPERTIES;
-                        animate = true;
                     } else if (visibleState != icon.getVisibleState()) {
                         animationProperties = DOT_ANIMATION_PROPERTIES;
                         animate = true;
@@ -591,17 +573,17 @@
                         animate = true;
                     }
                     if (needsCannedAnimation) {
-                        AnimationFilter animationFilter = mTempProperties.getAnimationFilter();
+                        AnimationFilter animationFilter = sTempProperties.getAnimationFilter();
                         animationFilter.reset();
                         animationFilter.combineFilter(
                                 ICON_ANIMATION_PROPERTIES.getAnimationFilter());
-                        mTempProperties.resetCustomInterpolators();
-                        mTempProperties.combineCustomInterpolators(ICON_ANIMATION_PROPERTIES);
+                        sTempProperties.resetCustomInterpolators();
+                        sTempProperties.combineCustomInterpolators(ICON_ANIMATION_PROPERTIES);
                         if (animationProperties != null) {
                             animationFilter.combineFilter(animationProperties.getAnimationFilter());
-                            mTempProperties.combineCustomInterpolators(animationProperties);
+                            sTempProperties.combineCustomInterpolators(animationProperties);
                         }
-                        animationProperties = mTempProperties;
+                        animationProperties = sTempProperties;
                         animationProperties.setDuration(CANNED_ANIMATION_DURATION);
                         animate = true;
                         mCannedAnimationStartIndex = indexOfChild(view);
@@ -610,11 +592,11 @@
                             && indexOfChild(view) > mCannedAnimationStartIndex
                             && (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN
                             || visibleState != StatusBarIconView.STATE_HIDDEN)) {
-                        AnimationFilter animationFilter = mTempProperties.getAnimationFilter();
+                        AnimationFilter animationFilter = sTempProperties.getAnimationFilter();
                         animationFilter.reset();
                         animationFilter.animateX();
-                        mTempProperties.resetCustomInterpolators();
-                        animationProperties = mTempProperties;
+                        sTempProperties.resetCustomInterpolators();
+                        animationProperties = sTempProperties;
                         animationProperties.setDuration(CANNED_ANIMATION_DURATION);
                         animate = true;
                     }
@@ -638,7 +620,6 @@
             justAdded = false;
             justReplaced = false;
             needsCannedAnimation = false;
-            justUndarkened = false;
         }
 
         private boolean shouldVibrateChange(boolean inShelfChanged) {
diff --git a/com/android/systemui/usb/UsbDebuggingActivity.java b/com/android/systemui/usb/UsbDebuggingActivity.java
index 3ebefbb..66d5ee1 100644
--- a/com/android/systemui/usb/UsbDebuggingActivity.java
+++ b/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -29,6 +29,7 @@
 import android.os.IBinder;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
+import android.util.EventLog;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -95,6 +96,7 @@
             if (((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0)
                     || ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED) != 0)) {
                 if (event.getAction() == MotionEvent.ACTION_UP) {
+                    EventLog.writeEvent(0x534e4554, "62187985"); // safety net logging
                     Toast.makeText(v.getContext(),
                             R.string.touch_filtered_warning,
                             Toast.LENGTH_SHORT).show();
diff --git a/com/android/systemui/volume/VolumeDialogImpl.java b/com/android/systemui/volume/VolumeDialogImpl.java
index 0fd6c74..761e979 100644
--- a/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/com/android/systemui/volume/VolumeDialogImpl.java
@@ -75,6 +75,7 @@
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
+import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.plugins.VolumeDialog;
 import com.android.systemui.plugins.VolumeDialogController;
@@ -515,6 +516,9 @@
         if (mSafetyWarning != null) return 5000;
         if (mExpanded || mExpandButtonAnimationRunning) return 5000;
         if (mActiveStream == AudioManager.STREAM_MUSIC) return 1500;
+        if (mZenFooter.shouldShowIntroduction()) {
+            return 6000;
+        }
         return 3000;
     }
 
diff --git a/com/android/systemui/volume/ZenFooter.java b/com/android/systemui/volume/ZenFooter.java
index 7464212..80e1629 100644
--- a/com/android/systemui/volume/ZenFooter.java
+++ b/com/android/systemui/volume/ZenFooter.java
@@ -152,6 +152,7 @@
                                 mController.getCurrentUser(), true /*shortVersion*/);
         Util.setText(mSummaryLine2, line2);
     }
+
     public boolean shouldShowIntroduction() {
         final boolean confirmed =  Prefs.getBoolean(mContext,
                 Prefs.Key.DND_CONFIRMED_ALARM_INTRODUCTION, false);
diff --git a/foo/bar/ComplexDao.java b/foo/bar/ComplexDao.java
index 87d0c49..ca90163 100644
--- a/foo/bar/ComplexDao.java
+++ b/foo/bar/ComplexDao.java
@@ -1,421 +1,64 @@
+/*
+ * Copyright (C) 2016 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 foo.bar;
-
-import android.arch.lifecycle.ComputableLiveData;
-import android.arch.lifecycle.LiveData;
-import android.arch.persistence.room.InvalidationTracker.Observer;
-import android.arch.persistence.room.RoomDatabase;
-import android.arch.persistence.room.RoomSQLiteQuery;
-import android.arch.persistence.room.util.StringUtil;
-import android.database.Cursor;
-import android.support.annotation.NonNull;
-import java.lang.Integer;
-import java.lang.Override;
-import java.lang.String;
-import java.lang.StringBuilder;
-import java.util.ArrayList;
+import android.arch.persistence.room.*;
 import java.util.List;
-import java.util.Set;
-import javax.annotation.Generated;
-
-@Generated("android.arch.persistence.room.RoomProcessor")
-public class ComplexDao_Impl extends ComplexDao {
-    private final RoomDatabase __db;
-
-    public ComplexDao_Impl(ComplexDatabase __db) {
-        super(__db);
-        this.__db = __db;
+import android.arch.lifecycle.LiveData;
+@Dao
+abstract class ComplexDao {
+    static class FullName {
+        public int id;
+        public String fullName;
     }
 
-    @Override
-    public List<ComplexDao.FullName> fullNames(int id) {
-        final String _sql = "SELECT name || lastName as fullName, uid as id FROM user where uid = ?";
-        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
-        int _argIndex = 1;
-        _statement.bindLong(_argIndex, id);
-        final Cursor _cursor = __db.query(_statement);
-        try {
-            final int _cursorIndexOfFullName = _cursor.getColumnIndexOrThrow("fullName");
-            final int _cursorIndexOfId = _cursor.getColumnIndexOrThrow("id");
-            final List<ComplexDao.FullName> _result = new ArrayList<ComplexDao.FullName>(_cursor.getCount());
-            while(_cursor.moveToNext()) {
-                final ComplexDao.FullName _item;
-                _item = new ComplexDao.FullName();
-                _item.fullName = _cursor.getString(_cursorIndexOfFullName);
-                _item.id = _cursor.getInt(_cursorIndexOfId);
-                _result.add(_item);
-            }
-            return _result;
-        } finally {
-            _cursor.close();
-            _statement.release();
-        }
+    private final ComplexDatabase mDb;
+
+    public ComplexDao(ComplexDatabase db) {
+        mDb = db;
     }
 
-    @Override
-    public User getById(int id) {
-        final String _sql = "SELECT * FROM user where uid = ?";
-        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
-        int _argIndex = 1;
-        _statement.bindLong(_argIndex, id);
-        final Cursor _cursor = __db.query(_statement);
-        try {
-            final int _cursorIndexOfUid = _cursor.getColumnIndexOrThrow("uid");
-            final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
-            final int _cursorIndexOfLastName = _cursor.getColumnIndexOrThrow("lastName");
-            final int _cursorIndexOfAge = _cursor.getColumnIndexOrThrow("ageColumn");
-            final User _result;
-            if(_cursor.moveToFirst()) {
-                _result = new User();
-                _result.uid = _cursor.getInt(_cursorIndexOfUid);
-                _result.name = _cursor.getString(_cursorIndexOfName);
-                final String _tmpLastName;
-                _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
-                _result.setLastName(_tmpLastName);
-                _result.age = _cursor.getInt(_cursorIndexOfAge);
-            } else {
-                _result = null;
-            }
-            return _result;
-        } finally {
-            _cursor.close();
-            _statement.release();
-        }
-    }
+    @Query("SELECT name || lastName as fullName, uid as id FROM user where uid = :id")
+    abstract public List<FullName> fullNames(int id);
 
-    @Override
-    public User findByName(String name, String lastName) {
-        final String _sql = "SELECT * FROM user where name LIKE ? AND lastName LIKE ?";
-        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 2);
-        int _argIndex = 1;
-        if (name == null) {
-            _statement.bindNull(_argIndex);
-        } else {
-            _statement.bindString(_argIndex, name);
-        }
-        _argIndex = 2;
-        if (lastName == null) {
-            _statement.bindNull(_argIndex);
-        } else {
-            _statement.bindString(_argIndex, lastName);
-        }
-        final Cursor _cursor = __db.query(_statement);
-        try {
-            final int _cursorIndexOfUid = _cursor.getColumnIndexOrThrow("uid");
-            final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
-            final int _cursorIndexOfLastName = _cursor.getColumnIndexOrThrow("lastName");
-            final int _cursorIndexOfAge = _cursor.getColumnIndexOrThrow("ageColumn");
-            final User _result;
-            if(_cursor.moveToFirst()) {
-                _result = new User();
-                _result.uid = _cursor.getInt(_cursorIndexOfUid);
-                _result.name = _cursor.getString(_cursorIndexOfName);
-                final String _tmpLastName;
-                _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
-                _result.setLastName(_tmpLastName);
-                _result.age = _cursor.getInt(_cursorIndexOfAge);
-            } else {
-                _result = null;
-            }
-            return _result;
-        } finally {
-            _cursor.close();
-            _statement.release();
-        }
-    }
+    @Query("SELECT * FROM user where uid = :id")
+    abstract public User getById(int id);
 
-    @Override
-    public List<User> loadAllByIds(int... ids) {
-        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
-        _stringBuilder.append("SELECT * FROM user where uid IN (");
-        final int _inputSize = ids.length;
-        StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
-        _stringBuilder.append(")");
-        final String _sql = _stringBuilder.toString();
-        final int _argCount = 0 + _inputSize;
-        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
-        int _argIndex = 1;
-        for (int _item : ids) {
-            _statement.bindLong(_argIndex, _item);
-            _argIndex ++;
-        }
-        final Cursor _cursor = __db.query(_statement);
-        try {
-            final int _cursorIndexOfUid = _cursor.getColumnIndexOrThrow("uid");
-            final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
-            final int _cursorIndexOfLastName = _cursor.getColumnIndexOrThrow("lastName");
-            final int _cursorIndexOfAge = _cursor.getColumnIndexOrThrow("ageColumn");
-            final List<User> _result = new ArrayList<User>(_cursor.getCount());
-            while(_cursor.moveToNext()) {
-                final User _item_1;
-                _item_1 = new User();
-                _item_1.uid = _cursor.getInt(_cursorIndexOfUid);
-                _item_1.name = _cursor.getString(_cursorIndexOfName);
-                final String _tmpLastName;
-                _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
-                _item_1.setLastName(_tmpLastName);
-                _item_1.age = _cursor.getInt(_cursorIndexOfAge);
-                _result.add(_item_1);
-            }
-            return _result;
-        } finally {
-            _cursor.close();
-            _statement.release();
-        }
-    }
+    @Query("SELECT * FROM user where name LIKE :name AND lastName LIKE :lastName")
+    abstract public User findByName(String name, String lastName);
 
-    @Override
-    int getAge(int id) {
-        final String _sql = "SELECT ageColumn FROM user where uid = ?";
-        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
-        int _argIndex = 1;
-        _statement.bindLong(_argIndex, id);
-        final Cursor _cursor = __db.query(_statement);
-        try {
-            final int _result;
-            if(_cursor.moveToFirst()) {
-                _result = _cursor.getInt(0);
-            } else {
-                _result = 0;
-            }
-            return _result;
-        } finally {
-            _cursor.close();
-            _statement.release();
-        }
-    }
+    @Query("SELECT * FROM user where uid IN (:ids)")
+    abstract public List<User> loadAllByIds(int... ids);
 
-    @Override
-    public int[] getAllAges(int... ids) {
-        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
-        _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
-        final int _inputSize = ids.length;
-        StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
-        _stringBuilder.append(")");
-        final String _sql = _stringBuilder.toString();
-        final int _argCount = 0 + _inputSize;
-        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
-        int _argIndex = 1;
-        for (int _item : ids) {
-            _statement.bindLong(_argIndex, _item);
-            _argIndex ++;
-        }
-        final Cursor _cursor = __db.query(_statement);
-        try {
-            final int[] _result = new int[_cursor.getCount()];
-            int _index = 0;
-            while(_cursor.moveToNext()) {
-                final int _item_1;
-                _item_1 = _cursor.getInt(0);
-                _result[_index] = _item_1;
-                _index ++;
-            }
-            return _result;
-        } finally {
-            _cursor.close();
-            _statement.release();
-        }
-    }
+    @Query("SELECT ageColumn FROM user where uid = :id")
+    abstract int getAge(int id);
 
-    @Override
-    public List<Integer> getAllAgesAsList(List<Integer> ids) {
-        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
-        _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
-        final int _inputSize = ids.size();
-        StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
-        _stringBuilder.append(")");
-        final String _sql = _stringBuilder.toString();
-        final int _argCount = 0 + _inputSize;
-        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
-        int _argIndex = 1;
-        for (Integer _item : ids) {
-            if (_item == null) {
-                _statement.bindNull(_argIndex);
-            } else {
-                _statement.bindLong(_argIndex, _item);
-            }
-            _argIndex ++;
-        }
-        final Cursor _cursor = __db.query(_statement);
-        try {
-            final List<Integer> _result = new ArrayList<Integer>(_cursor.getCount());
-            while(_cursor.moveToNext()) {
-                final Integer _item_1;
-                if (_cursor.isNull(0)) {
-                    _item_1 = null;
-                } else {
-                    _item_1 = _cursor.getInt(0);
-                }
-                _result.add(_item_1);
-            }
-            return _result;
-        } finally {
-            _cursor.close();
-            _statement.release();
-        }
-    }
+    @Query("SELECT ageColumn FROM user where uid IN(:ids)")
+    abstract public int[] getAllAges(int... ids);
 
-    @Override
-    public LiveData<User> getByIdLive(int id) {
-        final String _sql = "SELECT * FROM user where uid = ?";
-        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 1);
-        int _argIndex = 1;
-        _statement.bindLong(_argIndex, id);
-        return new ComputableLiveData<User>() {
-            private Observer _observer;
+    @Query("SELECT ageColumn FROM user where uid IN(:ids)")
+    abstract public List<Integer> getAllAgesAsList(List<Integer> ids);
 
-            @Override
-            protected User compute() {
-                if (_observer == null) {
-                    _observer = new Observer("user") {
-                        @Override
-                        public void onInvalidated(@NonNull Set<String> tables) {
-                            invalidate();
-                        }
-                    };
-                    __db.getInvalidationTracker().addWeakObserver(_observer);
-                }
-                final Cursor _cursor = __db.query(_statement);
-                try {
-                    final int _cursorIndexOfUid = _cursor.getColumnIndexOrThrow("uid");
-                    final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
-                    final int _cursorIndexOfLastName = _cursor.getColumnIndexOrThrow("lastName");
-                    final int _cursorIndexOfAge = _cursor.getColumnIndexOrThrow("ageColumn");
-                    final User _result;
-                    if(_cursor.moveToFirst()) {
-                        _result = new User();
-                        _result.uid = _cursor.getInt(_cursorIndexOfUid);
-                        _result.name = _cursor.getString(_cursorIndexOfName);
-                        final String _tmpLastName;
-                        _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
-                        _result.setLastName(_tmpLastName);
-                        _result.age = _cursor.getInt(_cursorIndexOfAge);
-                    } else {
-                        _result = null;
-                    }
-                    return _result;
-                } finally {
-                    _cursor.close();
-                }
-            }
+    @Query("SELECT * FROM user where uid = :id")
+    abstract public LiveData<User> getByIdLive(int id);
 
-            @Override
-            protected void finalize() {
-                _statement.release();
-            }
-        }.getLiveData();
-    }
+    @Query("SELECT * FROM user where uid IN (:ids)")
+    abstract public LiveData<List<User>> loadUsersByIdsLive(int... ids);
 
-    @Override
-    public LiveData<List<User>> loadUsersByIdsLive(int... ids) {
-        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
-        _stringBuilder.append("SELECT * FROM user where uid IN (");
-        final int _inputSize = ids.length;
-        StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
-        _stringBuilder.append(")");
-        final String _sql = _stringBuilder.toString();
-        final int _argCount = 0 + _inputSize;
-        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
-        int _argIndex = 1;
-        for (int _item : ids) {
-            _statement.bindLong(_argIndex, _item);
-            _argIndex ++;
-        }
-        return new ComputableLiveData<List<User>>() {
-            private Observer _observer;
-
-            @Override
-            protected List<User> compute() {
-                if (_observer == null) {
-                    _observer = new Observer("user") {
-                        @Override
-                        public void onInvalidated(@NonNull Set<String> tables) {
-                            invalidate();
-                        }
-                    };
-                    __db.getInvalidationTracker().addWeakObserver(_observer);
-                }
-                final Cursor _cursor = __db.query(_statement);
-                try {
-                    final int _cursorIndexOfUid = _cursor.getColumnIndexOrThrow("uid");
-                    final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");
-                    final int _cursorIndexOfLastName = _cursor.getColumnIndexOrThrow("lastName");
-                    final int _cursorIndexOfAge = _cursor.getColumnIndexOrThrow("ageColumn");
-                    final List<User> _result = new ArrayList<User>(_cursor.getCount());
-                    while(_cursor.moveToNext()) {
-                        final User _item_1;
-                        _item_1 = new User();
-                        _item_1.uid = _cursor.getInt(_cursorIndexOfUid);
-                        _item_1.name = _cursor.getString(_cursorIndexOfName);
-                        final String _tmpLastName;
-                        _tmpLastName = _cursor.getString(_cursorIndexOfLastName);
-                        _item_1.setLastName(_tmpLastName);
-                        _item_1.age = _cursor.getInt(_cursorIndexOfAge);
-                        _result.add(_item_1);
-                    }
-                    return _result;
-                } finally {
-                    _cursor.close();
-                }
-            }
-
-            @Override
-            protected void finalize() {
-                _statement.release();
-            }
-        }.getLiveData();
-    }
-
-    @Override
-    public List<Integer> getAllAgesAsList(List<Integer> ids1, int[] ids2, int... ids3) {
-        StringBuilder _stringBuilder = StringUtil.newStringBuilder();
-        _stringBuilder.append("SELECT ageColumn FROM user where uid IN(");
-        final int _inputSize = ids1.size();
-        StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
-        _stringBuilder.append(") OR uid IN (");
-        final int _inputSize_1 = ids2.length;
-        StringUtil.appendPlaceholders(_stringBuilder, _inputSize_1);
-        _stringBuilder.append(") OR uid IN (");
-        final int _inputSize_2 = ids3.length;
-        StringUtil.appendPlaceholders(_stringBuilder, _inputSize_2);
-        _stringBuilder.append(")");
-        final String _sql = _stringBuilder.toString();
-        final int _argCount = 0 + _inputSize + _inputSize_1 + _inputSize_2;
-        final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, _argCount);
-        int _argIndex = 1;
-        for (Integer _item : ids1) {
-            if (_item == null) {
-                _statement.bindNull(_argIndex);
-            } else {
-                _statement.bindLong(_argIndex, _item);
-            }
-            _argIndex ++;
-        }
-        _argIndex = 1 + _inputSize;
-        for (int _item_1 : ids2) {
-            _statement.bindLong(_argIndex, _item_1);
-            _argIndex ++;
-        }
-        _argIndex = 1 + _inputSize + _inputSize_1;
-        for (int _item_2 : ids3) {
-            _statement.bindLong(_argIndex, _item_2);
-            _argIndex ++;
-        }
-        final Cursor _cursor = __db.query(_statement);
-        try {
-            final List<Integer> _result = new ArrayList<Integer>(_cursor.getCount());
-            while(_cursor.moveToNext()) {
-                final Integer _item_3;
-                if (_cursor.isNull(0)) {
-                    _item_3 = null;
-                } else {
-                    _item_3 = _cursor.getInt(0);
-                }
-                _result.add(_item_3);
-            }
-            return _result;
-        } finally {
-            _cursor.close();
-            _statement.release();
-        }
-    }
+    @Query("SELECT ageColumn FROM user where uid IN(:ids1) OR uid IN (:ids2) OR uid IN (:ids3)")
+    abstract public List<Integer> getAllAgesAsList(List<Integer> ids1,
+            int[] ids2, int... ids3);
 }
diff --git a/foo/bar/ComplexDatabase.java b/foo/bar/ComplexDatabase.java
index 39d4d42..f35e0b8 100644
--- a/foo/bar/ComplexDatabase.java
+++ b/foo/bar/ComplexDatabase.java
@@ -1,98 +1,23 @@
+/*
+ * Copyright (C) 2016 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 foo.bar;
-
-import android.arch.persistence.db.SupportSQLiteDatabase;
-import android.arch.persistence.db.SupportSQLiteOpenHelper;
-import android.arch.persistence.db.SupportSQLiteOpenHelper.Callback;
-import android.arch.persistence.db.SupportSQLiteOpenHelper.Configuration;
-import android.arch.persistence.room.DatabaseConfiguration;
-import android.arch.persistence.room.InvalidationTracker;
-import android.arch.persistence.room.RoomOpenHelper;
-import android.arch.persistence.room.RoomOpenHelper.Delegate;
-import android.arch.persistence.room.util.TableInfo;
-import android.arch.persistence.room.util.TableInfo.Column;
-import android.arch.persistence.room.util.TableInfo.ForeignKey;
-import java.lang.IllegalStateException;
-import java.lang.Override;
-import java.lang.String;
-import java.util.HashMap;
-import java.util.HashSet;
-import javax.annotation.Generated;
-
-@Generated("android.arch.persistence.room.RoomProcessor")
-public class ComplexDatabase_Impl extends ComplexDatabase {
-    private volatile ComplexDao _complexDao;
-
-    protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
-        final SupportSQLiteOpenHelper.Callback _openCallback = new RoomOpenHelper(configuration, new RoomOpenHelper.Delegate() {
-            public void createAllTables(SupportSQLiteDatabase _db) {
-                _db.execSQL("CREATE TABLE IF NOT EXISTS `User` (`uid` INTEGER NOT NULL, `name` TEXT, `lastName` TEXT, `ageColumn` INTEGER NOT NULL, PRIMARY KEY(`uid`))");
-                _db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)");
-                _db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"6773601c5bcf94c71ee4eb0de04f21a4\")");
-            }
-
-            public void dropAllTables(SupportSQLiteDatabase _db) {
-                _db.execSQL("DROP TABLE IF EXISTS `User`");
-            }
-
-            protected void onCreate(SupportSQLiteDatabase _db) {
-                if (mCallbacks != null) {
-                    for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
-                        mCallbacks.get(_i).onCreate(_db);
-                    }
-                }
-            }
-
-            public void onOpen(SupportSQLiteDatabase _db) {
-                mDatabase = _db;
-                internalInitInvalidationTracker(_db);
-                if (mCallbacks != null) {
-                    for (int _i = 0, _size = mCallbacks.size(); _i < _size; _i++) {
-                        mCallbacks.get(_i).onOpen(_db);
-                    }
-                }
-            }
-
-            protected void validateMigration(SupportSQLiteDatabase _db) {
-                final HashMap<String, TableInfo.Column> _columnsUser = new HashMap<String, TableInfo.Column>(4);
-                _columnsUser.put("uid", new TableInfo.Column("uid", "INTEGER", true, 1));
-                _columnsUser.put("name", new TableInfo.Column("name", "TEXT", false, 0));
-                _columnsUser.put("lastName", new TableInfo.Column("lastName", "TEXT", false, 0));
-                _columnsUser.put("ageColumn", new TableInfo.Column("ageColumn", "INTEGER", true, 0));
-                final HashSet<TableInfo.ForeignKey> _foreignKeysUser = new HashSet<TableInfo.ForeignKey>(0);
-                final TableInfo _infoUser = new TableInfo("User", _columnsUser, _foreignKeysUser);
-                final TableInfo _existingUser = TableInfo.read(_db, "User");
-                if (! _infoUser.equals(_existingUser)) {
-                    throw new IllegalStateException("Migration didn't properly handle User(foo.bar.User).\n"
-                            + " Expected:\n" + _infoUser + "\n"
-                            + " Found:\n" + _existingUser);
-                }
-            }
-        }, "6773601c5bcf94c71ee4eb0de04f21a4");
-        final SupportSQLiteOpenHelper.Configuration _sqliteConfig = SupportSQLiteOpenHelper.Configuration.builder(configuration.context)
-                .name(configuration.name)
-                .version(1923)
-                .callback(_openCallback)
-                .build();
-        final SupportSQLiteOpenHelper _helper = configuration.sqliteOpenHelperFactory.create(_sqliteConfig);
-        return _helper;
-    }
-
-    @Override
-    protected InvalidationTracker createInvalidationTracker() {
-        return new InvalidationTracker(this, "User");
-    }
-
-    @Override
-    ComplexDao getComplexDao() {
-        if (_complexDao != null) {
-            return _complexDao;
-        } else {
-            synchronized(this) {
-                if(_complexDao == null) {
-                    _complexDao = new ComplexDao_Impl(this);
-                }
-                return _complexDao;
-            }
-        }
-    }
+import android.arch.persistence.room.*;
+import java.util.List;
+@Database(entities = {User.class}, version = 1923)
+abstract class ComplexDatabase extends RoomDatabase {
+    abstract ComplexDao getComplexDao();
 }
diff --git a/foo/bar/DeletionDao.java b/foo/bar/DeletionDao.java
index 067bf67..997f290 100644
--- a/foo/bar/DeletionDao.java
+++ b/foo/bar/DeletionDao.java
@@ -1,240 +1,51 @@
+/*
+ * Copyright (C) 2016 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 foo.bar;
-
-import android.arch.persistence.db.SupportSQLiteStatement;
-import android.arch.persistence.room.EntityDeletionOrUpdateAdapter;
-import android.arch.persistence.room.RoomDatabase;
-import android.arch.persistence.room.SharedSQLiteStatement;
-import android.arch.persistence.room.util.StringUtil;
-import java.lang.Override;
-import java.lang.String;
-import java.lang.StringBuilder;
+import android.arch.persistence.room.*;
 import java.util.List;
-import javax.annotation.Generated;
 
-@Generated("android.arch.persistence.room.RoomProcessor")
-public class DeletionDao_Impl implements DeletionDao {
-  private final RoomDatabase __db;
+@Dao
+abstract interface DeletionDao {
+    @Delete
+    void deleteUser(User user);
+    @Delete
+    void deleteUsers(User user1, List<User> others);
+    @Delete
+    void deleteArrayOfUsers(User[] users);
 
-  private final EntityDeletionOrUpdateAdapter __deletionAdapterOfUser;
+    @Delete
+    int deleteUserAndReturnCount(User user);
+    @Delete
+    int deleteUserAndReturnCount(User user1, List<User> others);
+    @Delete
+    int deleteUserAndReturnCount(User[] users);
 
-  private final EntityDeletionOrUpdateAdapter __deletionAdapterOfMultiPKeyEntity;
+    @Delete
+    int multiPKey(MultiPKeyEntity entity);
 
-  private final EntityDeletionOrUpdateAdapter __deletionAdapterOfBook;
+    @Query("DELETE FROM user where uid = :uid")
+    int deleteByUid(int uid);
 
-  private final SharedSQLiteStatement __preparedStmtOfDeleteByUid;
+    @Query("DELETE FROM user where uid IN(:uid)")
+    int deleteByUidList(int... uid);
 
-  private final SharedSQLiteStatement __preparedStmtOfDeleteEverything;
+    @Delete
+    void deleteUserAndBook(User user, Book book);
 
-  public DeletionDao_Impl(RoomDatabase __db) {
-    this.__db = __db;
-    this.__deletionAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
-      @Override
-      public String createQuery() {
-        return "DELETE FROM `User` WHERE `uid` = ?";
-      }
-
-      @Override
-      public void bind(SupportSQLiteStatement stmt, User value) {
-        stmt.bindLong(1, value.uid);
-      }
-    };
-    this.__deletionAdapterOfMultiPKeyEntity = new EntityDeletionOrUpdateAdapter<MultiPKeyEntity>(__db) {
-      @Override
-      public String createQuery() {
-        return "DELETE FROM `MultiPKeyEntity` WHERE `name` = ? AND `lastName` = ?";
-      }
-
-      @Override
-      public void bind(SupportSQLiteStatement stmt, MultiPKeyEntity value) {
-        if (value.name == null) {
-          stmt.bindNull(1);
-        } else {
-          stmt.bindString(1, value.name);
-        }
-        if (value.lastName == null) {
-          stmt.bindNull(2);
-        } else {
-          stmt.bindString(2, value.lastName);
-        }
-      }
-    };
-    this.__deletionAdapterOfBook = new EntityDeletionOrUpdateAdapter<Book>(__db) {
-      @Override
-      public String createQuery() {
-        return "DELETE FROM `Book` WHERE `bookId` = ?";
-      }
-
-      @Override
-      public void bind(SupportSQLiteStatement stmt, Book value) {
-        stmt.bindLong(1, value.bookId);
-      }
-    };
-    this.__preparedStmtOfDeleteByUid = new SharedSQLiteStatement(__db) {
-      @Override
-      public String createQuery() {
-        final String _query = "DELETE FROM user where uid = ?";
-        return _query;
-      }
-    };
-    this.__preparedStmtOfDeleteEverything = new SharedSQLiteStatement(__db) {
-      @Override
-      public String createQuery() {
-        final String _query = "DELETE FROM user";
-        return _query;
-      }
-    };
-  }
-
-  @Override
-  public void deleteUser(User user) {
-    __db.beginTransaction();
-    try {
-      __deletionAdapterOfUser.handle(user);
-      __db.setTransactionSuccessful();
-    } finally {
-      __db.endTransaction();
-    }
-  }
-
-  @Override
-  public void deleteUsers(User user1, List<User> others) {
-    __db.beginTransaction();
-    try {
-      __deletionAdapterOfUser.handle(user1);
-      __deletionAdapterOfUser.handleMultiple(others);
-      __db.setTransactionSuccessful();
-    } finally {
-      __db.endTransaction();
-    }
-  }
-
-  @Override
-  public void deleteArrayOfUsers(User[] users) {
-    __db.beginTransaction();
-    try {
-      __deletionAdapterOfUser.handleMultiple(users);
-      __db.setTransactionSuccessful();
-    } finally {
-      __db.endTransaction();
-    }
-  }
-
-  @Override
-  public int deleteUserAndReturnCount(User user) {
-    int _total = 0;
-    __db.beginTransaction();
-    try {
-      _total +=__deletionAdapterOfUser.handle(user);
-      __db.setTransactionSuccessful();
-      return _total;
-    } finally {
-      __db.endTransaction();
-    }
-  }
-
-  @Override
-  public int deleteUserAndReturnCount(User user1, List<User> others) {
-    int _total = 0;
-    __db.beginTransaction();
-    try {
-      _total +=__deletionAdapterOfUser.handle(user1);
-      _total +=__deletionAdapterOfUser.handleMultiple(others);
-      __db.setTransactionSuccessful();
-      return _total;
-    } finally {
-      __db.endTransaction();
-    }
-  }
-
-  @Override
-  public int deleteUserAndReturnCount(User[] users) {
-    int _total = 0;
-    __db.beginTransaction();
-    try {
-      _total +=__deletionAdapterOfUser.handleMultiple(users);
-      __db.setTransactionSuccessful();
-      return _total;
-    } finally {
-      __db.endTransaction();
-    }
-  }
-
-  @Override
-  public int multiPKey(MultiPKeyEntity entity) {
-    int _total = 0;
-    __db.beginTransaction();
-    try {
-      _total +=__deletionAdapterOfMultiPKeyEntity.handle(entity);
-      __db.setTransactionSuccessful();
-      return _total;
-    } finally {
-      __db.endTransaction();
-    }
-  }
-
-  @Override
-  public void deleteUserAndBook(User user, Book book) {
-    __db.beginTransaction();
-    try {
-      __deletionAdapterOfUser.handle(user);
-      __deletionAdapterOfBook.handle(book);
-      __db.setTransactionSuccessful();
-    } finally {
-      __db.endTransaction();
-    }
-  }
-
-  @Override
-  public int deleteByUid(int uid) {
-    final SupportSQLiteStatement _stmt = __preparedStmtOfDeleteByUid.acquire();
-    __db.beginTransaction();
-    try {
-      int _argIndex = 1;
-      _stmt.bindLong(_argIndex, uid);
-      final int _result = _stmt.executeUpdateDelete();
-      __db.setTransactionSuccessful();
-      return _result;
-    } finally {
-      __db.endTransaction();
-      __preparedStmtOfDeleteByUid.release(_stmt);
-    }
-  }
-
-  @Override
-  public int deleteEverything() {
-    final SupportSQLiteStatement _stmt = __preparedStmtOfDeleteEverything.acquire();
-    __db.beginTransaction();
-    try {
-      final int _result = _stmt.executeUpdateDelete();
-      __db.setTransactionSuccessful();
-      return _result;
-    } finally {
-      __db.endTransaction();
-      __preparedStmtOfDeleteEverything.release(_stmt);
-    }
-  }
-
-  @Override
-  public int deleteByUidList(int... uid) {
-    StringBuilder _stringBuilder = StringUtil.newStringBuilder();
-    _stringBuilder.append("DELETE FROM user where uid IN(");
-    final int _inputSize = uid.length;
-    StringUtil.appendPlaceholders(_stringBuilder, _inputSize);
-    _stringBuilder.append(")");
-    final String _sql = _stringBuilder.toString();
-    SupportSQLiteStatement _stmt = __db.compileStatement(_sql);
-    int _argIndex = 1;
-    for (int _item : uid) {
-      _stmt.bindLong(_argIndex, _item);
-      _argIndex ++;
-    }
-    __db.beginTransaction();
-    try {
-      final int _result = _stmt.executeUpdateDelete();
-      __db.setTransactionSuccessful();
-      return _result;
-    } finally {
-      __db.endTransaction();
-    }
-  }
+    @Query("DELETE FROM user")
+    int deleteEverything();
 }
diff --git a/foo/bar/UpdateDao.java b/foo/bar/UpdateDao.java
index 1190a0d..040b5c7 100644
--- a/foo/bar/UpdateDao.java
+++ b/foo/bar/UpdateDao.java
@@ -1,240 +1,48 @@
+/*
+ * Copyright (C) 2017 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 foo.bar;
-
-import android.arch.persistence.db.SupportSQLiteStatement;
-import android.arch.persistence.room.EntityDeletionOrUpdateAdapter;
-import android.arch.persistence.room.RoomDatabase;
-import android.arch.persistence.room.SharedSQLiteStatement;
-import java.lang.Override;
-import java.lang.String;
+import android.arch.persistence.room.*;
 import java.util.List;
-import javax.annotation.Generated;
 
-@Generated("android.arch.persistence.room.RoomProcessor")
-public class UpdateDao_Impl implements UpdateDao {
-  private final RoomDatabase __db;
+@Dao
+abstract interface UpdateDao {
+    @Update
+    void updateUser(User user);
+    @Update
+    void updateUsers(User user1, List<User> others);
+    @Update
+    void updateArrayOfUsers(User[] users);
 
-  private final EntityDeletionOrUpdateAdapter __updateAdapterOfUser;
+    @Update
+    int updateUserAndReturnCount(User user);
+    @Update
+    int updateUserAndReturnCount(User user1, List<User> others);
+    @Update
+    int updateUserAndReturnCount(User[] users);
 
-  private final EntityDeletionOrUpdateAdapter __updateAdapterOfMultiPKeyEntity;
+    @Update
+    int multiPKey(MultiPKeyEntity entity);
 
-  private final EntityDeletionOrUpdateAdapter __updateAdapterOfBook;
+    @Update
+    void updateUserAndBook(User user, Book book);
 
-  private final SharedSQLiteStatement __preparedStmtOfAgeUserByUid;
+    @Query("UPDATE User SET ageColumn = ageColumn + 1 WHERE uid = :uid")
+    void ageUserByUid(String uid);
 
-  private final SharedSQLiteStatement __preparedStmtOfAgeUserAll;
-
-  public UpdateDao_Impl(RoomDatabase __db) {
-    this.__db = __db;
-    this.__updateAdapterOfUser = new EntityDeletionOrUpdateAdapter<User>(__db) {
-      @Override
-      public String createQuery() {
-        return "UPDATE OR ABORT `User` SET `uid` = ?,`name` = ?,`lastName` = ?,`ageColumn` = ? WHERE `uid` = ?";
-      }
-
-      @Override
-      public void bind(SupportSQLiteStatement stmt, User value) {
-        stmt.bindLong(1, value.uid);
-        if (value.name == null) {
-          stmt.bindNull(2);
-        } else {
-          stmt.bindString(2, value.name);
-        }
-        if (value.getLastName() == null) {
-          stmt.bindNull(3);
-        } else {
-          stmt.bindString(3, value.getLastName());
-        }
-        stmt.bindLong(4, value.age);
-        stmt.bindLong(5, value.uid);
-      }
-    };
-    this.__updateAdapterOfMultiPKeyEntity = new EntityDeletionOrUpdateAdapter<MultiPKeyEntity>(__db) {
-      @Override
-      public String createQuery() {
-        return "UPDATE OR ABORT `MultiPKeyEntity` SET `name` = ?,`lastName` = ? WHERE `name` = ? AND `lastName` = ?";
-      }
-
-      @Override
-      public void bind(SupportSQLiteStatement stmt, MultiPKeyEntity value) {
-        if (value.name == null) {
-          stmt.bindNull(1);
-        } else {
-          stmt.bindString(1, value.name);
-        }
-        if (value.lastName == null) {
-          stmt.bindNull(2);
-        } else {
-          stmt.bindString(2, value.lastName);
-        }
-        if (value.name == null) {
-          stmt.bindNull(3);
-        } else {
-          stmt.bindString(3, value.name);
-        }
-        if (value.lastName == null) {
-          stmt.bindNull(4);
-        } else {
-          stmt.bindString(4, value.lastName);
-        }
-      }
-    };
-    this.__updateAdapterOfBook = new EntityDeletionOrUpdateAdapter<Book>(__db) {
-      @Override
-      public String createQuery() {
-        return "UPDATE OR ABORT `Book` SET `bookId` = ?,`uid` = ? WHERE `bookId` = ?";
-      }
-
-      @Override
-      public void bind(SupportSQLiteStatement stmt, Book value) {
-        stmt.bindLong(1, value.bookId);
-        stmt.bindLong(2, value.uid);
-        stmt.bindLong(3, value.bookId);
-      }
-    };
-    this.__preparedStmtOfAgeUserByUid = new SharedSQLiteStatement(__db) {
-      @Override
-      public String createQuery() {
-        final String _query = "UPDATE User SET ageColumn = ageColumn + 1 WHERE uid = ?";
-        return _query;
-      }
-    };
-    this.__preparedStmtOfAgeUserAll = new SharedSQLiteStatement(__db) {
-      @Override
-      public String createQuery() {
-        final String _query = "UPDATE User SET ageColumn = ageColumn + 1";
-        return _query;
-      }
-    };
-  }
-
-  @Override
-  public void updateUser(User user) {
-    __db.beginTransaction();
-    try {
-      __updateAdapterOfUser.handle(user);
-      __db.setTransactionSuccessful();
-    } finally {
-      __db.endTransaction();
-    }
-  }
-
-  @Override
-  public void updateUsers(User user1, List<User> others) {
-    __db.beginTransaction();
-    try {
-      __updateAdapterOfUser.handle(user1);
-      __updateAdapterOfUser.handleMultiple(others);
-      __db.setTransactionSuccessful();
-    } finally {
-      __db.endTransaction();
-    }
-  }
-
-  @Override
-  public void updateArrayOfUsers(User[] users) {
-    __db.beginTransaction();
-    try {
-      __updateAdapterOfUser.handleMultiple(users);
-      __db.setTransactionSuccessful();
-    } finally {
-      __db.endTransaction();
-    }
-  }
-
-  @Override
-  public int updateUserAndReturnCount(User user) {
-    int _total = 0;
-    __db.beginTransaction();
-    try {
-      _total +=__updateAdapterOfUser.handle(user);
-      __db.setTransactionSuccessful();
-      return _total;
-    } finally {
-      __db.endTransaction();
-    }
-  }
-
-  @Override
-  public int updateUserAndReturnCount(User user1, List<User> others) {
-    int _total = 0;
-    __db.beginTransaction();
-    try {
-      _total +=__updateAdapterOfUser.handle(user1);
-      _total +=__updateAdapterOfUser.handleMultiple(others);
-      __db.setTransactionSuccessful();
-      return _total;
-    } finally {
-      __db.endTransaction();
-    }
-  }
-
-  @Override
-  public int updateUserAndReturnCount(User[] users) {
-    int _total = 0;
-    __db.beginTransaction();
-    try {
-      _total +=__updateAdapterOfUser.handleMultiple(users);
-      __db.setTransactionSuccessful();
-      return _total;
-    } finally {
-      __db.endTransaction();
-    }
-  }
-
-  @Override
-  public int multiPKey(MultiPKeyEntity entity) {
-    int _total = 0;
-    __db.beginTransaction();
-    try {
-      _total +=__updateAdapterOfMultiPKeyEntity.handle(entity);
-      __db.setTransactionSuccessful();
-      return _total;
-    } finally {
-      __db.endTransaction();
-    }
-  }
-
-  @Override
-  public void updateUserAndBook(User user, Book book) {
-    __db.beginTransaction();
-    try {
-      __updateAdapterOfUser.handle(user);
-      __updateAdapterOfBook.handle(book);
-      __db.setTransactionSuccessful();
-    } finally {
-      __db.endTransaction();
-    }
-  }
-
-  @Override
-  public void ageUserByUid(String uid) {
-    final SupportSQLiteStatement _stmt = __preparedStmtOfAgeUserByUid.acquire();
-    __db.beginTransaction();
-    try {
-      int _argIndex = 1;
-      if (uid == null) {
-        _stmt.bindNull(_argIndex);
-      } else {
-        _stmt.bindString(_argIndex, uid);
-      }
-      _stmt.executeUpdateDelete();
-      __db.setTransactionSuccessful();
-    } finally {
-      __db.endTransaction();
-      __preparedStmtOfAgeUserByUid.release(_stmt);
-    }
-  }
-
-  @Override
-  public void ageUserAll() {
-    final SupportSQLiteStatement _stmt = __preparedStmtOfAgeUserAll.acquire();
-    __db.beginTransaction();
-    try {
-      _stmt.executeUpdateDelete();
-      __db.setTransactionSuccessful();
-    } finally {
-      __db.endTransaction();
-      __preparedStmtOfAgeUserAll.release(_stmt);
-    }
-  }
+    @Query("UPDATE User SET ageColumn = ageColumn + 1")
+    void ageUserAll();
 }
diff --git a/foo/bar/WriterDao.java b/foo/bar/WriterDao.java
index cfad046..e122479 100644
--- a/foo/bar/WriterDao.java
+++ b/foo/bar/WriterDao.java
@@ -15,131 +15,17 @@
  */
 
 package foo.bar;
-
-import android.arch.persistence.db.SupportSQLiteStatement;
-import android.arch.persistence.room.EntityInsertionAdapter;
-import android.arch.persistence.room.RoomDatabase;
-
-import java.lang.Override;
-import java.lang.String;
+import android.arch.persistence.room.*;
 import java.util.List;
-import javax.annotation.Generated;
 
-@Generated("android.arch.persistence.room.RoomProcessor")
-public class WriterDao_Impl implements WriterDao {
-    private final RoomDatabase __db;
-
-    private final EntityInsertionAdapter __insertionAdapterOfUser;
-
-    private final EntityInsertionAdapter __insertionAdapterOfUser_1;
-
-    private final EntityInsertionAdapter __insertionAdapterOfBook;
-
-    public WriterDao_Impl(RoomDatabase __db) {
-        this.__db = __db;
-        this.__insertionAdapterOfUser = new EntityInsertionAdapter<User>(__db) {
-            @Override
-            public String createQuery() {
-                return "INSERT OR ABORT INTO `User`(`uid`,`name`,`lastName`,`ageColumn`) VALUES"
-                        + " (?,?,?,?)";
-            }
-
-            @Override
-            public void bind(SupportSQLiteStatement stmt, User value) {
-                stmt.bindLong(1, value.uid);
-                if (value.name == null) {
-                    stmt.bindNull(2);
-                } else {
-                    stmt.bindString(2, value.name);
-                }
-                if (value.getLastName() == null) {
-                    stmt.bindNull(3);
-                } else {
-                    stmt.bindString(3, value.getLastName());
-                }
-                stmt.bindLong(4, value.age);
-            }
-        };
-        this.__insertionAdapterOfUser_1 = new EntityInsertionAdapter<User>(__db) {
-            @Override
-            public String createQuery() {
-                return "INSERT OR REPLACE INTO `User`(`uid`,`name`,`lastName`,`ageColumn`) VALUES"
-                        + " (?,?,?,?)";
-            }
-
-            @Override
-            public void bind(SupportSQLiteStatement stmt, User value) {
-                stmt.bindLong(1, value.uid);
-                if (value.name == null) {
-                    stmt.bindNull(2);
-                } else {
-                    stmt.bindString(2, value.name);
-                }
-                if (value.getLastName() == null) {
-                    stmt.bindNull(3);
-                } else {
-                    stmt.bindString(3, value.getLastName());
-                }
-                stmt.bindLong(4, value.age);
-            }
-        };
-        this.__insertionAdapterOfBook = new EntityInsertionAdapter<Book>(__db) {
-            @Override
-            public String createQuery() {
-                return "INSERT OR ABORT INTO `Book`(`bookId`,`uid`) VALUES (?,?)";
-            }
-
-            @Override
-            public void bind(SupportSQLiteStatement stmt, Book value) {
-                stmt.bindLong(1, value.bookId);
-                stmt.bindLong(2, value.uid);
-            }
-        };
-    }
-
-    @Override
-    public void insertUser(User user) {
-        __db.beginTransaction();
-        try {
-            __insertionAdapterOfUser.insert(user);
-            __db.setTransactionSuccessful();
-        } finally {
-            __db.endTransaction();
-        }
-    }
-
-    @Override
-    public void insertUsers(User user1, List<User> others) {
-        __db.beginTransaction();
-        try {
-            __insertionAdapterOfUser.insert(user1);
-            __insertionAdapterOfUser.insert(others);
-            __db.setTransactionSuccessful();
-        } finally {
-            __db.endTransaction();
-        }
-    }
-
-    @Override
-    public void insertUsers(User[] users) {
-        __db.beginTransaction();
-        try {
-            __insertionAdapterOfUser_1.insert(users);
-            __db.setTransactionSuccessful();
-        } finally {
-            __db.endTransaction();
-        }
-    }
-
-    @Override
-    public void insertUserAndBook(User user, Book book) {
-        __db.beginTransaction();
-        try {
-            __insertionAdapterOfUser.insert(user);
-            __insertionAdapterOfBook.insert(book);
-            __db.setTransactionSuccessful();
-        } finally {
-            __db.endTransaction();
-        }
-    }
+@Dao
+abstract interface WriterDao {
+    @Insert
+    void insertUser(User user);
+    @Insert
+    void insertUsers(User user1, List<User> others);
+    @Insert(onConflict=OnConflictStrategy.REPLACE)
+    void insertUsers(User[] users);
+    @Insert
+    void insertUserAndBook(User user, Book book);
 }
diff --git a/java/lang/invoke/CallSite.java b/java/lang/invoke/CallSite.java
index 85b4bb9..1ff1eb8 100644
--- a/java/lang/invoke/CallSite.java
+++ b/java/lang/invoke/CallSite.java
@@ -25,363 +25,15 @@
 
 package java.lang.invoke;
 
-// Android-changed: Not using Empty
-//import sun.invoke.empty.Empty;
-import static java.lang.invoke.MethodHandleStatics.*;
-import static java.lang.invoke.MethodHandles.Lookup.IMPL_LOOKUP;
-
-/**
- * A {@code CallSite} is a holder for a variable {@link MethodHandle},
- * which is called its {@code target}.
- * An {@code invokedynamic} instruction linked to a {@code CallSite} delegates
- * all calls to the site's current target.
- * A {@code CallSite} may be associated with several {@code invokedynamic}
- * instructions, or it may be "free floating", associated with none.
- * In any case, it may be invoked through an associated method handle
- * called its {@linkplain #dynamicInvoker dynamic invoker}.
- * <p>
- * {@code CallSite} is an abstract class which does not allow
- * direct subclassing by users.  It has three immediate,
- * concrete subclasses that may be either instantiated or subclassed.
- * <ul>
- * <li>If a mutable target is not required, an {@code invokedynamic} instruction
- * may be permanently bound by means of a {@linkplain ConstantCallSite constant call site}.
- * <li>If a mutable target is required which has volatile variable semantics,
- * because updates to the target must be immediately and reliably witnessed by other threads,
- * a {@linkplain VolatileCallSite volatile call site} may be used.
- * <li>Otherwise, if a mutable target is required,
- * a {@linkplain MutableCallSite mutable call site} may be used.
- * </ul>
- * <p>
- * A non-constant call site may be <em>relinked</em> by changing its target.
- * The new target must have the same {@linkplain MethodHandle#type() type}
- * as the previous target.
- * Thus, though a call site can be relinked to a series of
- * successive targets, it cannot change its type.
- * <p>
- * Here is a sample use of call sites and bootstrap methods which links every
- * dynamic call site to print its arguments:
-<blockquote><pre>{@code
-static void test() throws Throwable {
-    // THE FOLLOWING LINE IS PSEUDOCODE FOR A JVM INSTRUCTION
-    InvokeDynamic[#bootstrapDynamic].baz("baz arg", 2, 3.14);
-}
-private static void printArgs(Object... args) {
-  System.out.println(java.util.Arrays.deepToString(args));
-}
-private static final MethodHandle printArgs;
-static {
-  MethodHandles.Lookup lookup = MethodHandles.lookup();
-  Class thisClass = lookup.lookupClass();  // (who am I?)
-  printArgs = lookup.findStatic(thisClass,
-      "printArgs", MethodType.methodType(void.class, Object[].class));
-}
-private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) {
-  // ignore caller and name, but match the type:
-  return new ConstantCallSite(printArgs.asType(type));
-}
-}</pre></blockquote>
- * @author John Rose, JSR 292 EG
- */
 abstract
 public class CallSite {
-    // Android-changed: not used.
-    // static { MethodHandleImpl.initStatics(); }
 
-    // The actual payload of this call site:
-    /*package-private*/
-    MethodHandle target;    // Note: This field is known to the JVM.  Do not change.
+    public MethodType type() { return null; }
 
-    /**
-     * Make a blank call site object with the given method type.
-     * An initial target method is supplied which will throw
-     * an {@link IllegalStateException} if called.
-     * <p>
-     * Before this {@code CallSite} object is returned from a bootstrap method,
-     * it is usually provided with a more useful target method,
-     * via a call to {@link CallSite#setTarget(MethodHandle) setTarget}.
-     * @throws NullPointerException if the proposed type is null
-     */
-    /*package-private*/
-    CallSite(MethodType type) {
-        // Android-changed: No cache for these so create uninitializedCallSite target here using
-        // method handle transformations to create a method handle that has the expected method
-        // type but throws an IllegalStateException.
-        // target = makeUninitializedCallSite(type);
-        this.target = MethodHandles.throwException(type.returnType(), IllegalStateException.class);
-        this.target = MethodHandles.insertArguments(
-            this.target, 0, new IllegalStateException("uninitialized call site"));
-        if (type.parameterCount() > 0) {
-            this.target = MethodHandles.dropArguments(this.target, 0, type.ptypes());
-        }
-
-        // Android-changed: Using initializer method for GET_TARGET
-        // rather than complex static initializer.
-        initializeGetTarget();
-    }
-
-    /**
-     * Make a call site object equipped with an initial target method handle.
-     * @param target the method handle which will be the initial target of the call site
-     * @throws NullPointerException if the proposed target is null
-     */
-    /*package-private*/
-    CallSite(MethodHandle target) {
-        target.type();  // null check
-        this.target = target;
-
-        // Android-changed: Using initializer method for GET_TARGET
-        // rather than complex static initializer.
-        initializeGetTarget();
-    }
-
-    /**
-     * Make a call site object equipped with an initial target method handle.
-     * @param targetType the desired type of the call site
-     * @param createTargetHook a hook which will bind the call site to the target method handle
-     * @throws WrongMethodTypeException if the hook cannot be invoked on the required arguments,
-     *         or if the target returned by the hook is not of the given {@code targetType}
-     * @throws NullPointerException if the hook returns a null value
-     * @throws ClassCastException if the hook returns something other than a {@code MethodHandle}
-     * @throws Throwable anything else thrown by the hook function
-     */
-    /*package-private*/
-    CallSite(MethodType targetType, MethodHandle createTargetHook) throws Throwable {
-        this(targetType);
-        ConstantCallSite selfCCS = (ConstantCallSite) this;
-        MethodHandle boundTarget = (MethodHandle) createTargetHook.invokeWithArguments(selfCCS);
-        checkTargetChange(this.target, boundTarget);
-        this.target = boundTarget;
-
-        // Android-changed: Using initializer method for GET_TARGET
-        // rather than complex static initializer.
-        initializeGetTarget();
-    }
-
-    /**
-     * Returns the type of this call site's target.
-     * Although targets may change, any call site's type is permanent, and can never change to an unequal type.
-     * The {@code setTarget} method enforces this invariant by refusing any new target that does
-     * not have the previous target's type.
-     * @return the type of the current target, which is also the type of any future target
-     */
-    public MethodType type() {
-        // warning:  do not call getTarget here, because CCS.getTarget can throw IllegalStateException
-        return target.type();
-    }
-
-    /**
-     * Returns the target method of the call site, according to the
-     * behavior defined by this call site's specific class.
-     * The immediate subclasses of {@code CallSite} document the
-     * class-specific behaviors of this method.
-     *
-     * @return the current linkage state of the call site, its target method handle
-     * @see ConstantCallSite
-     * @see VolatileCallSite
-     * @see #setTarget
-     * @see ConstantCallSite#getTarget
-     * @see MutableCallSite#getTarget
-     * @see VolatileCallSite#getTarget
-     */
     public abstract MethodHandle getTarget();
 
-    /**
-     * Updates the target method of this call site, according to the
-     * behavior defined by this call site's specific class.
-     * The immediate subclasses of {@code CallSite} document the
-     * class-specific behaviors of this method.
-     * <p>
-     * The type of the new target must be {@linkplain MethodType#equals equal to}
-     * the type of the old target.
-     *
-     * @param newTarget the new target
-     * @throws NullPointerException if the proposed new target is null
-     * @throws WrongMethodTypeException if the proposed new target
-     *         has a method type that differs from the previous target
-     * @see CallSite#getTarget
-     * @see ConstantCallSite#setTarget
-     * @see MutableCallSite#setTarget
-     * @see VolatileCallSite#setTarget
-     */
     public abstract void setTarget(MethodHandle newTarget);
 
-    void checkTargetChange(MethodHandle oldTarget, MethodHandle newTarget) {
-        MethodType oldType = oldTarget.type();
-        MethodType newType = newTarget.type();  // null check!
-        if (!newType.equals(oldType))
-            throw wrongTargetType(newTarget, oldType);
-    }
-
-    private static WrongMethodTypeException wrongTargetType(MethodHandle target, MethodType type) {
-        return new WrongMethodTypeException(String.valueOf(target)+" should be of type "+type);
-    }
-
-    /**
-     * Produces a method handle equivalent to an invokedynamic instruction
-     * which has been linked to this call site.
-     * <p>
-     * This method is equivalent to the following code:
-     * <blockquote><pre>{@code
-     * MethodHandle getTarget, invoker, result;
-     * getTarget = MethodHandles.publicLookup().bind(this, "getTarget", MethodType.methodType(MethodHandle.class));
-     * invoker = MethodHandles.exactInvoker(this.type());
-     * result = MethodHandles.foldArguments(invoker, getTarget)
-     * }</pre></blockquote>
-     *
-     * @return a method handle which always invokes this call site's current target
-     */
     public abstract MethodHandle dynamicInvoker();
 
-    /*non-public*/ MethodHandle makeDynamicInvoker() {
-        // Android-changed: Use bindTo() rather than bindArgumentL() (not implemented).
-        MethodHandle getTarget = GET_TARGET.bindTo(this);
-        MethodHandle invoker = MethodHandles.exactInvoker(this.type());
-        return MethodHandles.foldArguments(invoker, getTarget);
-    }
-
-    // Android-changed: no longer final. GET_TARGET assigned in initializeGetTarget().
-    private static MethodHandle GET_TARGET = null;
-
-    private void initializeGetTarget() {
-        // Android-changed: moved from static initializer for
-        // GET_TARGET to avoid issues with running early. Called from
-        // constructors. CallSite creation is not performance critical.
-        synchronized (CallSite.class) {
-            if (GET_TARGET == null) {
-                try {
-                    GET_TARGET = IMPL_LOOKUP.
-                            findVirtual(CallSite.class, "getTarget",
-                                        MethodType.methodType(MethodHandle.class));
-                } catch (ReflectiveOperationException e) {
-                    throw new InternalError(e);
-                }
-            }
-        }
-    }
-
-    // Android-changed: not used.
-    // /** This guy is rolled into the default target if a MethodType is supplied to the constructor. */
-    // /*package-private*/
-    // static Empty uninitializedCallSite() {
-    //     throw new IllegalStateException("uninitialized call site");
-    // }
-
-    // unsafe stuff:
-    private static final long TARGET_OFFSET;
-    static {
-        try {
-            TARGET_OFFSET = UNSAFE.objectFieldOffset(CallSite.class.getDeclaredField("target"));
-        } catch (Exception ex) { throw new Error(ex); }
-    }
-
-    /*package-private*/
-    void setTargetNormal(MethodHandle newTarget) {
-        // Android-changed: Set value directly.
-        // MethodHandleNatives.setCallSiteTargetNormal(this, newTarget);
-        target = newTarget;
-    }
-    /*package-private*/
-    MethodHandle getTargetVolatile() {
-        return (MethodHandle) UNSAFE.getObjectVolatile(this, TARGET_OFFSET);
-    }
-    /*package-private*/
-    void setTargetVolatile(MethodHandle newTarget) {
-        // Android-changed: Set value directly.
-        // MethodHandleNatives.setCallSiteTargetVolatile(this, newTarget);
-        UNSAFE.putObjectVolatile(this, TARGET_OFFSET, newTarget);
-    }
-
-    // Android-changed: not used.
-    // this implements the upcall from the JVM, MethodHandleNatives.makeDynamicCallSite:
-    // static CallSite makeSite(MethodHandle bootstrapMethod,
-    //                          // Callee information:
-    //                          String name, MethodType type,
-    //                          // Extra arguments for BSM, if any:
-    //                          Object info,
-    //                          // Caller information:
-    //                          Class<?> callerClass) {
-    //     MethodHandles.Lookup caller = IMPL_LOOKUP.in(callerClass);
-    //     CallSite site;
-    //     try {
-    //         Object binding;
-    //         info = maybeReBox(info);
-    //         if (info == null) {
-    //             binding = bootstrapMethod.invoke(caller, name, type);
-    //         } else if (!info.getClass().isArray()) {
-    //             binding = bootstrapMethod.invoke(caller, name, type, info);
-    //         } else {
-    //             Object[] argv = (Object[]) info;
-    //             maybeReBoxElements(argv);
-    //             switch (argv.length) {
-    //             case 0:
-    //                 binding = bootstrapMethod.invoke(caller, name, type);
-    //                 break;
-    //             case 1:
-    //                 binding = bootstrapMethod.invoke(caller, name, type,
-    //                                                  argv[0]);
-    //                 break;
-    //             case 2:
-    //                 binding = bootstrapMethod.invoke(caller, name, type,
-    //                                                  argv[0], argv[1]);
-    //                 break;
-    //             case 3:
-    //                 binding = bootstrapMethod.invoke(caller, name, type,
-    //                                                  argv[0], argv[1], argv[2]);
-    //                 break;
-    //             case 4:
-    //                 binding = bootstrapMethod.invoke(caller, name, type,
-    //                                                  argv[0], argv[1], argv[2], argv[3]);
-    //                 break;
-    //             case 5:
-    //                 binding = bootstrapMethod.invoke(caller, name, type,
-    //                                                  argv[0], argv[1], argv[2], argv[3], argv[4]);
-    //                 break;
-    //             case 6:
-    //                 binding = bootstrapMethod.invoke(caller, name, type,
-    //                                                  argv[0], argv[1], argv[2], argv[3], argv[4], argv[5]);
-    //                 break;
-    //             default:
-    //                 final int NON_SPREAD_ARG_COUNT = 3;  // (caller, name, type)
-    //                 if (NON_SPREAD_ARG_COUNT + argv.length > MethodType.MAX_MH_ARITY)
-    //                     throw new BootstrapMethodError("too many bootstrap method arguments");
-    //                 MethodType bsmType = bootstrapMethod.type();
-    //                 MethodType invocationType = MethodType.genericMethodType(NON_SPREAD_ARG_COUNT + argv.length);
-    //                 MethodHandle typedBSM = bootstrapMethod.asType(invocationType);
-    //                 MethodHandle spreader = invocationType.invokers().spreadInvoker(NON_SPREAD_ARG_COUNT);
-    //                 binding = spreader.invokeExact(typedBSM, (Object)caller, (Object)name, (Object)type, argv);
-    //             }
-    //         }
-    //         //System.out.println("BSM for "+name+type+" => "+binding);
-    //         if (binding instanceof CallSite) {
-    //             site = (CallSite) binding;
-    //         }  else {
-    //             throw new ClassCastException("bootstrap method failed to produce a CallSite");
-    //         }
-    //         if (!site.getTarget().type().equals(type))
-    //             throw wrongTargetType(site.getTarget(), type);
-    //     } catch (Throwable ex) {
-    //         BootstrapMethodError bex;
-    //         if (ex instanceof BootstrapMethodError)
-    //             bex = (BootstrapMethodError) ex;
-    //         else
-    //             bex = new BootstrapMethodError("call site initialization exception", ex);
-    //         throw bex;
-    //     }
-    //     return site;
-    // }
-
-    // private static Object maybeReBox(Object x) {
-    //     if (x instanceof Integer) {
-    //         int xi = (int) x;
-    //         if (xi == (byte) xi)
-    //             x = xi;  // must rebox; see JLS 5.1.7
-    //     }
-    //     return x;
-    // }
-    // private static void maybeReBoxElements(Object[] xa) {
-    //     for (int i = 0; i < xa.length; i++) {
-    //         xa[i] = maybeReBox(xa[i]);
-    //     }
-    // }
 }
diff --git a/java/lang/invoke/MethodHandle.java b/java/lang/invoke/MethodHandle.java
index af3db10..159f9dd 100644
--- a/java/lang/invoke/MethodHandle.java
+++ b/java/lang/invoke/MethodHandle.java
@@ -25,1353 +25,28 @@
 
 package java.lang.invoke;
 
-
-import dalvik.system.EmulatedStackFrame;
-
-import static java.lang.invoke.MethodHandleStatics.*;
-
-/**
- * A method handle is a typed, directly executable reference to an underlying method,
- * constructor, field, or similar low-level operation, with optional
- * transformations of arguments or return values.
- * These transformations are quite general, and include such patterns as
- * {@linkplain #asType conversion},
- * {@linkplain #bindTo insertion},
- * {@linkplain java.lang.invoke.MethodHandles#dropArguments deletion},
- * and {@linkplain java.lang.invoke.MethodHandles#filterArguments substitution}.
- *
- * <h1>Method handle contents</h1>
- * Method handles are dynamically and strongly typed according to their parameter and return types.
- * They are not distinguished by the name or the defining class of their underlying methods.
- * A method handle must be invoked using a symbolic type descriptor which matches
- * the method handle's own {@linkplain #type type descriptor}.
- * <p>
- * Every method handle reports its type descriptor via the {@link #type type} accessor.
- * This type descriptor is a {@link java.lang.invoke.MethodType MethodType} object,
- * whose structure is a series of classes, one of which is
- * the return type of the method (or {@code void.class} if none).
- * <p>
- * A method handle's type controls the types of invocations it accepts,
- * and the kinds of transformations that apply to it.
- * <p>
- * A method handle contains a pair of special invoker methods
- * called {@link #invokeExact invokeExact} and {@link #invoke invoke}.
- * Both invoker methods provide direct access to the method handle's
- * underlying method, constructor, field, or other operation,
- * as modified by transformations of arguments and return values.
- * Both invokers accept calls which exactly match the method handle's own type.
- * The plain, inexact invoker also accepts a range of other call types.
- * <p>
- * Method handles are immutable and have no visible state.
- * Of course, they can be bound to underlying methods or data which exhibit state.
- * With respect to the Java Memory Model, any method handle will behave
- * as if all of its (internal) fields are final variables.  This means that any method
- * handle made visible to the application will always be fully formed.
- * This is true even if the method handle is published through a shared
- * variable in a data race.
- * <p>
- * Method handles cannot be subclassed by the user.
- * Implementations may (or may not) create internal subclasses of {@code MethodHandle}
- * which may be visible via the {@link java.lang.Object#getClass Object.getClass}
- * operation.  The programmer should not draw conclusions about a method handle
- * from its specific class, as the method handle class hierarchy (if any)
- * may change from time to time or across implementations from different vendors.
- *
- * <h1>Method handle compilation</h1>
- * A Java method call expression naming {@code invokeExact} or {@code invoke}
- * can invoke a method handle from Java source code.
- * From the viewpoint of source code, these methods can take any arguments
- * and their result can be cast to any return type.
- * Formally this is accomplished by giving the invoker methods
- * {@code Object} return types and variable arity {@code Object} arguments,
- * but they have an additional quality called <em>signature polymorphism</em>
- * which connects this freedom of invocation directly to the JVM execution stack.
- * <p>
- * As is usual with virtual methods, source-level calls to {@code invokeExact}
- * and {@code invoke} compile to an {@code invokevirtual} instruction.
- * More unusually, the compiler must record the actual argument types,
- * and may not perform method invocation conversions on the arguments.
- * Instead, it must push them on the stack according to their own unconverted types.
- * The method handle object itself is pushed on the stack before the arguments.
- * The compiler then calls the method handle with a symbolic type descriptor which
- * describes the argument and return types.
- * <p>
- * To issue a complete symbolic type descriptor, the compiler must also determine
- * the return type.  This is based on a cast on the method invocation expression,
- * if there is one, or else {@code Object} if the invocation is an expression
- * or else {@code void} if the invocation is a statement.
- * The cast may be to a primitive type (but not {@code void}).
- * <p>
- * As a corner case, an uncasted {@code null} argument is given
- * a symbolic type descriptor of {@code java.lang.Void}.
- * The ambiguity with the type {@code Void} is harmless, since there are no references of type
- * {@code Void} except the null reference.
- *
- * <h1>Method handle invocation</h1>
- * The first time a {@code invokevirtual} instruction is executed
- * it is linked, by symbolically resolving the names in the instruction
- * and verifying that the method call is statically legal.
- * This is true of calls to {@code invokeExact} and {@code invoke}.
- * In this case, the symbolic type descriptor emitted by the compiler is checked for
- * correct syntax and names it contains are resolved.
- * Thus, an {@code invokevirtual} instruction which invokes
- * a method handle will always link, as long
- * as the symbolic type descriptor is syntactically well-formed
- * and the types exist.
- * <p>
- * When the {@code invokevirtual} is executed after linking,
- * the receiving method handle's type is first checked by the JVM
- * to ensure that it matches the symbolic type descriptor.
- * If the type match fails, it means that the method which the
- * caller is invoking is not present on the individual
- * method handle being invoked.
- * <p>
- * In the case of {@code invokeExact}, the type descriptor of the invocation
- * (after resolving symbolic type names) must exactly match the method type
- * of the receiving method handle.
- * In the case of plain, inexact {@code invoke}, the resolved type descriptor
- * must be a valid argument to the receiver's {@link #asType asType} method.
- * Thus, plain {@code invoke} is more permissive than {@code invokeExact}.
- * <p>
- * After type matching, a call to {@code invokeExact} directly
- * and immediately invoke the method handle's underlying method
- * (or other behavior, as the case may be).
- * <p>
- * A call to plain {@code invoke} works the same as a call to
- * {@code invokeExact}, if the symbolic type descriptor specified by the caller
- * exactly matches the method handle's own type.
- * If there is a type mismatch, {@code invoke} attempts
- * to adjust the type of the receiving method handle,
- * as if by a call to {@link #asType asType},
- * to obtain an exactly invokable method handle {@code M2}.
- * This allows a more powerful negotiation of method type
- * between caller and callee.
- * <p>
- * (<em>Note:</em> The adjusted method handle {@code M2} is not directly observable,
- * and implementations are therefore not required to materialize it.)
- *
- * <h1>Invocation checking</h1>
- * In typical programs, method handle type matching will usually succeed.
- * But if a match fails, the JVM will throw a {@link WrongMethodTypeException},
- * either directly (in the case of {@code invokeExact}) or indirectly as if
- * by a failed call to {@code asType} (in the case of {@code invoke}).
- * <p>
- * Thus, a method type mismatch which might show up as a linkage error
- * in a statically typed program can show up as
- * a dynamic {@code WrongMethodTypeException}
- * in a program which uses method handles.
- * <p>
- * Because method types contain "live" {@code Class} objects,
- * method type matching takes into account both types names and class loaders.
- * Thus, even if a method handle {@code M} is created in one
- * class loader {@code L1} and used in another {@code L2},
- * method handle calls are type-safe, because the caller's symbolic type
- * descriptor, as resolved in {@code L2},
- * is matched against the original callee method's symbolic type descriptor,
- * as resolved in {@code L1}.
- * The resolution in {@code L1} happens when {@code M} is created
- * and its type is assigned, while the resolution in {@code L2} happens
- * when the {@code invokevirtual} instruction is linked.
- * <p>
- * Apart from the checking of type descriptors,
- * a method handle's capability to call its underlying method is unrestricted.
- * If a method handle is formed on a non-public method by a class
- * that has access to that method, the resulting handle can be used
- * in any place by any caller who receives a reference to it.
- * <p>
- * Unlike with the Core Reflection API, where access is checked every time
- * a reflective method is invoked,
- * method handle access checking is performed
- * <a href="MethodHandles.Lookup.html#access">when the method handle is created</a>.
- * In the case of {@code ldc} (see below), access checking is performed as part of linking
- * the constant pool entry underlying the constant method handle.
- * <p>
- * Thus, handles to non-public methods, or to methods in non-public classes,
- * should generally be kept secret.
- * They should not be passed to untrusted code unless their use from
- * the untrusted code would be harmless.
- *
- * <h1>Method handle creation</h1>
- * Java code can create a method handle that directly accesses
- * any method, constructor, or field that is accessible to that code.
- * This is done via a reflective, capability-based API called
- * {@link java.lang.invoke.MethodHandles.Lookup MethodHandles.Lookup}
- * For example, a static method handle can be obtained
- * from {@link java.lang.invoke.MethodHandles.Lookup#findStatic Lookup.findStatic}.
- * There are also conversion methods from Core Reflection API objects,
- * such as {@link java.lang.invoke.MethodHandles.Lookup#unreflect Lookup.unreflect}.
- * <p>
- * Like classes and strings, method handles that correspond to accessible
- * fields, methods, and constructors can also be represented directly
- * in a class file's constant pool as constants to be loaded by {@code ldc} bytecodes.
- * A new type of constant pool entry, {@code CONSTANT_MethodHandle},
- * refers directly to an associated {@code CONSTANT_Methodref},
- * {@code CONSTANT_InterfaceMethodref}, or {@code CONSTANT_Fieldref}
- * constant pool entry.
- * (For full details on method handle constants,
- * see sections 4.4.8 and 5.4.3.5 of the Java Virtual Machine Specification.)
- * <p>
- * Method handles produced by lookups or constant loads from methods or
- * constructors with the variable arity modifier bit ({@code 0x0080})
- * have a corresponding variable arity, as if they were defined with
- * the help of {@link #asVarargsCollector asVarargsCollector}.
- * <p>
- * A method reference may refer either to a static or non-static method.
- * In the non-static case, the method handle type includes an explicit
- * receiver argument, prepended before any other arguments.
- * In the method handle's type, the initial receiver argument is typed
- * according to the class under which the method was initially requested.
- * (E.g., if a non-static method handle is obtained via {@code ldc},
- * the type of the receiver is the class named in the constant pool entry.)
- * <p>
- * Method handle constants are subject to the same link-time access checks
- * their corresponding bytecode instructions, and the {@code ldc} instruction
- * will throw corresponding linkage errors if the bytecode behaviors would
- * throw such errors.
- * <p>
- * As a corollary of this, access to protected members is restricted
- * to receivers only of the accessing class, or one of its subclasses,
- * and the accessing class must in turn be a subclass (or package sibling)
- * of the protected member's defining class.
- * If a method reference refers to a protected non-static method or field
- * of a class outside the current package, the receiver argument will
- * be narrowed to the type of the accessing class.
- * <p>
- * When a method handle to a virtual method is invoked, the method is
- * always looked up in the receiver (that is, the first argument).
- * <p>
- * A non-virtual method handle to a specific virtual method implementation
- * can also be created.  These do not perform virtual lookup based on
- * receiver type.  Such a method handle simulates the effect of
- * an {@code invokespecial} instruction to the same method.
- *
- * <h1>Usage examples</h1>
- * Here are some examples of usage:
- * <blockquote><pre>{@code
-Object x, y; String s; int i;
-MethodType mt; MethodHandle mh;
-MethodHandles.Lookup lookup = MethodHandles.lookup();
-// mt is (char,char)String
-mt = MethodType.methodType(String.class, char.class, char.class);
-mh = lookup.findVirtual(String.class, "replace", mt);
-s = (String) mh.invokeExact("daddy",'d','n');
-// invokeExact(Ljava/lang/String;CC)Ljava/lang/String;
-assertEquals(s, "nanny");
-// weakly typed invocation (using MHs.invoke)
-s = (String) mh.invokeWithArguments("sappy", 'p', 'v');
-assertEquals(s, "savvy");
-// mt is (Object[])List
-mt = MethodType.methodType(java.util.List.class, Object[].class);
-mh = lookup.findStatic(java.util.Arrays.class, "asList", mt);
-assert(mh.isVarargsCollector());
-x = mh.invoke("one", "two");
-// invoke(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
-assertEquals(x, java.util.Arrays.asList("one","two"));
-// mt is (Object,Object,Object)Object
-mt = MethodType.genericMethodType(3);
-mh = mh.asType(mt);
-x = mh.invokeExact((Object)1, (Object)2, (Object)3);
-// invokeExact(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
-assertEquals(x, java.util.Arrays.asList(1,2,3));
-// mt is ()int
-mt = MethodType.methodType(int.class);
-mh = lookup.findVirtual(java.util.List.class, "size", mt);
-i = (int) mh.invokeExact(java.util.Arrays.asList(1,2,3));
-// invokeExact(Ljava/util/List;)I
-assert(i == 3);
-mt = MethodType.methodType(void.class, String.class);
-mh = lookup.findVirtual(java.io.PrintStream.class, "println", mt);
-mh.invokeExact(System.out, "Hello, world.");
-// invokeExact(Ljava/io/PrintStream;Ljava/lang/String;)V
- * }</pre></blockquote>
- * Each of the above calls to {@code invokeExact} or plain {@code invoke}
- * generates a single invokevirtual instruction with
- * the symbolic type descriptor indicated in the following comment.
- * In these examples, the helper method {@code assertEquals} is assumed to
- * be a method which calls {@link java.util.Objects#equals(Object,Object) Objects.equals}
- * on its arguments, and asserts that the result is true.
- *
- * <h1>Exceptions</h1>
- * The methods {@code invokeExact} and {@code invoke} are declared
- * to throw {@link java.lang.Throwable Throwable},
- * which is to say that there is no static restriction on what a method handle
- * can throw.  Since the JVM does not distinguish between checked
- * and unchecked exceptions (other than by their class, of course),
- * there is no particular effect on bytecode shape from ascribing
- * checked exceptions to method handle invocations.  But in Java source
- * code, methods which perform method handle calls must either explicitly
- * throw {@code Throwable}, or else must catch all
- * throwables locally, rethrowing only those which are legal in the context,
- * and wrapping ones which are illegal.
- *
- * <h1><a name="sigpoly"></a>Signature polymorphism</h1>
- * The unusual compilation and linkage behavior of
- * {@code invokeExact} and plain {@code invoke}
- * is referenced by the term <em>signature polymorphism</em>.
- * As defined in the Java Language Specification,
- * a signature polymorphic method is one which can operate with
- * any of a wide range of call signatures and return types.
- * <p>
- * In source code, a call to a signature polymorphic method will
- * compile, regardless of the requested symbolic type descriptor.
- * As usual, the Java compiler emits an {@code invokevirtual}
- * instruction with the given symbolic type descriptor against the named method.
- * The unusual part is that the symbolic type descriptor is derived from
- * the actual argument and return types, not from the method declaration.
- * <p>
- * When the JVM processes bytecode containing signature polymorphic calls,
- * it will successfully link any such call, regardless of its symbolic type descriptor.
- * (In order to retain type safety, the JVM will guard such calls with suitable
- * dynamic type checks, as described elsewhere.)
- * <p>
- * Bytecode generators, including the compiler back end, are required to emit
- * untransformed symbolic type descriptors for these methods.
- * Tools which determine symbolic linkage are required to accept such
- * untransformed descriptors, without reporting linkage errors.
- *
- * <h1>Interoperation between method handles and the Core Reflection API</h1>
- * Using factory methods in the {@link java.lang.invoke.MethodHandles.Lookup Lookup} API,
- * any class member represented by a Core Reflection API object
- * can be converted to a behaviorally equivalent method handle.
- * For example, a reflective {@link java.lang.reflect.Method Method} can
- * be converted to a method handle using
- * {@link java.lang.invoke.MethodHandles.Lookup#unreflect Lookup.unreflect}.
- * The resulting method handles generally provide more direct and efficient
- * access to the underlying class members.
- * <p>
- * As a special case,
- * when the Core Reflection API is used to view the signature polymorphic
- * methods {@code invokeExact} or plain {@code invoke} in this class,
- * they appear as ordinary non-polymorphic methods.
- * Their reflective appearance, as viewed by
- * {@link java.lang.Class#getDeclaredMethod Class.getDeclaredMethod},
- * is unaffected by their special status in this API.
- * For example, {@link java.lang.reflect.Method#getModifiers Method.getModifiers}
- * will report exactly those modifier bits required for any similarly
- * declared method, including in this case {@code native} and {@code varargs} bits.
- * <p>
- * As with any reflected method, these methods (when reflected) may be
- * invoked via {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}.
- * However, such reflective calls do not result in method handle invocations.
- * Such a call, if passed the required argument
- * (a single one, of type {@code Object[]}), will ignore the argument and
- * will throw an {@code UnsupportedOperationException}.
- * <p>
- * Since {@code invokevirtual} instructions can natively
- * invoke method handles under any symbolic type descriptor, this reflective view conflicts
- * with the normal presentation of these methods via bytecodes.
- * Thus, these two native methods, when reflectively viewed by
- * {@code Class.getDeclaredMethod}, may be regarded as placeholders only.
- * <p>
- * In order to obtain an invoker method for a particular type descriptor,
- * use {@link java.lang.invoke.MethodHandles#exactInvoker MethodHandles.exactInvoker},
- * or {@link java.lang.invoke.MethodHandles#invoker MethodHandles.invoker}.
- * The {@link java.lang.invoke.MethodHandles.Lookup#findVirtual Lookup.findVirtual}
- * API is also able to return a method handle
- * to call {@code invokeExact} or plain {@code invoke},
- * for any specified type descriptor .
- *
- * <h1>Interoperation between method handles and Java generics</h1>
- * A method handle can be obtained on a method, constructor, or field
- * which is declared with Java generic types.
- * As with the Core Reflection API, the type of the method handle
- * will constructed from the erasure of the source-level type.
- * When a method handle is invoked, the types of its arguments
- * or the return value cast type may be generic types or type instances.
- * If this occurs, the compiler will replace those
- * types by their erasures when it constructs the symbolic type descriptor
- * for the {@code invokevirtual} instruction.
- * <p>
- * Method handles do not represent
- * their function-like types in terms of Java parameterized (generic) types,
- * because there are three mismatches between function-like types and parameterized
- * Java types.
- * <ul>
- * <li>Method types range over all possible arities,
- * from no arguments to up to the  <a href="MethodHandle.html#maxarity">maximum number</a> of allowed arguments.
- * Generics are not variadic, and so cannot represent this.</li>
- * <li>Method types can specify arguments of primitive types,
- * which Java generic types cannot range over.</li>
- * <li>Higher order functions over method handles (combinators) are
- * often generic across a wide range of function types, including
- * those of multiple arities.  It is impossible to represent such
- * genericity with a Java type parameter.</li>
- * </ul>
- *
- * <h1><a name="maxarity"></a>Arity limits</h1>
- * The JVM imposes on all methods and constructors of any kind an absolute
- * limit of 255 stacked arguments.  This limit can appear more restrictive
- * in certain cases:
- * <ul>
- * <li>A {@code long} or {@code double} argument counts (for purposes of arity limits) as two argument slots.
- * <li>A non-static method consumes an extra argument for the object on which the method is called.
- * <li>A constructor consumes an extra argument for the object which is being constructed.
- * <li>Since a method handle&rsquo;s {@code invoke} method (or other signature-polymorphic method) is non-virtual,
- *     it consumes an extra argument for the method handle itself, in addition to any non-virtual receiver object.
- * </ul>
- * These limits imply that certain method handles cannot be created, solely because of the JVM limit on stacked arguments.
- * For example, if a static JVM method accepts exactly 255 arguments, a method handle cannot be created for it.
- * Attempts to create method handles with impossible method types lead to an {@link IllegalArgumentException}.
- * In particular, a method handle&rsquo;s type must not have an arity of the exact maximum 255.
- *
- * @see MethodType
- * @see MethodHandles
- * @author John Rose, JSR 292 EG
- */
 public abstract class MethodHandle {
-    // Android-changed:
-    //
-    // static { MethodHandleImpl.initStatics(); }
-    //
-    // LambdaForm and customizationCount are currently unused in our implementation
-    // and will be substituted with appropriate implementation / delegate classes.
-    //
-    // /*private*/ final LambdaForm form;
-    // form is not private so that invokers can easily fetch it
-    // /*non-public*/ byte customizationCount;
-    // customizationCount should be accessible from invokers
 
+    public MethodType type() { return null; }
 
-    /**
-     * Internal marker interface which distinguishes (to the Java compiler)
-     * those methods which are <a href="MethodHandle.html#sigpoly">signature polymorphic</a>.
-     *
-     * @hide
-     */
-    @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD})
-    @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
-    public @interface PolymorphicSignature { }
+    public final Object invokeExact(Object... args) throws Throwable { return null; }
 
-    /**
-     * The type of this method handle, this corresponds to the exact type of the method
-     * being invoked.
-     */
-    private final MethodType type;
+    public final Object invoke(Object... args) throws Throwable { return null; }
 
-    /**
-     * The nominal type of this method handle, will be non-null if a method handle declares
-     * a different type from its "real" type, which is either the type of the method being invoked
-     * or the type of the emulated stackframe expected by an underyling adapter.
-     */
-    private MethodType nominalType;
+    public Object invokeWithArguments(Object... arguments) throws Throwable { return null; }
 
-    /**
-     * The spread invoker associated with this type with zero trailing arguments.
-     * This is used to speed up invokeWithArguments.
-     */
-    private MethodHandle cachedSpreadInvoker;
+    public Object invokeWithArguments(java.util.List<?> arguments) throws Throwable { return null; }
 
-    /**
-     * The INVOKE* constants and SGET/SPUT and IGET/IPUT constants specify the behaviour of this
-     * method handle with respect to the ArtField* or the ArtMethod* that it operates on. These
-     * behaviours are equivalent to the dex bytecode behaviour on the respective method_id or
-     * field_id in the equivalent instruction.
-     *
-     * INVOKE_TRANSFORM is a special type of handle which doesn't encode any dex bytecode behaviour,
-     * instead it transforms the list of input arguments or performs other higher order operations
-     * before (optionally) delegating to another method handle.
-     *
-     * INVOKE_CALLSITE_TRANSFORM is a variation on INVOKE_TRANSFORM where the method type of
-     * a MethodHandle dynamically varies based on the callsite. This is used by
-     * the VarargsCollector implementation which places any number of trailing arguments
-     * into an array before invoking an arity method. The "any number of trailing arguments" means
-     * it would otherwise generate WrongMethodTypeExceptions as the callsite method type and
-     * VarargsCollector method type appear incompatible.
-     */
+    public MethodHandle asType(MethodType newType) { return null; }
 
-    /** @hide */ public static final int INVOKE_VIRTUAL = 0;
-    /** @hide */ public static final int INVOKE_SUPER = 1;
-    /** @hide */ public static final int INVOKE_DIRECT = 2;
-    /** @hide */ public static final int INVOKE_STATIC = 3;
-    /** @hide */ public static final int INVOKE_INTERFACE = 4;
-    /** @hide */ public static final int INVOKE_TRANSFORM = 5;
-    /** @hide */ public static final int INVOKE_CALLSITE_TRANSFORM = 6;
-    /** @hide */ public static final int IGET = 7;
-    /** @hide */ public static final int IPUT = 8;
-    /** @hide */ public static final int SGET = 9;
-    /** @hide */ public static final int SPUT = 10;
+    public MethodHandle asCollector(Class<?> arrayType, int arrayLength) { return null; }
 
-    // The kind of this method handle (used by the runtime). This is one of the INVOKE_*
-    // constants or SGET/SPUT, IGET/IPUT.
-    /** @hide */ protected final int handleKind;
+    public MethodHandle asVarargsCollector(Class<?> arrayType) { return null; }
 
-    // The ArtMethod* or ArtField* associated with this method handle (used by the runtime).
-    /** @hide */ protected final long artFieldOrMethod;
+    public boolean isVarargsCollector() { return false; }
 
-    /** @hide */
-    protected MethodHandle(long artFieldOrMethod, int handleKind, MethodType type) {
-        this.artFieldOrMethod = artFieldOrMethod;
-        this.handleKind = handleKind;
-        this.type = type;
-    }
+    public MethodHandle asFixedArity() { return null; }
 
-    /**
-     * Reports the type of this method handle.
-     * Every invocation of this method handle via {@code invokeExact} must exactly match this type.
-     * @return the method handle type
-     */
-    public MethodType type() {
-        if (nominalType != null) {
-            return nominalType;
-        }
+    public MethodHandle bindTo(Object x) { return null; }
 
-        return type;
-    }
-
-    /**
-     * Invokes the method handle, allowing any caller type descriptor, but requiring an exact type match.
-     * The symbolic type descriptor at the call site of {@code invokeExact} must
-     * exactly match this method handle's {@link #type type}.
-     * No conversions are allowed on arguments or return values.
-     * <p>
-     * When this method is observed via the Core Reflection API,
-     * it will appear as a single native method, taking an object array and returning an object.
-     * If this native method is invoked directly via
-     * {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}, via JNI,
-     * or indirectly via {@link java.lang.invoke.MethodHandles.Lookup#unreflect Lookup.unreflect},
-     * it will throw an {@code UnsupportedOperationException}.
-     * @param args the signature-polymorphic parameter list, statically represented using varargs
-     * @return the signature-polymorphic result, statically represented using {@code Object}
-     * @throws WrongMethodTypeException if the target's type is not identical with the caller's symbolic type descriptor
-     * @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call
-     */
-    public final native @PolymorphicSignature Object invokeExact(Object... args) throws Throwable;
-
-    /**
-     * Invokes the method handle, allowing any caller type descriptor,
-     * and optionally performing conversions on arguments and return values.
-     * <p>
-     * If the call site's symbolic type descriptor exactly matches this method handle's {@link #type type},
-     * the call proceeds as if by {@link #invokeExact invokeExact}.
-     * <p>
-     * Otherwise, the call proceeds as if this method handle were first
-     * adjusted by calling {@link #asType asType} to adjust this method handle
-     * to the required type, and then the call proceeds as if by
-     * {@link #invokeExact invokeExact} on the adjusted method handle.
-     * <p>
-     * There is no guarantee that the {@code asType} call is actually made.
-     * If the JVM can predict the results of making the call, it may perform
-     * adaptations directly on the caller's arguments,
-     * and call the target method handle according to its own exact type.
-     * <p>
-     * The resolved type descriptor at the call site of {@code invoke} must
-     * be a valid argument to the receivers {@code asType} method.
-     * In particular, the caller must specify the same argument arity
-     * as the callee's type,
-     * if the callee is not a {@linkplain #asVarargsCollector variable arity collector}.
-     * <p>
-     * When this method is observed via the Core Reflection API,
-     * it will appear as a single native method, taking an object array and returning an object.
-     * If this native method is invoked directly via
-     * {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}, via JNI,
-     * or indirectly via {@link java.lang.invoke.MethodHandles.Lookup#unreflect Lookup.unreflect},
-     * it will throw an {@code UnsupportedOperationException}.
-     * @param args the signature-polymorphic parameter list, statically represented using varargs
-     * @return the signature-polymorphic result, statically represented using {@code Object}
-     * @throws WrongMethodTypeException if the target's type cannot be adjusted to the caller's symbolic type descriptor
-     * @throws ClassCastException if the target's type can be adjusted to the caller, but a reference cast fails
-     * @throws Throwable anything thrown by the underlying method propagates unchanged through the method handle call
-     */
-    public final native @PolymorphicSignature Object invoke(Object... args) throws Throwable;
-
-    // Android-changed: Removed implementation details.
-    //
-    // /*non-public*/ final native @PolymorphicSignature Object invokeBasic(Object... args)
-    // /*non-public*/ static native @PolymorphicSignature Object linkToVirtual(Object... args)
-    // /*non-public*/ static native @PolymorphicSignature Object linkToStatic(Object... args)
-    // /*non-public*/ static native @PolymorphicSignature Object linkToSpecial(Object... args)
-    // /*non-public*/ static native @PolymorphicSignature Object linkToInterface(Object... args)
-
-    /**
-     * Performs a variable arity invocation, passing the arguments in the given list
-     * to the method handle, as if via an inexact {@link #invoke invoke} from a call site
-     * which mentions only the type {@code Object}, and whose arity is the length
-     * of the argument list.
-     * <p>
-     * Specifically, execution proceeds as if by the following steps,
-     * although the methods are not guaranteed to be called if the JVM
-     * can predict their effects.
-     * <ul>
-     * <li>Determine the length of the argument array as {@code N}.
-     *     For a null reference, {@code N=0}. </li>
-     * <li>Determine the general type {@code TN} of {@code N} arguments as
-     *     as {@code TN=MethodType.genericMethodType(N)}.</li>
-     * <li>Force the original target method handle {@code MH0} to the
-     *     required type, as {@code MH1 = MH0.asType(TN)}. </li>
-     * <li>Spread the array into {@code N} separate arguments {@code A0, ...}. </li>
-     * <li>Invoke the type-adjusted method handle on the unpacked arguments:
-     *     MH1.invokeExact(A0, ...). </li>
-     * <li>Take the return value as an {@code Object} reference. </li>
-     * </ul>
-     * <p>
-     * Because of the action of the {@code asType} step, the following argument
-     * conversions are applied as necessary:
-     * <ul>
-     * <li>reference casting
-     * <li>unboxing
-     * <li>widening primitive conversions
-     * </ul>
-     * <p>
-     * The result returned by the call is boxed if it is a primitive,
-     * or forced to null if the return type is void.
-     * <p>
-     * This call is equivalent to the following code:
-     * <blockquote><pre>{@code
-     * MethodHandle invoker = MethodHandles.spreadInvoker(this.type(), 0);
-     * Object result = invoker.invokeExact(this, arguments);
-     * }</pre></blockquote>
-     * <p>
-     * Unlike the signature polymorphic methods {@code invokeExact} and {@code invoke},
-     * {@code invokeWithArguments} can be accessed normally via the Core Reflection API and JNI.
-     * It can therefore be used as a bridge between native or reflective code and method handles.
-     *
-     * @param arguments the arguments to pass to the target
-     * @return the result returned by the target
-     * @throws ClassCastException if an argument cannot be converted by reference casting
-     * @throws WrongMethodTypeException if the target's type cannot be adjusted to take the given number of {@code Object} arguments
-     * @throws Throwable anything thrown by the target method invocation
-     * @see MethodHandles#spreadInvoker
-     */
-    public Object invokeWithArguments(Object... arguments) throws Throwable {
-        MethodHandle invoker = null;
-        synchronized (this) {
-            if (cachedSpreadInvoker == null) {
-                cachedSpreadInvoker = MethodHandles.spreadInvoker(this.type(), 0);
-            }
-
-            invoker = cachedSpreadInvoker;
-        }
-
-        return invoker.invoke(this, arguments);
-    }
-
-    /**
-     * Performs a variable arity invocation, passing the arguments in the given array
-     * to the method handle, as if via an inexact {@link #invoke invoke} from a call site
-     * which mentions only the type {@code Object}, and whose arity is the length
-     * of the argument array.
-     * <p>
-     * This method is also equivalent to the following code:
-     * <blockquote><pre>{@code
-     *   invokeWithArguments(arguments.toArray()
-     * }</pre></blockquote>
-     *
-     * @param arguments the arguments to pass to the target
-     * @return the result returned by the target
-     * @throws NullPointerException if {@code arguments} is a null reference
-     * @throws ClassCastException if an argument cannot be converted by reference casting
-     * @throws WrongMethodTypeException if the target's type cannot be adjusted to take the given number of {@code Object} arguments
-     * @throws Throwable anything thrown by the target method invocation
-     */
-    public Object invokeWithArguments(java.util.List<?> arguments) throws Throwable {
-        return invokeWithArguments(arguments.toArray());
-    }
-
-    /**
-     * Produces an adapter method handle which adapts the type of the
-     * current method handle to a new type.
-     * The resulting method handle is guaranteed to report a type
-     * which is equal to the desired new type.
-     * <p>
-     * If the original type and new type are equal, returns {@code this}.
-     * <p>
-     * The new method handle, when invoked, will perform the following
-     * steps:
-     * <ul>
-     * <li>Convert the incoming argument list to match the original
-     *     method handle's argument list.
-     * <li>Invoke the original method handle on the converted argument list.
-     * <li>Convert any result returned by the original method handle
-     *     to the return type of new method handle.
-     * </ul>
-     * <p>
-     * This method provides the crucial behavioral difference between
-     * {@link #invokeExact invokeExact} and plain, inexact {@link #invoke invoke}.
-     * The two methods
-     * perform the same steps when the caller's type descriptor exactly m atches
-     * the callee's, but when the types differ, plain {@link #invoke invoke}
-     * also calls {@code asType} (or some internal equivalent) in order
-     * to match up the caller's and callee's types.
-     * <p>
-     * If the current method is a variable arity method handle
-     * argument list conversion may involve the conversion and collection
-     * of several arguments into an array, as
-     * {@linkplain #asVarargsCollector described elsewhere}.
-     * In every other case, all conversions are applied <em>pairwise</em>,
-     * which means that each argument or return value is converted to
-     * exactly one argument or return value (or no return value).
-     * The applied conversions are defined by consulting the
-     * the corresponding component types of the old and new
-     * method handle types.
-     * <p>
-     * Let <em>T0</em> and <em>T1</em> be corresponding new and old parameter types,
-     * or old and new return types.  Specifically, for some valid index {@code i}, let
-     * <em>T0</em>{@code =newType.parameterType(i)} and <em>T1</em>{@code =this.type().parameterType(i)}.
-     * Or else, going the other way for return values, let
-     * <em>T0</em>{@code =this.type().returnType()} and <em>T1</em>{@code =newType.returnType()}.
-     * If the types are the same, the new method handle makes no change
-     * to the corresponding argument or return value (if any).
-     * Otherwise, one of the following conversions is applied
-     * if possible:
-     * <ul>
-     * <li>If <em>T0</em> and <em>T1</em> are references, then a cast to <em>T1</em> is applied.
-     *     (The types do not need to be related in any particular way.
-     *     This is because a dynamic value of null can convert to any reference type.)
-     * <li>If <em>T0</em> and <em>T1</em> are primitives, then a Java method invocation
-     *     conversion (JLS 5.3) is applied, if one exists.
-     *     (Specifically, <em>T0</em> must convert to <em>T1</em> by a widening primitive conversion.)
-     * <li>If <em>T0</em> is a primitive and <em>T1</em> a reference,
-     *     a Java casting conversion (JLS 5.5) is applied if one exists.
-     *     (Specifically, the value is boxed from <em>T0</em> to its wrapper class,
-     *     which is then widened as needed to <em>T1</em>.)
-     * <li>If <em>T0</em> is a reference and <em>T1</em> a primitive, an unboxing
-     *     conversion will be applied at runtime, possibly followed
-     *     by a Java method invocation conversion (JLS 5.3)
-     *     on the primitive value.  (These are the primitive widening conversions.)
-     *     <em>T0</em> must be a wrapper class or a supertype of one.
-     *     (In the case where <em>T0</em> is Object, these are the conversions
-     *     allowed by {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}.)
-     *     The unboxing conversion must have a possibility of success, which means that
-     *     if <em>T0</em> is not itself a wrapper class, there must exist at least one
-     *     wrapper class <em>TW</em> which is a subtype of <em>T0</em> and whose unboxed
-     *     primitive value can be widened to <em>T1</em>.
-     * <li>If the return type <em>T1</em> is marked as void, any returned value is discarded
-     * <li>If the return type <em>T0</em> is void and <em>T1</em> a reference, a null value is introduced.
-     * <li>If the return type <em>T0</em> is void and <em>T1</em> a primitive,
-     *     a zero value is introduced.
-     * </ul>
-     * (<em>Note:</em> Both <em>T0</em> and <em>T1</em> may be regarded as static types,
-     * because neither corresponds specifically to the <em>dynamic type</em> of any
-     * actual argument or return value.)
-     * <p>
-     * The method handle conversion cannot be made if any one of the required
-     * pairwise conversions cannot be made.
-     * <p>
-     * At runtime, the conversions applied to reference arguments
-     * or return values may require additional runtime checks which can fail.
-     * An unboxing operation may fail because the original reference is null,
-     * causing a {@link java.lang.NullPointerException NullPointerException}.
-     * An unboxing operation or a reference cast may also fail on a reference
-     * to an object of the wrong type,
-     * causing a {@link java.lang.ClassCastException ClassCastException}.
-     * Although an unboxing operation may accept several kinds of wrappers,
-     * if none are available, a {@code ClassCastException} will be thrown.
-     *
-     * @param newType the expected type of the new method handle
-     * @return a method handle which delegates to {@code this} after performing
-     *           any necessary argument conversions, and arranges for any
-     *           necessary return value conversions
-     * @throws NullPointerException if {@code newType} is a null reference
-     * @throws WrongMethodTypeException if the conversion cannot be made
-     * @see MethodHandles#explicitCastArguments
-     */
-    public MethodHandle asType(MethodType newType) {
-        // Fast path alternative to a heavyweight {@code asType} call.
-        // Return 'this' if the conversion will be a no-op.
-        if (newType == type) {
-            return this;
-        }
-
-        if (!type.isConvertibleTo(newType)) {
-            throw new WrongMethodTypeException("cannot convert " + this + " to " + newType);
-        }
-
-        MethodHandle mh = duplicate();
-        mh.nominalType = newType;
-        return mh;
-    }
-
-    /**
-     * Makes an <em>array-spreading</em> method handle, which accepts a trailing array argument
-     * and spreads its elements as positional arguments.
-     * The new method handle adapts, as its <i>target</i>,
-     * the current method handle.  The type of the adapter will be
-     * the same as the type of the target, except that the final
-     * {@code arrayLength} parameters of the target's type are replaced
-     * by a single array parameter of type {@code arrayType}.
-     * <p>
-     * If the array element type differs from any of the corresponding
-     * argument types on the original target,
-     * the original target is adapted to take the array elements directly,
-     * as if by a call to {@link #asType asType}.
-     * <p>
-     * When called, the adapter replaces a trailing array argument
-     * by the array's elements, each as its own argument to the target.
-     * (The order of the arguments is preserved.)
-     * They are converted pairwise by casting and/or unboxing
-     * to the types of the trailing parameters of the target.
-     * Finally the target is called.
-     * What the target eventually returns is returned unchanged by the adapter.
-     * <p>
-     * Before calling the target, the adapter verifies that the array
-     * contains exactly enough elements to provide a correct argument count
-     * to the target method handle.
-     * (The array may also be null when zero elements are required.)
-     * <p>
-     * If, when the adapter is called, the supplied array argument does
-     * not have the correct number of elements, the adapter will throw
-     * an {@link IllegalArgumentException} instead of invoking the target.
-     * <p>
-     * Here are some simple examples of array-spreading method handles:
-     * <blockquote><pre>{@code
-MethodHandle equals = publicLookup()
-  .findVirtual(String.class, "equals", methodType(boolean.class, Object.class));
-assert( (boolean) equals.invokeExact("me", (Object)"me"));
-assert(!(boolean) equals.invokeExact("me", (Object)"thee"));
-// spread both arguments from a 2-array:
-MethodHandle eq2 = equals.asSpreader(Object[].class, 2);
-assert( (boolean) eq2.invokeExact(new Object[]{ "me", "me" }));
-assert(!(boolean) eq2.invokeExact(new Object[]{ "me", "thee" }));
-// try to spread from anything but a 2-array:
-for (int n = 0; n <= 10; n++) {
-  Object[] badArityArgs = (n == 2 ? null : new Object[n]);
-  try { assert((boolean) eq2.invokeExact(badArityArgs) && false); }
-  catch (IllegalArgumentException ex) { } // OK
-}
-// spread both arguments from a String array:
-MethodHandle eq2s = equals.asSpreader(String[].class, 2);
-assert( (boolean) eq2s.invokeExact(new String[]{ "me", "me" }));
-assert(!(boolean) eq2s.invokeExact(new String[]{ "me", "thee" }));
-// spread second arguments from a 1-array:
-MethodHandle eq1 = equals.asSpreader(Object[].class, 1);
-assert( (boolean) eq1.invokeExact("me", new Object[]{ "me" }));
-assert(!(boolean) eq1.invokeExact("me", new Object[]{ "thee" }));
-// spread no arguments from a 0-array or null:
-MethodHandle eq0 = equals.asSpreader(Object[].class, 0);
-assert( (boolean) eq0.invokeExact("me", (Object)"me", new Object[0]));
-assert(!(boolean) eq0.invokeExact("me", (Object)"thee", (Object[])null));
-// asSpreader and asCollector are approximate inverses:
-for (int n = 0; n <= 2; n++) {
-    for (Class<?> a : new Class<?>[]{Object[].class, String[].class, CharSequence[].class}) {
-        MethodHandle equals2 = equals.asSpreader(a, n).asCollector(a, n);
-        assert( (boolean) equals2.invokeWithArguments("me", "me"));
-        assert(!(boolean) equals2.invokeWithArguments("me", "thee"));
-    }
-}
-MethodHandle caToString = publicLookup()
-  .findStatic(Arrays.class, "toString", methodType(String.class, char[].class));
-assertEquals("[A, B, C]", (String) caToString.invokeExact("ABC".toCharArray()));
-MethodHandle caString3 = caToString.asCollector(char[].class, 3);
-assertEquals("[A, B, C]", (String) caString3.invokeExact('A', 'B', 'C'));
-MethodHandle caToString2 = caString3.asSpreader(char[].class, 2);
-assertEquals("[A, B, C]", (String) caToString2.invokeExact('A', "BC".toCharArray()));
-     * }</pre></blockquote>
-     * @param arrayType usually {@code Object[]}, the type of the array argument from which to extract the spread arguments
-     * @param arrayLength the number of arguments to spread from an incoming array argument
-     * @return a new method handle which spreads its final array argument,
-     *         before calling the original method handle
-     * @throws NullPointerException if {@code arrayType} is a null reference
-     * @throws IllegalArgumentException if {@code arrayType} is not an array type,
-     *         or if target does not have at least
-     *         {@code arrayLength} parameter types,
-     *         or if {@code arrayLength} is negative,
-     *         or if the resulting method handle's type would have
-     *         <a href="MethodHandle.html#maxarity">too many parameters</a>
-     * @throws WrongMethodTypeException if the implied {@code asType} call fails
-     * @see #asCollector
-     */
-    public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
-        MethodType postSpreadType = asSpreaderChecks(arrayType, arrayLength);
-
-        final int targetParamCount = postSpreadType.parameterCount();
-        MethodType dropArrayArgs = postSpreadType.dropParameterTypes(
-                (targetParamCount - arrayLength), targetParamCount);
-        MethodType adapterType = dropArrayArgs.appendParameterTypes(arrayType);
-
-        return new Transformers.Spreader(this, adapterType, arrayLength);
-    }
-
-    /**
-     * See if {@code asSpreader} can be validly called with the given arguments.
-     * Return the type of the method handle call after spreading but before conversions.
-     */
-    private MethodType asSpreaderChecks(Class<?> arrayType, int arrayLength) {
-        spreadArrayChecks(arrayType, arrayLength);
-        int nargs = type().parameterCount();
-        if (nargs < arrayLength || arrayLength < 0)
-            throw newIllegalArgumentException("bad spread array length");
-        Class<?> arrayElement = arrayType.getComponentType();
-        MethodType mtype = type();
-        boolean match = true, fail = false;
-        for (int i = nargs - arrayLength; i < nargs; i++) {
-            Class<?> ptype = mtype.parameterType(i);
-            if (ptype != arrayElement) {
-                match = false;
-                if (!MethodType.canConvert(arrayElement, ptype)) {
-                    fail = true;
-                    break;
-                }
-            }
-        }
-        if (match)  return mtype;
-        MethodType needType = mtype.asSpreaderType(arrayType, arrayLength);
-        if (!fail)  return needType;
-        // elicit an error:
-        this.asType(needType);
-        throw newInternalError("should not return", null);
-    }
-
-    private void spreadArrayChecks(Class<?> arrayType, int arrayLength) {
-        Class<?> arrayElement = arrayType.getComponentType();
-        if (arrayElement == null)
-            throw newIllegalArgumentException("not an array type", arrayType);
-        if ((arrayLength & 0x7F) != arrayLength) {
-            if ((arrayLength & 0xFF) != arrayLength)
-                throw newIllegalArgumentException("array length is not legal", arrayLength);
-            assert(arrayLength >= 128);
-            if (arrayElement == long.class ||
-                arrayElement == double.class)
-                throw newIllegalArgumentException("array length is not legal for long[] or double[]", arrayLength);
-        }
-    }
-
-    /**
-     * Makes an <em>array-collecting</em> method handle, which accepts a given number of trailing
-     * positional arguments and collects them into an array argument.
-     * The new method handle adapts, as its <i>target</i>,
-     * the current method handle.  The type of the adapter will be
-     * the same as the type of the target, except that a single trailing
-     * parameter (usually of type {@code arrayType}) is replaced by
-     * {@code arrayLength} parameters whose type is element type of {@code arrayType}.
-     * <p>
-     * If the array type differs from the final argument type on the original target,
-     * the original target is adapted to take the array type directly,
-     * as if by a call to {@link #asType asType}.
-     * <p>
-     * When called, the adapter replaces its trailing {@code arrayLength}
-     * arguments by a single new array of type {@code arrayType}, whose elements
-     * comprise (in order) the replaced arguments.
-     * Finally the target is called.
-     * What the target eventually returns is returned unchanged by the adapter.
-     * <p>
-     * (The array may also be a shared constant when {@code arrayLength} is zero.)
-     * <p>
-     * (<em>Note:</em> The {@code arrayType} is often identical to the last
-     * parameter type of the original target.
-     * It is an explicit argument for symmetry with {@code asSpreader}, and also
-     * to allow the target to use a simple {@code Object} as its last parameter type.)
-     * <p>
-     * In order to create a collecting adapter which is not restricted to a particular
-     * number of collected arguments, use {@link #asVarargsCollector asVarargsCollector} instead.
-     * <p>
-     * Here are some examples of array-collecting method handles:
-     * <blockquote><pre>{@code
-MethodHandle deepToString = publicLookup()
-  .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
-assertEquals("[won]",   (String) deepToString.invokeExact(new Object[]{"won"}));
-MethodHandle ts1 = deepToString.asCollector(Object[].class, 1);
-assertEquals(methodType(String.class, Object.class), ts1.type());
-//assertEquals("[won]", (String) ts1.invokeExact(         new Object[]{"won"})); //FAIL
-assertEquals("[[won]]", (String) ts1.invokeExact((Object) new Object[]{"won"}));
-// arrayType can be a subtype of Object[]
-MethodHandle ts2 = deepToString.asCollector(String[].class, 2);
-assertEquals(methodType(String.class, String.class, String.class), ts2.type());
-assertEquals("[two, too]", (String) ts2.invokeExact("two", "too"));
-MethodHandle ts0 = deepToString.asCollector(Object[].class, 0);
-assertEquals("[]", (String) ts0.invokeExact());
-// collectors can be nested, Lisp-style
-MethodHandle ts22 = deepToString.asCollector(Object[].class, 3).asCollector(String[].class, 2);
-assertEquals("[A, B, [C, D]]", ((String) ts22.invokeExact((Object)'A', (Object)"B", "C", "D")));
-// arrayType can be any primitive array type
-MethodHandle bytesToString = publicLookup()
-  .findStatic(Arrays.class, "toString", methodType(String.class, byte[].class))
-  .asCollector(byte[].class, 3);
-assertEquals("[1, 2, 3]", (String) bytesToString.invokeExact((byte)1, (byte)2, (byte)3));
-MethodHandle longsToString = publicLookup()
-  .findStatic(Arrays.class, "toString", methodType(String.class, long[].class))
-  .asCollector(long[].class, 1);
-assertEquals("[123]", (String) longsToString.invokeExact((long)123));
-     * }</pre></blockquote>
-     * @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments
-     * @param arrayLength the number of arguments to collect into a new array argument
-     * @return a new method handle which collects some trailing argument
-     *         into an array, before calling the original method handle
-     * @throws NullPointerException if {@code arrayType} is a null reference
-     * @throws IllegalArgumentException if {@code arrayType} is not an array type
-     *         or {@code arrayType} is not assignable to this method handle's trailing parameter type,
-     *         or {@code arrayLength} is not a legal array size,
-     *         or the resulting method handle's type would have
-     *         <a href="MethodHandle.html#maxarity">too many parameters</a>
-     * @throws WrongMethodTypeException if the implied {@code asType} call fails
-     * @see #asSpreader
-     * @see #asVarargsCollector
-     */
-    public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
-        asCollectorChecks(arrayType, arrayLength);
-
-        return new Transformers.Collector(this, arrayType, arrayLength);
-    }
-
-    /**
-     * See if {@code asCollector} can be validly called with the given arguments.
-     * Return false if the last parameter is not an exact match to arrayType.
-     */
-    /*non-public*/ boolean asCollectorChecks(Class<?> arrayType, int arrayLength) {
-        spreadArrayChecks(arrayType, arrayLength);
-        int nargs = type().parameterCount();
-        if (nargs != 0) {
-            Class<?> lastParam = type().parameterType(nargs-1);
-            if (lastParam == arrayType)  return true;
-            if (lastParam.isAssignableFrom(arrayType))  return false;
-        }
-        throw newIllegalArgumentException("array type not assignable to trailing argument", this, arrayType);
-    }
-
-    /**
-     * Makes a <em>variable arity</em> adapter which is able to accept
-     * any number of trailing positional arguments and collect them
-     * into an array argument.
-     * <p>
-     * The type and behavior of the adapter will be the same as
-     * the type and behavior of the target, except that certain
-     * {@code invoke} and {@code asType} requests can lead to
-     * trailing positional arguments being collected into target's
-     * trailing parameter.
-     * Also, the last parameter type of the adapter will be
-     * {@code arrayType}, even if the target has a different
-     * last parameter type.
-     * <p>
-     * This transformation may return {@code this} if the method handle is
-     * already of variable arity and its trailing parameter type
-     * is identical to {@code arrayType}.
-     * <p>
-     * When called with {@link #invokeExact invokeExact}, the adapter invokes
-     * the target with no argument changes.
-     * (<em>Note:</em> This behavior is different from a
-     * {@linkplain #asCollector fixed arity collector},
-     * since it accepts a whole array of indeterminate length,
-     * rather than a fixed number of arguments.)
-     * <p>
-     * When called with plain, inexact {@link #invoke invoke}, if the caller
-     * type is the same as the adapter, the adapter invokes the target as with
-     * {@code invokeExact}.
-     * (This is the normal behavior for {@code invoke} when types match.)
-     * <p>
-     * Otherwise, if the caller and adapter arity are the same, and the
-     * trailing parameter type of the caller is a reference type identical to
-     * or assignable to the trailing parameter type of the adapter,
-     * the arguments and return values are converted pairwise,
-     * as if by {@link #asType asType} on a fixed arity
-     * method handle.
-     * <p>
-     * Otherwise, the arities differ, or the adapter's trailing parameter
-     * type is not assignable from the corresponding caller type.
-     * In this case, the adapter replaces all trailing arguments from
-     * the original trailing argument position onward, by
-     * a new array of type {@code arrayType}, whose elements
-     * comprise (in order) the replaced arguments.
-     * <p>
-     * The caller type must provides as least enough arguments,
-     * and of the correct type, to satisfy the target's requirement for
-     * positional arguments before the trailing array argument.
-     * Thus, the caller must supply, at a minimum, {@code N-1} arguments,
-     * where {@code N} is the arity of the target.
-     * Also, there must exist conversions from the incoming arguments
-     * to the target's arguments.
-     * As with other uses of plain {@code invoke}, if these basic
-     * requirements are not fulfilled, a {@code WrongMethodTypeException}
-     * may be thrown.
-     * <p>
-     * In all cases, what the target eventually returns is returned unchanged by the adapter.
-     * <p>
-     * In the final case, it is exactly as if the target method handle were
-     * temporarily adapted with a {@linkplain #asCollector fixed arity collector}
-     * to the arity required by the caller type.
-     * (As with {@code asCollector}, if the array length is zero,
-     * a shared constant may be used instead of a new array.
-     * If the implied call to {@code asCollector} would throw
-     * an {@code IllegalArgumentException} or {@code WrongMethodTypeException},
-     * the call to the variable arity adapter must throw
-     * {@code WrongMethodTypeException}.)
-     * <p>
-     * The behavior of {@link #asType asType} is also specialized for
-     * variable arity adapters, to maintain the invariant that
-     * plain, inexact {@code invoke} is always equivalent to an {@code asType}
-     * call to adjust the target type, followed by {@code invokeExact}.
-     * Therefore, a variable arity adapter responds
-     * to an {@code asType} request by building a fixed arity collector,
-     * if and only if the adapter and requested type differ either
-     * in arity or trailing argument type.
-     * The resulting fixed arity collector has its type further adjusted
-     * (if necessary) to the requested type by pairwise conversion,
-     * as if by another application of {@code asType}.
-     * <p>
-     * When a method handle is obtained by executing an {@code ldc} instruction
-     * of a {@code CONSTANT_MethodHandle} constant, and the target method is marked
-     * as a variable arity method (with the modifier bit {@code 0x0080}),
-     * the method handle will accept multiple arities, as if the method handle
-     * constant were created by means of a call to {@code asVarargsCollector}.
-     * <p>
-     * In order to create a collecting adapter which collects a predetermined
-     * number of arguments, and whose type reflects this predetermined number,
-     * use {@link #asCollector asCollector} instead.
-     * <p>
-     * No method handle transformations produce new method handles with
-     * variable arity, unless they are documented as doing so.
-     * Therefore, besides {@code asVarargsCollector},
-     * all methods in {@code MethodHandle} and {@code MethodHandles}
-     * will return a method handle with fixed arity,
-     * except in the cases where they are specified to return their original
-     * operand (e.g., {@code asType} of the method handle's own type).
-     * <p>
-     * Calling {@code asVarargsCollector} on a method handle which is already
-     * of variable arity will produce a method handle with the same type and behavior.
-     * It may (or may not) return the original variable arity method handle.
-     * <p>
-     * Here is an example, of a list-making variable arity method handle:
-     * <blockquote><pre>{@code
-MethodHandle deepToString = publicLookup()
-  .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
-MethodHandle ts1 = deepToString.asVarargsCollector(Object[].class);
-assertEquals("[won]",   (String) ts1.invokeExact(    new Object[]{"won"}));
-assertEquals("[won]",   (String) ts1.invoke(         new Object[]{"won"}));
-assertEquals("[won]",   (String) ts1.invoke(                      "won" ));
-assertEquals("[[won]]", (String) ts1.invoke((Object) new Object[]{"won"}));
-// findStatic of Arrays.asList(...) produces a variable arity method handle:
-MethodHandle asList = publicLookup()
-  .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class));
-assertEquals(methodType(List.class, Object[].class), asList.type());
-assert(asList.isVarargsCollector());
-assertEquals("[]", asList.invoke().toString());
-assertEquals("[1]", asList.invoke(1).toString());
-assertEquals("[two, too]", asList.invoke("two", "too").toString());
-String[] argv = { "three", "thee", "tee" };
-assertEquals("[three, thee, tee]", asList.invoke(argv).toString());
-assertEquals("[three, thee, tee]", asList.invoke((Object[])argv).toString());
-List ls = (List) asList.invoke((Object)argv);
-assertEquals(1, ls.size());
-assertEquals("[three, thee, tee]", Arrays.toString((Object[])ls.get(0)));
-     * }</pre></blockquote>
-     * <p style="font-size:smaller;">
-     * <em>Discussion:</em>
-     * These rules are designed as a dynamically-typed variation
-     * of the Java rules for variable arity methods.
-     * In both cases, callers to a variable arity method or method handle
-     * can either pass zero or more positional arguments, or else pass
-     * pre-collected arrays of any length.  Users should be aware of the
-     * special role of the final argument, and of the effect of a
-     * type match on that final argument, which determines whether
-     * or not a single trailing argument is interpreted as a whole
-     * array or a single element of an array to be collected.
-     * Note that the dynamic type of the trailing argument has no
-     * effect on this decision, only a comparison between the symbolic
-     * type descriptor of the call site and the type descriptor of the method handle.)
-     *
-     * @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments
-     * @return a new method handle which can collect any number of trailing arguments
-     *         into an array, before calling the original method handle
-     * @throws NullPointerException if {@code arrayType} is a null reference
-     * @throws IllegalArgumentException if {@code arrayType} is not an array type
-     *         or {@code arrayType} is not assignable to this method handle's trailing parameter type
-     * @see #asCollector
-     * @see #isVarargsCollector
-     * @see #asFixedArity
-     */
-    public MethodHandle asVarargsCollector(Class<?> arrayType) {
-        arrayType.getClass(); // explicit NPE
-        boolean lastMatch = asCollectorChecks(arrayType, 0);
-        if (isVarargsCollector() && lastMatch)
-            return this;
-
-        return new Transformers.VarargsCollector(this);
-    }
-
-    /**
-     * Determines if this method handle
-     * supports {@linkplain #asVarargsCollector variable arity} calls.
-     * Such method handles arise from the following sources:
-     * <ul>
-     * <li>a call to {@linkplain #asVarargsCollector asVarargsCollector}
-     * <li>a call to a {@linkplain java.lang.invoke.MethodHandles.Lookup lookup method}
-     *     which resolves to a variable arity Java method or constructor
-     * <li>an {@code ldc} instruction of a {@code CONSTANT_MethodHandle}
-     *     which resolves to a variable arity Java method or constructor
-     * </ul>
-     * @return true if this method handle accepts more than one arity of plain, inexact {@code invoke} calls
-     * @see #asVarargsCollector
-     * @see #asFixedArity
-     */
-    public boolean isVarargsCollector() {
-        return false;
-    }
-
-    /**
-     * Makes a <em>fixed arity</em> method handle which is otherwise
-     * equivalent to the current method handle.
-     * <p>
-     * If the current method handle is not of
-     * {@linkplain #asVarargsCollector variable arity},
-     * the current method handle is returned.
-     * This is true even if the current method handle
-     * could not be a valid input to {@code asVarargsCollector}.
-     * <p>
-     * Otherwise, the resulting fixed-arity method handle has the same
-     * type and behavior of the current method handle,
-     * except that {@link #isVarargsCollector isVarargsCollector}
-     * will be false.
-     * The fixed-arity method handle may (or may not) be the
-     * a previous argument to {@code asVarargsCollector}.
-     * <p>
-     * Here is an example, of a list-making variable arity method handle:
-     * <blockquote><pre>{@code
-MethodHandle asListVar = publicLookup()
-  .findStatic(Arrays.class, "asList", methodType(List.class, Object[].class))
-  .asVarargsCollector(Object[].class);
-MethodHandle asListFix = asListVar.asFixedArity();
-assertEquals("[1]", asListVar.invoke(1).toString());
-Exception caught = null;
-try { asListFix.invoke((Object)1); }
-catch (Exception ex) { caught = ex; }
-assert(caught instanceof ClassCastException);
-assertEquals("[two, too]", asListVar.invoke("two", "too").toString());
-try { asListFix.invoke("two", "too"); }
-catch (Exception ex) { caught = ex; }
-assert(caught instanceof WrongMethodTypeException);
-Object[] argv = { "three", "thee", "tee" };
-assertEquals("[three, thee, tee]", asListVar.invoke(argv).toString());
-assertEquals("[three, thee, tee]", asListFix.invoke(argv).toString());
-assertEquals(1, ((List) asListVar.invoke((Object)argv)).size());
-assertEquals("[three, thee, tee]", asListFix.invoke((Object)argv).toString());
-     * }</pre></blockquote>
-     *
-     * @return a new method handle which accepts only a fixed number of arguments
-     * @see #asVarargsCollector
-     * @see #isVarargsCollector
-     */
-    public MethodHandle asFixedArity() {
-        // Android-changed: implementation specific.
-        MethodHandle mh = this;
-        if (mh.isVarargsCollector()) {
-            mh = ((Transformers.VarargsCollector) mh).asFixedArity();
-        }
-        assert(!mh.isVarargsCollector());
-        return mh;
-    }
-
-    /**
-     * Binds a value {@code x} to the first argument of a method handle, without invoking it.
-     * The new method handle adapts, as its <i>target</i>,
-     * the current method handle by binding it to the given argument.
-     * The type of the bound handle will be
-     * the same as the type of the target, except that a single leading
-     * reference parameter will be omitted.
-     * <p>
-     * When called, the bound handle inserts the given value {@code x}
-     * as a new leading argument to the target.  The other arguments are
-     * also passed unchanged.
-     * What the target eventually returns is returned unchanged by the bound handle.
-     * <p>
-     * The reference {@code x} must be convertible to the first parameter
-     * type of the target.
-     * <p>
-     * (<em>Note:</em>  Because method handles are immutable, the target method handle
-     * retains its original type and behavior.)
-     * @param x  the value to bind to the first argument of the target
-     * @return a new method handle which prepends the given value to the incoming
-     *         argument list, before calling the original method handle
-     * @throws IllegalArgumentException if the target does not have a
-     *         leading parameter type that is a reference type
-     * @throws ClassCastException if {@code x} cannot be converted
-     *         to the leading parameter type of the target
-     * @see MethodHandles#insertArguments
-     */
-    public MethodHandle bindTo(Object x) {
-        x = type.leadingReferenceParameter().cast(x);  // throw CCE if needed
-
-        return new Transformers.BindTo(this, x);
-    }
-
-    /**
-     * Returns a string representation of the method handle,
-     * starting with the string {@code "MethodHandle"} and
-     * ending with the string representation of the method handle's type.
-     * In other words, this method returns a string equal to the value of:
-     * <blockquote><pre>{@code
-     * "MethodHandle" + type().toString()
-     * }</pre></blockquote>
-     * <p>
-     * (<em>Note:</em>  Future releases of this API may add further information
-     * to the string representation.
-     * Therefore, the present syntax should not be parsed by applications.)
-     *
-     * @return a string representation of the method handle
-     */
-    @Override
-    public String toString() {
-        // Android-changed: Removed debugging support.
-        return "MethodHandle"+type;
-    }
-
-    /** @hide */
-    public int getHandleKind() {
-        return handleKind;
-    }
-
-    /** @hide */
-    protected void transform(EmulatedStackFrame arguments) throws Throwable {
-        throw new AssertionError("MethodHandle.transform should never be called.");
-    }
-
-    /**
-     * Creates a copy of this method handle, copying all relevant data.
-     *
-     * @hide
-     */
-    protected MethodHandle duplicate() {
-        try {
-            return (MethodHandle) this.clone();
-        } catch (CloneNotSupportedException cnse) {
-            throw new AssertionError("Subclass of Transformer is not cloneable");
-        }
-    }
-
-
-    /**
-     * This is the entry point for all transform calls, and dispatches to the protected
-     * transform method. This layer of indirection exists purely for convenience, because
-     * we can invoke-direct on a fixed ArtMethod for all transform variants.
-     *
-     * NOTE: If this extra layer of indirection proves to be a problem, we can get rid
-     * of this layer of indirection at the cost of some additional ugliness.
-     */
-    private void transformInternal(EmulatedStackFrame arguments) throws Throwable {
-        transform(arguments);
-    }
-
-    // Android-changed: Removed implementation details :
-    //
-    // String standardString();
-    // String debugString();
-    //
-    //// Implementation methods.
-    //// Sub-classes can override these default implementations.
-    //// All these methods assume arguments are already validated.
-    //
-    // Other transforms to do:  convert, explicitCast, permute, drop, filter, fold, GWT, catch
-    //
-    // BoundMethodHandle bindArgumentL(int pos, Object value);
-    // /*non-public*/ MethodHandle setVarargs(MemberName member);
-    // /*non-public*/ MethodHandle viewAsType(MethodType newType, boolean strict);
-    // /*non-public*/ boolean viewAsTypeChecks(MethodType newType, boolean strict);
-    //
-    // Decoding
-    //
-    // /*non-public*/ LambdaForm internalForm();
-    // /*non-public*/ MemberName internalMemberName();
-    // /*non-public*/ Class<?> internalCallerClass();
-    // /*non-public*/ MethodHandleImpl.Intrinsic intrinsicName();
-    // /*non-public*/ MethodHandle withInternalMemberName(MemberName member, boolean isInvokeSpecial);
-    // /*non-public*/ boolean isInvokeSpecial();
-    // /*non-public*/ Object internalValues();
-    // /*non-public*/ Object internalProperties();
-    //
-    //// Method handle implementation methods.
-    //// Sub-classes can override these default implementations.
-    //// All these methods assume arguments are already validated.
-    //
-    // /*non-public*/ abstract MethodHandle copyWith(MethodType mt, LambdaForm lf);
-    // abstract BoundMethodHandle rebind();
-    // /*non-public*/ void updateForm(LambdaForm newForm);
-    // /*non-public*/ void customize();
-    // private static final long FORM_OFFSET;
 }
diff --git a/java/lang/invoke/MethodHandles.java b/java/lang/invoke/MethodHandles.java
index 94c9917..f27ad98 100644
--- a/java/lang/invoke/MethodHandles.java
+++ b/java/lang/invoke/MethodHandles.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,2877 +25,129 @@
 
 package java.lang.invoke;
 
-import java.lang.reflect.*;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
 import java.util.List;
-import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.NoSuchElementException;
 
-import dalvik.system.VMStack;
-import sun.invoke.util.VerifyAccess;
-import sun.invoke.util.Wrapper;
-import static java.lang.invoke.MethodHandleStatics.*;
-
-/**
- * This class consists exclusively of static methods that operate on or return
- * method handles. They fall into several categories:
- * <ul>
- * <li>Lookup methods which help create method handles for methods and fields.
- * <li>Combinator methods, which combine or transform pre-existing method handles into new ones.
- * <li>Other factory methods to create method handles that emulate other common JVM operations or control flow patterns.
- * </ul>
- * <p>
- * @author John Rose, JSR 292 EG
- * @since 1.7
- */
 public class MethodHandles {
 
-    private MethodHandles() { }  // do not instantiate
+    public static Lookup lookup() { return null; }
 
-    // Android-changed: We do not use MemberName / MethodHandleImpl.
-    //
-    // private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory();
-    // static { MethodHandleImpl.initStatics(); }
-    // See IMPL_LOOKUP below.
+    public static Lookup publicLookup() { return null; }
 
-    //// Method handle creation from ordinary methods.
-
-    /**
-     * Returns a {@link Lookup lookup object} with
-     * full capabilities to emulate all supported bytecode behaviors of the caller.
-     * These capabilities include <a href="MethodHandles.Lookup.html#privacc">private access</a> to the caller.
-     * Factory methods on the lookup object can create
-     * <a href="MethodHandleInfo.html#directmh">direct method handles</a>
-     * for any member that the caller has access to via bytecodes,
-     * including protected and private fields and methods.
-     * This lookup object is a <em>capability</em> which may be delegated to trusted agents.
-     * Do not store it in place where untrusted code can access it.
-     * <p>
-     * This method is caller sensitive, which means that it may return different
-     * values to different callers.
-     * <p>
-     * For any given caller class {@code C}, the lookup object returned by this call
-     * has equivalent capabilities to any lookup object
-     * supplied by the JVM to the bootstrap method of an
-     * <a href="package-summary.html#indyinsn">invokedynamic instruction</a>
-     * executing in the same caller class {@code C}.
-     * @return a lookup object for the caller of this method, with private access
-     */
-    // Android-changed: Remove caller sensitive.
-    // @CallerSensitive
-    public static Lookup lookup() {
-        // Android-changed: Do not use Reflection.getCallerClass().
-        return new Lookup(VMStack.getStackClass1());
-    }
-
-    /**
-     * Returns a {@link Lookup lookup object} which is trusted minimally.
-     * It can only be used to create method handles to
-     * publicly accessible fields and methods.
-     * <p>
-     * As a matter of pure convention, the {@linkplain Lookup#lookupClass lookup class}
-     * of this lookup object will be {@link java.lang.Object}.
-     *
-     * <p style="font-size:smaller;">
-     * <em>Discussion:</em>
-     * The lookup class can be changed to any other class {@code C} using an expression of the form
-     * {@link Lookup#in publicLookup().in(C.class)}.
-     * Since all classes have equal access to public names,
-     * such a change would confer no new access rights.
-     * A public lookup object is always subject to
-     * <a href="MethodHandles.Lookup.html#secmgr">security manager checks</a>.
-     * Also, it cannot access
-     * <a href="MethodHandles.Lookup.html#callsens">caller sensitive methods</a>.
-     * @return a lookup object which is trusted minimally
-     */
-    public static Lookup publicLookup() {
-        return Lookup.PUBLIC_LOOKUP;
-    }
-
-    /**
-     * Performs an unchecked "crack" of a
-     * <a href="MethodHandleInfo.html#directmh">direct method handle</a>.
-     * The result is as if the user had obtained a lookup object capable enough
-     * to crack the target method handle, called
-     * {@link java.lang.invoke.MethodHandles.Lookup#revealDirect Lookup.revealDirect}
-     * on the target to obtain its symbolic reference, and then called
-     * {@link java.lang.invoke.MethodHandleInfo#reflectAs MethodHandleInfo.reflectAs}
-     * to resolve the symbolic reference to a member.
-     * <p>
-     * If there is a security manager, its {@code checkPermission} method
-     * is called with a {@code ReflectPermission("suppressAccessChecks")} permission.
-     * @param <T> the desired type of the result, either {@link Member} or a subtype
-     * @param target a direct method handle to crack into symbolic reference components
-     * @param expected a class object representing the desired result type {@code T}
-     * @return a reference to the method, constructor, or field object
-     * @exception SecurityException if the caller is not privileged to call {@code setAccessible}
-     * @exception NullPointerException if either argument is {@code null}
-     * @exception IllegalArgumentException if the target is not a direct method handle
-     * @exception ClassCastException if the member is not of the expected type
-     * @since 1.8
-     */
     public static <T extends Member> T
-    reflectAs(Class<T> expected, MethodHandle target) {
-        MethodHandleImpl directTarget = getMethodHandleImpl(target);
-        // Given that this is specified to be an "unchecked" crack, we can directly allocate
-        // a member from the underlying ArtField / Method and bypass all associated access checks.
-        return expected.cast(directTarget.getMemberInternal());
-    }
+    reflectAs(Class<T> expected, MethodHandle target) { return null; }
 
-    /**
-     * A <em>lookup object</em> is a factory for creating method handles,
-     * when the creation requires access checking.
-     * Method handles do not perform
-     * access checks when they are called, but rather when they are created.
-     * Therefore, method handle access
-     * restrictions must be enforced when a method handle is created.
-     * The caller class against which those restrictions are enforced
-     * is known as the {@linkplain #lookupClass lookup class}.
-     * <p>
-     * A lookup class which needs to create method handles will call
-     * {@link #lookup MethodHandles.lookup} to create a factory for itself.
-     * When the {@code Lookup} factory object is created, the identity of the lookup class is
-     * determined, and securely stored in the {@code Lookup} object.
-     * The lookup class (or its delegates) may then use factory methods
-     * on the {@code Lookup} object to create method handles for access-checked members.
-     * This includes all methods, constructors, and fields which are allowed to the lookup class,
-     * even private ones.
-     *
-     * <h1><a name="lookups"></a>Lookup Factory Methods</h1>
-     * The factory methods on a {@code Lookup} object correspond to all major
-     * use cases for methods, constructors, and fields.
-     * Each method handle created by a factory method is the functional
-     * equivalent of a particular <em>bytecode behavior</em>.
-     * (Bytecode behaviors are described in section 5.4.3.5 of the Java Virtual Machine Specification.)
-     * Here is a summary of the correspondence between these factory methods and
-     * the behavior the resulting method handles:
-     * <table border=1 cellpadding=5 summary="lookup method behaviors">
-     * <tr>
-     *     <th><a name="equiv"></a>lookup expression</th>
-     *     <th>member</th>
-     *     <th>bytecode behavior</th>
-     * </tr>
-     * <tr>
-     *     <td>{@link java.lang.invoke.MethodHandles.Lookup#findGetter lookup.findGetter(C.class,"f",FT.class)}</td>
-     *     <td>{@code FT f;}</td><td>{@code (T) this.f;}</td>
-     * </tr>
-     * <tr>
-     *     <td>{@link java.lang.invoke.MethodHandles.Lookup#findStaticGetter lookup.findStaticGetter(C.class,"f",FT.class)}</td>
-     *     <td>{@code static}<br>{@code FT f;}</td><td>{@code (T) C.f;}</td>
-     * </tr>
-     * <tr>
-     *     <td>{@link java.lang.invoke.MethodHandles.Lookup#findSetter lookup.findSetter(C.class,"f",FT.class)}</td>
-     *     <td>{@code FT f;}</td><td>{@code this.f = x;}</td>
-     * </tr>
-     * <tr>
-     *     <td>{@link java.lang.invoke.MethodHandles.Lookup#findStaticSetter lookup.findStaticSetter(C.class,"f",FT.class)}</td>
-     *     <td>{@code static}<br>{@code FT f;}</td><td>{@code C.f = arg;}</td>
-     * </tr>
-     * <tr>
-     *     <td>{@link java.lang.invoke.MethodHandles.Lookup#findVirtual lookup.findVirtual(C.class,"m",MT)}</td>
-     *     <td>{@code T m(A*);}</td><td>{@code (T) this.m(arg*);}</td>
-     * </tr>
-     * <tr>
-     *     <td>{@link java.lang.invoke.MethodHandles.Lookup#findStatic lookup.findStatic(C.class,"m",MT)}</td>
-     *     <td>{@code static}<br>{@code T m(A*);}</td><td>{@code (T) C.m(arg*);}</td>
-     * </tr>
-     * <tr>
-     *     <td>{@link java.lang.invoke.MethodHandles.Lookup#findSpecial lookup.findSpecial(C.class,"m",MT,this.class)}</td>
-     *     <td>{@code T m(A*);}</td><td>{@code (T) super.m(arg*);}</td>
-     * </tr>
-     * <tr>
-     *     <td>{@link java.lang.invoke.MethodHandles.Lookup#findConstructor lookup.findConstructor(C.class,MT)}</td>
-     *     <td>{@code C(A*);}</td><td>{@code new C(arg*);}</td>
-     * </tr>
-     * <tr>
-     *     <td>{@link java.lang.invoke.MethodHandles.Lookup#unreflectGetter lookup.unreflectGetter(aField)}</td>
-     *     <td>({@code static})?<br>{@code FT f;}</td><td>{@code (FT) aField.get(thisOrNull);}</td>
-     * </tr>
-     * <tr>
-     *     <td>{@link java.lang.invoke.MethodHandles.Lookup#unreflectSetter lookup.unreflectSetter(aField)}</td>
-     *     <td>({@code static})?<br>{@code FT f;}</td><td>{@code aField.set(thisOrNull, arg);}</td>
-     * </tr>
-     * <tr>
-     *     <td>{@link java.lang.invoke.MethodHandles.Lookup#unreflect lookup.unreflect(aMethod)}</td>
-     *     <td>({@code static})?<br>{@code T m(A*);}</td><td>{@code (T) aMethod.invoke(thisOrNull, arg*);}</td>
-     * </tr>
-     * <tr>
-     *     <td>{@link java.lang.invoke.MethodHandles.Lookup#unreflectConstructor lookup.unreflectConstructor(aConstructor)}</td>
-     *     <td>{@code C(A*);}</td><td>{@code (C) aConstructor.newInstance(arg*);}</td>
-     * </tr>
-     * <tr>
-     *     <td>{@link java.lang.invoke.MethodHandles.Lookup#unreflect lookup.unreflect(aMethod)}</td>
-     *     <td>({@code static})?<br>{@code T m(A*);}</td><td>{@code (T) aMethod.invoke(thisOrNull, arg*);}</td>
-     * </tr>
-     * </table>
-     *
-     * Here, the type {@code C} is the class or interface being searched for a member,
-     * documented as a parameter named {@code refc} in the lookup methods.
-     * The method type {@code MT} is composed from the return type {@code T}
-     * and the sequence of argument types {@code A*}.
-     * The constructor also has a sequence of argument types {@code A*} and
-     * is deemed to return the newly-created object of type {@code C}.
-     * Both {@code MT} and the field type {@code FT} are documented as a parameter named {@code type}.
-     * The formal parameter {@code this} stands for the self-reference of type {@code C};
-     * if it is present, it is always the leading argument to the method handle invocation.
-     * (In the case of some {@code protected} members, {@code this} may be
-     * restricted in type to the lookup class; see below.)
-     * The name {@code arg} stands for all the other method handle arguments.
-     * In the code examples for the Core Reflection API, the name {@code thisOrNull}
-     * stands for a null reference if the accessed method or field is static,
-     * and {@code this} otherwise.
-     * The names {@code aMethod}, {@code aField}, and {@code aConstructor} stand
-     * for reflective objects corresponding to the given members.
-     * <p>
-     * In cases where the given member is of variable arity (i.e., a method or constructor)
-     * the returned method handle will also be of {@linkplain MethodHandle#asVarargsCollector variable arity}.
-     * In all other cases, the returned method handle will be of fixed arity.
-     * <p style="font-size:smaller;">
-     * <em>Discussion:</em>
-     * The equivalence between looked-up method handles and underlying
-     * class members and bytecode behaviors
-     * can break down in a few ways:
-     * <ul style="font-size:smaller;">
-     * <li>If {@code C} is not symbolically accessible from the lookup class's loader,
-     * the lookup can still succeed, even when there is no equivalent
-     * Java expression or bytecoded constant.
-     * <li>Likewise, if {@code T} or {@code MT}
-     * is not symbolically accessible from the lookup class's loader,
-     * the lookup can still succeed.
-     * For example, lookups for {@code MethodHandle.invokeExact} and
-     * {@code MethodHandle.invoke} will always succeed, regardless of requested type.
-     * <li>If there is a security manager installed, it can forbid the lookup
-     * on various grounds (<a href="MethodHandles.Lookup.html#secmgr">see below</a>).
-     * By contrast, the {@code ldc} instruction on a {@code CONSTANT_MethodHandle}
-     * constant is not subject to security manager checks.
-     * <li>If the looked-up method has a
-     * <a href="MethodHandle.html#maxarity">very large arity</a>,
-     * the method handle creation may fail, due to the method handle
-     * type having too many parameters.
-     * </ul>
-     *
-     * <h1><a name="access"></a>Access checking</h1>
-     * Access checks are applied in the factory methods of {@code Lookup},
-     * when a method handle is created.
-     * This is a key difference from the Core Reflection API, since
-     * {@link java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}
-     * performs access checking against every caller, on every call.
-     * <p>
-     * All access checks start from a {@code Lookup} object, which
-     * compares its recorded lookup class against all requests to
-     * create method handles.
-     * A single {@code Lookup} object can be used to create any number
-     * of access-checked method handles, all checked against a single
-     * lookup class.
-     * <p>
-     * A {@code Lookup} object can be shared with other trusted code,
-     * such as a metaobject protocol.
-     * A shared {@code Lookup} object delegates the capability
-     * to create method handles on private members of the lookup class.
-     * Even if privileged code uses the {@code Lookup} object,
-     * the access checking is confined to the privileges of the
-     * original lookup class.
-     * <p>
-     * A lookup can fail, because
-     * the containing class is not accessible to the lookup class, or
-     * because the desired class member is missing, or because the
-     * desired class member is not accessible to the lookup class, or
-     * because the lookup object is not trusted enough to access the member.
-     * In any of these cases, a {@code ReflectiveOperationException} will be
-     * thrown from the attempted lookup.  The exact class will be one of
-     * the following:
-     * <ul>
-     * <li>NoSuchMethodException &mdash; if a method is requested but does not exist
-     * <li>NoSuchFieldException &mdash; if a field is requested but does not exist
-     * <li>IllegalAccessException &mdash; if the member exists but an access check fails
-     * </ul>
-     * <p>
-     * In general, the conditions under which a method handle may be
-     * looked up for a method {@code M} are no more restrictive than the conditions
-     * under which the lookup class could have compiled, verified, and resolved a call to {@code M}.
-     * Where the JVM would raise exceptions like {@code NoSuchMethodError},
-     * a method handle lookup will generally raise a corresponding
-     * checked exception, such as {@code NoSuchMethodException}.
-     * And the effect of invoking the method handle resulting from the lookup
-     * is <a href="MethodHandles.Lookup.html#equiv">exactly equivalent</a>
-     * to executing the compiled, verified, and resolved call to {@code M}.
-     * The same point is true of fields and constructors.
-     * <p style="font-size:smaller;">
-     * <em>Discussion:</em>
-     * Access checks only apply to named and reflected methods,
-     * constructors, and fields.
-     * Other method handle creation methods, such as
-     * {@link MethodHandle#asType MethodHandle.asType},
-     * do not require any access checks, and are used
-     * independently of any {@code Lookup} object.
-     * <p>
-     * If the desired member is {@code protected}, the usual JVM rules apply,
-     * including the requirement that the lookup class must be either be in the
-     * same package as the desired member, or must inherit that member.
-     * (See the Java Virtual Machine Specification, sections 4.9.2, 5.4.3.5, and 6.4.)
-     * In addition, if the desired member is a non-static field or method
-     * in a different package, the resulting method handle may only be applied
-     * to objects of the lookup class or one of its subclasses.
-     * This requirement is enforced by narrowing the type of the leading
-     * {@code this} parameter from {@code C}
-     * (which will necessarily be a superclass of the lookup class)
-     * to the lookup class itself.
-     * <p>
-     * The JVM imposes a similar requirement on {@code invokespecial} instruction,
-     * that the receiver argument must match both the resolved method <em>and</em>
-     * the current class.  Again, this requirement is enforced by narrowing the
-     * type of the leading parameter to the resulting method handle.
-     * (See the Java Virtual Machine Specification, section 4.10.1.9.)
-     * <p>
-     * The JVM represents constructors and static initializer blocks as internal methods
-     * with special names ({@code "<init>"} and {@code "<clinit>"}).
-     * The internal syntax of invocation instructions allows them to refer to such internal
-     * methods as if they were normal methods, but the JVM bytecode verifier rejects them.
-     * A lookup of such an internal method will produce a {@code NoSuchMethodException}.
-     * <p>
-     * In some cases, access between nested classes is obtained by the Java compiler by creating
-     * an wrapper method to access a private method of another class
-     * in the same top-level declaration.
-     * For example, a nested class {@code C.D}
-     * can access private members within other related classes such as
-     * {@code C}, {@code C.D.E}, or {@code C.B},
-     * but the Java compiler may need to generate wrapper methods in
-     * those related classes.  In such cases, a {@code Lookup} object on
-     * {@code C.E} would be unable to those private members.
-     * A workaround for this limitation is the {@link Lookup#in Lookup.in} method,
-     * which can transform a lookup on {@code C.E} into one on any of those other
-     * classes, without special elevation of privilege.
-     * <p>
-     * The accesses permitted to a given lookup object may be limited,
-     * according to its set of {@link #lookupModes lookupModes},
-     * to a subset of members normally accessible to the lookup class.
-     * For example, the {@link #publicLookup publicLookup}
-     * method produces a lookup object which is only allowed to access
-     * public members in public classes.
-     * The caller sensitive method {@link #lookup lookup}
-     * produces a lookup object with full capabilities relative to
-     * its caller class, to emulate all supported bytecode behaviors.
-     * Also, the {@link Lookup#in Lookup.in} method may produce a lookup object
-     * with fewer access modes than the original lookup object.
-     *
-     * <p style="font-size:smaller;">
-     * <a name="privacc"></a>
-     * <em>Discussion of private access:</em>
-     * We say that a lookup has <em>private access</em>
-     * if its {@linkplain #lookupModes lookup modes}
-     * include the possibility of accessing {@code private} members.
-     * As documented in the relevant methods elsewhere,
-     * only lookups with private access possess the following capabilities:
-     * <ul style="font-size:smaller;">
-     * <li>access private fields, methods, and constructors of the lookup class
-     * <li>create method handles which invoke <a href="MethodHandles.Lookup.html#callsens">caller sensitive</a> methods,
-     *     such as {@code Class.forName}
-     * <li>create method handles which {@link Lookup#findSpecial emulate invokespecial} instructions
-     * <li>avoid <a href="MethodHandles.Lookup.html#secmgr">package access checks</a>
-     *     for classes accessible to the lookup class
-     * <li>create {@link Lookup#in delegated lookup objects} which have private access to other classes
-     *     within the same package member
-     * </ul>
-     * <p style="font-size:smaller;">
-     * Each of these permissions is a consequence of the fact that a lookup object
-     * with private access can be securely traced back to an originating class,
-     * whose <a href="MethodHandles.Lookup.html#equiv">bytecode behaviors</a> and Java language access permissions
-     * can be reliably determined and emulated by method handles.
-     *
-     * <h1><a name="secmgr"></a>Security manager interactions</h1>
-     * Although bytecode instructions can only refer to classes in
-     * a related class loader, this API can search for methods in any
-     * class, as long as a reference to its {@code Class} object is
-     * available.  Such cross-loader references are also possible with the
-     * Core Reflection API, and are impossible to bytecode instructions
-     * such as {@code invokestatic} or {@code getfield}.
-     * There is a {@linkplain java.lang.SecurityManager security manager API}
-     * to allow applications to check such cross-loader references.
-     * These checks apply to both the {@code MethodHandles.Lookup} API
-     * and the Core Reflection API
-     * (as found on {@link java.lang.Class Class}).
-     * <p>
-     * If a security manager is present, member lookups are subject to
-     * additional checks.
-     * From one to three calls are made to the security manager.
-     * Any of these calls can refuse access by throwing a
-     * {@link java.lang.SecurityException SecurityException}.
-     * Define {@code smgr} as the security manager,
-     * {@code lookc} as the lookup class of the current lookup object,
-     * {@code refc} as the containing class in which the member
-     * is being sought, and {@code defc} as the class in which the
-     * member is actually defined.
-     * The value {@code lookc} is defined as <em>not present</em>
-     * if the current lookup object does not have
-     * <a href="MethodHandles.Lookup.html#privacc">private access</a>.
-     * The calls are made according to the following rules:
-     * <ul>
-     * <li><b>Step 1:</b>
-     *     If {@code lookc} is not present, or if its class loader is not
-     *     the same as or an ancestor of the class loader of {@code refc},
-     *     then {@link SecurityManager#checkPackageAccess
-     *     smgr.checkPackageAccess(refcPkg)} is called,
-     *     where {@code refcPkg} is the package of {@code refc}.
-     * <li><b>Step 2:</b>
-     *     If the retrieved member is not public and
-     *     {@code lookc} is not present, then
-     *     {@link SecurityManager#checkPermission smgr.checkPermission}
-     *     with {@code RuntimePermission("accessDeclaredMembers")} is called.
-     * <li><b>Step 3:</b>
-     *     If the retrieved member is not public,
-     *     and if {@code lookc} is not present,
-     *     and if {@code defc} and {@code refc} are different,
-     *     then {@link SecurityManager#checkPackageAccess
-     *     smgr.checkPackageAccess(defcPkg)} is called,
-     *     where {@code defcPkg} is the package of {@code defc}.
-     * </ul>
-     * Security checks are performed after other access checks have passed.
-     * Therefore, the above rules presuppose a member that is public,
-     * or else that is being accessed from a lookup class that has
-     * rights to access the member.
-     *
-     * <h1><a name="callsens"></a>Caller sensitive methods</h1>
-     * A small number of Java methods have a special property called caller sensitivity.
-     * A <em>caller-sensitive</em> method can behave differently depending on the
-     * identity of its immediate caller.
-     * <p>
-     * If a method handle for a caller-sensitive method is requested,
-     * the general rules for <a href="MethodHandles.Lookup.html#equiv">bytecode behaviors</a> apply,
-     * but they take account of the lookup class in a special way.
-     * The resulting method handle behaves as if it were called
-     * from an instruction contained in the lookup class,
-     * so that the caller-sensitive method detects the lookup class.
-     * (By contrast, the invoker of the method handle is disregarded.)
-     * Thus, in the case of caller-sensitive methods,
-     * different lookup classes may give rise to
-     * differently behaving method handles.
-     * <p>
-     * In cases where the lookup object is
-     * {@link #publicLookup publicLookup()},
-     * or some other lookup object without
-     * <a href="MethodHandles.Lookup.html#privacc">private access</a>,
-     * the lookup class is disregarded.
-     * In such cases, no caller-sensitive method handle can be created,
-     * access is forbidden, and the lookup fails with an
-     * {@code IllegalAccessException}.
-     * <p style="font-size:smaller;">
-     * <em>Discussion:</em>
-     * For example, the caller-sensitive method
-     * {@link java.lang.Class#forName(String) Class.forName(x)}
-     * can return varying classes or throw varying exceptions,
-     * depending on the class loader of the class that calls it.
-     * A public lookup of {@code Class.forName} will fail, because
-     * there is no reasonable way to determine its bytecode behavior.
-     * <p style="font-size:smaller;">
-     * If an application caches method handles for broad sharing,
-     * it should use {@code publicLookup()} to create them.
-     * If there is a lookup of {@code Class.forName}, it will fail,
-     * and the application must take appropriate action in that case.
-     * It may be that a later lookup, perhaps during the invocation of a
-     * bootstrap method, can incorporate the specific identity
-     * of the caller, making the method accessible.
-     * <p style="font-size:smaller;">
-     * The function {@code MethodHandles.lookup} is caller sensitive
-     * so that there can be a secure foundation for lookups.
-     * Nearly all other methods in the JSR 292 API rely on lookup
-     * objects to check access requests.
-     */
-    // Android-changed: Change link targets from MethodHandles#[public]Lookup to
-    // #[public]Lookup to work around complaints from javadoc.
     public static final
     class Lookup {
-        /** The class on behalf of whom the lookup is being performed. */
-        /* @NonNull */ private final Class<?> lookupClass;
+        public static final int PUBLIC = 0;
 
-        /** The allowed sorts of members which may be looked up (PUBLIC, etc.). */
-        private final int allowedModes;
+        public static final int PRIVATE = 0;
 
-        /** A single-bit mask representing {@code public} access,
-         *  which may contribute to the result of {@link #lookupModes lookupModes}.
-         *  The value, {@code 0x01}, happens to be the same as the value of the
-         *  {@code public} {@linkplain java.lang.reflect.Modifier#PUBLIC modifier bit}.
-         */
-        public static final int PUBLIC = Modifier.PUBLIC;
+        public static final int PROTECTED = 0;
 
-        /** A single-bit mask representing {@code private} access,
-         *  which may contribute to the result of {@link #lookupModes lookupModes}.
-         *  The value, {@code 0x02}, happens to be the same as the value of the
-         *  {@code private} {@linkplain java.lang.reflect.Modifier#PRIVATE modifier bit}.
-         */
-        public static final int PRIVATE = Modifier.PRIVATE;
+        public static final int PACKAGE =  0;
 
-        /** A single-bit mask representing {@code protected} access,
-         *  which may contribute to the result of {@link #lookupModes lookupModes}.
-         *  The value, {@code 0x04}, happens to be the same as the value of the
-         *  {@code protected} {@linkplain java.lang.reflect.Modifier#PROTECTED modifier bit}.
-         */
-        public static final int PROTECTED = Modifier.PROTECTED;
+        public Class<?> lookupClass() { return null; }
 
-        /** A single-bit mask representing {@code package} access (default access),
-         *  which may contribute to the result of {@link #lookupModes lookupModes}.
-         *  The value is {@code 0x08}, which does not correspond meaningfully to
-         *  any particular {@linkplain java.lang.reflect.Modifier modifier bit}.
-         */
-        public static final int PACKAGE = Modifier.STATIC;
+        public int lookupModes() { return 0; }
 
-        private static final int ALL_MODES = (PUBLIC | PRIVATE | PROTECTED | PACKAGE);
+        public Lookup in(Class<?> requestedLookupClass) { return null; }
 
-        // Android-note: Android has no notion of a trusted lookup. If required, such lookups
-        // are performed by the runtime. As a result, we always use lookupClass, which will always
-        // be non-null in our implementation.
-        //
-        // private static final int TRUSTED   = -1;
-
-        private static int fixmods(int mods) {
-            mods &= (ALL_MODES - PACKAGE);
-            return (mods != 0) ? mods : PACKAGE;
-        }
-
-        /** Tells which class is performing the lookup.  It is this class against
-         *  which checks are performed for visibility and access permissions.
-         *  <p>
-         *  The class implies a maximum level of access permission,
-         *  but the permissions may be additionally limited by the bitmask
-         *  {@link #lookupModes lookupModes}, which controls whether non-public members
-         *  can be accessed.
-         *  @return the lookup class, on behalf of which this lookup object finds members
-         */
-        public Class<?> lookupClass() {
-            return lookupClass;
-        }
-
-        /** Tells which access-protection classes of members this lookup object can produce.
-         *  The result is a bit-mask of the bits
-         *  {@linkplain #PUBLIC PUBLIC (0x01)},
-         *  {@linkplain #PRIVATE PRIVATE (0x02)},
-         *  {@linkplain #PROTECTED PROTECTED (0x04)},
-         *  and {@linkplain #PACKAGE PACKAGE (0x08)}.
-         *  <p>
-         *  A freshly-created lookup object
-         *  on the {@linkplain java.lang.invoke.MethodHandles#lookup() caller's class}
-         *  has all possible bits set, since the caller class can access all its own members.
-         *  A lookup object on a new lookup class
-         *  {@linkplain java.lang.invoke.MethodHandles.Lookup#in created from a previous lookup object}
-         *  may have some mode bits set to zero.
-         *  The purpose of this is to restrict access via the new lookup object,
-         *  so that it can access only names which can be reached by the original
-         *  lookup object, and also by the new lookup class.
-         *  @return the lookup modes, which limit the kinds of access performed by this lookup object
-         */
-        public int lookupModes() {
-            return allowedModes & ALL_MODES;
-        }
-
-        /** Embody the current class (the lookupClass) as a lookup class
-         * for method handle creation.
-         * Must be called by from a method in this package,
-         * which in turn is called by a method not in this package.
-         */
-        Lookup(Class<?> lookupClass) {
-            this(lookupClass, ALL_MODES);
-            // make sure we haven't accidentally picked up a privileged class:
-            checkUnprivilegedlookupClass(lookupClass, ALL_MODES);
-        }
-
-        private Lookup(Class<?> lookupClass, int allowedModes) {
-            this.lookupClass = lookupClass;
-            this.allowedModes = allowedModes;
-        }
-
-        /**
-         * Creates a lookup on the specified new lookup class.
-         * The resulting object will report the specified
-         * class as its own {@link #lookupClass lookupClass}.
-         * <p>
-         * However, the resulting {@code Lookup} object is guaranteed
-         * to have no more access capabilities than the original.
-         * In particular, access capabilities can be lost as follows:<ul>
-         * <li>If the new lookup class differs from the old one,
-         * protected members will not be accessible by virtue of inheritance.
-         * (Protected members may continue to be accessible because of package sharing.)
-         * <li>If the new lookup class is in a different package
-         * than the old one, protected and default (package) members will not be accessible.
-         * <li>If the new lookup class is not within the same package member
-         * as the old one, private members will not be accessible.
-         * <li>If the new lookup class is not accessible to the old lookup class,
-         * then no members, not even public members, will be accessible.
-         * (In all other cases, public members will continue to be accessible.)
-         * </ul>
-         *
-         * @param requestedLookupClass the desired lookup class for the new lookup object
-         * @return a lookup object which reports the desired lookup class
-         * @throws NullPointerException if the argument is null
-         */
-        public Lookup in(Class<?> requestedLookupClass) {
-            requestedLookupClass.getClass();  // null check
-            // Android-changed: There's no notion of a trusted lookup.
-            // if (allowedModes == TRUSTED)  // IMPL_LOOKUP can make any lookup at all
-            //    return new Lookup(requestedLookupClass, ALL_MODES);
-
-            if (requestedLookupClass == this.lookupClass)
-                return this;  // keep same capabilities
-            int newModes = (allowedModes & (ALL_MODES & ~PROTECTED));
-            if ((newModes & PACKAGE) != 0
-                && !VerifyAccess.isSamePackage(this.lookupClass, requestedLookupClass)) {
-                newModes &= ~(PACKAGE|PRIVATE);
-            }
-            // Allow nestmate lookups to be created without special privilege:
-            if ((newModes & PRIVATE) != 0
-                && !VerifyAccess.isSamePackageMember(this.lookupClass, requestedLookupClass)) {
-                newModes &= ~PRIVATE;
-            }
-            if ((newModes & PUBLIC) != 0
-                && !VerifyAccess.isClassAccessible(requestedLookupClass, this.lookupClass, allowedModes)) {
-                // The requested class it not accessible from the lookup class.
-                // No permissions.
-                newModes = 0;
-            }
-            checkUnprivilegedlookupClass(requestedLookupClass, newModes);
-            return new Lookup(requestedLookupClass, newModes);
-        }
-
-        // Make sure outer class is initialized first.
-        //
-        // Android-changed: Removed unnecessary reference to IMPL_NAMES.
-        // static { IMPL_NAMES.getClass(); }
-
-        /** Version of lookup which is trusted minimally.
-         *  It can only be used to create method handles to
-         *  publicly accessible members.
-         */
-        static final Lookup PUBLIC_LOOKUP = new Lookup(Object.class, PUBLIC);
-
-        /** Package-private version of lookup which is trusted. */
-        static final Lookup IMPL_LOOKUP = new Lookup(Object.class, ALL_MODES);
-
-        private static void checkUnprivilegedlookupClass(Class<?> lookupClass, int allowedModes) {
-            String name = lookupClass.getName();
-            if (name.startsWith("java.lang.invoke."))
-                throw newIllegalArgumentException("illegal lookupClass: "+lookupClass);
-
-            // For caller-sensitive MethodHandles.lookup()
-            // disallow lookup more restricted packages
-            //
-            // Android-changed: The bootstrap classloader isn't null.
-            if (allowedModes == ALL_MODES &&
-                    lookupClass.getClassLoader() == Object.class.getClassLoader()) {
-                if (name.startsWith("java.") ||
-                        (name.startsWith("sun.")
-                                && !name.startsWith("sun.invoke.")
-                                && !name.equals("sun.reflect.ReflectionFactory"))) {
-                    throw newIllegalArgumentException("illegal lookupClass: " + lookupClass);
-                }
-            }
-        }
-
-        /**
-         * Displays the name of the class from which lookups are to be made.
-         * (The name is the one reported by {@link java.lang.Class#getName() Class.getName}.)
-         * If there are restrictions on the access permitted to this lookup,
-         * this is indicated by adding a suffix to the class name, consisting
-         * of a slash and a keyword.  The keyword represents the strongest
-         * allowed access, and is chosen as follows:
-         * <ul>
-         * <li>If no access is allowed, the suffix is "/noaccess".
-         * <li>If only public access is allowed, the suffix is "/public".
-         * <li>If only public and package access are allowed, the suffix is "/package".
-         * <li>If only public, package, and private access are allowed, the suffix is "/private".
-         * </ul>
-         * If none of the above cases apply, it is the case that full
-         * access (public, package, private, and protected) is allowed.
-         * In this case, no suffix is added.
-         * This is true only of an object obtained originally from
-         * {@link java.lang.invoke.MethodHandles#lookup MethodHandles.lookup}.
-         * Objects created by {@link java.lang.invoke.MethodHandles.Lookup#in Lookup.in}
-         * always have restricted access, and will display a suffix.
-         * <p>
-         * (It may seem strange that protected access should be
-         * stronger than private access.  Viewed independently from
-         * package access, protected access is the first to be lost,
-         * because it requires a direct subclass relationship between
-         * caller and callee.)
-         * @see #in
-         */
-        @Override
-        public String toString() {
-            String cname = lookupClass.getName();
-            switch (allowedModes) {
-            case 0:  // no privileges
-                return cname + "/noaccess";
-            case PUBLIC:
-                return cname + "/public";
-            case PUBLIC|PACKAGE:
-                return cname + "/package";
-            case ALL_MODES & ~PROTECTED:
-                return cname + "/private";
-            case ALL_MODES:
-                return cname;
-            // Android-changed: No support for TRUSTED callers.
-            // case TRUSTED:
-            //    return "/trusted";  // internal only; not exported
-            default:  // Should not happen, but it's a bitfield...
-                cname = cname + "/" + Integer.toHexString(allowedModes);
-                assert(false) : cname;
-                return cname;
-            }
-        }
-
-        /**
-         * Produces a method handle for a static method.
-         * The type of the method handle will be that of the method.
-         * (Since static methods do not take receivers, there is no
-         * additional receiver argument inserted into the method handle type,
-         * as there would be with {@link #findVirtual findVirtual} or {@link #findSpecial findSpecial}.)
-         * The method and all its argument types must be accessible to the lookup object.
-         * <p>
-         * The returned method handle will have
-         * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
-         * the method's variable arity modifier bit ({@code 0x0080}) is set.
-         * <p>
-         * If the returned method handle is invoked, the method's class will
-         * be initialized, if it has not already been initialized.
-         * <p><b>Example:</b>
-         * <blockquote><pre>{@code
-import static java.lang.invoke.MethodHandles.*;
-import static java.lang.invoke.MethodType.*;
-...
-MethodHandle MH_asList = publicLookup().findStatic(Arrays.class,
-  "asList", methodType(List.class, Object[].class));
-assertEquals("[x, y]", MH_asList.invoke("x", "y").toString());
-         * }</pre></blockquote>
-         * @param refc the class from which the method is accessed
-         * @param name the name of the method
-         * @param type the type of the method
-         * @return the desired method handle
-         * @throws NoSuchMethodException if the method does not exist
-         * @throws IllegalAccessException if access checking fails,
-         *                                or if the method is not {@code static},
-         *                                or if the method's variable arity modifier bit
-         *                                is set and {@code asVarargsCollector} fails
-         * @exception SecurityException if a security manager is present and it
-         *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
-         * @throws NullPointerException if any argument is null
-         */
         public
-        MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
-            Method method = refc.getDeclaredMethod(name, type.ptypes());
-            final int modifiers = method.getModifiers();
-            if (!Modifier.isStatic(modifiers)) {
-                throw new IllegalAccessException("Method" + method + " is not static");
-            }
-            checkReturnType(method, type);
-            checkAccess(refc, method.getDeclaringClass(), modifiers, method.getName());
-            return createMethodHandle(method, MethodHandle.INVOKE_STATIC, type);
-        }
+        MethodHandle findStatic(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException { return null; }
 
-        private MethodHandle findVirtualForMH(String name, MethodType type) {
-            // these names require special lookups because of the implicit MethodType argument
-            if ("invoke".equals(name))
-                return invoker(type);
-            if ("invokeExact".equals(name))
-                return exactInvoker(type);
-            return null;
-        }
+        public MethodHandle findVirtual(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException { return null; }
 
-        private static MethodHandle createMethodHandle(Method method, int handleKind,
-                                                       MethodType methodType) {
-            MethodHandle mh = new MethodHandleImpl(method.getArtMethod(), handleKind, methodType);
-            if (method.isVarArgs()) {
-                return new Transformers.VarargsCollector(mh);
-            } else {
-                return mh;
-            }
-        }
+        public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoSuchMethodException, IllegalAccessException { return null; }
 
-        /**
-         * Produces a method handle for a virtual method.
-         * The type of the method handle will be that of the method,
-         * with the receiver type (usually {@code refc}) prepended.
-         * The method and all its argument types must be accessible to the lookup object.
-         * <p>
-         * When called, the handle will treat the first argument as a receiver
-         * and dispatch on the receiver's type to determine which method
-         * implementation to enter.
-         * (The dispatching action is identical with that performed by an
-         * {@code invokevirtual} or {@code invokeinterface} instruction.)
-         * <p>
-         * The first argument will be of type {@code refc} if the lookup
-         * class has full privileges to access the member.  Otherwise
-         * the member must be {@code protected} and the first argument
-         * will be restricted in type to the lookup class.
-         * <p>
-         * The returned method handle will have
-         * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
-         * the method's variable arity modifier bit ({@code 0x0080}) is set.
-         * <p>
-         * Because of the general <a href="MethodHandles.Lookup.html#equiv">equivalence</a> between {@code invokevirtual}
-         * instructions and method handles produced by {@code findVirtual},
-         * if the class is {@code MethodHandle} and the name string is
-         * {@code invokeExact} or {@code invoke}, the resulting
-         * method handle is equivalent to one produced by
-         * {@link java.lang.invoke.MethodHandles#exactInvoker MethodHandles.exactInvoker} or
-         * {@link java.lang.invoke.MethodHandles#invoker MethodHandles.invoker}
-         * with the same {@code type} argument.
-         *
-         * <b>Example:</b>
-         * <blockquote><pre>{@code
-import static java.lang.invoke.MethodHandles.*;
-import static java.lang.invoke.MethodType.*;
-...
-MethodHandle MH_concat = publicLookup().findVirtual(String.class,
-  "concat", methodType(String.class, String.class));
-MethodHandle MH_hashCode = publicLookup().findVirtual(Object.class,
-  "hashCode", methodType(int.class));
-MethodHandle MH_hashCode_String = publicLookup().findVirtual(String.class,
-  "hashCode", methodType(int.class));
-assertEquals("xy", (String) MH_concat.invokeExact("x", "y"));
-assertEquals("xy".hashCode(), (int) MH_hashCode.invokeExact((Object)"xy"));
-assertEquals("xy".hashCode(), (int) MH_hashCode_String.invokeExact("xy"));
-// interface method:
-MethodHandle MH_subSequence = publicLookup().findVirtual(CharSequence.class,
-  "subSequence", methodType(CharSequence.class, int.class, int.class));
-assertEquals("def", MH_subSequence.invoke("abcdefghi", 3, 6).toString());
-// constructor "internal method" must be accessed differently:
-MethodType MT_newString = methodType(void.class); //()V for new String()
-try { assertEquals("impossible", lookup()
-        .findVirtual(String.class, "<init>", MT_newString));
- } catch (NoSuchMethodException ex) { } // OK
-MethodHandle MH_newString = publicLookup()
-  .findConstructor(String.class, MT_newString);
-assertEquals("", (String) MH_newString.invokeExact());
-         * }</pre></blockquote>
-         *
-         * @param refc the class or interface from which the method is accessed
-         * @param name the name of the method
-         * @param type the type of the method, with the receiver argument omitted
-         * @return the desired method handle
-         * @throws NoSuchMethodException if the method does not exist
-         * @throws IllegalAccessException if access checking fails,
-         *                                or if the method is {@code static}
-         *                                or if the method's variable arity modifier bit
-         *                                is set and {@code asVarargsCollector} fails
-         * @exception SecurityException if a security manager is present and it
-         *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
-         * @throws NullPointerException if any argument is null
-         */
-        public MethodHandle findVirtual(Class<?> refc, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
-            // Special case : when we're looking up a virtual method on the MethodHandles class
-            // itself, we can return one of our specialized invokers.
-            if (refc == MethodHandle.class) {
-                MethodHandle mh = findVirtualForMH(name, type);
-                if (mh != null) {
-                    return mh;
-                }
-            }
-
-            Method method = refc.getInstanceMethod(name, type.ptypes());
-            if (method == null) {
-                // This is pretty ugly and a consequence of the MethodHandles API. We have to throw
-                // an IAE and not an NSME if the method exists but is static (even though the RI's
-                // IAE has a message that says "no such method"). We confine the ugliness and
-                // slowness to the failure case, and allow getInstanceMethod to remain fairly
-                // general.
-                try {
-                    Method m = refc.getDeclaredMethod(name, type.ptypes());
-                    if (Modifier.isStatic(m.getModifiers())) {
-                        throw new IllegalAccessException("Method" + m + " is static");
-                    }
-                } catch (NoSuchMethodException ignored) {
-                }
-
-                throw new NoSuchMethodException(name + " "  + Arrays.toString(type.ptypes()));
-            }
-            checkReturnType(method, type);
-
-            // We have a valid method, perform access checks.
-            checkAccess(refc, method.getDeclaringClass(), method.getModifiers(), method.getName());
-
-            // Insert the leading reference parameter.
-            MethodType handleType = type.insertParameterTypes(0, refc);
-            return createMethodHandle(method, MethodHandle.INVOKE_VIRTUAL, handleType);
-        }
-
-        /**
-         * Produces a method handle which creates an object and initializes it, using
-         * the constructor of the specified type.
-         * The parameter types of the method handle will be those of the constructor,
-         * while the return type will be a reference to the constructor's class.
-         * The constructor and all its argument types must be accessible to the lookup object.
-         * <p>
-         * The requested type must have a return type of {@code void}.
-         * (This is consistent with the JVM's treatment of constructor type descriptors.)
-         * <p>
-         * The returned method handle will have
-         * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
-         * the constructor's variable arity modifier bit ({@code 0x0080}) is set.
-         * <p>
-         * If the returned method handle is invoked, the constructor's class will
-         * be initialized, if it has not already been initialized.
-         * <p><b>Example:</b>
-         * <blockquote><pre>{@code
-import static java.lang.invoke.MethodHandles.*;
-import static java.lang.invoke.MethodType.*;
-...
-MethodHandle MH_newArrayList = publicLookup().findConstructor(
-  ArrayList.class, methodType(void.class, Collection.class));
-Collection orig = Arrays.asList("x", "y");
-Collection copy = (ArrayList) MH_newArrayList.invokeExact(orig);
-assert(orig != copy);
-assertEquals(orig, copy);
-// a variable-arity constructor:
-MethodHandle MH_newProcessBuilder = publicLookup().findConstructor(
-  ProcessBuilder.class, methodType(void.class, String[].class));
-ProcessBuilder pb = (ProcessBuilder)
-  MH_newProcessBuilder.invoke("x", "y", "z");
-assertEquals("[x, y, z]", pb.command().toString());
-         * }</pre></blockquote>
-         * @param refc the class or interface from which the method is accessed
-         * @param type the type of the method, with the receiver argument omitted, and a void return type
-         * @return the desired method handle
-         * @throws NoSuchMethodException if the constructor does not exist
-         * @throws IllegalAccessException if access checking fails
-         *                                or if the method's variable arity modifier bit
-         *                                is set and {@code asVarargsCollector} fails
-         * @exception SecurityException if a security manager is present and it
-         *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
-         * @throws NullPointerException if any argument is null
-         */
-        public MethodHandle findConstructor(Class<?> refc, MethodType type) throws NoSuchMethodException, IllegalAccessException {
-            if (refc.isArray()) {
-                throw new NoSuchMethodException("no constructor for array class: " + refc.getName());
-            }
-            // The queried |type| is (PT1,PT2,..)V
-            Constructor constructor = refc.getDeclaredConstructor(type.ptypes());
-            if (constructor == null) {
-                throw new NoSuchMethodException(
-                    "No constructor for " + constructor.getDeclaringClass() + " matching " + type);
-            }
-            checkAccess(refc, constructor.getDeclaringClass(), constructor.getModifiers(),
-                    constructor.getName());
-
-            return createMethodHandleForConstructor(constructor);
-        }
-
-        private MethodHandle createMethodHandleForConstructor(Constructor constructor) {
-            Class<?> refc = constructor.getDeclaringClass();
-            MethodType constructorType =
-                    MethodType.methodType(refc, constructor.getParameterTypes());
-            MethodHandle mh;
-            if (refc == String.class) {
-                // String constructors have optimized StringFactory methods
-                // that matches returned type. These factory methods combine the
-                // memory allocation and initialization calls for String objects.
-                mh = new MethodHandleImpl(constructor.getArtMethod(), MethodHandle.INVOKE_DIRECT,
-                                          constructorType);
-            } else {
-                // Constructors for all other classes use a Construct transformer to perform
-                // their memory allocation and call to <init>.
-                MethodType initType = initMethodType(constructorType);
-                MethodHandle initHandle = new MethodHandleImpl(
-                    constructor.getArtMethod(), MethodHandle.INVOKE_DIRECT, initType);
-                mh = new Transformers.Construct(initHandle, constructorType);
-            }
-
-            if (constructor.isVarArgs()) {
-                mh = new Transformers.VarargsCollector(mh);
-            }
-            return mh;
-        }
-
-        private static MethodType initMethodType(MethodType constructorType) {
-            // Returns a MethodType appropriate for class <init>
-            // methods. Constructor MethodTypes have the form
-            // (PT1,PT2,...)C and class <init> MethodTypes have the
-            // form (C,PT1,PT2,...)V.
-            assert constructorType.rtype() != void.class;
-
-            // Insert constructorType C as the first parameter type in
-            // the MethodType for <init>.
-            Class<?> [] initPtypes = new Class<?> [constructorType.ptypes().length + 1];
-            initPtypes[0] = constructorType.rtype();
-            System.arraycopy(constructorType.ptypes(), 0, initPtypes, 1,
-                             constructorType.ptypes().length);
-
-            // Set the return type for the <init> MethodType to be void.
-            return MethodType.methodType(void.class, initPtypes);
-        }
-
-        /**
-         * Produces an early-bound method handle for a virtual method.
-         * It will bypass checks for overriding methods on the receiver,
-         * <a href="MethodHandles.Lookup.html#equiv">as if called</a> from an {@code invokespecial}
-         * instruction from within the explicitly specified {@code specialCaller}.
-         * The type of the method handle will be that of the method,
-         * with a suitably restricted receiver type prepended.
-         * (The receiver type will be {@code specialCaller} or a subtype.)
-         * The method and all its argument types must be accessible
-         * to the lookup object.
-         * <p>
-         * Before method resolution,
-         * if the explicitly specified caller class is not identical with the
-         * lookup class, or if this lookup object does not have
-         * <a href="MethodHandles.Lookup.html#privacc">private access</a>
-         * privileges, the access fails.
-         * <p>
-         * The returned method handle will have
-         * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
-         * the method's variable arity modifier bit ({@code 0x0080}) is set.
-         * <p style="font-size:smaller;">
-         * <em>(Note:  JVM internal methods named {@code "<init>"} are not visible to this API,
-         * even though the {@code invokespecial} instruction can refer to them
-         * in special circumstances.  Use {@link #findConstructor findConstructor}
-         * to access instance initialization methods in a safe manner.)</em>
-         * <p><b>Example:</b>
-         * <blockquote><pre>{@code
-import static java.lang.invoke.MethodHandles.*;
-import static java.lang.invoke.MethodType.*;
-...
-static class Listie extends ArrayList {
-  public String toString() { return "[wee Listie]"; }
-  static Lookup lookup() { return MethodHandles.lookup(); }
-}
-...
-// no access to constructor via invokeSpecial:
-MethodHandle MH_newListie = Listie.lookup()
-  .findConstructor(Listie.class, methodType(void.class));
-Listie l = (Listie) MH_newListie.invokeExact();
-try { assertEquals("impossible", Listie.lookup().findSpecial(
-        Listie.class, "<init>", methodType(void.class), Listie.class));
- } catch (NoSuchMethodException ex) { } // OK
-// access to super and self methods via invokeSpecial:
-MethodHandle MH_super = Listie.lookup().findSpecial(
-  ArrayList.class, "toString" , methodType(String.class), Listie.class);
-MethodHandle MH_this = Listie.lookup().findSpecial(
-  Listie.class, "toString" , methodType(String.class), Listie.class);
-MethodHandle MH_duper = Listie.lookup().findSpecial(
-  Object.class, "toString" , methodType(String.class), Listie.class);
-assertEquals("[]", (String) MH_super.invokeExact(l));
-assertEquals(""+l, (String) MH_this.invokeExact(l));
-assertEquals("[]", (String) MH_duper.invokeExact(l)); // ArrayList method
-try { assertEquals("inaccessible", Listie.lookup().findSpecial(
-        String.class, "toString", methodType(String.class), Listie.class));
- } catch (IllegalAccessException ex) { } // OK
-Listie subl = new Listie() { public String toString() { return "[subclass]"; } };
-assertEquals(""+l, (String) MH_this.invokeExact(subl)); // Listie method
-         * }</pre></blockquote>
-         *
-         * @param refc the class or interface from which the method is accessed
-         * @param name the name of the method (which must not be "&lt;init&gt;")
-         * @param type the type of the method, with the receiver argument omitted
-         * @param specialCaller the proposed calling class to perform the {@code invokespecial}
-         * @return the desired method handle
-         * @throws NoSuchMethodException if the method does not exist
-         * @throws IllegalAccessException if access checking fails
-         *                                or if the method's variable arity modifier bit
-         *                                is set and {@code asVarargsCollector} fails
-         * @exception SecurityException if a security manager is present and it
-         *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
-         * @throws NullPointerException if any argument is null
-         */
         public MethodHandle findSpecial(Class<?> refc, String name, MethodType type,
-                                        Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
-            if (specialCaller == null) {
-                throw new NullPointerException("specialCaller == null");
-            }
+                                        Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException { return null; }
 
-            if (type == null) {
-                throw new NullPointerException("type == null");
-            }
+        public MethodHandle findGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException { return null; }
 
-            if (name == null) {
-                throw new NullPointerException("name == null");
-            }
+        public MethodHandle findSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException { return null; }
 
-            if (refc == null) {
-                throw new NullPointerException("ref == null");
-            }
+        public MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException { return null; }
 
-            // Make sure that the special caller is identical to the lookup class or that we have
-            // private access.
-            checkSpecialCaller(specialCaller);
+        public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException { return null; }
 
-            // Even though constructors are invoked using a "special" invoke, handles to them can't
-            // be created using findSpecial. Callers must use findConstructor instead. Similarly,
-            // there is no path for calling static class initializers.
-            if (name.startsWith("<")) {
-                throw new NoSuchMethodException(name + " is not a valid method name.");
-            }
+        public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException { return null; }
 
-            Method method = refc.getDeclaredMethod(name, type.ptypes());
-            checkReturnType(method, type);
-            return findSpecial(method, type, refc, specialCaller);
-        }
+        public MethodHandle unreflect(Method m) throws IllegalAccessException { return null; }
 
-        private MethodHandle findSpecial(Method method, MethodType type,
-                                         Class<?> refc, Class<?> specialCaller)
-                throws IllegalAccessException {
-            if (Modifier.isStatic(method.getModifiers())) {
-                throw new IllegalAccessException("expected a non-static method:" + method);
-            }
+        public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws IllegalAccessException { return null; }
 
-            if (Modifier.isPrivate(method.getModifiers())) {
-                // Since this is a private method, we'll need to also make sure that the
-                // lookup class is the same as the refering class. We've already checked that
-                // the specialCaller is the same as the special lookup class, both of these must
-                // be the same as the declaring class(*) in order to access the private method.
-                //
-                // (*) Well, this isn't true for nested classes but OpenJDK doesn't support those
-                // either.
-                if (refc != lookupClass()) {
-                    throw new IllegalAccessException("no private access for invokespecial : "
-                            + refc + ", from" + this);
-                }
+        public MethodHandle unreflectConstructor(Constructor<?> c) throws IllegalAccessException { return null; }
 
-                // This is a private method, so there's nothing special to do.
-                MethodType handleType = type.insertParameterTypes(0, refc);
-                return createMethodHandle(method, MethodHandle.INVOKE_DIRECT, handleType);
-            }
+        public MethodHandle unreflectGetter(Field f) throws IllegalAccessException { return null; }
 
-            // This is a public, protected or package-private method, which means we're expecting
-            // invoke-super semantics. We'll have to restrict the receiver type appropriately on the
-            // handle once we check that there really is a "super" relationship between them.
-            if (!method.getDeclaringClass().isAssignableFrom(specialCaller)) {
-                throw new IllegalAccessException(refc + "is not assignable from " + specialCaller);
-            }
+        public MethodHandle unreflectSetter(Field f) throws IllegalAccessException { return null; }
 
-            // Note that we restrict the receiver to "specialCaller" instances.
-            MethodType handleType = type.insertParameterTypes(0, specialCaller);
-            return createMethodHandle(method, MethodHandle.INVOKE_SUPER, handleType);
-        }
+        public MethodHandleInfo revealDirect(MethodHandle target) { return null; }
 
-        /**
-         * Produces a method handle giving read access to a non-static field.
-         * The type of the method handle will have a return type of the field's
-         * value type.
-         * The method handle's single argument will be the instance containing
-         * the field.
-         * Access checking is performed immediately on behalf of the lookup class.
-         * @param refc the class or interface from which the method is accessed
-         * @param name the field's name
-         * @param type the field's type
-         * @return a method handle which can load values from the field
-         * @throws NoSuchFieldException if the field does not exist
-         * @throws IllegalAccessException if access checking fails, or if the field is {@code static}
-         * @exception SecurityException if a security manager is present and it
-         *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
-         * @throws NullPointerException if any argument is null
-         */
-        public MethodHandle findGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
-            return findAccessor(refc, name, type, MethodHandle.IGET);
-        }
-
-        private MethodHandle findAccessor(Class<?> refc, String name, Class<?> type, int kind)
-            throws NoSuchFieldException, IllegalAccessException {
-            final Field field = refc.getDeclaredField(name);
-            final Class<?> fieldType = field.getType();
-            if (fieldType != type) {
-                throw new NoSuchFieldException(
-                        "Field has wrong type: " + fieldType + " != " + type);
-            }
-
-            return findAccessor(field, refc, type, kind, true /* performAccessChecks */);
-        }
-
-        private MethodHandle findAccessor(Field field, Class<?> refc, Class<?> fieldType, int kind,
-                                          boolean performAccessChecks)
-                throws IllegalAccessException {
-            if (!performAccessChecks) {
-                checkAccess(refc, field.getDeclaringClass(), field.getModifiers(), field.getName());
-            }
-
-            final boolean isStaticKind = kind == MethodHandle.SGET || kind == MethodHandle.SPUT;
-            final int modifiers = field.getModifiers();
-            if (Modifier.isStatic(modifiers) != isStaticKind) {
-                String reason = "Field " + field + " is " +
-                        (isStaticKind ? "not " : "") + "static";
-                throw new IllegalAccessException(reason);
-            }
-
-            final boolean isSetterKind = kind == MethodHandle.IPUT || kind == MethodHandle.SPUT;
-            if (Modifier.isFinal(modifiers) && isSetterKind) {
-                throw new IllegalAccessException("Field " + field + " is final");
-            }
-
-            final MethodType methodType;
-            switch (kind) {
-                case MethodHandle.SGET:
-                    methodType = MethodType.methodType(fieldType);
-                    break;
-                case MethodHandle.SPUT:
-                    methodType = MethodType.methodType(void.class, fieldType);
-                    break;
-                case MethodHandle.IGET:
-                    methodType = MethodType.methodType(fieldType, refc);
-                    break;
-                case MethodHandle.IPUT:
-                    methodType = MethodType.methodType(void.class, refc, fieldType);
-                    break;
-                default:
-                    throw new IllegalArgumentException("Invalid kind " + kind);
-            }
-            return new MethodHandleImpl(field.getArtField(), kind, methodType);
-        }
-
-        /**
-         * Produces a method handle giving write access to a non-static field.
-         * The type of the method handle will have a void return type.
-         * The method handle will take two arguments, the instance containing
-         * the field, and the value to be stored.
-         * The second argument will be of the field's value type.
-         * Access checking is performed immediately on behalf of the lookup class.
-         * @param refc the class or interface from which the method is accessed
-         * @param name the field's name
-         * @param type the field's type
-         * @return a method handle which can store values into the field
-         * @throws NoSuchFieldException if the field does not exist
-         * @throws IllegalAccessException if access checking fails, or if the field is {@code static}
-         * @exception SecurityException if a security manager is present and it
-         *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
-         * @throws NullPointerException if any argument is null
-         */
-        public MethodHandle findSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
-            return findAccessor(refc, name, type, MethodHandle.IPUT);
-        }
-
-        /**
-         * Produces a method handle giving read access to a static field.
-         * The type of the method handle will have a return type of the field's
-         * value type.
-         * The method handle will take no arguments.
-         * Access checking is performed immediately on behalf of the lookup class.
-         * <p>
-         * If the returned method handle is invoked, the field's class will
-         * be initialized, if it has not already been initialized.
-         * @param refc the class or interface from which the method is accessed
-         * @param name the field's name
-         * @param type the field's type
-         * @return a method handle which can load values from the field
-         * @throws NoSuchFieldException if the field does not exist
-         * @throws IllegalAccessException if access checking fails, or if the field is not {@code static}
-         * @exception SecurityException if a security manager is present and it
-         *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
-         * @throws NullPointerException if any argument is null
-         */
-        public MethodHandle findStaticGetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
-            return findAccessor(refc, name, type, MethodHandle.SGET);
-        }
-
-        /**
-         * Produces a method handle giving write access to a static field.
-         * The type of the method handle will have a void return type.
-         * The method handle will take a single
-         * argument, of the field's value type, the value to be stored.
-         * Access checking is performed immediately on behalf of the lookup class.
-         * <p>
-         * If the returned method handle is invoked, the field's class will
-         * be initialized, if it has not already been initialized.
-         * @param refc the class or interface from which the method is accessed
-         * @param name the field's name
-         * @param type the field's type
-         * @return a method handle which can store values into the field
-         * @throws NoSuchFieldException if the field does not exist
-         * @throws IllegalAccessException if access checking fails, or if the field is not {@code static}
-         * @exception SecurityException if a security manager is present and it
-         *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
-         * @throws NullPointerException if any argument is null
-         */
-        public MethodHandle findStaticSetter(Class<?> refc, String name, Class<?> type) throws NoSuchFieldException, IllegalAccessException {
-            return findAccessor(refc, name, type, MethodHandle.SPUT);
-        }
-
-        /**
-         * Produces an early-bound method handle for a non-static method.
-         * The receiver must have a supertype {@code defc} in which a method
-         * of the given name and type is accessible to the lookup class.
-         * The method and all its argument types must be accessible to the lookup object.
-         * The type of the method handle will be that of the method,
-         * without any insertion of an additional receiver parameter.
-         * The given receiver will be bound into the method handle,
-         * so that every call to the method handle will invoke the
-         * requested method on the given receiver.
-         * <p>
-         * The returned method handle will have
-         * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
-         * the method's variable arity modifier bit ({@code 0x0080}) is set
-         * <em>and</em> the trailing array argument is not the only argument.
-         * (If the trailing array argument is the only argument,
-         * the given receiver value will be bound to it.)
-         * <p>
-         * This is equivalent to the following code:
-         * <blockquote><pre>{@code
-import static java.lang.invoke.MethodHandles.*;
-import static java.lang.invoke.MethodType.*;
-...
-MethodHandle mh0 = lookup().findVirtual(defc, name, type);
-MethodHandle mh1 = mh0.bindTo(receiver);
-MethodType mt1 = mh1.type();
-if (mh0.isVarargsCollector())
-  mh1 = mh1.asVarargsCollector(mt1.parameterType(mt1.parameterCount()-1));
-return mh1;
-         * }</pre></blockquote>
-         * where {@code defc} is either {@code receiver.getClass()} or a super
-         * type of that class, in which the requested method is accessible
-         * to the lookup class.
-         * (Note that {@code bindTo} does not preserve variable arity.)
-         * @param receiver the object from which the method is accessed
-         * @param name the name of the method
-         * @param type the type of the method, with the receiver argument omitted
-         * @return the desired method handle
-         * @throws NoSuchMethodException if the method does not exist
-         * @throws IllegalAccessException if access checking fails
-         *                                or if the method's variable arity modifier bit
-         *                                is set and {@code asVarargsCollector} fails
-         * @exception SecurityException if a security manager is present and it
-         *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
-         * @throws NullPointerException if any argument is null
-         * @see MethodHandle#bindTo
-         * @see #findVirtual
-         */
-        public MethodHandle bind(Object receiver, String name, MethodType type) throws NoSuchMethodException, IllegalAccessException {
-            MethodHandle handle = findVirtual(receiver.getClass(), name, type);
-            MethodHandle adapter = handle.bindTo(receiver);
-            MethodType adapterType = adapter.type();
-            if (handle.isVarargsCollector()) {
-                adapter = adapter.asVarargsCollector(
-                        adapterType.parameterType(adapterType.parameterCount() - 1));
-            }
-
-            return adapter;
-        }
-
-        /**
-         * Makes a <a href="MethodHandleInfo.html#directmh">direct method handle</a>
-         * to <i>m</i>, if the lookup class has permission.
-         * If <i>m</i> is non-static, the receiver argument is treated as an initial argument.
-         * If <i>m</i> is virtual, overriding is respected on every call.
-         * Unlike the Core Reflection API, exceptions are <em>not</em> wrapped.
-         * The type of the method handle will be that of the method,
-         * with the receiver type prepended (but only if it is non-static).
-         * If the method's {@code accessible} flag is not set,
-         * access checking is performed immediately on behalf of the lookup class.
-         * If <i>m</i> is not public, do not share the resulting handle with untrusted parties.
-         * <p>
-         * The returned method handle will have
-         * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
-         * the method's variable arity modifier bit ({@code 0x0080}) is set.
-         * <p>
-         * If <i>m</i> is static, and
-         * if the returned method handle is invoked, the method's class will
-         * be initialized, if it has not already been initialized.
-         * @param m the reflected method
-         * @return a method handle which can invoke the reflected method
-         * @throws IllegalAccessException if access checking fails
-         *                                or if the method's variable arity modifier bit
-         *                                is set and {@code asVarargsCollector} fails
-         * @throws NullPointerException if the argument is null
-         */
-        public MethodHandle unreflect(Method m) throws IllegalAccessException {
-            if (m == null) {
-                throw new NullPointerException("m == null");
-            }
-
-            MethodType methodType = MethodType.methodType(m.getReturnType(),
-                    m.getParameterTypes());
-
-            // We should only perform access checks if setAccessible hasn't been called yet.
-            if (!m.isAccessible()) {
-                checkAccess(m.getDeclaringClass(), m.getDeclaringClass(), m.getModifiers(),
-                        m.getName());
-            }
-
-            if (Modifier.isStatic(m.getModifiers())) {
-                return createMethodHandle(m, MethodHandle.INVOKE_STATIC, methodType);
-            } else {
-                methodType = methodType.insertParameterTypes(0, m.getDeclaringClass());
-                return createMethodHandle(m, MethodHandle.INVOKE_VIRTUAL, methodType);
-            }
-        }
-
-        /**
-         * Produces a method handle for a reflected method.
-         * It will bypass checks for overriding methods on the receiver,
-         * <a href="MethodHandles.Lookup.html#equiv">as if called</a> from an {@code invokespecial}
-         * instruction from within the explicitly specified {@code specialCaller}.
-         * The type of the method handle will be that of the method,
-         * with a suitably restricted receiver type prepended.
-         * (The receiver type will be {@code specialCaller} or a subtype.)
-         * If the method's {@code accessible} flag is not set,
-         * access checking is performed immediately on behalf of the lookup class,
-         * as if {@code invokespecial} instruction were being linked.
-         * <p>
-         * Before method resolution,
-         * if the explicitly specified caller class is not identical with the
-         * lookup class, or if this lookup object does not have
-         * <a href="MethodHandles.Lookup.html#privacc">private access</a>
-         * privileges, the access fails.
-         * <p>
-         * The returned method handle will have
-         * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
-         * the method's variable arity modifier bit ({@code 0x0080}) is set.
-         * @param m the reflected method
-         * @param specialCaller the class nominally calling the method
-         * @return a method handle which can invoke the reflected method
-         * @throws IllegalAccessException if access checking fails
-         *                                or if the method's variable arity modifier bit
-         *                                is set and {@code asVarargsCollector} fails
-         * @throws NullPointerException if any argument is null
-         */
-        public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws IllegalAccessException {
-            if (m == null) {
-                throw new NullPointerException("m == null");
-            }
-
-            if (specialCaller == null) {
-                throw new NullPointerException("specialCaller == null");
-            }
-
-            if (!m.isAccessible()) {
-                checkSpecialCaller(specialCaller);
-            }
-
-            final MethodType methodType = MethodType.methodType(m.getReturnType(),
-                    m.getParameterTypes());
-            return findSpecial(m, methodType, m.getDeclaringClass() /* refc */, specialCaller);
-        }
-
-        /**
-         * Produces a method handle for a reflected constructor.
-         * The type of the method handle will be that of the constructor,
-         * with the return type changed to the declaring class.
-         * The method handle will perform a {@code newInstance} operation,
-         * creating a new instance of the constructor's class on the
-         * arguments passed to the method handle.
-         * <p>
-         * If the constructor's {@code accessible} flag is not set,
-         * access checking is performed immediately on behalf of the lookup class.
-         * <p>
-         * The returned method handle will have
-         * {@linkplain MethodHandle#asVarargsCollector variable arity} if and only if
-         * the constructor's variable arity modifier bit ({@code 0x0080}) is set.
-         * <p>
-         * If the returned method handle is invoked, the constructor's class will
-         * be initialized, if it has not already been initialized.
-         * @param c the reflected constructor
-         * @return a method handle which can invoke the reflected constructor
-         * @throws IllegalAccessException if access checking fails
-         *                                or if the method's variable arity modifier bit
-         *                                is set and {@code asVarargsCollector} fails
-         * @throws NullPointerException if the argument is null
-         */
-        public MethodHandle unreflectConstructor(Constructor<?> c) throws IllegalAccessException {
-            if (c == null) {
-                throw new NullPointerException("c == null");
-            }
-
-            if (!c.isAccessible()) {
-                checkAccess(c.getDeclaringClass(), c.getDeclaringClass(), c.getModifiers(),
-                        c.getName());
-            }
-
-            return createMethodHandleForConstructor(c);
-        }
-
-        /**
-         * Produces a method handle giving read access to a reflected field.
-         * The type of the method handle will have a return type of the field's
-         * value type.
-         * If the field is static, the method handle will take no arguments.
-         * Otherwise, its single argument will be the instance containing
-         * the field.
-         * If the field's {@code accessible} flag is not set,
-         * access checking is performed immediately on behalf of the lookup class.
-         * <p>
-         * If the field is static, and
-         * if the returned method handle is invoked, the field's class will
-         * be initialized, if it has not already been initialized.
-         * @param f the reflected field
-         * @return a method handle which can load values from the reflected field
-         * @throws IllegalAccessException if access checking fails
-         * @throws NullPointerException if the argument is null
-         */
-        public MethodHandle unreflectGetter(Field f) throws IllegalAccessException {
-            return findAccessor(f, f.getDeclaringClass(), f.getType(),
-                    Modifier.isStatic(f.getModifiers()) ? MethodHandle.SGET : MethodHandle.IGET,
-                    f.isAccessible() /* performAccessChecks */);
-        }
-
-        /**
-         * Produces a method handle giving write access to a reflected field.
-         * The type of the method handle will have a void return type.
-         * If the field is static, the method handle will take a single
-         * argument, of the field's value type, the value to be stored.
-         * Otherwise, the two arguments will be the instance containing
-         * the field, and the value to be stored.
-         * If the field's {@code accessible} flag is not set,
-         * access checking is performed immediately on behalf of the lookup class.
-         * <p>
-         * If the field is static, and
-         * if the returned method handle is invoked, the field's class will
-         * be initialized, if it has not already been initialized.
-         * @param f the reflected field
-         * @return a method handle which can store values into the reflected field
-         * @throws IllegalAccessException if access checking fails
-         * @throws NullPointerException if the argument is null
-         */
-        public MethodHandle unreflectSetter(Field f) throws IllegalAccessException {
-            return findAccessor(f, f.getDeclaringClass(), f.getType(),
-                    Modifier.isStatic(f.getModifiers()) ? MethodHandle.SPUT : MethodHandle.IPUT,
-                    f.isAccessible() /* performAccessChecks */);
-        }
-
-        /**
-         * Cracks a <a href="MethodHandleInfo.html#directmh">direct method handle</a>
-         * created by this lookup object or a similar one.
-         * Security and access checks are performed to ensure that this lookup object
-         * is capable of reproducing the target method handle.
-         * This means that the cracking may fail if target is a direct method handle
-         * but was created by an unrelated lookup object.
-         * This can happen if the method handle is <a href="MethodHandles.Lookup.html#callsens">caller sensitive</a>
-         * and was created by a lookup object for a different class.
-         * @param target a direct method handle to crack into symbolic reference components
-         * @return a symbolic reference which can be used to reconstruct this method handle from this lookup object
-         * @exception SecurityException if a security manager is present and it
-         *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
-         * @throws IllegalArgumentException if the target is not a direct method handle or if access checking fails
-         * @exception NullPointerException if the target is {@code null}
-         * @see MethodHandleInfo
-         * @since 1.8
-         */
-        public MethodHandleInfo revealDirect(MethodHandle target) {
-            MethodHandleImpl directTarget = getMethodHandleImpl(target);
-            MethodHandleInfo info = directTarget.reveal();
-
-            try {
-                checkAccess(lookupClass(), info.getDeclaringClass(), info.getModifiers(),
-                        info.getName());
-            } catch (IllegalAccessException exception) {
-                throw new IllegalArgumentException("Unable to access memeber.", exception);
-            }
-
-            return info;
-        }
-
-        private boolean hasPrivateAccess() {
-            return (allowedModes & PRIVATE) != 0;
-        }
-
-        /** Check public/protected/private bits on the symbolic reference class and its member. */
-        void checkAccess(Class<?> refc, Class<?> defc, int mods, String methName)
-                throws IllegalAccessException {
-            int allowedModes = this.allowedModes;
-
-            if (Modifier.isProtected(mods) &&
-                    defc == Object.class &&
-                    "clone".equals(methName) &&
-                    refc.isArray()) {
-                // The JVM does this hack also.
-                // (See ClassVerifier::verify_invoke_instructions
-                // and LinkResolver::check_method_accessability.)
-                // Because the JVM does not allow separate methods on array types,
-                // there is no separate method for int[].clone.
-                // All arrays simply inherit Object.clone.
-                // But for access checking logic, we make Object.clone
-                // (normally protected) appear to be public.
-                // Later on, when the DirectMethodHandle is created,
-                // its leading argument will be restricted to the
-                // requested array type.
-                // N.B. The return type is not adjusted, because
-                // that is *not* the bytecode behavior.
-                mods ^= Modifier.PROTECTED | Modifier.PUBLIC;
-            }
-
-            if (Modifier.isProtected(mods) && Modifier.isConstructor(mods)) {
-                // cannot "new" a protected ctor in a different package
-                mods ^= Modifier.PROTECTED;
-            }
-
-            if (Modifier.isPublic(mods) && Modifier.isPublic(refc.getModifiers()) && allowedModes != 0)
-                return;  // common case
-            int requestedModes = fixmods(mods);  // adjust 0 => PACKAGE
-            if ((requestedModes & allowedModes) != 0) {
-                if (VerifyAccess.isMemberAccessible(refc, defc, mods, lookupClass(), allowedModes))
-                    return;
-            } else {
-                // Protected members can also be checked as if they were package-private.
-                if ((requestedModes & PROTECTED) != 0 && (allowedModes & PACKAGE) != 0
-                        && VerifyAccess.isSamePackage(defc, lookupClass()))
-                    return;
-            }
-
-            throwMakeAccessException(accessFailedMessage(refc, defc, mods), this);
-        }
-
-        String accessFailedMessage(Class<?> refc, Class<?> defc, int mods) {
-            // check the class first:
-            boolean classOK = (Modifier.isPublic(defc.getModifiers()) &&
-                    (defc == refc ||
-                            Modifier.isPublic(refc.getModifiers())));
-            if (!classOK && (allowedModes & PACKAGE) != 0) {
-                classOK = (VerifyAccess.isClassAccessible(defc, lookupClass(), ALL_MODES) &&
-                        (defc == refc ||
-                                VerifyAccess.isClassAccessible(refc, lookupClass(), ALL_MODES)));
-            }
-            if (!classOK)
-                return "class is not public";
-            if (Modifier.isPublic(mods))
-                return "access to public member failed";  // (how?)
-            if (Modifier.isPrivate(mods))
-                return "member is private";
-            if (Modifier.isProtected(mods))
-                return "member is protected";
-            return "member is private to package";
-        }
-
-        // Android-changed: checkSpecialCaller assumes that ALLOW_NESTMATE_ACCESS = false,
-        // as in upstream OpenJDK.
-        //
-        // private static final boolean ALLOW_NESTMATE_ACCESS = false;
-
-        private void checkSpecialCaller(Class<?> specialCaller) throws IllegalAccessException {
-            // Android-changed: No support for TRUSTED lookups. Also construct the
-            // IllegalAccessException by hand because the upstream code implicitly assumes
-            // that the lookupClass == specialCaller.
-            //
-            // if (allowedModes == TRUSTED)  return;
-            if (!hasPrivateAccess() || (specialCaller != lookupClass())) {
-                throw new IllegalAccessException("no private access for invokespecial : "
-                        + specialCaller + ", from" + this);
-            }
-        }
-
-        private void throwMakeAccessException(String message, Object from) throws
-                IllegalAccessException{
-            message = message + ": "+ toString();
-            if (from != null)  message += ", from " + from;
-            throw new IllegalAccessException(message);
-        }
-
-        private void checkReturnType(Method method, MethodType methodType)
-                throws NoSuchMethodException {
-            if (method.getReturnType() != methodType.rtype()) {
-                throw new NoSuchMethodException(method.getName() + methodType);
-            }
-        }
     }
 
-    /**
-     * "Cracks" {@code target} to reveal the underlying {@code MethodHandleImpl}.
-     */
-    private static MethodHandleImpl getMethodHandleImpl(MethodHandle target) {
-        // Special case : We implement handles to constructors as transformers,
-        // so we must extract the underlying handle from the transformer.
-        if (target instanceof Transformers.Construct) {
-            target = ((Transformers.Construct) target).getConstructorHandle();
-        }
-
-        // Special case: Var-args methods are also implemented as Transformers,
-        // so we should get the underlying handle in that case as well.
-        if (target instanceof Transformers.VarargsCollector) {
-            target = target.asFixedArity();
-        }
-
-        if (target instanceof MethodHandleImpl) {
-            return (MethodHandleImpl) target;
-        }
-
-        throw new IllegalArgumentException(target + " is not a direct handle");
-    }
-
-    /**
-     * Produces a method handle giving read access to elements of an array.
-     * The type of the method handle will have a return type of the array's
-     * element type.  Its first argument will be the array type,
-     * and the second will be {@code int}.
-     * @param arrayClass an array type
-     * @return a method handle which can load values from the given array type
-     * @throws NullPointerException if the argument is null
-     * @throws  IllegalArgumentException if arrayClass is not an array type
-     */
     public static
-    MethodHandle arrayElementGetter(Class<?> arrayClass) throws IllegalArgumentException {
-        final Class<?> componentType = arrayClass.getComponentType();
-        if (componentType == null) {
-            throw new IllegalArgumentException("Not an array type: " + arrayClass);
-        }
+    MethodHandle arrayElementGetter(Class<?> arrayClass) throws IllegalArgumentException { return null; }
 
-        if (componentType.isPrimitive()) {
-            try {
-                return Lookup.PUBLIC_LOOKUP.findStatic(MethodHandles.class,
-                        "arrayElementGetter",
-                        MethodType.methodType(componentType, arrayClass, int.class));
-            } catch (NoSuchMethodException | IllegalAccessException exception) {
-                throw new AssertionError(exception);
-            }
-        }
-
-        return new Transformers.ReferenceArrayElementGetter(arrayClass);
-    }
-
-    /** @hide */ public static byte arrayElementGetter(byte[] array, int i) { return array[i]; }
-    /** @hide */ public static boolean arrayElementGetter(boolean[] array, int i) { return array[i]; }
-    /** @hide */ public static char arrayElementGetter(char[] array, int i) { return array[i]; }
-    /** @hide */ public static short arrayElementGetter(short[] array, int i) { return array[i]; }
-    /** @hide */ public static int arrayElementGetter(int[] array, int i) { return array[i]; }
-    /** @hide */ public static long arrayElementGetter(long[] array, int i) { return array[i]; }
-    /** @hide */ public static float arrayElementGetter(float[] array, int i) { return array[i]; }
-    /** @hide */ public static double arrayElementGetter(double[] array, int i) { return array[i]; }
-
-    /**
-     * Produces a method handle giving write access to elements of an array.
-     * The type of the method handle will have a void return type.
-     * Its last argument will be the array's element type.
-     * The first and second arguments will be the array type and int.
-     * @param arrayClass the class of an array
-     * @return a method handle which can store values into the array type
-     * @throws NullPointerException if the argument is null
-     * @throws IllegalArgumentException if arrayClass is not an array type
-     */
     public static
-    MethodHandle arrayElementSetter(Class<?> arrayClass) throws IllegalArgumentException {
-        final Class<?> componentType = arrayClass.getComponentType();
-        if (componentType == null) {
-            throw new IllegalArgumentException("Not an array type: " + arrayClass);
-        }
+    MethodHandle arrayElementSetter(Class<?> arrayClass) throws IllegalArgumentException { return null; }
 
-        if (componentType.isPrimitive()) {
-            try {
-                return Lookup.PUBLIC_LOOKUP.findStatic(MethodHandles.class,
-                        "arrayElementSetter",
-                        MethodType.methodType(void.class, arrayClass, int.class, componentType));
-            } catch (NoSuchMethodException | IllegalAccessException exception) {
-                throw new AssertionError(exception);
-            }
-        }
-
-        return new Transformers.ReferenceArrayElementSetter(arrayClass);
-    }
-
-    /** @hide */
-    public static void arrayElementSetter(byte[] array, int i, byte val) { array[i] = val; }
-    /** @hide */
-    public static void arrayElementSetter(boolean[] array, int i, boolean val) { array[i] = val; }
-    /** @hide */
-    public static void arrayElementSetter(char[] array, int i, char val) { array[i] = val; }
-    /** @hide */
-    public static void arrayElementSetter(short[] array, int i, short val) { array[i] = val; }
-    /** @hide */
-    public static void arrayElementSetter(int[] array, int i, int val) { array[i] = val; }
-    /** @hide */
-    public static void arrayElementSetter(long[] array, int i, long val) { array[i] = val; }
-    /** @hide */
-    public static void arrayElementSetter(float[] array, int i, float val) { array[i] = val; }
-    /** @hide */
-    public static void arrayElementSetter(double[] array, int i, double val) { array[i] = val; }
-
-
-    /// method handle invocation (reflective style)
-
-    /**
-     * Produces a method handle which will invoke any method handle of the
-     * given {@code type}, with a given number of trailing arguments replaced by
-     * a single trailing {@code Object[]} array.
-     * The resulting invoker will be a method handle with the following
-     * arguments:
-     * <ul>
-     * <li>a single {@code MethodHandle} target
-     * <li>zero or more leading values (counted by {@code leadingArgCount})
-     * <li>an {@code Object[]} array containing trailing arguments
-     * </ul>
-     * <p>
-     * The invoker will invoke its target like a call to {@link MethodHandle#invoke invoke} with
-     * the indicated {@code type}.
-     * That is, if the target is exactly of the given {@code type}, it will behave
-     * like {@code invokeExact}; otherwise it behave as if {@link MethodHandle#asType asType}
-     * is used to convert the target to the required {@code type}.
-     * <p>
-     * The type of the returned invoker will not be the given {@code type}, but rather
-     * will have all parameters except the first {@code leadingArgCount}
-     * replaced by a single array of type {@code Object[]}, which will be
-     * the final parameter.
-     * <p>
-     * Before invoking its target, the invoker will spread the final array, apply
-     * reference casts as necessary, and unbox and widen primitive arguments.
-     * If, when the invoker is called, the supplied array argument does
-     * not have the correct number of elements, the invoker will throw
-     * an {@link IllegalArgumentException} instead of invoking the target.
-     * <p>
-     * This method is equivalent to the following code (though it may be more efficient):
-     * <blockquote><pre>{@code
-MethodHandle invoker = MethodHandles.invoker(type);
-int spreadArgCount = type.parameterCount() - leadingArgCount;
-invoker = invoker.asSpreader(Object[].class, spreadArgCount);
-return invoker;
-     * }</pre></blockquote>
-     * This method throws no reflective or security exceptions.
-     * @param type the desired target type
-     * @param leadingArgCount number of fixed arguments, to be passed unchanged to the target
-     * @return a method handle suitable for invoking any method handle of the given type
-     * @throws NullPointerException if {@code type} is null
-     * @throws IllegalArgumentException if {@code leadingArgCount} is not in
-     *                  the range from 0 to {@code type.parameterCount()} inclusive,
-     *                  or if the resulting method handle's type would have
-     *          <a href="MethodHandle.html#maxarity">too many parameters</a>
-     */
     static public
-    MethodHandle spreadInvoker(MethodType type, int leadingArgCount) {
-        if (leadingArgCount < 0 || leadingArgCount > type.parameterCount())
-            throw newIllegalArgumentException("bad argument count", leadingArgCount);
+    MethodHandle spreadInvoker(MethodType type, int leadingArgCount) { return null; }
 
-        MethodHandle invoker = MethodHandles.invoker(type);
-        int spreadArgCount = type.parameterCount() - leadingArgCount;
-        invoker = invoker.asSpreader(Object[].class, spreadArgCount);
-        return invoker;
-    }
-
-    /**
-     * Produces a special <em>invoker method handle</em> which can be used to
-     * invoke any method handle of the given type, as if by {@link MethodHandle#invokeExact invokeExact}.
-     * The resulting invoker will have a type which is
-     * exactly equal to the desired type, except that it will accept
-     * an additional leading argument of type {@code MethodHandle}.
-     * <p>
-     * This method is equivalent to the following code (though it may be more efficient):
-     * {@code publicLookup().findVirtual(MethodHandle.class, "invokeExact", type)}
-     *
-     * <p style="font-size:smaller;">
-     * <em>Discussion:</em>
-     * Invoker method handles can be useful when working with variable method handles
-     * of unknown types.
-     * For example, to emulate an {@code invokeExact} call to a variable method
-     * handle {@code M}, extract its type {@code T},
-     * look up the invoker method {@code X} for {@code T},
-     * and call the invoker method, as {@code X.invoke(T, A...)}.
-     * (It would not work to call {@code X.invokeExact}, since the type {@code T}
-     * is unknown.)
-     * If spreading, collecting, or other argument transformations are required,
-     * they can be applied once to the invoker {@code X} and reused on many {@code M}
-     * method handle values, as long as they are compatible with the type of {@code X}.
-     * <p style="font-size:smaller;">
-     * <em>(Note:  The invoker method is not available via the Core Reflection API.
-     * An attempt to call {@linkplain java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}
-     * on the declared {@code invokeExact} or {@code invoke} method will raise an
-     * {@link java.lang.UnsupportedOperationException UnsupportedOperationException}.)</em>
-     * <p>
-     * This method throws no reflective or security exceptions.
-     * @param type the desired target type
-     * @return a method handle suitable for invoking any method handle of the given type
-     * @throws IllegalArgumentException if the resulting method handle's type would have
-     *          <a href="MethodHandle.html#maxarity">too many parameters</a>
-     */
     static public
-    MethodHandle exactInvoker(MethodType type) {
-        return new Transformers.Invoker(type, true /* isExactInvoker */);
-    }
+    MethodHandle exactInvoker(MethodType type) { return null; }
 
-    /**
-     * Produces a special <em>invoker method handle</em> which can be used to
-     * invoke any method handle compatible with the given type, as if by {@link MethodHandle#invoke invoke}.
-     * The resulting invoker will have a type which is
-     * exactly equal to the desired type, except that it will accept
-     * an additional leading argument of type {@code MethodHandle}.
-     * <p>
-     * Before invoking its target, if the target differs from the expected type,
-     * the invoker will apply reference casts as
-     * necessary and box, unbox, or widen primitive values, as if by {@link MethodHandle#asType asType}.
-     * Similarly, the return value will be converted as necessary.
-     * If the target is a {@linkplain MethodHandle#asVarargsCollector variable arity method handle},
-     * the required arity conversion will be made, again as if by {@link MethodHandle#asType asType}.
-     * <p>
-     * This method is equivalent to the following code (though it may be more efficient):
-     * {@code publicLookup().findVirtual(MethodHandle.class, "invoke", type)}
-     * <p style="font-size:smaller;">
-     * <em>Discussion:</em>
-     * A {@linkplain MethodType#genericMethodType general method type} is one which
-     * mentions only {@code Object} arguments and return values.
-     * An invoker for such a type is capable of calling any method handle
-     * of the same arity as the general type.
-     * <p style="font-size:smaller;">
-     * <em>(Note:  The invoker method is not available via the Core Reflection API.
-     * An attempt to call {@linkplain java.lang.reflect.Method#invoke java.lang.reflect.Method.invoke}
-     * on the declared {@code invokeExact} or {@code invoke} method will raise an
-     * {@link java.lang.UnsupportedOperationException UnsupportedOperationException}.)</em>
-     * <p>
-     * This method throws no reflective or security exceptions.
-     * @param type the desired target type
-     * @return a method handle suitable for invoking any method handle convertible to the given type
-     * @throws IllegalArgumentException if the resulting method handle's type would have
-     *          <a href="MethodHandle.html#maxarity">too many parameters</a>
-     */
     static public
-    MethodHandle invoker(MethodType type) {
-        return new Transformers.Invoker(type, false /* isExactInvoker */);
-    }
+    MethodHandle invoker(MethodType type) { return null; }
 
-    // Android-changed: Basic invokers are not supported.
-    //
-    // static /*non-public*/
-    // MethodHandle basicInvoker(MethodType type) {
-    //     return type.invokers().basicInvoker();
-    // }
-
-     /// method handle modification (creation from other method handles)
-
-    /**
-     * Produces a method handle which adapts the type of the
-     * given method handle to a new type by pairwise argument and return type conversion.
-     * The original type and new type must have the same number of arguments.
-     * The resulting method handle is guaranteed to report a type
-     * which is equal to the desired new type.
-     * <p>
-     * If the original type and new type are equal, returns target.
-     * <p>
-     * The same conversions are allowed as for {@link MethodHandle#asType MethodHandle.asType},
-     * and some additional conversions are also applied if those conversions fail.
-     * Given types <em>T0</em>, <em>T1</em>, one of the following conversions is applied
-     * if possible, before or instead of any conversions done by {@code asType}:
-     * <ul>
-     * <li>If <em>T0</em> and <em>T1</em> are references, and <em>T1</em> is an interface type,
-     *     then the value of type <em>T0</em> is passed as a <em>T1</em> without a cast.
-     *     (This treatment of interfaces follows the usage of the bytecode verifier.)
-     * <li>If <em>T0</em> is boolean and <em>T1</em> is another primitive,
-     *     the boolean is converted to a byte value, 1 for true, 0 for false.
-     *     (This treatment follows the usage of the bytecode verifier.)
-     * <li>If <em>T1</em> is boolean and <em>T0</em> is another primitive,
-     *     <em>T0</em> is converted to byte via Java casting conversion (JLS 5.5),
-     *     and the low order bit of the result is tested, as if by {@code (x & 1) != 0}.
-     * <li>If <em>T0</em> and <em>T1</em> are primitives other than boolean,
-     *     then a Java casting conversion (JLS 5.5) is applied.
-     *     (Specifically, <em>T0</em> will convert to <em>T1</em> by
-     *     widening and/or narrowing.)
-     * <li>If <em>T0</em> is a reference and <em>T1</em> a primitive, an unboxing
-     *     conversion will be applied at runtime, possibly followed
-     *     by a Java casting conversion (JLS 5.5) on the primitive value,
-     *     possibly followed by a conversion from byte to boolean by testing
-     *     the low-order bit.
-     * <li>If <em>T0</em> is a reference and <em>T1</em> a primitive,
-     *     and if the reference is null at runtime, a zero value is introduced.
-     * </ul>
-     * @param target the method handle to invoke after arguments are retyped
-     * @param newType the expected type of the new method handle
-     * @return a method handle which delegates to the target after performing
-     *           any necessary argument conversions, and arranges for any
-     *           necessary return value conversions
-     * @throws NullPointerException if either argument is null
-     * @throws WrongMethodTypeException if the conversion cannot be made
-     * @see MethodHandle#asType
-     */
     public static
-    MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) {
-        explicitCastArgumentsChecks(target, newType);
-        // use the asTypeCache when possible:
-        MethodType oldType = target.type();
-        if (oldType == newType) return target;
-        if (oldType.explicitCastEquivalentToAsType(newType)) {
-            return target.asFixedArity().asType(newType);
-        }
+    MethodHandle explicitCastArguments(MethodHandle target, MethodType newType) { return null; }
 
-        return new Transformers.ExplicitCastArguments(target, newType);
-    }
-
-    private static void explicitCastArgumentsChecks(MethodHandle target, MethodType newType) {
-        if (target.type().parameterCount() != newType.parameterCount()) {
-            throw new WrongMethodTypeException("cannot explicitly cast " + target + " to " + newType);
-        }
-    }
-
-    /**
-     * Produces a method handle which adapts the calling sequence of the
-     * given method handle to a new type, by reordering the arguments.
-     * The resulting method handle is guaranteed to report a type
-     * which is equal to the desired new type.
-     * <p>
-     * The given array controls the reordering.
-     * Call {@code #I} the number of incoming parameters (the value
-     * {@code newType.parameterCount()}, and call {@code #O} the number
-     * of outgoing parameters (the value {@code target.type().parameterCount()}).
-     * Then the length of the reordering array must be {@code #O},
-     * and each element must be a non-negative number less than {@code #I}.
-     * For every {@code N} less than {@code #O}, the {@code N}-th
-     * outgoing argument will be taken from the {@code I}-th incoming
-     * argument, where {@code I} is {@code reorder[N]}.
-     * <p>
-     * No argument or return value conversions are applied.
-     * The type of each incoming argument, as determined by {@code newType},
-     * must be identical to the type of the corresponding outgoing parameter
-     * or parameters in the target method handle.
-     * The return type of {@code newType} must be identical to the return
-     * type of the original target.
-     * <p>
-     * The reordering array need not specify an actual permutation.
-     * An incoming argument will be duplicated if its index appears
-     * more than once in the array, and an incoming argument will be dropped
-     * if its index does not appear in the array.
-     * As in the case of {@link #dropArguments(MethodHandle,int,List) dropArguments},
-     * incoming arguments which are not mentioned in the reordering array
-     * are may be any type, as determined only by {@code newType}.
-     * <blockquote><pre>{@code
-import static java.lang.invoke.MethodHandles.*;
-import static java.lang.invoke.MethodType.*;
-...
-MethodType intfn1 = methodType(int.class, int.class);
-MethodType intfn2 = methodType(int.class, int.class, int.class);
-MethodHandle sub = ... (int x, int y) -> (x-y) ...;
-assert(sub.type().equals(intfn2));
-MethodHandle sub1 = permuteArguments(sub, intfn2, 0, 1);
-MethodHandle rsub = permuteArguments(sub, intfn2, 1, 0);
-assert((int)rsub.invokeExact(1, 100) == 99);
-MethodHandle add = ... (int x, int y) -> (x+y) ...;
-assert(add.type().equals(intfn2));
-MethodHandle twice = permuteArguments(add, intfn1, 0, 0);
-assert(twice.type().equals(intfn1));
-assert((int)twice.invokeExact(21) == 42);
-     * }</pre></blockquote>
-     * @param target the method handle to invoke after arguments are reordered
-     * @param newType the expected type of the new method handle
-     * @param reorder an index array which controls the reordering
-     * @return a method handle which delegates to the target after it
-     *           drops unused arguments and moves and/or duplicates the other arguments
-     * @throws NullPointerException if any argument is null
-     * @throws IllegalArgumentException if the index array length is not equal to
-     *                  the arity of the target, or if any index array element
-     *                  not a valid index for a parameter of {@code newType},
-     *                  or if two corresponding parameter types in
-     *                  {@code target.type()} and {@code newType} are not identical,
-     */
     public static
-    MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) {
-        reorder = reorder.clone();  // get a private copy
-        MethodType oldType = target.type();
-        permuteArgumentChecks(reorder, newType, oldType);
+    MethodHandle permuteArguments(MethodHandle target, MethodType newType, int... reorder) { return null; }
 
-        return new Transformers.PermuteArguments(newType, target, reorder);
-    }
-
-    // Android-changed: findFirstDupOrDrop is unused and removed.
-    // private static int findFirstDupOrDrop(int[] reorder, int newArity);
-
-    private static boolean permuteArgumentChecks(int[] reorder, MethodType newType, MethodType oldType) {
-        if (newType.returnType() != oldType.returnType())
-            throw newIllegalArgumentException("return types do not match",
-                    oldType, newType);
-        if (reorder.length == oldType.parameterCount()) {
-            int limit = newType.parameterCount();
-            boolean bad = false;
-            for (int j = 0; j < reorder.length; j++) {
-                int i = reorder[j];
-                if (i < 0 || i >= limit) {
-                    bad = true; break;
-                }
-                Class<?> src = newType.parameterType(i);
-                Class<?> dst = oldType.parameterType(j);
-                if (src != dst)
-                    throw newIllegalArgumentException("parameter types do not match after reorder",
-                            oldType, newType);
-            }
-            if (!bad)  return true;
-        }
-        throw newIllegalArgumentException("bad reorder array: "+Arrays.toString(reorder));
-    }
-
-    /**
-     * Produces a method handle of the requested return type which returns the given
-     * constant value every time it is invoked.
-     * <p>
-     * Before the method handle is returned, the passed-in value is converted to the requested type.
-     * If the requested type is primitive, widening primitive conversions are attempted,
-     * else reference conversions are attempted.
-     * <p>The returned method handle is equivalent to {@code identity(type).bindTo(value)}.
-     * @param type the return type of the desired method handle
-     * @param value the value to return
-     * @return a method handle of the given return type and no arguments, which always returns the given value
-     * @throws NullPointerException if the {@code type} argument is null
-     * @throws ClassCastException if the value cannot be converted to the required return type
-     * @throws IllegalArgumentException if the given type is {@code void.class}
-     */
     public static
-    MethodHandle constant(Class<?> type, Object value) {
-        if (type.isPrimitive()) {
-            if (type == void.class)
-                throw newIllegalArgumentException("void type");
-            Wrapper w = Wrapper.forPrimitiveType(type);
-            value = w.convert(value, type);
-        }
+    MethodHandle constant(Class<?> type, Object value) { return null; }
 
-        return new Transformers.Constant(type, value);
-    }
-
-    /**
-     * Produces a method handle which returns its sole argument when invoked.
-     * @param type the type of the sole parameter and return value of the desired method handle
-     * @return a unary method handle which accepts and returns the given type
-     * @throws NullPointerException if the argument is null
-     * @throws IllegalArgumentException if the given type is {@code void.class}
-     */
     public static
-    MethodHandle identity(Class<?> type) {
-        if (type == null) {
-            throw new NullPointerException("type == null");
-        }
+    MethodHandle identity(Class<?> type) { return null; }
 
-        if (type.isPrimitive()) {
-            try {
-                return Lookup.PUBLIC_LOOKUP.findStatic(MethodHandles.class, "identity",
-                        MethodType.methodType(type, type));
-            } catch (NoSuchMethodException | IllegalAccessException e) {
-                throw new AssertionError(e);
-            }
-        }
-
-        return new Transformers.ReferenceIdentity(type);
-    }
-
-    /** @hide */ public static byte identity(byte val) { return val; }
-    /** @hide */ public static boolean identity(boolean val) { return val; }
-    /** @hide */ public static char identity(char val) { return val; }
-    /** @hide */ public static short identity(short val) { return val; }
-    /** @hide */ public static int identity(int val) { return val; }
-    /** @hide */ public static long identity(long val) { return val; }
-    /** @hide */ public static float identity(float val) { return val; }
-    /** @hide */ public static double identity(double val) { return val; }
-
-    /**
-     * Provides a target method handle with one or more <em>bound arguments</em>
-     * in advance of the method handle's invocation.
-     * The formal parameters to the target corresponding to the bound
-     * arguments are called <em>bound parameters</em>.
-     * Returns a new method handle which saves away the bound arguments.
-     * When it is invoked, it receives arguments for any non-bound parameters,
-     * binds the saved arguments to their corresponding parameters,
-     * and calls the original target.
-     * <p>
-     * The type of the new method handle will drop the types for the bound
-     * parameters from the original target type, since the new method handle
-     * will no longer require those arguments to be supplied by its callers.
-     * <p>
-     * Each given argument object must match the corresponding bound parameter type.
-     * If a bound parameter type is a primitive, the argument object
-     * must be a wrapper, and will be unboxed to produce the primitive value.
-     * <p>
-     * The {@code pos} argument selects which parameters are to be bound.
-     * It may range between zero and <i>N-L</i> (inclusively),
-     * where <i>N</i> is the arity of the target method handle
-     * and <i>L</i> is the length of the values array.
-     * @param target the method handle to invoke after the argument is inserted
-     * @param pos where to insert the argument (zero for the first)
-     * @param values the series of arguments to insert
-     * @return a method handle which inserts an additional argument,
-     *         before calling the original method handle
-     * @throws NullPointerException if the target or the {@code values} array is null
-     * @see MethodHandle#bindTo
-     */
     public static
-    MethodHandle insertArguments(MethodHandle target, int pos, Object... values) {
-        int insCount = values.length;
-        Class<?>[] ptypes = insertArgumentsChecks(target, insCount, pos);
-        if (insCount == 0)  {
-            return target;
-        }
+    MethodHandle insertArguments(MethodHandle target, int pos, Object... values) { return null; }
 
-        // Throw ClassCastExceptions early if we can't cast any of the provided values
-        // to the required type.
-        for (int i = 0; i < insCount; i++) {
-            final Class<?> ptype = ptypes[pos + i];
-            if (!ptype.isPrimitive()) {
-                ptypes[pos + i].cast(values[i]);
-            } else {
-                // Will throw a ClassCastException if something terrible happens.
-                values[i] = Wrapper.forPrimitiveType(ptype).convert(values[i], ptype);
-            }
-        }
-
-        return new Transformers.InsertArguments(target, pos, values);
-    }
-
-    // Android-changed: insertArgumentPrimitive is unused.
-    //
-    // private static BoundMethodHandle insertArgumentPrimitive(BoundMethodHandle result, int pos,
-    //                                                          Class<?> ptype, Object value) {
-    //     Wrapper w = Wrapper.forPrimitiveType(ptype);
-    //     // perform unboxing and/or primitive conversion
-    //     value = w.convert(value, ptype);
-    //     switch (w) {
-    //     case INT:     return result.bindArgumentI(pos, (int)value);
-    //     case LONG:    return result.bindArgumentJ(pos, (long)value);
-    //     case FLOAT:   return result.bindArgumentF(pos, (float)value);
-    //     case DOUBLE:  return result.bindArgumentD(pos, (double)value);
-    //     default:      return result.bindArgumentI(pos, ValueConversions.widenSubword(value));
-    //     }
-    // }
-
-    private static Class<?>[] insertArgumentsChecks(MethodHandle target, int insCount, int pos) throws RuntimeException {
-        MethodType oldType = target.type();
-        int outargs = oldType.parameterCount();
-        int inargs  = outargs - insCount;
-        if (inargs < 0)
-            throw newIllegalArgumentException("too many values to insert");
-        if (pos < 0 || pos > inargs)
-            throw newIllegalArgumentException("no argument type to append");
-        return oldType.ptypes();
-    }
-
-    /**
-     * Produces a method handle which will discard some dummy arguments
-     * before calling some other specified <i>target</i> method handle.
-     * The type of the new method handle will be the same as the target's type,
-     * except it will also include the dummy argument types,
-     * at some given position.
-     * <p>
-     * The {@code pos} argument may range between zero and <i>N</i>,
-     * where <i>N</i> is the arity of the target.
-     * If {@code pos} is zero, the dummy arguments will precede
-     * the target's real arguments; if {@code pos} is <i>N</i>
-     * they will come after.
-     * <p>
-     * <b>Example:</b>
-     * <blockquote><pre>{@code
-import static java.lang.invoke.MethodHandles.*;
-import static java.lang.invoke.MethodType.*;
-...
-MethodHandle cat = lookup().findVirtual(String.class,
-  "concat", methodType(String.class, String.class));
-assertEquals("xy", (String) cat.invokeExact("x", "y"));
-MethodType bigType = cat.type().insertParameterTypes(0, int.class, String.class);
-MethodHandle d0 = dropArguments(cat, 0, bigType.parameterList().subList(0,2));
-assertEquals(bigType, d0.type());
-assertEquals("yz", (String) d0.invokeExact(123, "x", "y", "z"));
-     * }</pre></blockquote>
-     * <p>
-     * This method is also equivalent to the following code:
-     * <blockquote><pre>
-     * {@link #dropArguments(MethodHandle,int,Class...) dropArguments}{@code (target, pos, valueTypes.toArray(new Class[0]))}
-     * </pre></blockquote>
-     * @param target the method handle to invoke after the arguments are dropped
-     * @param valueTypes the type(s) of the argument(s) to drop
-     * @param pos position of first argument to drop (zero for the leftmost)
-     * @return a method handle which drops arguments of the given types,
-     *         before calling the original method handle
-     * @throws NullPointerException if the target is null,
-     *                              or if the {@code valueTypes} list or any of its elements is null
-     * @throws IllegalArgumentException if any element of {@code valueTypes} is {@code void.class},
-     *                  or if {@code pos} is negative or greater than the arity of the target,
-     *                  or if the new method handle's type would have too many parameters
-     */
     public static
-    MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) {
-        valueTypes = copyTypes(valueTypes);
-        MethodType oldType = target.type();  // get NPE
-        int dropped = dropArgumentChecks(oldType, pos, valueTypes);
+    MethodHandle dropArguments(MethodHandle target, int pos, List<Class<?>> valueTypes) { return null; }
 
-        MethodType newType = oldType.insertParameterTypes(pos, valueTypes);
-        if (dropped == 0) {
-            return target;
-        }
-
-        return new Transformers.DropArguments(newType, target, pos, valueTypes.size());
-    }
-
-    private static List<Class<?>> copyTypes(List<Class<?>> types) {
-        Object[] a = types.toArray();
-        return Arrays.asList(Arrays.copyOf(a, a.length, Class[].class));
-    }
-
-    private static int dropArgumentChecks(MethodType oldType, int pos, List<Class<?>> valueTypes) {
-        int dropped = valueTypes.size();
-        MethodType.checkSlotCount(dropped);
-        int outargs = oldType.parameterCount();
-        int inargs  = outargs + dropped;
-        if (pos < 0 || pos > outargs)
-            throw newIllegalArgumentException("no argument type to remove"
-                    + Arrays.asList(oldType, pos, valueTypes, inargs, outargs)
-                    );
-        return dropped;
-    }
-
-    /**
-     * Produces a method handle which will discard some dummy arguments
-     * before calling some other specified <i>target</i> method handle.
-     * The type of the new method handle will be the same as the target's type,
-     * except it will also include the dummy argument types,
-     * at some given position.
-     * <p>
-     * The {@code pos} argument may range between zero and <i>N</i>,
-     * where <i>N</i> is the arity of the target.
-     * If {@code pos} is zero, the dummy arguments will precede
-     * the target's real arguments; if {@code pos} is <i>N</i>
-     * they will come after.
-     * <p>
-     * <b>Example:</b>
-     * <blockquote><pre>{@code
-import static java.lang.invoke.MethodHandles.*;
-import static java.lang.invoke.MethodType.*;
-...
-MethodHandle cat = lookup().findVirtual(String.class,
-  "concat", methodType(String.class, String.class));
-assertEquals("xy", (String) cat.invokeExact("x", "y"));
-MethodHandle d0 = dropArguments(cat, 0, String.class);
-assertEquals("yz", (String) d0.invokeExact("x", "y", "z"));
-MethodHandle d1 = dropArguments(cat, 1, String.class);
-assertEquals("xz", (String) d1.invokeExact("x", "y", "z"));
-MethodHandle d2 = dropArguments(cat, 2, String.class);
-assertEquals("xy", (String) d2.invokeExact("x", "y", "z"));
-MethodHandle d12 = dropArguments(cat, 1, int.class, boolean.class);
-assertEquals("xz", (String) d12.invokeExact("x", 12, true, "z"));
-     * }</pre></blockquote>
-     * <p>
-     * This method is also equivalent to the following code:
-     * <blockquote><pre>
-     * {@link #dropArguments(MethodHandle,int,List) dropArguments}{@code (target, pos, Arrays.asList(valueTypes))}
-     * </pre></blockquote>
-     * @param target the method handle to invoke after the arguments are dropped
-     * @param valueTypes the type(s) of the argument(s) to drop
-     * @param pos position of first argument to drop (zero for the leftmost)
-     * @return a method handle which drops arguments of the given types,
-     *         before calling the original method handle
-     * @throws NullPointerException if the target is null,
-     *                              or if the {@code valueTypes} array or any of its elements is null
-     * @throws IllegalArgumentException if any element of {@code valueTypes} is {@code void.class},
-     *                  or if {@code pos} is negative or greater than the arity of the target,
-     *                  or if the new method handle's type would have
-     *                  <a href="MethodHandle.html#maxarity">too many parameters</a>
-     */
     public static
-    MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes) {
-        return dropArguments(target, pos, Arrays.asList(valueTypes));
-    }
+    MethodHandle dropArguments(MethodHandle target, int pos, Class<?>... valueTypes) { return null; }
 
-    /**
-     * Adapts a target method handle by pre-processing
-     * one or more of its arguments, each with its own unary filter function,
-     * and then calling the target with each pre-processed argument
-     * replaced by the result of its corresponding filter function.
-     * <p>
-     * The pre-processing is performed by one or more method handles,
-     * specified in the elements of the {@code filters} array.
-     * The first element of the filter array corresponds to the {@code pos}
-     * argument of the target, and so on in sequence.
-     * <p>
-     * Null arguments in the array are treated as identity functions,
-     * and the corresponding arguments left unchanged.
-     * (If there are no non-null elements in the array, the original target is returned.)
-     * Each filter is applied to the corresponding argument of the adapter.
-     * <p>
-     * If a filter {@code F} applies to the {@code N}th argument of
-     * the target, then {@code F} must be a method handle which
-     * takes exactly one argument.  The type of {@code F}'s sole argument
-     * replaces the corresponding argument type of the target
-     * in the resulting adapted method handle.
-     * The return type of {@code F} must be identical to the corresponding
-     * parameter type of the target.
-     * <p>
-     * It is an error if there are elements of {@code filters}
-     * (null or not)
-     * which do not correspond to argument positions in the target.
-     * <p><b>Example:</b>
-     * <blockquote><pre>{@code
-import static java.lang.invoke.MethodHandles.*;
-import static java.lang.invoke.MethodType.*;
-...
-MethodHandle cat = lookup().findVirtual(String.class,
-  "concat", methodType(String.class, String.class));
-MethodHandle upcase = lookup().findVirtual(String.class,
-  "toUpperCase", methodType(String.class));
-assertEquals("xy", (String) cat.invokeExact("x", "y"));
-MethodHandle f0 = filterArguments(cat, 0, upcase);
-assertEquals("Xy", (String) f0.invokeExact("x", "y")); // Xy
-MethodHandle f1 = filterArguments(cat, 1, upcase);
-assertEquals("xY", (String) f1.invokeExact("x", "y")); // xY
-MethodHandle f2 = filterArguments(cat, 0, upcase, upcase);
-assertEquals("XY", (String) f2.invokeExact("x", "y")); // XY
-     * }</pre></blockquote>
-     * <p> Here is pseudocode for the resulting adapter:
-     * <blockquote><pre>{@code
-     * V target(P... p, A[i]... a[i], B... b);
-     * A[i] filter[i](V[i]);
-     * T adapter(P... p, V[i]... v[i], B... b) {
-     *   return target(p..., f[i](v[i])..., b...);
-     * }
-     * }</pre></blockquote>
-     *
-     * @param target the method handle to invoke after arguments are filtered
-     * @param pos the position of the first argument to filter
-     * @param filters method handles to call initially on filtered arguments
-     * @return method handle which incorporates the specified argument filtering logic
-     * @throws NullPointerException if the target is null
-     *                              or if the {@code filters} array is null
-     * @throws IllegalArgumentException if a non-null element of {@code filters}
-     *          does not match a corresponding argument type of target as described above,
-     *          or if the {@code pos+filters.length} is greater than {@code target.type().parameterCount()},
-     *          or if the resulting method handle's type would have
-     *          <a href="MethodHandle.html#maxarity">too many parameters</a>
-     */
     public static
-    MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) {
-        filterArgumentsCheckArity(target, pos, filters);
+    MethodHandle filterArguments(MethodHandle target, int pos, MethodHandle... filters) { return null; }
 
-        for (int i = 0; i < filters.length; ++i) {
-            filterArgumentChecks(target, i + pos, filters[i]);
-        }
-
-        return new Transformers.FilterArguments(target, pos, filters);
-    }
-
-    private static void filterArgumentsCheckArity(MethodHandle target, int pos, MethodHandle[] filters) {
-        MethodType targetType = target.type();
-        int maxPos = targetType.parameterCount();
-        if (pos + filters.length > maxPos)
-            throw newIllegalArgumentException("too many filters");
-    }
-
-    private static void filterArgumentChecks(MethodHandle target, int pos, MethodHandle filter) throws RuntimeException {
-        MethodType targetType = target.type();
-        MethodType filterType = filter.type();
-        if (filterType.parameterCount() != 1
-            || filterType.returnType() != targetType.parameterType(pos))
-            throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
-    }
-
-    /**
-     * Adapts a target method handle by pre-processing
-     * a sub-sequence of its arguments with a filter (another method handle).
-     * The pre-processed arguments are replaced by the result (if any) of the
-     * filter function.
-     * The target is then called on the modified (usually shortened) argument list.
-     * <p>
-     * If the filter returns a value, the target must accept that value as
-     * its argument in position {@code pos}, preceded and/or followed by
-     * any arguments not passed to the filter.
-     * If the filter returns void, the target must accept all arguments
-     * not passed to the filter.
-     * No arguments are reordered, and a result returned from the filter
-     * replaces (in order) the whole subsequence of arguments originally
-     * passed to the adapter.
-     * <p>
-     * The argument types (if any) of the filter
-     * replace zero or one argument types of the target, at position {@code pos},
-     * in the resulting adapted method handle.
-     * The return type of the filter (if any) must be identical to the
-     * argument type of the target at position {@code pos}, and that target argument
-     * is supplied by the return value of the filter.
-     * <p>
-     * In all cases, {@code pos} must be greater than or equal to zero, and
-     * {@code pos} must also be less than or equal to the target's arity.
-     * <p><b>Example:</b>
-     * <blockquote><pre>{@code
-import static java.lang.invoke.MethodHandles.*;
-import static java.lang.invoke.MethodType.*;
-...
-MethodHandle deepToString = publicLookup()
-  .findStatic(Arrays.class, "deepToString", methodType(String.class, Object[].class));
-
-MethodHandle ts1 = deepToString.asCollector(String[].class, 1);
-assertEquals("[strange]", (String) ts1.invokeExact("strange"));
-
-MethodHandle ts2 = deepToString.asCollector(String[].class, 2);
-assertEquals("[up, down]", (String) ts2.invokeExact("up", "down"));
-
-MethodHandle ts3 = deepToString.asCollector(String[].class, 3);
-MethodHandle ts3_ts2 = collectArguments(ts3, 1, ts2);
-assertEquals("[top, [up, down], strange]",
-             (String) ts3_ts2.invokeExact("top", "up", "down", "strange"));
-
-MethodHandle ts3_ts2_ts1 = collectArguments(ts3_ts2, 3, ts1);
-assertEquals("[top, [up, down], [strange]]",
-             (String) ts3_ts2_ts1.invokeExact("top", "up", "down", "strange"));
-
-MethodHandle ts3_ts2_ts3 = collectArguments(ts3_ts2, 1, ts3);
-assertEquals("[top, [[up, down, strange], charm], bottom]",
-             (String) ts3_ts2_ts3.invokeExact("top", "up", "down", "strange", "charm", "bottom"));
-     * }</pre></blockquote>
-     * <p> Here is pseudocode for the resulting adapter:
-     * <blockquote><pre>{@code
-     * T target(A...,V,C...);
-     * V filter(B...);
-     * T adapter(A... a,B... b,C... c) {
-     *   V v = filter(b...);
-     *   return target(a...,v,c...);
-     * }
-     * // and if the filter has no arguments:
-     * T target2(A...,V,C...);
-     * V filter2();
-     * T adapter2(A... a,C... c) {
-     *   V v = filter2();
-     *   return target2(a...,v,c...);
-     * }
-     * // and if the filter has a void return:
-     * T target3(A...,C...);
-     * void filter3(B...);
-     * void adapter3(A... a,B... b,C... c) {
-     *   filter3(b...);
-     *   return target3(a...,c...);
-     * }
-     * }</pre></blockquote>
-     * <p>
-     * A collection adapter {@code collectArguments(mh, 0, coll)} is equivalent to
-     * one which first "folds" the affected arguments, and then drops them, in separate
-     * steps as follows:
-     * <blockquote><pre>{@code
-     * mh = MethodHandles.dropArguments(mh, 1, coll.type().parameterList()); //step 2
-     * mh = MethodHandles.foldArguments(mh, coll); //step 1
-     * }</pre></blockquote>
-     * If the target method handle consumes no arguments besides than the result
-     * (if any) of the filter {@code coll}, then {@code collectArguments(mh, 0, coll)}
-     * is equivalent to {@code filterReturnValue(coll, mh)}.
-     * If the filter method handle {@code coll} consumes one argument and produces
-     * a non-void result, then {@code collectArguments(mh, N, coll)}
-     * is equivalent to {@code filterArguments(mh, N, coll)}.
-     * Other equivalences are possible but would require argument permutation.
-     *
-     * @param target the method handle to invoke after filtering the subsequence of arguments
-     * @param pos the position of the first adapter argument to pass to the filter,
-     *            and/or the target argument which receives the result of the filter
-     * @param filter method handle to call on the subsequence of arguments
-     * @return method handle which incorporates the specified argument subsequence filtering logic
-     * @throws NullPointerException if either argument is null
-     * @throws IllegalArgumentException if the return type of {@code filter}
-     *          is non-void and is not the same as the {@code pos} argument of the target,
-     *          or if {@code pos} is not between 0 and the target's arity, inclusive,
-     *          or if the resulting method handle's type would have
-     *          <a href="MethodHandle.html#maxarity">too many parameters</a>
-     * @see MethodHandles#foldArguments
-     * @see MethodHandles#filterArguments
-     * @see MethodHandles#filterReturnValue
-     */
     public static
-    MethodHandle collectArguments(MethodHandle target, int pos, MethodHandle filter) {
-        MethodType newType = collectArgumentsChecks(target, pos, filter);
-        return new Transformers.CollectArguments(target, filter, pos, newType);
-    }
+    MethodHandle collectArguments(MethodHandle target, int pos, MethodHandle filter) { return null; }
 
-    private static MethodType collectArgumentsChecks(MethodHandle target, int pos, MethodHandle filter) throws RuntimeException {
-        MethodType targetType = target.type();
-        MethodType filterType = filter.type();
-        Class<?> rtype = filterType.returnType();
-        List<Class<?>> filterArgs = filterType.parameterList();
-        if (rtype == void.class) {
-            return targetType.insertParameterTypes(pos, filterArgs);
-        }
-        if (rtype != targetType.parameterType(pos)) {
-            throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
-        }
-        return targetType.dropParameterTypes(pos, pos+1).insertParameterTypes(pos, filterArgs);
-    }
-
-    /**
-     * Adapts a target method handle by post-processing
-     * its return value (if any) with a filter (another method handle).
-     * The result of the filter is returned from the adapter.
-     * <p>
-     * If the target returns a value, the filter must accept that value as
-     * its only argument.
-     * If the target returns void, the filter must accept no arguments.
-     * <p>
-     * The return type of the filter
-     * replaces the return type of the target
-     * in the resulting adapted method handle.
-     * The argument type of the filter (if any) must be identical to the
-     * return type of the target.
-     * <p><b>Example:</b>
-     * <blockquote><pre>{@code
-import static java.lang.invoke.MethodHandles.*;
-import static java.lang.invoke.MethodType.*;
-...
-MethodHandle cat = lookup().findVirtual(String.class,
-  "concat", methodType(String.class, String.class));
-MethodHandle length = lookup().findVirtual(String.class,
-  "length", methodType(int.class));
-System.out.println((String) cat.invokeExact("x", "y")); // xy
-MethodHandle f0 = filterReturnValue(cat, length);
-System.out.println((int) f0.invokeExact("x", "y")); // 2
-     * }</pre></blockquote>
-     * <p> Here is pseudocode for the resulting adapter:
-     * <blockquote><pre>{@code
-     * V target(A...);
-     * T filter(V);
-     * T adapter(A... a) {
-     *   V v = target(a...);
-     *   return filter(v);
-     * }
-     * // and if the target has a void return:
-     * void target2(A...);
-     * T filter2();
-     * T adapter2(A... a) {
-     *   target2(a...);
-     *   return filter2();
-     * }
-     * // and if the filter has a void return:
-     * V target3(A...);
-     * void filter3(V);
-     * void adapter3(A... a) {
-     *   V v = target3(a...);
-     *   filter3(v);
-     * }
-     * }</pre></blockquote>
-     * @param target the method handle to invoke before filtering the return value
-     * @param filter method handle to call on the return value
-     * @return method handle which incorporates the specified return value filtering logic
-     * @throws NullPointerException if either argument is null
-     * @throws IllegalArgumentException if the argument list of {@code filter}
-     *          does not match the return type of target as described above
-     */
     public static
-    MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) {
-        MethodType targetType = target.type();
-        MethodType filterType = filter.type();
-        filterReturnValueChecks(targetType, filterType);
+    MethodHandle filterReturnValue(MethodHandle target, MethodHandle filter) { return null; }
 
-        return new Transformers.FilterReturnValue(target, filter);
-    }
-
-    private static void filterReturnValueChecks(MethodType targetType, MethodType filterType) throws RuntimeException {
-        Class<?> rtype = targetType.returnType();
-        int filterValues = filterType.parameterCount();
-        if (filterValues == 0
-                ? (rtype != void.class)
-                : (rtype != filterType.parameterType(0) || filterValues != 1))
-            throw newIllegalArgumentException("target and filter types do not match", targetType, filterType);
-    }
-
-    /**
-     * Adapts a target method handle by pre-processing
-     * some of its arguments, and then calling the target with
-     * the result of the pre-processing, inserted into the original
-     * sequence of arguments.
-     * <p>
-     * The pre-processing is performed by {@code combiner}, a second method handle.
-     * Of the arguments passed to the adapter, the first {@code N} arguments
-     * are copied to the combiner, which is then called.
-     * (Here, {@code N} is defined as the parameter count of the combiner.)
-     * After this, control passes to the target, with any result
-     * from the combiner inserted before the original {@code N} incoming
-     * arguments.
-     * <p>
-     * If the combiner returns a value, the first parameter type of the target
-     * must be identical with the return type of the combiner, and the next
-     * {@code N} parameter types of the target must exactly match the parameters
-     * of the combiner.
-     * <p>
-     * If the combiner has a void return, no result will be inserted,
-     * and the first {@code N} parameter types of the target
-     * must exactly match the parameters of the combiner.
-     * <p>
-     * The resulting adapter is the same type as the target, except that the
-     * first parameter type is dropped,
-     * if it corresponds to the result of the combiner.
-     * <p>
-     * (Note that {@link #dropArguments(MethodHandle,int,List) dropArguments} can be used to remove any arguments
-     * that either the combiner or the target does not wish to receive.
-     * If some of the incoming arguments are destined only for the combiner,
-     * consider using {@link MethodHandle#asCollector asCollector} instead, since those
-     * arguments will not need to be live on the stack on entry to the
-     * target.)
-     * <p><b>Example:</b>
-     * <blockquote><pre>{@code
-import static java.lang.invoke.MethodHandles.*;
-import static java.lang.invoke.MethodType.*;
-...
-MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class,
-  "println", methodType(void.class, String.class))
-    .bindTo(System.out);
-MethodHandle cat = lookup().findVirtual(String.class,
-  "concat", methodType(String.class, String.class));
-assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
-MethodHandle catTrace = foldArguments(cat, trace);
-// also prints "boo":
-assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
-     * }</pre></blockquote>
-     * <p> Here is pseudocode for the resulting adapter:
-     * <blockquote><pre>{@code
-     * // there are N arguments in A...
-     * T target(V, A[N]..., B...);
-     * V combiner(A...);
-     * T adapter(A... a, B... b) {
-     *   V v = combiner(a...);
-     *   return target(v, a..., b...);
-     * }
-     * // and if the combiner has a void return:
-     * T target2(A[N]..., B...);
-     * void combiner2(A...);
-     * T adapter2(A... a, B... b) {
-     *   combiner2(a...);
-     *   return target2(a..., b...);
-     * }
-     * }</pre></blockquote>
-     * @param target the method handle to invoke after arguments are combined
-     * @param combiner method handle to call initially on the incoming arguments
-     * @return method handle which incorporates the specified argument folding logic
-     * @throws NullPointerException if either argument is null
-     * @throws IllegalArgumentException if {@code combiner}'s return type
-     *          is non-void and not the same as the first argument type of
-     *          the target, or if the initial {@code N} argument types
-     *          of the target
-     *          (skipping one matching the {@code combiner}'s return type)
-     *          are not identical with the argument types of {@code combiner}
-     */
     public static
-    MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) {
-        int foldPos = 0;
-        MethodType targetType = target.type();
-        MethodType combinerType = combiner.type();
-        Class<?> rtype = foldArgumentChecks(foldPos, targetType, combinerType);
+    MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) { return null; }
 
-        return new Transformers.FoldArguments(target, combiner);
-    }
-
-    private static Class<?> foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) {
-        int foldArgs   = combinerType.parameterCount();
-        Class<?> rtype = combinerType.returnType();
-        int foldVals = rtype == void.class ? 0 : 1;
-        int afterInsertPos = foldPos + foldVals;
-        boolean ok = (targetType.parameterCount() >= afterInsertPos + foldArgs);
-        if (ok && !(combinerType.parameterList()
-                    .equals(targetType.parameterList().subList(afterInsertPos,
-                                                               afterInsertPos + foldArgs))))
-            ok = false;
-        if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(0))
-            ok = false;
-        if (!ok)
-            throw misMatchedTypes("target and combiner types", targetType, combinerType);
-        return rtype;
-    }
-
-    /**
-     * Makes a method handle which adapts a target method handle,
-     * by guarding it with a test, a boolean-valued method handle.
-     * If the guard fails, a fallback handle is called instead.
-     * All three method handles must have the same corresponding
-     * argument and return types, except that the return type
-     * of the test must be boolean, and the test is allowed
-     * to have fewer arguments than the other two method handles.
-     * <p> Here is pseudocode for the resulting adapter:
-     * <blockquote><pre>{@code
-     * boolean test(A...);
-     * T target(A...,B...);
-     * T fallback(A...,B...);
-     * T adapter(A... a,B... b) {
-     *   if (test(a...))
-     *     return target(a..., b...);
-     *   else
-     *     return fallback(a..., b...);
-     * }
-     * }</pre></blockquote>
-     * Note that the test arguments ({@code a...} in the pseudocode) cannot
-     * be modified by execution of the test, and so are passed unchanged
-     * from the caller to the target or fallback as appropriate.
-     * @param test method handle used for test, must return boolean
-     * @param target method handle to call if test passes
-     * @param fallback method handle to call if test fails
-     * @return method handle which incorporates the specified if/then/else logic
-     * @throws NullPointerException if any argument is null
-     * @throws IllegalArgumentException if {@code test} does not return boolean,
-     *          or if all three method types do not match (with the return
-     *          type of {@code test} changed to match that of the target).
-     */
     public static
     MethodHandle guardWithTest(MethodHandle test,
                                MethodHandle target,
-                               MethodHandle fallback) {
-        MethodType gtype = test.type();
-        MethodType ttype = target.type();
-        MethodType ftype = fallback.type();
-        if (!ttype.equals(ftype))
-            throw misMatchedTypes("target and fallback types", ttype, ftype);
-        if (gtype.returnType() != boolean.class)
-            throw newIllegalArgumentException("guard type is not a predicate "+gtype);
-        List<Class<?>> targs = ttype.parameterList();
-        List<Class<?>> gargs = gtype.parameterList();
-        if (!targs.equals(gargs)) {
-            int gpc = gargs.size(), tpc = targs.size();
-            if (gpc >= tpc || !targs.subList(0, gpc).equals(gargs))
-                throw misMatchedTypes("target and test types", ttype, gtype);
-            test = dropArguments(test, gpc, targs.subList(gpc, tpc));
-            gtype = test.type();
-        }
+                               MethodHandle fallback) { return null; }
 
-        return new Transformers.GuardWithTest(test, target, fallback);
-    }
-
-    static RuntimeException misMatchedTypes(String what, MethodType t1, MethodType t2) {
-        return newIllegalArgumentException(what + " must match: " + t1 + " != " + t2);
-    }
-
-    /**
-     * Makes a method handle which adapts a target method handle,
-     * by running it inside an exception handler.
-     * If the target returns normally, the adapter returns that value.
-     * If an exception matching the specified type is thrown, the fallback
-     * handle is called instead on the exception, plus the original arguments.
-     * <p>
-     * The target and handler must have the same corresponding
-     * argument and return types, except that handler may omit trailing arguments
-     * (similarly to the predicate in {@link #guardWithTest guardWithTest}).
-     * Also, the handler must have an extra leading parameter of {@code exType} or a supertype.
-     * <p> Here is pseudocode for the resulting adapter:
-     * <blockquote><pre>{@code
-     * T target(A..., B...);
-     * T handler(ExType, A...);
-     * T adapter(A... a, B... b) {
-     *   try {
-     *     return target(a..., b...);
-     *   } catch (ExType ex) {
-     *     return handler(ex, a...);
-     *   }
-     * }
-     * }</pre></blockquote>
-     * Note that the saved arguments ({@code a...} in the pseudocode) cannot
-     * be modified by execution of the target, and so are passed unchanged
-     * from the caller to the handler, if the handler is invoked.
-     * <p>
-     * The target and handler must return the same type, even if the handler
-     * always throws.  (This might happen, for instance, because the handler
-     * is simulating a {@code finally} clause).
-     * To create such a throwing handler, compose the handler creation logic
-     * with {@link #throwException throwException},
-     * in order to create a method handle of the correct return type.
-     * @param target method handle to call
-     * @param exType the type of exception which the handler will catch
-     * @param handler method handle to call if a matching exception is thrown
-     * @return method handle which incorporates the specified try/catch logic
-     * @throws NullPointerException if any argument is null
-     * @throws IllegalArgumentException if {@code handler} does not accept
-     *          the given exception type, or if the method handle types do
-     *          not match in their return types and their
-     *          corresponding parameters
-     */
     public static
     MethodHandle catchException(MethodHandle target,
                                 Class<? extends Throwable> exType,
-                                MethodHandle handler) {
-        MethodType ttype = target.type();
-        MethodType htype = handler.type();
-        if (htype.parameterCount() < 1 ||
-            !htype.parameterType(0).isAssignableFrom(exType))
-            throw newIllegalArgumentException("handler does not accept exception type "+exType);
-        if (htype.returnType() != ttype.returnType())
-            throw misMatchedTypes("target and handler return types", ttype, htype);
-        List<Class<?>> targs = ttype.parameterList();
-        List<Class<?>> hargs = htype.parameterList();
-        hargs = hargs.subList(1, hargs.size());  // omit leading parameter from handler
-        if (!targs.equals(hargs)) {
-            int hpc = hargs.size(), tpc = targs.size();
-            if (hpc >= tpc || !targs.subList(0, hpc).equals(hargs))
-                throw misMatchedTypes("target and handler types", ttype, htype);
-        }
+                                MethodHandle handler) { return null; }
 
-        return new Transformers.CatchException(target, handler, exType);
-    }
-
-    /**
-     * Produces a method handle which will throw exceptions of the given {@code exType}.
-     * The method handle will accept a single argument of {@code exType},
-     * and immediately throw it as an exception.
-     * The method type will nominally specify a return of {@code returnType}.
-     * The return type may be anything convenient:  It doesn't matter to the
-     * method handle's behavior, since it will never return normally.
-     * @param returnType the return type of the desired method handle
-     * @param exType the parameter type of the desired method handle
-     * @return method handle which can throw the given exceptions
-     * @throws NullPointerException if either argument is null
-     */
     public static
-    MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType) {
-        if (!Throwable.class.isAssignableFrom(exType))
-            throw new ClassCastException(exType.getName());
-
-        return new Transformers.AlwaysThrow(returnType, exType);
-    }
+    MethodHandle throwException(Class<?> returnType, Class<? extends Throwable> exType) { return null; }
 }
diff --git a/java/lang/invoke/MethodType.java b/java/lang/invoke/MethodType.java
index bfa7ccd..4cb5c22 100644
--- a/java/lang/invoke/MethodType.java
+++ b/java/lang/invoke/MethodType.java
@@ -25,1227 +25,78 @@
 
 package java.lang.invoke;
 
-import sun.invoke.util.Wrapper;
-import java.lang.ref.WeakReference;
-import java.lang.ref.Reference;
-import java.lang.ref.ReferenceQueue;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ConcurrentHashMap;
-import sun.invoke.util.BytecodeDescriptor;
-import static java.lang.invoke.MethodHandleStatics.*;
 
-/**
- * A method type represents the arguments and return type accepted and
- * returned by a method handle, or the arguments and return type passed
- * and expected  by a method handle caller.  Method types must be properly
- * matched between a method handle and all its callers,
- * and the JVM's operations enforce this matching at, specifically
- * during calls to {@link MethodHandle#invokeExact MethodHandle.invokeExact}
- * and {@link MethodHandle#invoke MethodHandle.invoke}, and during execution
- * of {@code invokedynamic} instructions.
- * <p>
- * The structure is a return type accompanied by any number of parameter types.
- * The types (primitive, {@code void}, and reference) are represented by {@link Class} objects.
- * (For ease of exposition, we treat {@code void} as if it were a type.
- * In fact, it denotes the absence of a return type.)
- * <p>
- * All instances of {@code MethodType} are immutable.
- * Two instances are completely interchangeable if they compare equal.
- * Equality depends on pairwise correspondence of the return and parameter types and on nothing else.
- * <p>
- * This type can be created only by factory methods.
- * All factory methods may cache values, though caching is not guaranteed.
- * Some factory methods are static, while others are virtual methods which
- * modify precursor method types, e.g., by changing a selected parameter.
- * <p>
- * Factory methods which operate on groups of parameter types
- * are systematically presented in two versions, so that both Java arrays and
- * Java lists can be used to work with groups of parameter types.
- * The query methods {@code parameterArray} and {@code parameterList}
- * also provide a choice between arrays and lists.
- * <p>
- * {@code MethodType} objects are sometimes derived from bytecode instructions
- * such as {@code invokedynamic}, specifically from the type descriptor strings associated
- * with the instructions in a class file's constant pool.
- * <p>
- * Like classes and strings, method types can also be represented directly
- * in a class file's constant pool as constants.
- * A method type may be loaded by an {@code ldc} instruction which refers
- * to a suitable {@code CONSTANT_MethodType} constant pool entry.
- * The entry refers to a {@code CONSTANT_Utf8} spelling for the descriptor string.
- * (For full details on method type constants,
- * see sections 4.4.8 and 5.4.3.5 of the Java Virtual Machine Specification.)
- * <p>
- * When the JVM materializes a {@code MethodType} from a descriptor string,
- * all classes named in the descriptor must be accessible, and will be loaded.
- * (But the classes need not be initialized, as is the case with a {@code CONSTANT_Class}.)
- * This loading may occur at any time before the {@code MethodType} object is first derived.
- * @author John Rose, JSR 292 EG
- */
 public final
 class MethodType implements java.io.Serializable {
-    private static final long serialVersionUID = 292L;  // {rtype, {ptype...}}
 
-    // The rtype and ptypes fields define the structural identity of the method type:
-    private final Class<?>   rtype;
-    private final Class<?>[] ptypes;
-
-    // The remaining fields are caches of various sorts:
-    private @Stable MethodTypeForm form; // erased form, plus cached data about primitives
-    private @Stable MethodType wrapAlt;  // alternative wrapped/unwrapped version
-    // Android-changed: Remove adapter cache. We're not dynamically generating any
-    // adapters at this point.
-    // private @Stable Invokers invokers;   // cache of handy higher-order adapters
-    private @Stable String methodDescriptor;  // cache for toMethodDescriptorString
-
-    /**
-     * Check the given parameters for validity and store them into the final fields.
-     */
-    private MethodType(Class<?> rtype, Class<?>[] ptypes, boolean trusted) {
-        checkRtype(rtype);
-        checkPtypes(ptypes);
-        this.rtype = rtype;
-        // defensively copy the array passed in by the user
-        this.ptypes = trusted ? ptypes : Arrays.copyOf(ptypes, ptypes.length);
-    }
-
-    /**
-     * Construct a temporary unchecked instance of MethodType for use only as a key to the intern table.
-     * Does not check the given parameters for validity, and must be discarded after it is used as a searching key.
-     * The parameters are reversed for this constructor, so that is is not accidentally used.
-     */
-    private MethodType(Class<?>[] ptypes, Class<?> rtype) {
-        this.rtype = rtype;
-        this.ptypes = ptypes;
-    }
-
-    /*trusted*/ MethodTypeForm form() { return form; }
-    /*trusted*/ /** @hide */ public Class<?> rtype() { return rtype; }
-    /*trusted*/ /** @hide */ public Class<?>[] ptypes() { return ptypes; }
-
-    // Android-changed: Removed method setForm. It's unused in the JDK and there's no
-    // good reason to allow the form to be set externally.
-    //
-    // void setForm(MethodTypeForm f) { form = f; }
-
-    /** This number, mandated by the JVM spec as 255,
-     *  is the maximum number of <em>slots</em>
-     *  that any Java method can receive in its argument list.
-     *  It limits both JVM signatures and method type objects.
-     *  The longest possible invocation will look like
-     *  {@code staticMethod(arg1, arg2, ..., arg255)} or
-     *  {@code x.virtualMethod(arg1, arg2, ..., arg254)}.
-     */
-    /*non-public*/ static final int MAX_JVM_ARITY = 255;  // this is mandated by the JVM spec.
-
-    /** This number is the maximum arity of a method handle, 254.
-     *  It is derived from the absolute JVM-imposed arity by subtracting one,
-     *  which is the slot occupied by the method handle itself at the
-     *  beginning of the argument list used to invoke the method handle.
-     *  The longest possible invocation will look like
-     *  {@code mh.invoke(arg1, arg2, ..., arg254)}.
-     */
-    // Issue:  Should we allow MH.invokeWithArguments to go to the full 255?
-    /*non-public*/ static final int MAX_MH_ARITY = MAX_JVM_ARITY-1;  // deduct one for mh receiver
-
-    /** This number is the maximum arity of a method handle invoker, 253.
-     *  It is derived from the absolute JVM-imposed arity by subtracting two,
-     *  which are the slots occupied by invoke method handle, and the
-     *  target method handle, which are both at the beginning of the argument
-     *  list used to invoke the target method handle.
-     *  The longest possible invocation will look like
-     *  {@code invokermh.invoke(targetmh, arg1, arg2, ..., arg253)}.
-     */
-    /*non-public*/ static final int MAX_MH_INVOKER_ARITY = MAX_MH_ARITY-1;  // deduct one more for invoker
-
-    private static void checkRtype(Class<?> rtype) {
-        Objects.requireNonNull(rtype);
-    }
-    private static void checkPtype(Class<?> ptype) {
-        Objects.requireNonNull(ptype);
-        if (ptype == void.class)
-            throw newIllegalArgumentException("parameter type cannot be void");
-    }
-    /** Return number of extra slots (count of long/double args). */
-    private static int checkPtypes(Class<?>[] ptypes) {
-        int slots = 0;
-        for (Class<?> ptype : ptypes) {
-            checkPtype(ptype);
-            if (ptype == double.class || ptype == long.class) {
-                slots++;
-            }
-        }
-        checkSlotCount(ptypes.length + slots);
-        return slots;
-    }
-    static void checkSlotCount(int count) {
-        assert((MAX_JVM_ARITY & (MAX_JVM_ARITY+1)) == 0);
-        // MAX_JVM_ARITY must be power of 2 minus 1 for following code trick to work:
-        if ((count & MAX_JVM_ARITY) != count)
-            throw newIllegalArgumentException("bad parameter count "+count);
-    }
-    private static IndexOutOfBoundsException newIndexOutOfBoundsException(Object num) {
-        if (num instanceof Integer)  num = "bad index: "+num;
-        return new IndexOutOfBoundsException(num.toString());
-    }
-
-    static final ConcurrentWeakInternSet<MethodType> internTable = new ConcurrentWeakInternSet<>();
-
-    static final Class<?>[] NO_PTYPES = {};
-
-    /**
-     * Finds or creates an instance of the given method type.
-     * @param rtype  the return type
-     * @param ptypes the parameter types
-     * @return a method type with the given components
-     * @throws NullPointerException if {@code rtype} or {@code ptypes} or any element of {@code ptypes} is null
-     * @throws IllegalArgumentException if any element of {@code ptypes} is {@code void.class}
-     */
     public static
     MethodType methodType(Class<?> rtype, Class<?>[] ptypes) {
-        return makeImpl(rtype, ptypes, false);
+        return null;
     }
 
-    /**
-     * Finds or creates a method type with the given components.
-     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
-     * @param rtype  the return type
-     * @param ptypes the parameter types
-     * @return a method type with the given components
-     * @throws NullPointerException if {@code rtype} or {@code ptypes} or any element of {@code ptypes} is null
-     * @throws IllegalArgumentException if any element of {@code ptypes} is {@code void.class}
-     */
     public static
     MethodType methodType(Class<?> rtype, List<Class<?>> ptypes) {
-        boolean notrust = false;  // random List impl. could return evil ptypes array
-        return makeImpl(rtype, listToArray(ptypes), notrust);
+        return null;
     }
 
-    private static Class<?>[] listToArray(List<Class<?>> ptypes) {
-        // sanity check the size before the toArray call, since size might be huge
-        checkSlotCount(ptypes.size());
-        return ptypes.toArray(NO_PTYPES);
-    }
-
-    /**
-     * Finds or creates a method type with the given components.
-     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
-     * The leading parameter type is prepended to the remaining array.
-     * @param rtype  the return type
-     * @param ptype0 the first parameter type
-     * @param ptypes the remaining parameter types
-     * @return a method type with the given components
-     * @throws NullPointerException if {@code rtype} or {@code ptype0} or {@code ptypes} or any element of {@code ptypes} is null
-     * @throws IllegalArgumentException if {@code ptype0} or {@code ptypes} or any element of {@code ptypes} is {@code void.class}
-     */
     public static
-    MethodType methodType(Class<?> rtype, Class<?> ptype0, Class<?>... ptypes) {
-        Class<?>[] ptypes1 = new Class<?>[1+ptypes.length];
-        ptypes1[0] = ptype0;
-        System.arraycopy(ptypes, 0, ptypes1, 1, ptypes.length);
-        return makeImpl(rtype, ptypes1, true);
-    }
+    MethodType methodType(Class<?> rtype, Class<?> ptype0, Class<?>... ptypes) { return null; }
 
-    /**
-     * Finds or creates a method type with the given components.
-     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
-     * The resulting method has no parameter types.
-     * @param rtype  the return type
-     * @return a method type with the given return value
-     * @throws NullPointerException if {@code rtype} is null
-     */
     public static
-    MethodType methodType(Class<?> rtype) {
-        return makeImpl(rtype, NO_PTYPES, true);
-    }
+    MethodType methodType(Class<?> rtype) { return null; }
 
-    /**
-     * Finds or creates a method type with the given components.
-     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
-     * The resulting method has the single given parameter type.
-     * @param rtype  the return type
-     * @param ptype0 the parameter type
-     * @return a method type with the given return value and parameter type
-     * @throws NullPointerException if {@code rtype} or {@code ptype0} is null
-     * @throws IllegalArgumentException if {@code ptype0} is {@code void.class}
-     */
     public static
-    MethodType methodType(Class<?> rtype, Class<?> ptype0) {
-        return makeImpl(rtype, new Class<?>[]{ ptype0 }, true);
-    }
+    MethodType methodType(Class<?> rtype, Class<?> ptype0) { return null; }
 
-    /**
-     * Finds or creates a method type with the given components.
-     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
-     * The resulting method has the same parameter types as {@code ptypes},
-     * and the specified return type.
-     * @param rtype  the return type
-     * @param ptypes the method type which supplies the parameter types
-     * @return a method type with the given components
-     * @throws NullPointerException if {@code rtype} or {@code ptypes} is null
-     */
     public static
-    MethodType methodType(Class<?> rtype, MethodType ptypes) {
-        return makeImpl(rtype, ptypes.ptypes, true);
-    }
+    MethodType methodType(Class<?> rtype, MethodType ptypes) { return null; }
 
-    /**
-     * Sole factory method to find or create an interned method type.
-     * @param rtype desired return type
-     * @param ptypes desired parameter types
-     * @param trusted whether the ptypes can be used without cloning
-     * @return the unique method type of the desired structure
-     */
-    /*trusted*/ static
-    MethodType makeImpl(Class<?> rtype, Class<?>[] ptypes, boolean trusted) {
-        MethodType mt = internTable.get(new MethodType(ptypes, rtype));
-        if (mt != null)
-            return mt;
-        if (ptypes.length == 0) {
-            ptypes = NO_PTYPES; trusted = true;
-        }
-        mt = new MethodType(rtype, ptypes, trusted);
-        // promote the object to the Real Thing, and reprobe
-        mt.form = MethodTypeForm.findForm(mt);
-        return internTable.add(mt);
-    }
-    private static final MethodType[] objectOnlyTypes = new MethodType[20];
-
-    /**
-     * Finds or creates a method type whose components are {@code Object} with an optional trailing {@code Object[]} array.
-     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
-     * All parameters and the return type will be {@code Object},
-     * except the final array parameter if any, which will be {@code Object[]}.
-     * @param objectArgCount number of parameters (excluding the final array parameter if any)
-     * @param finalArray whether there will be a trailing array parameter, of type {@code Object[]}
-     * @return a generally applicable method type, for all calls of the given fixed argument count and a collected array of further arguments
-     * @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255 (or 254, if {@code finalArray} is true)
-     * @see #genericMethodType(int)
-     */
     public static
-    MethodType genericMethodType(int objectArgCount, boolean finalArray) {
-        MethodType mt;
-        checkSlotCount(objectArgCount);
-        int ivarargs = (!finalArray ? 0 : 1);
-        int ootIndex = objectArgCount*2 + ivarargs;
-        if (ootIndex < objectOnlyTypes.length) {
-            mt = objectOnlyTypes[ootIndex];
-            if (mt != null)  return mt;
-        }
-        Class<?>[] ptypes = new Class<?>[objectArgCount + ivarargs];
-        Arrays.fill(ptypes, Object.class);
-        if (ivarargs != 0)  ptypes[objectArgCount] = Object[].class;
-        mt = makeImpl(Object.class, ptypes, true);
-        if (ootIndex < objectOnlyTypes.length) {
-            objectOnlyTypes[ootIndex] = mt;     // cache it here also!
-        }
-        return mt;
-    }
+    MethodType genericMethodType(int objectArgCount, boolean finalArray) { return null; }
 
-    /**
-     * Finds or creates a method type whose components are all {@code Object}.
-     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
-     * All parameters and the return type will be Object.
-     * @param objectArgCount number of parameters
-     * @return a generally applicable method type, for all calls of the given argument count
-     * @throws IllegalArgumentException if {@code objectArgCount} is negative or greater than 255
-     * @see #genericMethodType(int, boolean)
-     */
     public static
-    MethodType genericMethodType(int objectArgCount) {
-        return genericMethodType(objectArgCount, false);
-    }
+    MethodType genericMethodType(int objectArgCount) { return null; }
 
-    /**
-     * Finds or creates a method type with a single different parameter type.
-     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
-     * @param num    the index (zero-based) of the parameter type to change
-     * @param nptype a new parameter type to replace the old one with
-     * @return the same type, except with the selected parameter changed
-     * @throws IndexOutOfBoundsException if {@code num} is not a valid index into {@code parameterArray()}
-     * @throws IllegalArgumentException if {@code nptype} is {@code void.class}
-     * @throws NullPointerException if {@code nptype} is null
-     */
-    public MethodType changeParameterType(int num, Class<?> nptype) {
-        if (parameterType(num) == nptype)  return this;
-        checkPtype(nptype);
-        Class<?>[] nptypes = ptypes.clone();
-        nptypes[num] = nptype;
-        return makeImpl(rtype, nptypes, true);
-    }
+    public MethodType changeParameterType(int num, Class<?> nptype) { return null; }
 
-    /**
-     * Finds or creates a method type with additional parameter types.
-     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
-     * @param num    the position (zero-based) of the inserted parameter type(s)
-     * @param ptypesToInsert zero or more new parameter types to insert into the parameter list
-     * @return the same type, except with the selected parameter(s) inserted
-     * @throws IndexOutOfBoundsException if {@code num} is negative or greater than {@code parameterCount()}
-     * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
-     *                                  or if the resulting method type would have more than 255 parameter slots
-     * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
-     */
-    public MethodType insertParameterTypes(int num, Class<?>... ptypesToInsert) {
-        int len = ptypes.length;
-        if (num < 0 || num > len)
-            throw newIndexOutOfBoundsException(num);
-        int ins = checkPtypes(ptypesToInsert);
-        checkSlotCount(parameterSlotCount() + ptypesToInsert.length + ins);
-        int ilen = ptypesToInsert.length;
-        if (ilen == 0)  return this;
-        Class<?>[] nptypes = Arrays.copyOfRange(ptypes, 0, len+ilen);
-        System.arraycopy(nptypes, num, nptypes, num+ilen, len-num);
-        System.arraycopy(ptypesToInsert, 0, nptypes, num, ilen);
-        return makeImpl(rtype, nptypes, true);
-    }
+    public MethodType insertParameterTypes(int num, Class<?>... ptypesToInsert) { return null; }
 
-    /**
-     * Finds or creates a method type with additional parameter types.
-     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
-     * @param ptypesToInsert zero or more new parameter types to insert after the end of the parameter list
-     * @return the same type, except with the selected parameter(s) appended
-     * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
-     *                                  or if the resulting method type would have more than 255 parameter slots
-     * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
-     */
-    public MethodType appendParameterTypes(Class<?>... ptypesToInsert) {
-        return insertParameterTypes(parameterCount(), ptypesToInsert);
-    }
+    public MethodType appendParameterTypes(Class<?>... ptypesToInsert) { return null; }
 
-    /**
-     * Finds or creates a method type with additional parameter types.
-     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
-     * @param num    the position (zero-based) of the inserted parameter type(s)
-     * @param ptypesToInsert zero or more new parameter types to insert into the parameter list
-     * @return the same type, except with the selected parameter(s) inserted
-     * @throws IndexOutOfBoundsException if {@code num} is negative or greater than {@code parameterCount()}
-     * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
-     *                                  or if the resulting method type would have more than 255 parameter slots
-     * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
-     */
-    public MethodType insertParameterTypes(int num, List<Class<?>> ptypesToInsert) {
-        return insertParameterTypes(num, listToArray(ptypesToInsert));
-    }
+    public MethodType insertParameterTypes(int num, List<Class<?>> ptypesToInsert) { return null; }
 
-    /**
-     * Finds or creates a method type with additional parameter types.
-     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
-     * @param ptypesToInsert zero or more new parameter types to insert after the end of the parameter list
-     * @return the same type, except with the selected parameter(s) appended
-     * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
-     *                                  or if the resulting method type would have more than 255 parameter slots
-     * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
-     */
-    public MethodType appendParameterTypes(List<Class<?>> ptypesToInsert) {
-        return insertParameterTypes(parameterCount(), ptypesToInsert);
-    }
+    public MethodType appendParameterTypes(List<Class<?>> ptypesToInsert) { return null; }
 
-     /**
-     * Finds or creates a method type with modified parameter types.
-     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
-     * @param start  the position (zero-based) of the first replaced parameter type(s)
-     * @param end    the position (zero-based) after the last replaced parameter type(s)
-     * @param ptypesToInsert zero or more new parameter types to insert into the parameter list
-     * @return the same type, except with the selected parameter(s) replaced
-     * @throws IndexOutOfBoundsException if {@code start} is negative or greater than {@code parameterCount()}
-     *                                  or if {@code end} is negative or greater than {@code parameterCount()}
-     *                                  or if {@code start} is greater than {@code end}
-     * @throws IllegalArgumentException if any element of {@code ptypesToInsert} is {@code void.class}
-     *                                  or if the resulting method type would have more than 255 parameter slots
-     * @throws NullPointerException if {@code ptypesToInsert} or any of its elements is null
-     */
-    /*non-public*/ MethodType replaceParameterTypes(int start, int end, Class<?>... ptypesToInsert) {
-        if (start == end)
-            return insertParameterTypes(start, ptypesToInsert);
-        int len = ptypes.length;
-        if (!(0 <= start && start <= end && end <= len))
-            throw newIndexOutOfBoundsException("start="+start+" end="+end);
-        int ilen = ptypesToInsert.length;
-        if (ilen == 0)
-            return dropParameterTypes(start, end);
-        return dropParameterTypes(start, end).insertParameterTypes(start, ptypesToInsert);
-    }
+    public MethodType dropParameterTypes(int start, int end) { return null; }
 
-    /** Replace the last arrayLength parameter types with the component type of arrayType.
-     * @param arrayType any array type
-     * @param arrayLength the number of parameter types to change
-     * @return the resulting type
-     */
-    /*non-public*/ MethodType asSpreaderType(Class<?> arrayType, int arrayLength) {
-        assert(parameterCount() >= arrayLength);
-        int spreadPos = ptypes.length - arrayLength;
-        if (arrayLength == 0)  return this;  // nothing to change
-        if (arrayType == Object[].class) {
-            if (isGeneric())  return this;  // nothing to change
-            if (spreadPos == 0) {
-                // no leading arguments to preserve; go generic
-                MethodType res = genericMethodType(arrayLength);
-                if (rtype != Object.class) {
-                    res = res.changeReturnType(rtype);
-                }
-                return res;
-            }
-        }
-        Class<?> elemType = arrayType.getComponentType();
-        assert(elemType != null);
-        for (int i = spreadPos; i < ptypes.length; i++) {
-            if (ptypes[i] != elemType) {
-                Class<?>[] fixedPtypes = ptypes.clone();
-                Arrays.fill(fixedPtypes, i, ptypes.length, elemType);
-                return methodType(rtype, fixedPtypes);
-            }
-        }
-        return this;  // arguments check out; no change
-    }
+    public MethodType changeReturnType(Class<?> nrtype) { return null; }
 
-    /** Return the leading parameter type, which must exist and be a reference.
-     *  @return the leading parameter type, after error checks
-     */
-    /*non-public*/ Class<?> leadingReferenceParameter() {
-        Class<?> ptype;
-        if (ptypes.length == 0 ||
-            (ptype = ptypes[0]).isPrimitive())
-            throw newIllegalArgumentException("no leading reference parameter");
-        return ptype;
-    }
+    public boolean hasPrimitives() { return false; }
 
-    /** Delete the last parameter type and replace it with arrayLength copies of the component type of arrayType.
-     * @param arrayType any array type
-     * @param arrayLength the number of parameter types to insert
-     * @return the resulting type
-     */
-    /*non-public*/ MethodType asCollectorType(Class<?> arrayType, int arrayLength) {
-        assert(parameterCount() >= 1);
-        assert(lastParameterType().isAssignableFrom(arrayType));
-        MethodType res;
-        if (arrayType == Object[].class) {
-            res = genericMethodType(arrayLength);
-            if (rtype != Object.class) {
-                res = res.changeReturnType(rtype);
-            }
-        } else {
-            Class<?> elemType = arrayType.getComponentType();
-            assert(elemType != null);
-            res = methodType(rtype, Collections.nCopies(arrayLength, elemType));
-        }
-        if (ptypes.length == 1) {
-            return res;
-        } else {
-            return res.insertParameterTypes(0, parameterList().subList(0, ptypes.length-1));
-        }
-    }
+    public boolean hasWrappers() { return false; }
 
-    /**
-     * Finds or creates a method type with some parameter types omitted.
-     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
-     * @param start  the index (zero-based) of the first parameter type to remove
-     * @param end    the index (greater than {@code start}) of the first parameter type after not to remove
-     * @return the same type, except with the selected parameter(s) removed
-     * @throws IndexOutOfBoundsException if {@code start} is negative or greater than {@code parameterCount()}
-     *                                  or if {@code end} is negative or greater than {@code parameterCount()}
-     *                                  or if {@code start} is greater than {@code end}
-     */
-    public MethodType dropParameterTypes(int start, int end) {
-        int len = ptypes.length;
-        if (!(0 <= start && start <= end && end <= len))
-            throw newIndexOutOfBoundsException("start="+start+" end="+end);
-        if (start == end)  return this;
-        Class<?>[] nptypes;
-        if (start == 0) {
-            if (end == len) {
-                // drop all parameters
-                nptypes = NO_PTYPES;
-            } else {
-                // drop initial parameter(s)
-                nptypes = Arrays.copyOfRange(ptypes, end, len);
-            }
-        } else {
-            if (end == len) {
-                // drop trailing parameter(s)
-                nptypes = Arrays.copyOfRange(ptypes, 0, start);
-            } else {
-                int tail = len - end;
-                nptypes = Arrays.copyOfRange(ptypes, 0, start + tail);
-                System.arraycopy(ptypes, end, nptypes, start, tail);
-            }
-        }
-        return makeImpl(rtype, nptypes, true);
-    }
+    public MethodType erase() { return null; }
 
-    /**
-     * Finds or creates a method type with a different return type.
-     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
-     * @param nrtype a return parameter type to replace the old one with
-     * @return the same type, except with the return type change
-     * @throws NullPointerException if {@code nrtype} is null
-     */
-    public MethodType changeReturnType(Class<?> nrtype) {
-        if (returnType() == nrtype)  return this;
-        return makeImpl(nrtype, ptypes, true);
-    }
+    public MethodType generic() { return null; }
 
-    /**
-     * Reports if this type contains a primitive argument or return value.
-     * The return type {@code void} counts as a primitive.
-     * @return true if any of the types are primitives
-     */
-    public boolean hasPrimitives() {
-        return form.hasPrimitives();
-    }
+    public MethodType wrap() { return null; }
 
-    /**
-     * Reports if this type contains a wrapper argument or return value.
-     * Wrappers are types which box primitive values, such as {@link Integer}.
-     * The reference type {@code java.lang.Void} counts as a wrapper,
-     * if it occurs as a return type.
-     * @return true if any of the types are wrappers
-     */
-    public boolean hasWrappers() {
-        return unwrap() != this;
-    }
+    public MethodType unwrap() { return null; }
 
-    /**
-     * Erases all reference types to {@code Object}.
-     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
-     * All primitive types (including {@code void}) will remain unchanged.
-     * @return a version of the original type with all reference types replaced
-     */
-    public MethodType erase() {
-        return form.erasedType();
-    }
+    public Class<?> parameterType(int num) { return null; }
 
-    /**
-     * Erases all reference types to {@code Object}, and all subword types to {@code int}.
-     * This is the reduced type polymorphism used by private methods
-     * such as {@link MethodHandle#invokeBasic invokeBasic}.
-     * @return a version of the original type with all reference and subword types replaced
-     */
-    /*non-public*/ MethodType basicType() {
-        return form.basicType();
-    }
+    public int parameterCount() { return 0; }
 
-    /**
-     * @return a version of the original type with MethodHandle prepended as the first argument
-     */
-    /*non-public*/ MethodType invokerType() {
-        return insertParameterTypes(0, MethodHandle.class);
-    }
+    public Class<?> returnType() { return null; }
 
-    /**
-     * Converts all types, both reference and primitive, to {@code Object}.
-     * Convenience method for {@link #genericMethodType(int) genericMethodType}.
-     * The expression {@code type.wrap().erase()} produces the same value
-     * as {@code type.generic()}.
-     * @return a version of the original type with all types replaced
-     */
-    public MethodType generic() {
-        return genericMethodType(parameterCount());
-    }
+    public List<Class<?>> parameterList() { return null; }
 
-    /*non-public*/ boolean isGeneric() {
-        return this == erase() && !hasPrimitives();
-    }
+    public Class<?>[] parameterArray() { return null; }
 
-    /**
-     * Converts all primitive types to their corresponding wrapper types.
-     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
-     * All reference types (including wrapper types) will remain unchanged.
-     * A {@code void} return type is changed to the type {@code java.lang.Void}.
-     * The expression {@code type.wrap().erase()} produces the same value
-     * as {@code type.generic()}.
-     * @return a version of the original type with all primitive types replaced
-     */
-    public MethodType wrap() {
-        return hasPrimitives() ? wrapWithPrims(this) : this;
-    }
-
-    /**
-     * Converts all wrapper types to their corresponding primitive types.
-     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
-     * All primitive types (including {@code void}) will remain unchanged.
-     * A return type of {@code java.lang.Void} is changed to {@code void}.
-     * @return a version of the original type with all wrapper types replaced
-     */
-    public MethodType unwrap() {
-        MethodType noprims = !hasPrimitives() ? this : wrapWithPrims(this);
-        return unwrapWithNoPrims(noprims);
-    }
-
-    private static MethodType wrapWithPrims(MethodType pt) {
-        assert(pt.hasPrimitives());
-        MethodType wt = pt.wrapAlt;
-        if (wt == null) {
-            // fill in lazily
-            wt = MethodTypeForm.canonicalize(pt, MethodTypeForm.WRAP, MethodTypeForm.WRAP);
-            assert(wt != null);
-            pt.wrapAlt = wt;
-        }
-        return wt;
-    }
-
-    private static MethodType unwrapWithNoPrims(MethodType wt) {
-        assert(!wt.hasPrimitives());
-        MethodType uwt = wt.wrapAlt;
-        if (uwt == null) {
-            // fill in lazily
-            uwt = MethodTypeForm.canonicalize(wt, MethodTypeForm.UNWRAP, MethodTypeForm.UNWRAP);
-            if (uwt == null)
-                uwt = wt;    // type has no wrappers or prims at all
-            wt.wrapAlt = uwt;
-        }
-        return uwt;
-    }
-
-    /**
-     * Returns the parameter type at the specified index, within this method type.
-     * @param num the index (zero-based) of the desired parameter type
-     * @return the selected parameter type
-     * @throws IndexOutOfBoundsException if {@code num} is not a valid index into {@code parameterArray()}
-     */
-    public Class<?> parameterType(int num) {
-        return ptypes[num];
-    }
-    /**
-     * Returns the number of parameter types in this method type.
-     * @return the number of parameter types
-     */
-    public int parameterCount() {
-        return ptypes.length;
-    }
-    /**
-     * Returns the return type of this method type.
-     * @return the return type
-     */
-    public Class<?> returnType() {
-        return rtype;
-    }
-
-    /**
-     * Presents the parameter types as a list (a convenience method).
-     * The list will be immutable.
-     * @return the parameter types (as an immutable list)
-     */
-    public List<Class<?>> parameterList() {
-        return Collections.unmodifiableList(Arrays.asList(ptypes.clone()));
-    }
-
-    /*non-public*/ Class<?> lastParameterType() {
-        int len = ptypes.length;
-        return len == 0 ? void.class : ptypes[len-1];
-    }
-
-    /**
-     * Presents the parameter types as an array (a convenience method).
-     * Changes to the array will not result in changes to the type.
-     * @return the parameter types (as a fresh copy if necessary)
-     */
-    public Class<?>[] parameterArray() {
-        return ptypes.clone();
-    }
-
-    /**
-     * Compares the specified object with this type for equality.
-     * That is, it returns <tt>true</tt> if and only if the specified object
-     * is also a method type with exactly the same parameters and return type.
-     * @param x object to compare
-     * @see Object#equals(Object)
-     */
-    @Override
-    public boolean equals(Object x) {
-        return this == x || x instanceof MethodType && equals((MethodType)x);
-    }
-
-    private boolean equals(MethodType that) {
-        return this.rtype == that.rtype
-            && Arrays.equals(this.ptypes, that.ptypes);
-    }
-
-    /**
-     * Returns the hash code value for this method type.
-     * It is defined to be the same as the hashcode of a List
-     * whose elements are the return type followed by the
-     * parameter types.
-     * @return the hash code value for this method type
-     * @see Object#hashCode()
-     * @see #equals(Object)
-     * @see List#hashCode()
-     */
-    @Override
-    public int hashCode() {
-      int hashCode = 31 + rtype.hashCode();
-      for (Class<?> ptype : ptypes)
-          hashCode = 31*hashCode + ptype.hashCode();
-      return hashCode;
-    }
-
-    /**
-     * Returns a string representation of the method type,
-     * of the form {@code "(PT0,PT1...)RT"}.
-     * The string representation of a method type is a
-     * parenthesis enclosed, comma separated list of type names,
-     * followed immediately by the return type.
-     * <p>
-     * Each type is represented by its
-     * {@link java.lang.Class#getSimpleName simple name}.
-     */
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("(");
-        for (int i = 0; i < ptypes.length; i++) {
-            if (i > 0)  sb.append(",");
-            sb.append(ptypes[i].getSimpleName());
-        }
-        sb.append(")");
-        sb.append(rtype.getSimpleName());
-        return sb.toString();
-    }
-
-    /** True if the old return type can always be viewed (w/o casting) under new return type,
-     *  and the new parameters can be viewed (w/o casting) under the old parameter types.
-     */
-    // Android-changed: Removed implementation details.
-    // boolean isViewableAs(MethodType newType, boolean keepInterfaces);
-    // boolean parametersAreViewableAs(MethodType newType, boolean keepInterfaces);
-    /*non-public*/
-    boolean isConvertibleTo(MethodType newType) {
-        MethodTypeForm oldForm = this.form();
-        MethodTypeForm newForm = newType.form();
-        if (oldForm == newForm)
-            // same parameter count, same primitive/object mix
-            return true;
-        if (!canConvert(returnType(), newType.returnType()))
-            return false;
-        Class<?>[] srcTypes = newType.ptypes;
-        Class<?>[] dstTypes = ptypes;
-        if (srcTypes == dstTypes)
-            return true;
-        int argc;
-        if ((argc = srcTypes.length) != dstTypes.length)
-            return false;
-        if (argc <= 1) {
-            if (argc == 1 && !canConvert(srcTypes[0], dstTypes[0]))
-                return false;
-            return true;
-        }
-        if ((oldForm.primitiveParameterCount() == 0 && oldForm.erasedType == this) ||
-            (newForm.primitiveParameterCount() == 0 && newForm.erasedType == newType)) {
-            // Somewhat complicated test to avoid a loop of 2 or more trips.
-            // If either type has only Object parameters, we know we can convert.
-            assert(canConvertParameters(srcTypes, dstTypes));
-            return true;
-        }
-        return canConvertParameters(srcTypes, dstTypes);
-    }
-
-    /** Returns true if MHs.explicitCastArguments produces the same result as MH.asType.
-     *  If the type conversion is impossible for either, the result should be false.
-     */
-    /*non-public*/
-    boolean explicitCastEquivalentToAsType(MethodType newType) {
-        if (this == newType)  return true;
-        if (!explicitCastEquivalentToAsType(rtype, newType.rtype)) {
-            return false;
-        }
-        Class<?>[] srcTypes = newType.ptypes;
-        Class<?>[] dstTypes = ptypes;
-        if (dstTypes == srcTypes) {
-            return true;
-        }
-        assert(dstTypes.length == srcTypes.length);
-        for (int i = 0; i < dstTypes.length; i++) {
-            if (!explicitCastEquivalentToAsType(srcTypes[i], dstTypes[i])) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /** Reports true if the src can be converted to the dst, by both asType and MHs.eCE,
-     *  and with the same effect.
-     *  MHs.eCA has the following "upgrades" to MH.asType:
-     *  1. interfaces are unchecked (that is, treated as if aliased to Object)
-     *     Therefore, {@code Object->CharSequence} is possible in both cases but has different semantics
-     *  2a. the full matrix of primitive-to-primitive conversions is supported
-     *      Narrowing like {@code long->byte} and basic-typing like {@code boolean->int}
-     *      are not supported by asType, but anything supported by asType is equivalent
-     *      with MHs.eCE.
-     *  2b. conversion of void->primitive means explicit cast has to insert zero/false/null.
-     *  3a. unboxing conversions can be followed by the full matrix of primitive conversions
-     *  3b. unboxing of null is permitted (creates a zero primitive value)
-     * Other than interfaces, reference-to-reference conversions are the same.
-     * Boxing primitives to references is the same for both operators.
-     */
-    private static boolean explicitCastEquivalentToAsType(Class<?> src, Class<?> dst) {
-        if (src == dst || dst == Object.class || dst == void.class) {
-            return true;
-        } else if (src.isPrimitive() && src != void.class) {
-            // Could be a prim/prim conversion, where casting is a strict superset.
-            // Or a boxing conversion, which is always to an exact wrapper class.
-            return canConvert(src, dst);
-        } else if (dst.isPrimitive()) {
-            // Unboxing behavior is different between MHs.eCA & MH.asType (see 3b).
-            return false;
-        } else {
-            // R->R always works, but we have to avoid a check-cast to an interface.
-            return !dst.isInterface() || dst.isAssignableFrom(src);
-        }
-    }
-
-    private boolean canConvertParameters(Class<?>[] srcTypes, Class<?>[] dstTypes) {
-        for (int i = 0; i < srcTypes.length; i++) {
-            if (!canConvert(srcTypes[i], dstTypes[i])) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /*non-public*/
-    static boolean canConvert(Class<?> src, Class<?> dst) {
-        // short-circuit a few cases:
-        if (src == dst || src == Object.class || dst == Object.class)  return true;
-        // the remainder of this logic is documented in MethodHandle.asType
-        if (src.isPrimitive()) {
-            // can force void to an explicit null, a la reflect.Method.invoke
-            // can also force void to a primitive zero, by analogy
-            if (src == void.class)  return true;  //or !dst.isPrimitive()?
-            Wrapper sw = Wrapper.forPrimitiveType(src);
-            if (dst.isPrimitive()) {
-                // P->P must widen
-                return Wrapper.forPrimitiveType(dst).isConvertibleFrom(sw);
-            } else {
-                // P->R must box and widen
-                return dst.isAssignableFrom(sw.wrapperType());
-            }
-        } else if (dst.isPrimitive()) {
-            // any value can be dropped
-            if (dst == void.class)  return true;
-            Wrapper dw = Wrapper.forPrimitiveType(dst);
-            // R->P must be able to unbox (from a dynamically chosen type) and widen
-            // For example:
-            //   Byte/Number/Comparable/Object -> dw:Byte -> byte.
-            //   Character/Comparable/Object -> dw:Character -> char
-            //   Boolean/Comparable/Object -> dw:Boolean -> boolean
-            // This means that dw must be cast-compatible with src.
-            if (src.isAssignableFrom(dw.wrapperType())) {
-                return true;
-            }
-            // The above does not work if the source reference is strongly typed
-            // to a wrapper whose primitive must be widened.  For example:
-            //   Byte -> unbox:byte -> short/int/long/float/double
-            //   Character -> unbox:char -> int/long/float/double
-            if (Wrapper.isWrapperType(src) &&
-                dw.isConvertibleFrom(Wrapper.forWrapperType(src))) {
-                // can unbox from src and then widen to dst
-                return true;
-            }
-            // We have already covered cases which arise due to runtime unboxing
-            // of a reference type which covers several wrapper types:
-            //   Object -> cast:Integer -> unbox:int -> long/float/double
-            //   Serializable -> cast:Byte -> unbox:byte -> byte/short/int/long/float/double
-            // An marginal case is Number -> dw:Character -> char, which would be OK if there were a
-            // subclass of Number which wraps a value that can convert to char.
-            // Since there is none, we don't need an extra check here to cover char or boolean.
-            return false;
-        } else {
-            // R->R always works, since null is always valid dynamically
-            return true;
-        }
-    }
-
-    /** Reports the number of JVM stack slots required to invoke a method
-     * of this type.  Note that (for historical reasons) the JVM requires
-     * a second stack slot to pass long and double arguments.
-     * So this method returns {@link #parameterCount() parameterCount} plus the
-     * number of long and double parameters (if any).
-     * <p>
-     * This method is included for the benefit of applications that must
-     * generate bytecodes that process method handles and invokedynamic.
-     * @return the number of JVM stack slots for this type's parameters
-     */
-    /*non-public*/ int parameterSlotCount() {
-        return form.parameterSlotCount();
-    }
-
-    /// Queries which have to do with the bytecode architecture
-
-    // Android-changed: These methods aren't needed on Android and are unused within the JDK.
-    //
-    // int parameterSlotDepth(int num);
-    // int returnSlotCount();
-    //
-    // Android-changed: Removed cache of higher order adapters.
-    //
-    // Invokers invokers();
-
-    /**
-     * Finds or creates an instance of a method type, given the spelling of its bytecode descriptor.
-     * Convenience method for {@link #methodType(java.lang.Class, java.lang.Class[]) methodType}.
-     * Any class or interface name embedded in the descriptor string
-     * will be resolved by calling {@link ClassLoader#loadClass(java.lang.String)}
-     * on the given loader (or if it is null, on the system class loader).
-     * <p>
-     * Note that it is possible to encounter method types which cannot be
-     * constructed by this method, because their component types are
-     * not all reachable from a common class loader.
-     * <p>
-     * This method is included for the benefit of applications that must
-     * generate bytecodes that process method handles and {@code invokedynamic}.
-     * @param descriptor a bytecode-level type descriptor string "(T...)T"
-     * @param loader the class loader in which to look up the types
-     * @return a method type matching the bytecode-level type descriptor
-     * @throws NullPointerException if the string is null
-     * @throws IllegalArgumentException if the string is not well-formed
-     * @throws TypeNotPresentException if a named type cannot be found
-     */
     public static MethodType fromMethodDescriptorString(String descriptor, ClassLoader loader)
-        throws IllegalArgumentException, TypeNotPresentException
-    {
-        if (!descriptor.startsWith("(") ||  // also generates NPE if needed
-            descriptor.indexOf(')') < 0 ||
-            descriptor.indexOf('.') >= 0)
-            throw newIllegalArgumentException("not a method descriptor: "+descriptor);
-        List<Class<?>> types = BytecodeDescriptor.parseMethod(descriptor, loader);
-        Class<?> rtype = types.remove(types.size() - 1);
-        checkSlotCount(types.size());
-        Class<?>[] ptypes = listToArray(types);
-        return makeImpl(rtype, ptypes, true);
-    }
+        throws IllegalArgumentException, TypeNotPresentException { return null; }
 
-    /**
-     * Produces a bytecode descriptor representation of the method type.
-     * <p>
-     * Note that this is not a strict inverse of {@link #fromMethodDescriptorString fromMethodDescriptorString}.
-     * Two distinct classes which share a common name but have different class loaders
-     * will appear identical when viewed within descriptor strings.
-     * <p>
-     * This method is included for the benefit of applications that must
-     * generate bytecodes that process method handles and {@code invokedynamic}.
-     * {@link #fromMethodDescriptorString(java.lang.String, java.lang.ClassLoader) fromMethodDescriptorString},
-     * because the latter requires a suitable class loader argument.
-     * @return the bytecode type descriptor representation
-     */
-    public String toMethodDescriptorString() {
-        String desc = methodDescriptor;
-        if (desc == null) {
-            desc = BytecodeDescriptor.unparse(this);
-            methodDescriptor = desc;
-        }
-        return desc;
-    }
-
-    /*non-public*/ static String toFieldDescriptorString(Class<?> cls) {
-        return BytecodeDescriptor.unparse(cls);
-    }
-
-    /// Serialization.
-
-    /**
-     * There are no serializable fields for {@code MethodType}.
-     */
-    private static final java.io.ObjectStreamField[] serialPersistentFields = { };
-
-    /**
-     * Save the {@code MethodType} instance to a stream.
-     *
-     * @serialData
-     * For portability, the serialized format does not refer to named fields.
-     * Instead, the return type and parameter type arrays are written directly
-     * from the {@code writeObject} method, using two calls to {@code s.writeObject}
-     * as follows:
-     * <blockquote><pre>{@code
-s.writeObject(this.returnType());
-s.writeObject(this.parameterArray());
-     * }</pre></blockquote>
-     * <p>
-     * The deserialized field values are checked as if they were
-     * provided to the factory method {@link #methodType(Class,Class[]) methodType}.
-     * For example, null values, or {@code void} parameter types,
-     * will lead to exceptions during deserialization.
-     * @param s the stream to write the object to
-     * @throws java.io.IOException if there is a problem writing the object
-     */
-    private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
-        s.defaultWriteObject();  // requires serialPersistentFields to be an empty array
-        s.writeObject(returnType());
-        s.writeObject(parameterArray());
-    }
-
-    /**
-     * Reconstitute the {@code MethodType} instance from a stream (that is,
-     * deserialize it).
-     * This instance is a scratch object with bogus final fields.
-     * It provides the parameters to the factory method called by
-     * {@link #readResolve readResolve}.
-     * After that call it is discarded.
-     * @param s the stream to read the object from
-     * @throws java.io.IOException if there is a problem reading the object
-     * @throws ClassNotFoundException if one of the component classes cannot be resolved
-     * @see #MethodType()
-     * @see #readResolve
-     * @see #writeObject
-     */
-    private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
-        s.defaultReadObject();  // requires serialPersistentFields to be an empty array
-
-        Class<?>   returnType     = (Class<?>)   s.readObject();
-        Class<?>[] parameterArray = (Class<?>[]) s.readObject();
-
-        // Probably this object will never escape, but let's check
-        // the field values now, just to be sure.
-        checkRtype(returnType);
-        checkPtypes(parameterArray);
-
-        parameterArray = parameterArray.clone();  // make sure it is unshared
-        MethodType_init(returnType, parameterArray);
-    }
-
-    /**
-     * For serialization only.
-     * Sets the final fields to null, pending {@code Unsafe.putObject}.
-     */
-    private MethodType() {
-        this.rtype = null;
-        this.ptypes = null;
-    }
-    private void MethodType_init(Class<?> rtype, Class<?>[] ptypes) {
-        // In order to communicate these values to readResolve, we must
-        // store them into the implementation-specific final fields.
-        checkRtype(rtype);
-        checkPtypes(ptypes);
-        UNSAFE.putObject(this, rtypeOffset, rtype);
-        UNSAFE.putObject(this, ptypesOffset, ptypes);
-    }
-
-    // Support for resetting final fields while deserializing
-    private static final long rtypeOffset, ptypesOffset;
-    static {
-        try {
-            rtypeOffset = UNSAFE.objectFieldOffset
-                (MethodType.class.getDeclaredField("rtype"));
-            ptypesOffset = UNSAFE.objectFieldOffset
-                (MethodType.class.getDeclaredField("ptypes"));
-        } catch (Exception ex) {
-            throw new Error(ex);
-        }
-    }
-
-    /**
-     * Resolves and initializes a {@code MethodType} object
-     * after serialization.
-     * @return the fully initialized {@code MethodType} object
-     */
-    private Object readResolve() {
-        // Do not use a trusted path for deserialization:
-        //return makeImpl(rtype, ptypes, true);
-        // Verify all operands, and make sure ptypes is unshared:
-        return methodType(rtype, ptypes);
-    }
-
-    /**
-     * Simple implementation of weak concurrent intern set.
-     *
-     * @param <T> interned type
-     */
-    private static class ConcurrentWeakInternSet<T> {
-
-        private final ConcurrentMap<WeakEntry<T>, WeakEntry<T>> map;
-        private final ReferenceQueue<T> stale;
-
-        public ConcurrentWeakInternSet() {
-            this.map = new ConcurrentHashMap<>();
-            this.stale = new ReferenceQueue<>();
-        }
-
-        /**
-         * Get the existing interned element.
-         * This method returns null if no element is interned.
-         *
-         * @param elem element to look up
-         * @return the interned element
-         */
-        public T get(T elem) {
-            if (elem == null) throw new NullPointerException();
-            expungeStaleElements();
-
-            WeakEntry<T> value = map.get(new WeakEntry<>(elem));
-            if (value != null) {
-                T res = value.get();
-                if (res != null) {
-                    return res;
-                }
-            }
-            return null;
-        }
-
-        /**
-         * Interns the element.
-         * Always returns non-null element, matching the one in the intern set.
-         * Under the race against another add(), it can return <i>different</i>
-         * element, if another thread beats us to interning it.
-         *
-         * @param elem element to add
-         * @return element that was actually added
-         */
-        public T add(T elem) {
-            if (elem == null) throw new NullPointerException();
-
-            // Playing double race here, and so spinloop is required.
-            // First race is with two concurrent updaters.
-            // Second race is with GC purging weak ref under our feet.
-            // Hopefully, we almost always end up with a single pass.
-            T interned;
-            WeakEntry<T> e = new WeakEntry<>(elem, stale);
-            do {
-                expungeStaleElements();
-                WeakEntry<T> exist = map.putIfAbsent(e, e);
-                interned = (exist == null) ? elem : exist.get();
-            } while (interned == null);
-            return interned;
-        }
-
-        private void expungeStaleElements() {
-            Reference<? extends T> reference;
-            while ((reference = stale.poll()) != null) {
-                map.remove(reference);
-            }
-        }
-
-        private static class WeakEntry<T> extends WeakReference<T> {
-
-            public final int hashcode;
-
-            public WeakEntry(T key, ReferenceQueue<T> queue) {
-                super(key, queue);
-                hashcode = key.hashCode();
-            }
-
-            public WeakEntry(T key) {
-                super(key);
-                hashcode = key.hashCode();
-            }
-
-            @Override
-            public boolean equals(Object obj) {
-                if (obj instanceof WeakEntry) {
-                    Object that = ((WeakEntry) obj).get();
-                    Object mine = get();
-                    return (that == null || mine == null) ? (this == obj) : mine.equals(that);
-                }
-                return false;
-            }
-
-            @Override
-            public int hashCode() {
-                return hashcode;
-            }
-
-        }
-    }
+    public String toMethodDescriptorString() { return null; }
 
 }
diff --git a/java/net/DatagramSocket.java b/java/net/DatagramSocket.java
index 5beab30..3e3faf7 100644
--- a/java/net/DatagramSocket.java
+++ b/java/net/DatagramSocket.java
@@ -1355,24 +1355,4 @@
         return impl.fd;
     }
 
-    // Android-added: setNetworkInterface() to set the network interface used by this socket.
-    /**
-     * Sets the network interface used by this socket.  Any packets sent
-     * via this socket are transmitted via the specified interface.  Any
-     * packets received by this socket will come from the specified
-     * interface.  Broadcast datagrams received on this interface will
-     * be processed by this socket. This corresponds to Linux's SO_BINDTODEVICE.
-     *
-     * @hide used by GoogleTV for DHCP
-     */
-    public void setNetworkInterface(NetworkInterface netInterface) throws SocketException {
-        if (netInterface == null) {
-            throw new NullPointerException("netInterface == null");
-        }
-        try {
-            Libcore.os.setsockoptIfreq(impl.fd, SOL_SOCKET, SO_BINDTODEVICE, netInterface.getName());
-        } catch (ErrnoException errnoException) {
-            throw errnoException.rethrowAsSocketException();
-        }
-    }
 }
diff --git a/java/net/HttpURLConnection.java b/java/net/HttpURLConnection.java
index db05c5f..b72168b 100644
--- a/java/net/HttpURLConnection.java
+++ b/java/net/HttpURLConnection.java
@@ -315,7 +315,7 @@
      * server. In this case, {@link #getHeaderField(int) getHeaderField(0)} returns the status
      * line, but {@code getHeaderFieldKey(0)} returns null.
      *
-     * @param   n   an index, where n >=0.
+     * @param   n   an index, where {@code n >=0}.
      * @return  the key for the {@code n}<sup>th</sup> header field,
      *          or {@code null} if the key does not exist.
      */
@@ -466,7 +466,7 @@
      * {@link #getHeaderFieldKey getHeaderFieldKey} method to iterate through all
      * the headers in the message.
      *
-     * @param   n   an index, where n>=0.
+     * @param   n   an index, where {@code n>=0}.
      * @return  the value of the {@code n}<sup>th</sup> header field,
      *          or {@code null} if the value does not exist.
      * @see     java.net.HttpURLConnection#getHeaderFieldKey(int)
diff --git a/java/net/IDN.java b/java/net/IDN.java
index 8ea32c8..4639c8f 100644
--- a/java/net/IDN.java
+++ b/java/net/IDN.java
@@ -77,9 +77,6 @@
      */
     public static final int USE_STD3_ASCII_RULES = 0x02;
 
-    private IDN() {
-    }
-
 
     /**
      * Translates a string from Unicode to ASCII Compatible Encoding (ACE),
@@ -106,11 +103,13 @@
      * @throws IllegalArgumentException   if the input string doesn't conform to RFC 3490 specification
      */
     public static String toASCII(String input, int flag) {
+        // BEGIN Android-changed: Use ICU4J implementation
         try {
             return IDNA.convertIDNToASCII(input, flag).toString();
         } catch (android.icu.text.StringPrepParseException e) {
             throw new IllegalArgumentException("Invalid input to toASCII: " + input, e);
         }
+        // END Android-changed: Use ICU4J implementation
     }
 
 
@@ -154,6 +153,7 @@
      * @return          the translated {@code String}
      */
     public static String toUnicode(String input, int flag) {
+        // BEGIN Android-changed: Use ICU4J implementation
         try {
             // ICU only translates separators to ASCII for toASCII.
             // Java expects the translation for toUnicode too.
@@ -163,8 +163,10 @@
             // the original string is returned.
             return input;
         }
+        // END Android-changed: Use ICU4J implementation
     }
 
+    // BEGIN Android-added: Use ICU4J implementation
     private static boolean isLabelSeperator(char c) {
         return (c == '\u3002' || c == '\uff0e' || c == '\uff61');
     }
@@ -177,7 +179,7 @@
         }
         return input;
     }
-
+    // END Android-added: Use ICU4J implementation
 
     /**
      * Translates a string from ASCII Compatible Encoding (ACE) to Unicode,
@@ -196,4 +198,296 @@
     public static String toUnicode(String input) {
         return toUnicode(input, 0);
     }
+
+
+    /* ---------------- Private members -------------- */
+
+    // Android-removed: Private helper methods, unused because we use ICU.
+    /*
+    // ACE Prefix is "xn--"
+    private static final String ACE_PREFIX = "xn--";
+    private static final int ACE_PREFIX_LENGTH = ACE_PREFIX.length();
+
+    private static final int MAX_LABEL_LENGTH   = 63;
+
+    // single instance of nameprep
+    private static StringPrep namePrep = null;
+
+    static {
+        InputStream stream = null;
+
+        try {
+            final String IDN_PROFILE = "uidna.spp";
+            if (System.getSecurityManager() != null) {
+                stream = AccessController.doPrivileged(new PrivilegedAction<InputStream>() {
+                    public InputStream run() {
+                        return StringPrep.class.getResourceAsStream(IDN_PROFILE);
+                    }
+                });
+            } else {
+                stream = StringPrep.class.getResourceAsStream(IDN_PROFILE);
+            }
+
+            namePrep = new StringPrep(stream);
+            stream.close();
+        } catch (IOException e) {
+            // should never reach here
+            assert false;
+        }
+    }
+    */
+
+    /* ---------------- Private operations -------------- */
+
+
+    //
+    // to suppress the default zero-argument constructor
+    //
+    private IDN() {}
+
+    // Android-removed: Private helper methods, unused because we use ICU.
+    /*
+    //
+    // toASCII operation; should only apply to a single label
+    //
+    private static String toASCIIInternal(String label, int flag)
+    {
+        // step 1
+        // Check if the string contains code points outside the ASCII range 0..0x7c.
+        boolean isASCII  = isAllASCII(label);
+        StringBuffer dest;
+
+        // step 2
+        // perform the nameprep operation; flag ALLOW_UNASSIGNED is used here
+        if (!isASCII) {
+            UCharacterIterator iter = UCharacterIterator.getInstance(label);
+            try {
+                dest = namePrep.prepare(iter, flag);
+            } catch (java.text.ParseException e) {
+                throw new IllegalArgumentException(e);
+            }
+        } else {
+            dest = new StringBuffer(label);
+        }
+
+        // step 8, move forward to check the smallest number of the code points
+        // the length must be inside 1..63
+        if (dest.length() == 0) {
+            throw new IllegalArgumentException(
+                        "Empty label is not a legal name");
+        }
+
+        // step 3
+        // Verify the absence of non-LDH ASCII code points
+        //   0..0x2c, 0x2e..0x2f, 0x3a..0x40, 0x5b..0x60, 0x7b..0x7f
+        // Verify the absence of leading and trailing hyphen
+        boolean useSTD3ASCIIRules = ((flag & USE_STD3_ASCII_RULES) != 0);
+        if (useSTD3ASCIIRules) {
+            for (int i = 0; i < dest.length(); i++) {
+                int c = dest.charAt(i);
+                if (isNonLDHAsciiCodePoint(c)) {
+                    throw new IllegalArgumentException(
+                        "Contains non-LDH ASCII characters");
+                }
+            }
+
+            if (dest.charAt(0) == '-' ||
+                dest.charAt(dest.length() - 1) == '-') {
+
+                throw new IllegalArgumentException(
+                        "Has leading or trailing hyphen");
+            }
+        }
+
+        if (!isASCII) {
+            // step 4
+            // If all code points are inside 0..0x7f, skip to step 8
+            if (!isAllASCII(dest.toString())) {
+                // step 5
+                // verify the sequence does not begin with ACE prefix
+                if(!startsWithACEPrefix(dest)){
+
+                    // step 6
+                    // encode the sequence with punycode
+                    try {
+                        dest = Punycode.encode(dest, null);
+                    } catch (java.text.ParseException e) {
+                        throw new IllegalArgumentException(e);
+                    }
+
+                    dest = toASCIILower(dest);
+
+                    // step 7
+                    // prepend the ACE prefix
+                    dest.insert(0, ACE_PREFIX);
+                } else {
+                    throw new IllegalArgumentException("The input starts with the ACE Prefix");
+                }
+
+            }
+        }
+
+        // step 8
+        // the length must be inside 1..63
+        if (dest.length() > MAX_LABEL_LENGTH) {
+            throw new IllegalArgumentException("The label in the input is too long");
+        }
+
+        return dest.toString();
+    }
+
+    //
+    // toUnicode operation; should only apply to a single label
+    //
+    private static String toUnicodeInternal(String label, int flag) {
+        boolean[] caseFlags = null;
+        StringBuffer dest;
+
+        // step 1
+        // find out if all the codepoints in input are ASCII
+        boolean isASCII = isAllASCII(label);
+
+        if(!isASCII){
+            // step 2
+            // perform the nameprep operation; flag ALLOW_UNASSIGNED is used here
+            try {
+                UCharacterIterator iter = UCharacterIterator.getInstance(label);
+                dest = namePrep.prepare(iter, flag);
+            } catch (Exception e) {
+                // toUnicode never fails; if any step fails, return the input string
+                return label;
+            }
+        } else {
+            dest = new StringBuffer(label);
+        }
+
+        // step 3
+        // verify ACE Prefix
+        if(startsWithACEPrefix(dest)) {
+
+            // step 4
+            // Remove the ACE Prefix
+            String temp = dest.substring(ACE_PREFIX_LENGTH, dest.length());
+
+            try {
+                // step 5
+                // Decode using punycode
+                StringBuffer decodeOut = Punycode.decode(new StringBuffer(temp), null);
+
+                // step 6
+                // Apply toASCII
+                String toASCIIOut = toASCII(decodeOut.toString(), flag);
+
+                // step 7
+                // verify
+                if (toASCIIOut.equalsIgnoreCase(dest.toString())) {
+                    // step 8
+                    // return output of step 5
+                    return decodeOut.toString();
+                }
+            } catch (Exception ignored) {
+                // no-op
+            }
+        }
+
+        // just return the input
+        return label;
+    }
+
+
+    //
+    // LDH stands for "letter/digit/hyphen", with characters restricted to the
+    // 26-letter Latin alphabet <A-Z a-z>, the digits <0-9>, and the hyphen
+    // <->.
+    // Non LDH refers to characters in the ASCII range, but which are not
+    // letters, digits or the hypen.
+    //
+    // non-LDH = 0..0x2C, 0x2E..0x2F, 0x3A..0x40, 0x5B..0x60, 0x7B..0x7F
+    //
+    private static boolean isNonLDHAsciiCodePoint(int ch){
+        return (0x0000 <= ch && ch <= 0x002C) ||
+               (0x002E <= ch && ch <= 0x002F) ||
+               (0x003A <= ch && ch <= 0x0040) ||
+               (0x005B <= ch && ch <= 0x0060) ||
+               (0x007B <= ch && ch <= 0x007F);
+    }
+
+    //
+    // search dots in a string and return the index of that character;
+    // or if there is no dots, return the length of input string
+    // dots might be: \u002E (full stop), \u3002 (ideographic full stop), \uFF0E (fullwidth full stop),
+    // and \uFF61 (halfwidth ideographic full stop).
+    //
+    private static int searchDots(String s, int start) {
+        int i;
+        for (i = start; i < s.length(); i++) {
+            if (isLabelSeparator(s.charAt(i))) {
+                break;
+            }
+        }
+
+        return i;
+    }
+
+    //
+    // to check if a string is a root label, ".".
+    //
+    private static boolean isRootLabel(String s) {
+        return (s.length() == 1 && isLabelSeparator(s.charAt(0)));
+    }
+
+    //
+    // to check if a character is a label separator, i.e. a dot character.
+    //
+    private static boolean isLabelSeparator(char c) {
+        return (c == '.' || c == '\u3002' || c == '\uFF0E' || c == '\uFF61');
+    }
+
+    //
+    // to check if a string only contains US-ASCII code point
+    //
+    private static boolean isAllASCII(String input) {
+        boolean isASCII = true;
+        for (int i = 0; i < input.length(); i++) {
+            int c = input.charAt(i);
+            if (c > 0x7F) {
+                isASCII = false;
+                break;
+            }
+        }
+        return isASCII;
+    }
+
+    //
+    // to check if a string starts with ACE-prefix
+    //
+    private static boolean startsWithACEPrefix(StringBuffer input){
+        boolean startsWithPrefix = true;
+
+        if(input.length() < ACE_PREFIX_LENGTH){
+            return false;
+        }
+        for(int i = 0; i < ACE_PREFIX_LENGTH; i++){
+            if(toASCIILower(input.charAt(i)) != ACE_PREFIX.charAt(i)){
+                startsWithPrefix = false;
+            }
+        }
+        return startsWithPrefix;
+    }
+
+    private static char toASCIILower(char ch){
+        if('A' <= ch && ch <= 'Z'){
+            return (char)(ch + 'a' - 'A');
+        }
+        return ch;
+    }
+
+    private static StringBuffer toASCIILower(StringBuffer input){
+        StringBuffer dest = new StringBuffer();
+        for(int i = 0; i < input.length();i++){
+            dest.append(toASCIILower(input.charAt(i)));
+        }
+        return dest;
+    }
+    */
 }
diff --git a/java/net/InMemoryCookieStore.java b/java/net/InMemoryCookieStore.java
index 288dddb..3ee98dc 100644
--- a/java/net/InMemoryCookieStore.java
+++ b/java/net/InMemoryCookieStore.java
@@ -36,6 +36,13 @@
 import java.util.Iterator;
 import java.util.concurrent.locks.ReentrantLock;
 
+// Android-changed: App compat changes and bug fixes
+// b/26456024 Add targetSdkVersion based compatibility for domain matching
+// b/33034917 Support clearing cookies by adding it with "max-age=0"
+// b/25897688 InMemoryCookieStore ignores scheme (http/https) port and path of the cookie
+// Remove cookieJar and domainIndex. Use urlIndex as single Cookie storage
+// Fix InMemoryCookieStore#remove to verify cookie URI before removal
+// Fix InMemoryCookieStore#removeAll to return false if it's empty.
 /**
  * A simple in-memory java.net.CookieStore implementation
  *
@@ -45,11 +52,23 @@
  */
 public class InMemoryCookieStore implements CookieStore {
     // the in-memory representation of cookies
+    // BEGIN Android-removed: Remove cookieJar and domainIndex
+    /*
+    private List<HttpCookie> cookieJar = null;
+
+    // the cookies are indexed by its domain and associated uri (if present)
+    // CAUTION: when a cookie removed from main data structure (i.e. cookieJar),
+    //          it won't be cleared in domainIndex & uriIndex. Double-check the
+    //          presence of cookie when retrieve one form index store.
+    private Map<String, List<HttpCookie>> domainIndex = null;
+    */
+    // END Android-removed: Remove cookieJar and domainIndex
     private Map<URI, List<HttpCookie>> uriIndex = null;
 
     // use ReentrantLock instead of syncronized for scalability
     private ReentrantLock lock = null;
 
+    // BEGIN Android-changed: Add targetSdkVersion and remove cookieJar and domainIndex
     private final boolean applyMCompatibility;
 
     /**
@@ -64,6 +83,7 @@
         lock = new ReentrantLock(false);
         applyMCompatibility = (targetSdkVersion <= 23);
     }
+    // END Android-changed: Add targetSdkVersion and remove cookieJar and domainIndex
 
     /**
      * Add one cookie into cookie store.
@@ -101,6 +121,7 @@
         }
 
         List<HttpCookie> cookies = new ArrayList<HttpCookie>();
+        // BEGIN Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https)
         lock.lock();
         try {
             // check domainIndex first
@@ -110,7 +131,7 @@
         } finally {
             lock.unlock();
         }
-
+        // END Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https)
         return cookies;
     }
 
@@ -118,6 +139,7 @@
      * Get all cookies in cookie store, except those have expired
      */
     public List<HttpCookie> getCookies() {
+        // BEGIN Android-changed: Remove cookieJar and domainIndex
         List<HttpCookie> rt = new ArrayList<HttpCookie>();
 
         lock.lock();
@@ -137,6 +159,7 @@
             rt = Collections.unmodifiableList(rt);
             lock.unlock();
         }
+        // END Android-changed: Remove cookieJar and domainIndex
 
         return rt;
     }
@@ -169,6 +192,7 @@
             throw new NullPointerException("cookie is null");
         }
 
+        // BEGIN Android-changed: Fix uri not being removed from uriIndex
         lock.lock();
         try {
             uri = getEffectiveURI(uri);
@@ -185,6 +209,7 @@
         } finally {
             lock.unlock();
         }
+        // END Android-changed: Fix uri not being removed from uriIndex
     }
 
 
@@ -193,6 +218,7 @@
      */
     public boolean removeAll() {
         lock.lock();
+        // BEGIN Android-change: Return false if it's empty.
         boolean result = false;
 
         try {
@@ -203,6 +229,7 @@
         }
 
         return result;
+        // END Android-change: Return false if it's empty.
     }
 
 
@@ -251,6 +278,7 @@
             // need to check H & D component
             String D = host.substring(lengthDiff);
 
+            // Android-changed: b/26456024 targetSdkVersion based compatibility for domain matching
             // Android M and earlier: Cookies with domain "foo.com" would not match "bar.foo.com".
             // The RFC dictates that the user agent must treat those domains as if they had a
             // leading period and must therefore match "bar.foo.com".
@@ -270,6 +298,7 @@
 
     private void getInternal1(List<HttpCookie> cookies, Map<URI, List<HttpCookie>> cookieIndex,
             String host) {
+        // BEGIN Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https)
         // Use a separate list to handle cookies that need to be removed so
         // that there is no conflict with iterators.
         ArrayList<HttpCookie> toRemove = new ArrayList<HttpCookie>();
@@ -298,6 +327,7 @@
             }
             toRemove.clear();
         }
+        // END Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https)
     }
 
     // @param cookies           [OUT] contains the found cookies
@@ -308,6 +338,7 @@
         void getInternal2(List<HttpCookie> cookies, Map<T, List<HttpCookie>> cookieIndex,
                           T comparator)
     {
+        // BEGIN Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https)
         // Removed cookieJar
         for (T index : cookieIndex.keySet()) {
             if ((index == comparator) || (index != null && comparator.compareTo(index) == 0)) {
@@ -329,6 +360,7 @@
                 } // end of indexedCookies != null
             } // end of comparator.compareTo(index) == 0
         } // end of cookieIndex iteration
+        // END Android-changed: b/25897688 InMemoryCookieStore ignores scheme (http/https)
     }
 
     // add 'cookie' indexed by 'index' into 'indexStore'
@@ -339,6 +371,7 @@
         // Android-changed: "index" can be null. We only use the URI based
         // index on Android and we want to support null URIs. The underlying
         // store is a HashMap which will support null keys anyway.
+        // if (index != null) {
         List<HttpCookie> cookies = indexStore.get(index);
         if (cookies != null) {
             // there may already have the same cookie, so remove it first
@@ -359,6 +392,7 @@
     //
     private URI getEffectiveURI(URI uri) {
         URI effectiveURI = null;
+        // Android-added: Fix NullPointerException
         if (uri == null) {
             return null;
         }
diff --git a/java/net/Inet4Address.java b/java/net/Inet4Address.java
index 9c8e5fb..1fb7b92 100644
--- a/java/net/Inet4Address.java
+++ b/java/net/Inet4Address.java
@@ -93,6 +93,7 @@
      *  serialized */
     private static final long serialVersionUID = 3286316764910316507L;
 
+    // BEGIN Android-added: Define special-purpose IPv4 address
     /** @hide */
     public static final InetAddress ANY = new Inet4Address(null, new byte[] { 0, 0, 0, 0 });
 
@@ -103,7 +104,18 @@
     /** @hide */
     public static final InetAddress LOOPBACK =
             new Inet4Address("localhost", new byte[] { 127, 0, 0, 1 });
+    // END Android-added: Define special-purpose IPv4 address
 
+
+    // BEGIN Android-removed: Android doesn't need to call native init
+    /*
+     * Perform initializations.
+     *
+    static {
+        init();
+    }
+    */
+    // END Android-removed: Android doesn't need to call native init
     Inet4Address() {
         super();
         holder().hostName = null;
@@ -378,4 +390,12 @@
     {
         return (src[0] & 0xff) + "." + (src[1] & 0xff) + "." + (src[2] & 0xff) + "." + (src[3] & 0xff);
     }
+
+    // BEGIN Android-removed: Android doesn't need to call native init
+    /*
+     * Perform class load-time initializations.
+     *
+    private static native void init();
+    */
+    // END Android-removed: Android doesn't need to call native init
 }
diff --git a/java/net/Inet6Address.java b/java/net/Inet6Address.java
index ee946ee..c0aadb3 100644
--- a/java/net/Inet6Address.java
+++ b/java/net/Inet6Address.java
@@ -178,6 +178,15 @@
 class Inet6Address extends InetAddress {
     final static int INADDRSZ = 16;
 
+    // BEGIN Android-removed: Remove special handling for link-local addresses
+    /*
+    * cached scope_id - for link-local address use only.
+    *
+    private transient int cached_scope_id;  // 0
+    */
+    // END Android-removed: Remove special handling for link-local addresses
+
+    // BEGIN Android-added: Define special-purpose IPv6 address
     /** @hide */
     public static final InetAddress ANY =
             new Inet6Address("::", new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0);
@@ -185,6 +194,7 @@
     /** @hide */
     public static final InetAddress LOOPBACK = new Inet6Address("ip6-localhost",
             new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 0);
+    // END Android-added: Define special-purpose IPv6 address
 
     private class Inet6AddressHolder {
 
@@ -379,6 +389,13 @@
 
     private static final long serialVersionUID = 6880410070516793377L;
 
+    // BEGIN Android-removed: Android doesn't need to call native init
+    /*
+    // Perform native initialization
+    static { init(); }
+    // END Android-removed: Android doesn't need to call native init
+    */
+
     Inet6Address() {
         super();
         holder.init(null, AF_INET6);
@@ -401,12 +418,16 @@
         } catch (UnknownHostException e) {} /* cant happen if ifname is null */
     }
 
-    Inet6Address (String hostName, byte addr[], NetworkInterface nif) throws UnknownHostException {
+    Inet6Address (String hostName, byte addr[], NetworkInterface nif)
+        throws UnknownHostException
+    {
         holder6 = new Inet6AddressHolder();
         initif (hostName, addr, nif);
     }
 
-    Inet6Address (String hostName, byte addr[], String ifname) throws UnknownHostException {
+    Inet6Address (String hostName, byte addr[], String ifname)
+        throws UnknownHostException
+    {
         holder6 = new Inet6AddressHolder();
         initstr (hostName, addr, ifname);
     }
@@ -495,8 +516,9 @@
         }
     }
 
-    private void initif(String hostName, byte addr[],NetworkInterface nif) throws UnknownHostException {
-        holder().hostName = hostName;
+    private void initif(String hostName, byte addr[], NetworkInterface nif)
+        throws UnknownHostException
+    {
         int family = -1;
         holder6.init(addr, nif);
 
@@ -948,4 +970,12 @@
         }
         return sb.toString();
     }
+
+    // BEGIN Android-removed: Android doesn't need to call native init
+    /*
+     * Perform class load-time initializations.
+     *
+    private static native void init();
+    */
+    // END Android-removed: Android doesn't need to call native init
 }
diff --git a/java/net/URLStreamHandler.java b/java/net/URLStreamHandler.java
index eac8a78..d5380c1 100644
--- a/java/net/URLStreamHandler.java
+++ b/java/net/URLStreamHandler.java
@@ -134,9 +134,9 @@
 
         boolean isRelPath = false;
         boolean queryOnly = false;
-        // BEGIN Android-changed
+        // BEGIN Android-changed: App compat
         boolean querySet = false;
-        // END Android-changed
+        // END Android-changed: App compat
 
 // FIX: should not assume query if opaque
         // Strip off the query part
@@ -148,22 +148,22 @@
                 if (limit > queryStart)
                     limit = queryStart;
                 spec = spec.substring(0, queryStart);
-                // BEGIN Android-changed
+                // BEGIN Android-changed: App compat
                 querySet = true;
-                // END Android-changed
+                // END Android-changed: App compat
             }
         }
 
         int i = 0;
         // Parse the authority part if any
-        // BEGIN Android-changed
+        // BEGIN Android-changed: App compat
         // boolean isUNCName = (start <= limit - 4) &&
         //                 (spec.charAt(start) == '/') &&
         //                 (spec.charAt(start + 1) == '/') &&
         //                 (spec.charAt(start + 2) == '/') &&
         //                 (spec.charAt(start + 3) == '/');
         boolean isUNCName = false;
-        // END Android-changed
+        // END Android-changed: App compat
         if (!isUNCName && (start <= limit - 2) && (spec.charAt(start) == '/') &&
             (spec.charAt(start + 1) == '/')) {
             start += 2;
@@ -226,7 +226,7 @@
                     if (ind >= 0) {
                         // port can be null according to RFC2396
                         if (host.length() > (ind + 1)) {
-                            // BEGIN Android-changed
+                            // BEGIN Android-changed: App compat
                             // port = Integer.parseInt(host.substring(ind + 1));
                             char firstPortChar = host.charAt(ind+1);
                             if (firstPortChar >= '0' && firstPortChar <= '9') {
@@ -235,7 +235,7 @@
                                 throw new IllegalArgumentException("invalid port: " +
                                                                    host.substring(ind + 1));
                             }
-                            // END Android-changed
+                            // END Android-changed: App compat
                         }
                         host = host.substring(0, ind);
                     }
@@ -248,16 +248,16 @@
                                                    port);
             start = i;
 
-            // BEGIN Android-changed
             // If the authority is defined then the path is defined by the
             // spec only; See RFC 2396 Section 5.2.4.
+            // BEGIN Android-changed: App compat
             // if (authority != null && authority.length() > 0)
             //   path = "";
             path = null;
             if (!querySet) {
                 query = null;
             }
-            // END Android-changed
+            // END Android-changed: App compat
         }
 
         if (host == null) {
@@ -282,14 +282,14 @@
                 path = seperator + spec.substring(start, limit);
             }
         }
-        // BEGIN Android-changed
+        // BEGIN Android-changed: App compat
         //else if (queryOnly && path != null) {
         //    int ind = path.lastIndexOf('/');
         //    if (ind < 0)
         //        ind = 0;
         //    path = path.substring(0, ind) + "/";
         //}
-        // END Android-changed
+        // END Android-changed: App compat
         if (path == null)
             path = "";
 
@@ -304,20 +304,22 @@
             // Remove embedded /../ if possible
             i = 0;
             while ((i = path.indexOf("/../", i)) >= 0) {
-                // BEGIN Android-changed
+                // BEGIN Android-changed: App compat
                 /*
                  * Trailing /../
                  */
                 if (i == 0) {
                     path = path.substring(i + 3);
                     i = 0;
-                // END Android-changed
+                // END Android-changed: App compat
                 /*
                  * A "/../" will cancel the previous segment and itself,
                  * unless that segment is a "/../" itself
                  * i.e. "/a/b/../c" becomes "/a/c"
                  * but "/../../a" should stay unchanged
                  */
+                // Android-changed: App compat
+                // if (i > 0 && (limit = path.lastIndexOf('/', i - 1)) >= 0 &&
                 } else if (i > 0 && (limit = path.lastIndexOf('/', i - 1)) >= 0 &&
                     (path.indexOf("/../", limit) != 0)) {
                     path = path.substring(0, limit) + path.substring(i + 3);
@@ -343,7 +345,7 @@
             if (path.endsWith("/."))
                 path = path.substring(0, path.length() -1);
 
-            // Remove trailing ?
+            // Android-changed: App compat: Remove trailing ?
             if (path.endsWith("?"))
                 path = path.substring(0, path.length() -1);
         }
@@ -374,6 +376,7 @@
      * @since 1.3
      */
     protected boolean equals(URL u1, URL u2) {
+        // Android-changed: Avoid network I/O
         return Objects.equals(u1.getRef(), u2.getRef()) &&
                Objects.equals(u1.getQuery(), u2.getQuery()) &&
                // sameFile compares the protocol, file, port & host components of
@@ -390,6 +393,7 @@
      * @since 1.3
      */
     protected int hashCode(URL u) {
+        // Android-changed: Avoid network I/O
         // Hash on the same set of fields that we compare in equals().
         return Objects.hash(
                 u.getRef(),
@@ -503,6 +507,8 @@
         if (u.getRef() != null)
             len += 1 + u.getRef().length();
 
+        // BEGIN Android-changed: Add a toExternalForm variant that optionally escapes illegal chars
+        // TODO: The variant has been removed. We can potentially revert the change
         StringBuilder result = new StringBuilder(len);
         result.append(u.getProtocol());
         result.append(":");
@@ -514,6 +520,7 @@
         if (fileAndQuery != null) {
             result.append(fileAndQuery);
         }
+        // END Android-changed: Add a toExternalForm variant that optionally escapes illegal chars
         if (u.getRef() != null) {
             result.append("#");
             result.append(u.getRef());
diff --git a/package.xml b/package.xml
index b822a6d..5420793 100644
--- a/package.xml
+++ b/package.xml
@@ -118,4 +118,4 @@
 14.5 EXPORT RESTRICTIONS. THE SDK IS SUBJECT TO UNITED STATES EXPORT LAWS AND REGULATIONS. YOU MUST COMPLY WITH ALL DOMESTIC AND INTERNATIONAL EXPORT LAWS AND REGULATIONS THAT APPLY TO THE SDK. THESE LAWS INCLUDE RESTRICTIONS ON DESTINATIONS, END USERS AND END USE.
 
 14.6 The rights granted in the License Agreement may not be assigned or transferred by either you or Google without the prior written approval of the other party. Neither you nor Google shall be permitted to delegate their responsibilities or obligations under the License Agreement without the prior written approval of the other party.
-November 20, 2015</license><localPackage path="sources;android-28" obsolete="false"><type-details xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns6:sourceDetailsType"><api-level>28</api-level><codename></codename></type-details><revision><major>1</major></revision><display-name>Sources for Android PI</display-name><uses-license ref="android-sdk-license"/></localPackage></ns2:repository>
+November 20, 2015</license><localPackage path="sources;android-28" obsolete="false"><type-details xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns6:sourceDetailsType"><api-level>28</api-level><codename></codename></type-details><revision><major>1</major></revision><display-name>Sources for Android P</display-name><uses-license ref="android-sdk-license"/></localPackage></ns2:repository>